Fraction.java

/**
* Copyright 2010,2021 Nikolas S Boyd.
Permission is granted to copy this work provided this copyright statement is retained in all copies.

*/
package Hoot.Magnitudes;

import Hoot.Runtime.Functions.*;
import Hoot.Runtime.Faces.*;
import Hoot.Runtime.Values.*;
import Hoot.Runtime.Blocks.*;
import Smalltalk.Core.*;
import Smalltalk.Blocks.*;
import Smalltalk.Magnitudes.*;
import Hoot.Behaviors.*;
import Hoot.Behaviors.Nil;
import Hoot.Behaviors.Object;
import Hoot.Behaviors.True;
import Hoot.Behaviors.False;
import Hoot.Behaviors.Boolean;
import Hoot.Collections.*;
import Hoot.Collections.String;
import java.lang.Math;
import java.util.List;
import java.math.BigDecimal;
import Hoot.Magnitudes.Number;
import Hoot.Runtime.Names.Primitive;
import static Hoot.Magnitudes.SmallInteger.*;
import Smalltalk.Magnitudes.Ordinal;
import Smalltalk.Magnitudes.Numeric;
import Smalltalk.Magnitudes.Fractional;

public class Fraction extends Rational implements Fractional
{

  public static Metaclass type() { return (Metaclass)Metaclass.$class; }
  @Override public Metaclass $class() { return (Metaclass)Metaclass.$class; }
  public static class Metaclass extends Rational.Metaclass implements Fractional.Metatype
  {
    static final Fraction.Metaclass $class = new Fraction.Metaclass();
    public Metaclass() {
      this(Fraction.Metaclass.class);
    }

    public Metaclass(java.lang.Class aClass) {
      super(aClass);
    }

    @Override public java.lang.Class outerClass() { return Fraction.class; }


    /**
     * @return 
     */
    @Override public   Rational coerce(final Number aNumber)
    {
      java.lang.String exitID = "FractionMetatype>>coerce";
      Frame f0 = new Frame(exitID);
      return f0.evaluate(() -> {
      return (Rational)f0.exit(exitID, aNumber.asFraction());
      });
    }

    /**
     * @return 
     */
    public   Fraction numerator_denominator(final LongInteger upper, final LongInteger lower)
    {
      java.lang.String exitID = "FractionMetatype>>numerator_denominator";
      Frame f0 = new Frame(exitID);
      return f0.evaluate(() -> {
      return (Fraction)f0.exit(exitID, new Fraction(((Integer)upper), ((Integer)lower)));
      });
    }

    /**
     * @return 
     */
    public   Fraction numerator_denominator(final Ordinal upper, final Ordinal lower)
    {
      java.lang.String exitID = "FractionMetatype>>numerator_denominator";
      Frame f0 = new Frame(exitID);
      return f0.evaluate(() -> {
      return (Fraction)f0.exit(exitID, this.rationalized_over(((Integer)upper), ((Integer)lower)));
      });
    }

    /**
     * @return 
     */
    public   Fraction rationalized_over(final Number upper, final Number lower)
    {
      java.lang.String exitID = "FractionMetatype>>rationalized_over";
      Frame f0 = new Frame(exitID);
      SmallInteger num = upper.truncated().asSmallInteger();

      SmallInteger den = lower.truncated().asSmallInteger();
      return this.rationalized(Primitive.rationalize(num.intValue(), den.intValue()));
    }

    /**
     * @return 
     */
    public   Fraction rationalized(final java.lang.Integer ... parts)
    {
      java.lang.String exitID = "FractionMetatype>>rationalized";
      Frame f0 = new Frame(exitID);
      List<java.lang.Integer> ps = Utils.wrap(parts);
      return new Fraction(ps.get(0), ps.get(1));
    }
  }


  protected static SmallInteger Generality = SmallInteger.from(40);
  protected Integer numerator = SmallInteger.from(0);
  protected Integer denominator = SmallInteger.from(1);

  /**
   * 
   */
  public    Fraction()
  {
    java.lang.String exitID = "Fraction>>Fraction";
    Frame f0 = new Frame(exitID);
  }

  /**
   * 
   */
  public    Fraction(final Integer upper)
  {
    java.lang.String exitID = "Fraction>>Fraction";
    Frame f0 = new Frame(exitID);
     numerator = upper;
  }

