unikl.disco.misc.NumberObj.java Source code

Java tutorial

Introduction

Here is the source code for unikl.disco.misc.NumberObj.java

Source

/*
 *  (c) 2017 Michael A. Beck, Sebastian Henningsen
 *        disco | Distributed Computer Systems Lab
 *        University of Kaiserslautern, Germany
 *  All Rights Reserved.
 *
 * This software is work in progress and is released in the hope that it will
 * be useful to the scientific community. It is provided "as is" without
 * express or implied warranty, including but not limited to the correctness
 * of the code or its suitability for any particular purpose.
 *
 * This software is provided under the MIT License, however, we would 
 * appreciate it if you contacted the respective authors prior to commercial use.
 *
 * If you find our software useful, we would appreciate if you mentioned it
 * in any publication arising from the use of this software or acknowledge
 * our work otherwise. We would also like to hear of any fixes or useful
 */
package unikl.disco.misc;

import org.apache.commons.math3.fraction.Fraction;
import java.lang.Number;

//TODO What to return in the default case?
//TODO What to return if the type check fails?
//TODO Throw proper Exceptions such as FractionConversionException
public class NumberObj {
    //FIXME Implement a global flag in Analysis.java (?) and read that out
    //   private static NumberObjType type = NumberObjType.DOUBLE;
    private static NumberObjType type = NumberObjType.RATIONAL;

    private static boolean performTypeChecks = false;

    private Number value;

    public static final NumberObj POSITIVE_INFINITY = getPosInfinity();
    public static final NumberObj NEGATIVE_INFINITY = getNegInfinity();
    public static final NumberObj NaN = getNaN();

    public NumberObj() {
        switch (type) {
        case DOUBLE:
            this.value = new Double(0.0);
            break;
        case RATIONAL:
            this.value = new Fraction(0.0);
            break;
        default:
        }
    }

    public NumberObj(double value) {
        switch (type) {
        case DOUBLE:
            this.value = new Double(value);
            break;
        case RATIONAL:
            this.value = new Fraction(value);
            break;
        default:
            System.exit(0);
        }
    }

    private NumberObj(Number value) {
        this.value = value;
    }

    public static NumberObj DoubleToNumberObj(Double doubleObj) {
        return new NumberObj(doubleObj);
    }

    private static NumberObj getNaN() {
        switch (type) {
        case DOUBLE:
            return new NumberObj(Double.NaN);
        case RATIONAL:
            return new NumberObj(new Fraction(Double.NaN));
        default:
            return null;
        }
    }

    private static NumberObj getPosInfinity() {
        switch (type) {
        case DOUBLE:
            return new NumberObj(Double.POSITIVE_INFINITY);
        case RATIONAL:
            // Fraction is based in Integer and thus there's no infinity (and it is prone to overflows)
            return new NumberObj(new Fraction(Integer.MAX_VALUE));
        default:
            return null;
        }
    }

    private static NumberObj getNegInfinity() {
        switch (type) {
        case DOUBLE:
            return new NumberObj(Double.NEGATIVE_INFINITY);
        case RATIONAL:
            // Fraction is based in Integer and thus there's no infinity (and it is prone to overflows)
            return new NumberObj(new Fraction(Integer.MIN_VALUE));
        default:
            return null;
        }
    }

    public static NumberObj getEpsilon() {
        switch (type) {
        case DOUBLE:
            return new NumberObj(new Double(1e-6));
        case RATIONAL:
            // unfortunately you cannot give the constructor the double value 0.0000001
            return new NumberObj(new Fraction(1, 1000000));
        default:
            return null;
        }
    }

    public NumberObjType getType() {
        return type;
    }

