/* * RapidMiner * * Copyright (C) 2001-2011 by Rapid-I and the contributors * * Complete list of developers available at our web site: * * http://rapid-i.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.operator.ports.metadata; import java.io.Serializable; import com.rapidminer.tools.Tools; /** A number which is not known exactly, but maybe only in terms of upper or lower bounds. * E.g. after applying an attribute filter, the number of examples is at most the number it * was before, but the exact value is unknown. * * @author Simon Fischer * */ public abstract class MDNumber<T extends Number> implements Serializable, Comparable<MDNumber<T>> { private static final long serialVersionUID = 1L; public enum Relation { AT_LEAST, EQUAL, AT_MOST, UNKNOWN } private Relation relation = Relation.UNKNOWN; private T number; public MDNumber() { relation = Relation.UNKNOWN; setNumber(null); } protected MDNumber(MDNumber<T> other) { this.setNumber(other.getNumber()); this.relation = other.relation; } public MDNumber(T number) { this.setNumber(number); this.relation = Relation.EQUAL; } public MetaDataInfo equals(T value) { if ((relation == Relation.EQUAL) && this.getNumber().equals(value)) { return MetaDataInfo.YES; } else { return MetaDataInfo.UNKNOWN; } } public MetaDataInfo isAtMost(T value) { switch (relation) { case AT_LEAST: return MetaDataInfo.UNKNOWN; case EQUAL: if (this.getNumber().doubleValue() <= value.doubleValue()) { return MetaDataInfo.YES; } else { return MetaDataInfo.NO; } case AT_MOST: if (this.getNumber().doubleValue() <= value.doubleValue()) { return MetaDataInfo.YES; } else { return MetaDataInfo.UNKNOWN; } case UNKNOWN: default: return MetaDataInfo.UNKNOWN; } } public MetaDataInfo isAtLeast(T value) { switch (relation) { case AT_MOST: return MetaDataInfo.UNKNOWN; case EQUAL: if (this.getNumber().doubleValue() >= value.doubleValue()) { return MetaDataInfo.YES; } else { return MetaDataInfo.NO; } case AT_LEAST: if (this.getNumber().doubleValue() >= value.doubleValue()) { return MetaDataInfo.YES; } else { return MetaDataInfo.UNKNOWN; } case UNKNOWN: default: return MetaDataInfo.UNKNOWN; } } public void increaseByUnknownAmount() { switch (relation) { case AT_MOST: relation = Relation.UNKNOWN; break; case EQUAL: relation = Relation.AT_LEAST; break; case AT_LEAST: case UNKNOWN: // stays like it is break; } } public void reduceByUnknownAmount() { switch (relation) { case AT_LEAST: relation = Relation.UNKNOWN; break; case EQUAL: relation = Relation.AT_MOST; break; case AT_MOST: case UNKNOWN: // stays like it is break; } } public boolean isKnown() { return relation == Relation.EQUAL; } public void setUnkown() { this.relation = Relation.UNKNOWN; } /** Returns the value. Call this method only if {@link #isKnown()} returns true. Otherwise, * a runtime exception will be thrown. */ public T getValue() { if (isKnown()) { return getNumber(); } else { throw new IllegalStateException("Value is currently not exactly known."); } } public Relation getRelation() { return relation; } @Override public String toString() { switch (relation) { case EQUAL: return "=" + Tools.formatNumber(getNumber().doubleValue(), 3); case AT_LEAST: return "\u2265" + Tools.formatNumber(getNumber().doubleValue(), 3); case AT_MOST: return "\u2264" + Tools.formatNumber(getNumber().doubleValue(), 3); case UNKNOWN: default: return "?"; } } public abstract MDNumber<T> copy(); protected void setNumber(T number) { this.number = number; } /** * This returns the estimated number regardless of relation. Please take care to handle the relation * correctly. */ public T getNumber() { return number; } public abstract MDNumber<T> add(T add); public abstract MDNumber<T> multiply(double factor); public int compareTo(MDNumber<T> other) { return (int) Math.signum(this.number.doubleValue() - other.number.doubleValue()); } }