  /**
   * 
   */
  public    Fraction(final int upper, final int lower)
  {
    this(SmallInteger.from(upper), SmallInteger.from(lower));
    java.lang.String exitID = "Fraction>>Fraction";
    Frame f0 = new Frame(exitID);
  }

  /**
   * 
   */
  public    Fraction(final Integer upper, final Integer lower)
  {
    java.lang.String exitID = "Fraction>>Fraction";
    Frame f0 = new Frame(exitID);
     numerator = upper;

     denominator = lower;
  }

  /**
   * @return 
   */
  @Override public   Integer numerator()
  {
    java.lang.String exitID = "Fraction>>numerator";
    Frame f0 = new Frame(exitID);
    return (Integer)numerator;
  }

  /**
   * @return 
   */
  @Override public   Integer denominator()
  {
    java.lang.String exitID = "Fraction>>denominator";
    Frame f0 = new Frame(exitID);
    return (Integer)denominator;
  }

  /**
   * @return 
   */
  public   Rational minus(final Rational aNumber)
  {
    java.lang.String exitID = "Fraction>>minus";
    Frame f0 = new Frame(exitID);
    return f0.evaluate(() -> {
    (denominator.equals(aNumber.denominator())).ifTrue(Closure.with(f2 -> {
      return (Rational)f0.exit(exitID, this.$class().numerator_denominator((numerator.minus(aNumber.numerator())), denominator));
    }, ""));
    return (Rational)f0.exit(exitID, this.$class().numerator_denominator(this.crossDiff(aNumber), this.underProduct(aNumber)));
    });
  }

  /**
   * @return 
   */
  public   Rational plus(final Rational aNumber)
  {
    java.lang.String exitID = "Fraction>>plus";
    Frame f0 = new Frame(exitID);
    return f0.evaluate(() -> {
    (denominator.equals(aNumber.denominator())).ifTrue(Closure.with(f2 -> {
      return (Rational)f0.exit(exitID, this.$class().numerator_denominator((numerator.plus(aNumber.numerator())), denominator));
    }, ""));
    return (Rational)f0.exit(exitID, this.$class().numerator_denominator(this.crossSum(aNumber), this.underProduct(aNumber)));
    });
  }

  /**
   * @return 
   */
  public   Rational times(final Rational aNumber)
  {
    java.lang.String exitID = "Fraction>>times";
    Frame f0 = new Frame(exitID);
    return (Rational)this.$class().numerator_denominator(this.overProduct(aNumber), this.underProduct(aNumber));
  }

  /**
   * @return 
   */
  public   Rational divideWith(final Rational aNumber)
  {
    java.lang.String exitID = "Fraction>>divideWith";
    Frame f0 = new Frame(exitID);
    return (Rational)this.$class().numerator_denominator(this.crossProduct(aNumber), aNumber.crossProduct(this));
  }

  /**
   * @return 
   */
  @Override public   Number plus(final Numeric aNumber)
  {
    java.lang.String exitID = "Fraction>>plus";
    Frame f0 = new Frame(exitID);
    return (Number)(this.plus(((Rational)aNumber.asRational())));
  }

  /**
   * @return 
   */
  @Override public   Number times(final Numeric aNumber)
  {
    java.lang.String exitID = "Fraction>>times";
    Frame f0 = new Frame(exitID);
    return (Number)(this.times(((Rational)aNumber.asRational())));
  }

  /**
   * @return 
   */
  @Override public   Number divideWith(final Numeric aNumber)
  {
    java.lang.String exitID = "Fraction>>divideWith";
    Frame f0 = new Frame(exitID);
    return (Number)(this.divideWith(((Rational)aNumber.asRational())));
  }

  /**
   * @return 
   */
  @Override public   Fraction asFraction()
  {
    java.lang.String exitID = "Fraction>>asFraction";
    Frame f0 = new Frame(exitID);
    return (Fraction)this;
  }

  /**
   * @return 
   */
  @Override public   Float asFloat()
  {
    java.lang.String exitID = "Fraction>>asFloat";
    Frame f0 = new Frame(exitID);
    return (Float)Float.from(this.primitiveFloat());
  }

  /**
   * @return 
   */
  @Override public   Double asFloatD()
  {
    java.lang.String exitID = "Fraction>>asFloatD";
    Frame f0 = new Frame(exitID);
    return (Double)Double.from(this.primitiveDouble());
  }