    private static boolean TypeCheck(NumberObj num1, NumberObj num2) {
        if (num1.getType() != num2.getType()) {
            try {
                throw new Exception();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    public Number getValue() {
        return value;
    }

    // In order to simplify the transition from the primitive data type double to
    // a Double object wrapped around it or a rational number object
    // these functions emulate copy by value for objects that
    // typically inhibit copy by reference in Java
    public static NumberObj add(NumberObj num1, NumberObj num2) {
        if (performTypeChecks) {
            TypeCheck(num1, num2);
        }

        if (num1.equals(NaN) || num2.equals(NaN)) {
            return NaN;
        }
        // prevent overflow exception when adding integer based number representations like Fraction
        if (num1.equals(POSITIVE_INFINITY) || num2.equals(POSITIVE_INFINITY)) {
            return POSITIVE_INFINITY;
        }
        if (num1.equals(NEGATIVE_INFINITY) || num2.equals(NEGATIVE_INFINITY)) {
            return NEGATIVE_INFINITY;
        }

        switch (num1.getType()) {
        case DOUBLE:
            Double double1 = (Double) num1.getValue();
            Double double2 = (Double) num2.getValue();
            return new NumberObj(double1.doubleValue() + double2.doubleValue());
        case RATIONAL:
            Fraction frac1 = (Fraction) num1.getValue();
            Fraction frac2 = (Fraction) num2.getValue();
            // may throw MathArithmeticException due to integer overflow
            return new NumberObj(frac1.add(frac2));
        default:
            return null;
        }
    }

    public static NumberObj sub(NumberObj num1, NumberObj num2) {
        if (performTypeChecks) {
            TypeCheck(num1, num2);
        }

        if (num1.equals(NaN) || num2.equals(NaN)) {
            return NaN;
        }
        // prevent overflow exception when adding integer based number representations like Fraction
        if (num1.equals(POSITIVE_INFINITY) || num2.equals(POSITIVE_INFINITY)) {
            return POSITIVE_INFINITY;
        }
        if (num1.equals(NEGATIVE_INFINITY) || num2.equals(NEGATIVE_INFINITY)) {
            return NEGATIVE_INFINITY;
        }

        switch (num1.getType()) {
        case DOUBLE:
            Double double1 = (Double) num1.getValue();
            Double double2 = (Double) num2.getValue();
            return new NumberObj(double1.doubleValue() - double2.doubleValue());
        case RATIONAL:
            Fraction frac1 = (Fraction) num1.getValue();
            Fraction frac2 = (Fraction) num2.getValue();
            // may throw MathArithmeticException due to integer overflow
            return new NumberObj(frac1.subtract(frac2));
        default:
            return null;
        }
    }

    public static NumberObj mult(NumberObj num1, NumberObj num2) {
        if (performTypeChecks) {
            TypeCheck(num1, num2);
        }

        if (num1.equals(NaN) || num2.equals(NaN)) {
            return NaN;
        }
        // prevent overflow exception when adding integer based number representations like Fraction
        if (num1.equals(POSITIVE_INFINITY) || num2.equals(POSITIVE_INFINITY)) {
            return POSITIVE_INFINITY;
        }
        if (num1.equals(NEGATIVE_INFINITY) || num2.equals(NEGATIVE_INFINITY)) {
            return NEGATIVE_INFINITY;
        }

        switch (num1.getType()) {
        case DOUBLE:
            Double double1 = (Double) num1.getValue();
            Double double2 = (Double) num2.getValue();
            return new NumberObj(double1.doubleValue() * double2.doubleValue());
        case RATIONAL:
            Fraction frac1 = (Fraction) num1.getValue();
            Fraction frac2 = (Fraction) num2.getValue();
            // may throw MathArithmeticException due to integer overflow
            return new NumberObj(frac1.multiply(frac2));
        default:
            return null;
        }
    }

    public static NumberObj div(NumberObj num1, NumberObj num2) {
        if (performTypeChecks) {
            TypeCheck(num1, num2);
        }

        if (num1.equals(NaN) || num2.equals(NaN)) {
            return NaN;
        }
        // Integer based number representations use Integer.MAX_VALUE to signal infinity so special treatment is necessary when dividing
        if (num1.equals(POSITIVE_INFINITY)) {
            return POSITIVE_INFINITY;
        }
        if (num2.equals(POSITIVE_INFINITY)) {
            return new NumberObj(0.0);
        }
        if (num1.equals(NEGATIVE_INFINITY)) {
            return NEGATIVE_INFINITY;
        }
        if (num2.equals(NEGATIVE_INFINITY)) {
            return new NumberObj(0.0);
        }

        switch (num1.getType()) {
        case DOUBLE:
            Double double1 = (Double) num1.getValue();
            Double double2 = (Double) num2.getValue();
            return new NumberObj(double1.doubleValue() / double2.doubleValue());
        case RATIONAL:
            Fraction frac2 = (Fraction) num2.getValue();
            if (frac2.getNumerator() == 0) {
                return getPosInfinity();
                //              return getNaN();
            } else {
                Fraction frac1 = (Fraction) num1.getValue();
                return new NumberObj(frac1.divide(frac2));
            }
        default:
            return null;
        }
    }

    public NumberObj copy() {
        switch (type) {
        case DOUBLE:
            return new NumberObj(value);
        case RATIONAL:
            return new NumberObj(value);
        default:
            return new NumberObj(0.0);
        }
    }

    /*
     * java.lang.Math's max(double, double) description:
     * 
     * Returns the greater of two double values. That is, the result is the argument closer to positive infinity.
     * If the arguments have the same value, the result is that same value. If either value is NaN, then the result is NaN.
     * Unlike the numerical comparison operators, this method considers negative zero to be strictly smaller than positive zero.
     * If one argument is positive zero and the other negative zero, the result is positive zero.
     * 
     * SB's thoughts because there's no max() in Fraction:
     * "result is that same value" implicitly uses copy by value semantics
     * Can a fraction be NaN?
     * The -/+ zero stuff is not needed
     */
    public static NumberObj max(NumberObj num1, NumberObj num2) {
        if (performTypeChecks) {
            TypeCheck(num1, num2);
        }

        switch (num1.getType()) {
        case DOUBLE:
            Double double1 = (Double) num1.getValue();
            Double double2 = (Double) num2.getValue();
            return new NumberObj(Math.max(double1.doubleValue(), double2.doubleValue()));
        case RATIONAL:
            Fraction frac1 = (Fraction) num1.getValue();
            Fraction frac2 = (Fraction) num2.getValue();

            // operate on Doubles to prevent getting out of Integer's range           
            double frac1_numerator = new Double(frac1.getNumerator()) * new Double(frac2.getDenominator());
            double frac2_numerator = new Double(frac2.getNumerator()) * new Double(frac1.getDenominator());

            if (frac1_numerator >= frac2_numerator) {
                return new NumberObj(frac1);
            } else {
                return new NumberObj(frac2);
            }
        default:
            return null;
        }
    }

    public static NumberObj min(NumberObj num1, NumberObj num2) {
        if (performTypeChecks) {
            TypeCheck(num1, num2);
        }

        switch (num1.getType()) {
        case DOUBLE:
            Double double1 = (Double) num1.getValue();
            Double double2 = (Double) num2.getValue();
            return new NumberObj(Math.min(double1.doubleValue(), double2.doubleValue()));
        case RATIONAL:
            Fraction frac1 = (Fraction) num1.getValue();
            Fraction frac2 = (Fraction) num2.getValue();

            // operate on Doubles to prevent getting out of Integer's range 
            double frac1_numerator = new Double(frac1.getNumerator()) * new Double(frac2.getDenominator());
            double frac2_numerator = new Double(frac2.getNumerator()) * new Double(frac1.getDenominator());

            if (frac1_numerator < frac2_numerator) {
                return new NumberObj(frac1);
            } else {
                return new NumberObj(frac2);
            }
        default:
            return null;
        }
    }

    public boolean equals(double num2) {
        if (num2 == Double.NaN) {
            return value.equals(NaN);
        }
        if (num2 == Double.POSITIVE_INFINITY) {
            return value.equals(POSITIVE_INFINITY);
        }
        if (num2 == Double.NEGATIVE_INFINITY) {
            return value.equals(NEGATIVE_INFINITY);
        }
        return equals(new NumberObj(num2));
    }

    public boolean equals(NumberObj num2) {
        if (performTypeChecks) {
            TypeCheck(this, num2);
        }

        if (this == NaN & num2 == NaN) {
            return true;
        }
        if (this == POSITIVE_INFINITY & num2 == POSITIVE_INFINITY) {
            return true;
        }
        if (this == NEGATIVE_INFINITY & num2 == NEGATIVE_INFINITY) {
            return true;
        }

        switch (type) {
        case DOUBLE:
            Double double2 = (Double) num2.getValue();
            return value.doubleValue() == double2.doubleValue();
        case RATIONAL:
            //Fractions's equals() method is inherited from java.lang.Object
            Fraction frac1 = (Fraction) value;
            Fraction frac2 = (Fraction) num2.getValue();

            // operate on Doubles to prevent getting out of Integer's range 
            double frac1_num = new Double(frac1.getNumerator()) * new Double(frac2.getDenominator());
            double frac2_num = new Double(frac2.getNumerator()) * new Double(frac1.getDenominator());

            if (frac1_num == frac2_num) {
                return true;
            } else {
                return false;
            }
        default:
            return false;
        }
    }

    public boolean greater(NumberObj num2) {
        if (performTypeChecks) {
            TypeCheck(this, num2);
        }

        if (this == NaN || num2 == NaN) {
            return false;
        }
        if (this == POSITIVE_INFINITY & num2 == POSITIVE_INFINITY) {
            return false;
        }
        if (this == NEGATIVE_INFINITY & num2 == NEGATIVE_INFINITY) {
            return false;
        }

        switch (type) {
        case DOUBLE:
            Double double2 = (Double) num2.getValue();
            return value.doubleValue() > double2.doubleValue();
        case RATIONAL:
            Fraction frac1 = (Fraction) value;
            Fraction frac2 = (Fraction) num2.getValue();

            // operate on Doubles to prevent getting out of Integer's range 
            double frac1_num = new Double(frac1.getNumerator()) * new Double(frac2.getDenominator());
            double frac2_num = new Double(frac2.getNumerator()) * new Double(frac1.getDenominator());

            if (frac1_num > frac2_num) {
                return true;
            } else {
                return false;
            }
        default:
            return false;
        }
    }

    public boolean ge(NumberObj num2) {
        if (performTypeChecks) {
            TypeCheck(this, num2);
        }

        if (this == NaN || num2 == NaN) {
            return false;
        }
        if (this == POSITIVE_INFINITY & num2 == POSITIVE_INFINITY) {
            return true;
        }
        if (this == NEGATIVE_INFINITY & num2 == NEGATIVE_INFINITY) {
            return true;
        }

        switch (type) {
        case DOUBLE:
            Double double2 = (Double) num2.getValue();
            return value.doubleValue() >= double2.doubleValue();
        case RATIONAL:
            Fraction frac1 = (Fraction) value;
            Fraction frac2 = (Fraction) num2.getValue();

            // operate on Doubles to prevent getting out of Integer's range 
            double frac1_num = new Double(frac1.getNumerator()) * new Double(frac2.getDenominator());
            double frac2_num = new Double(frac2.getNumerator()) * new Double(frac1.getDenominator());

            if (frac1_num >= frac2_num) {
                return true;
            } else {
                return false;
            }
        default:
            return false;
        }
    }

    public boolean less(NumberObj num2) {
        if (performTypeChecks) {
            TypeCheck(this, num2);
        }

        if (this == NaN || num2 == NaN) {
            return false;
        }
        if (this == POSITIVE_INFINITY & num2 == POSITIVE_INFINITY) {
            return false;
        }
        if (this == NEGATIVE_INFINITY & num2 == NEGATIVE_INFINITY) {
            return false;
        }

        switch (type) {
        case DOUBLE:
            Double double2 = (Double) num2.getValue();
            return value.doubleValue() < double2.doubleValue();
        case RATIONAL:
            Fraction frac1 = (Fraction) value;
            Fraction frac2 = (Fraction) num2.getValue();

            // operate on Doubles to prevent getting out of Integer's range 
            double frac1_num = new Double(frac1.getNumerator()) * new Double(frac2.getDenominator());
            double frac2_num = new Double(frac2.getNumerator()) * new Double(frac1.getDenominator());

            if (frac1_num < frac2_num) {
                return true;
            } else {
                return false;
            }
        default:
            return false;
        }
    }

    public boolean le(NumberObj num2) {
        if (performTypeChecks) {
            TypeCheck(this, num2);
        }

        if (this == NaN || num2 == NaN) {
            return false;
        }
        if (this == POSITIVE_INFINITY & num2 == POSITIVE_INFINITY) {
            return true;
        }
        if (this == NEGATIVE_INFINITY & num2 == NEGATIVE_INFINITY) {
            return true;
        }

        switch (type) {
        case DOUBLE:
            Double double2 = (Double) num2.getValue();
            return value.doubleValue() <= double2.doubleValue();
        case RATIONAL:
            Fraction frac1 = (Fraction) value;
            Fraction frac2 = (Fraction) num2.getValue();

            // operate on Doubles to prevent getting out of Integer's range 
            double frac1_num = new Double(frac1.getNumerator()) * new Double(frac2.getDenominator());
            double frac2_num = new Double(frac2.getNumerator()) * new Double(frac1.getDenominator());

            if (frac1_num <= frac2_num) {
                return true;
            } else {
                return false;
            }
        default:
            return false;
        }
    }

    public static NumberObj abs(NumberObj num) {
        if (num.equals(NaN)) {
            return NaN;
        }
        if (num.equals(POSITIVE_INFINITY)) {
            return POSITIVE_INFINITY;
        }
        if (num.equals(NEGATIVE_INFINITY)) {
            return NEGATIVE_INFINITY;
        }

        switch (num.getType()) {
        case DOUBLE:
            Double double_value = (Double) num.getValue();
            return new NumberObj(Math.abs(double_value.doubleValue()));
        case RATIONAL:
            Fraction frac = (Fraction) num.getValue();
            return new NumberObj(frac.abs());
        default:
            return new NumberObj(0.0);
        }
    }

    public static NumberObj negate(NumberObj num) {
        if (num.equals(NaN)) {
            return NaN;
        }
        if (num.equals(POSITIVE_INFINITY)) {
            return NEGATIVE_INFINITY;
        }
        if (num.equals(NEGATIVE_INFINITY)) {
            return POSITIVE_INFINITY;
        }

        switch (num.getType()) {
        case DOUBLE:
            Double double_value = (Double) num.getValue();
            return new NumberObj(-(double_value.doubleValue()));
        case RATIONAL:
            Fraction frac = (Fraction) num.getValue();
            return new NumberObj(frac.negate());
        default:
            return new NumberObj(0.0);
        }
    }

    @Override
    public String toString() {
        if (this.equals(NaN)) {
            return "NaN";
        }
        if (this.equals(POSITIVE_INFINITY)) {
            return "Infinity";
        }
        if (this.equals(NEGATIVE_INFINITY)) {
            return "-Infinity";
        }

        switch (this.getType()) {
        case DOUBLE:
            return Double.toString((Double) value);
        case RATIONAL:
            return ((Fraction) value).toString();
        default:
            return "invalied number";
        }
    }
}