  /**
   * @return 
   */
  @Override public   Integer generality()
  {
    java.lang.String exitID = "Fraction>>generality";
    Frame f0 = new Frame(exitID);
    return (Integer)Generality;
  }

  /**
   * @return 
   */
  @Override public   java.lang.Number elementaryNumber()
  {
    java.lang.String exitID = "Fraction>>elementaryNumber";
    Frame f0 = new Frame(exitID);
    return (java.lang.Number)this.primitiveDouble();
  }

  /**
   * @return 
   */
  @Override public   long primitiveLong()
  {
    java.lang.String exitID = "Fraction>>primitiveLong";
    Frame f0 = new Frame(exitID);
    java.lang.Double value = this.primitiveDouble();
    if ((value < 0)) {
      return ((long)Math.ceil(value));
    };
    return (long)((long)Math.floor(value));
  }

  /**
   * @return 
   */
  @Override public   java.lang.Float primitiveFloat()
  {
    java.lang.String exitID = "Fraction>>primitiveFloat";
    Frame f0 = new Frame(exitID);
    return (java.lang.Float)this.primitiveDouble().floatValue();
  }

  /**
   * @return 
   */
  @Override public   java.lang.Double primitiveDouble()
  {
    java.lang.String exitID = "Fraction>>primitiveDouble";
    Frame f0 = new Frame(exitID);
    java.lang.Double n = numerator.primitiveDouble();

    java.lang.Double d = denominator.primitiveDouble();
    return (java.lang.Double)(n / d);
  }

  /**
   * @return 
   */
  @Override public   Fixed asFixedPoint(final Ordinal scale)
  {
    java.lang.String exitID = "Fraction>>asFixedPoint";
    Frame f0 = new Frame(exitID);
    return (Fixed)this.scaledAt(((Integer)scale));
  }

  /**
   * @return 
   */
  @Override public   Fixed asScaledDecimal(final Ordinal scale)
  {
    java.lang.String exitID = "Fraction>>asScaledDecimal";
    Frame f0 = new Frame(exitID);
    return (Fixed)this.scaledAt(((Integer)scale));
  }

  /**
   * @return 
   */
  public   Fixed scaledZero()
  {
    java.lang.String exitID = "Fraction>>scaledZero";
    Frame f0 = new Frame(exitID);
    return (Fixed)new Fixed(numerator, denominator);
  }

  /**
   * @return 
   */
  public   Fixed scaledAt(final Integer scale)
  {
    java.lang.String exitID = "Fraction>>scaledAt";
    Frame f0 = new Frame(exitID);
    return (Fixed)new Fixed(numerator, denominator, scale);
  }

  /**
   * @return 
   */
  @Override public   BigDecimal asDecimal()
  {
    java.lang.String exitID = "Fraction>>asDecimal";
    Frame f0 = new Frame(exitID);
    int n = numerator.printString().length();

    int d = denominator.printString().length();

    int r = 4;
    return (BigDecimal)numerator.asDecimal().divide(denominator.asDecimal(), Math.min(n, d), r);
  }

  /**
   * @return 
   */
  @Override public   Fraction negated()
  {
    java.lang.String exitID = "Fraction>>negated";
    Frame f0 = new Frame(exitID);
    return (Fraction)this.$class().numerator_denominator(numerator.negated(), denominator);
  }

  /**
   * @return 
   */
  @Override public   Fraction reciprocal()
  {
    java.lang.String exitID = "Fraction>>reciprocal";
    Frame f0 = new Frame(exitID);
    return (Fraction)this.$class().numerator_denominator(denominator, numerator);
  }

  /**
   * @return 
   */
  @Override public   Integer rounded()
  {
    java.lang.String exitID = "Fraction>>rounded";
    Frame f0 = new Frame(exitID);
    return (Integer)(this.plus((this.sign().divideWith(Duality)))).truncated();
  }

  /**
   * @return 
   */
  @Override public   java.lang.String toString()
  {
    java.lang.String exitID = "Fraction>>toString";
    Frame f0 = new Frame(exitID);
    return (java.lang.String)this.printTerm().primitiveString();
  }

  /**
   * @return 
   */
  @Override public   String printString()
  {
    java.lang.String exitID = "Fraction>>printString";
    Frame f0 = new Frame(exitID);
    return (String)String.from(numerator.printString() + " / " + denominator.printString());
  }
}