Browse Source

Refactoring (Fraction): Added whole value to avoid precision problems for larger fractions.

Matthias Uiberacker 7 years ago
parent
commit
828fa17443

+ 381 - 338
src/Common/DataObjects/Fraction.ts

@@ -5,379 +5,422 @@
  * A class representing mathematical fractions, which have a numerator and a denominator.
  */
 export class Fraction {
-    private static maximumAllowedNumber: number = 46340;
-    private numerator: number = 0;
-    private denominator: number = 1;
-    private realValue: number;
-
-    /**
-     * Returns the maximum of two fractions (does not clone)
-     * @param f1
-     * @param f2
-     * @returns {Fraction}
-     */
-    public static max(f1: Fraction, f2: Fraction): Fraction {
-        if (f1.RealValue > f2.RealValue) {
-            return f1;
-        } else {
-            return f2;
-        }
+  private static maximumAllowedNumber: number = 46340; // sqrt(int.Max) --> signed int with 4 bytes (2^31)
+  private numerator: number = 0;
+  private denominator: number = 1;
+  private wholeValue: number = 0;
+  private realValue: number;
+
+  /**
+   * Returns the maximum of two fractions (does not clone)
+   * @param f1
+   * @param f2
+   * @returns {Fraction}
+   */
+  public static max(f1: Fraction, f2: Fraction): Fraction {
+    if (f1.RealValue > f2.RealValue) {
+      return f1;
+    } else {
+      return f2;
     }
-
-    public static Equal(f1: Fraction, f2: Fraction): boolean {
-        return f1.Denominator === f2.Denominator && f1.Numerator === f2.Numerator;
+  }
+
+  public static Equal(f1: Fraction, f2: Fraction): boolean {
+    return f1.wholeValue === f2.wholeValue && f1.Denominator === f2.Denominator && f1.Numerator === f2.Numerator;
+  }
+
+  /**
+   * The same as Fraction.clone
+   * @param fraction
+   * @returns {Fraction}
+   */
+  public static createFromFraction(fraction: Fraction): Fraction {
+    return new Fraction(fraction.numerator, fraction.denominator, fraction.wholeValue, false);
+  }
+
+  public static plus(f1: Fraction, f2: Fraction): Fraction {
+    let sum: Fraction = f1.clone();
+    sum.Add(f2);
+    return sum;
+  }
+
+  public static minus(f1: Fraction, f2: Fraction): Fraction {
+    let sum: Fraction = f1.clone();
+    sum.Sub(f2);
+    return sum;
+  }
+
+  private static greatestCommonDenominator(a: number, b: number): number {
+    if (a === 0) {
+      return b;
     }
 
-    /**
-     * The same as Fraction.clone
-     * @param fraction
-     * @returns {Fraction}
-     */
-    public static createFromFraction(fraction: Fraction): Fraction {
-        return new Fraction(fraction.numerator, fraction.denominator);
+    if (b === 1) {
+      return 1;
     }
 
-    public static plus (f1: Fraction, f2: Fraction): Fraction {
-        let sum: Fraction = f1.clone();
-        sum.Add(f2);
-        return sum;
+    while (b !== 0) {
+      if (a > b) {
+        a -= b;
+      } else {
+        b -= a;
+      }
     }
 
-    public static minus(f1: Fraction , f2: Fraction): Fraction {
-        let sum: Fraction = f1.clone();
-        sum.Sub(f2);
-        return sum;
+    return a;
+  }
+
+  /**
+   *
+   * @param numerator
+   * @param denominator
+   * @param wholeValue - the integer number, needed for values greater than 1
+   * @param simplify - If simplify is true, then the fraction is simplified
+   *      to make both the numerator and denominator coprime, and less than maximumAllowedNumber.
+   */
+  constructor(numerator: number = 0, denominator: number = 1, wholeValue: number = 0, simplify: boolean = true) {
+    this.numerator = numerator;
+    this.denominator = denominator;
+    this.wholeValue = wholeValue;
+
+    if (simplify) {
+      this.simplify();
     }
+    this.setRealValue();
+  }
 
-    private static greatestCommonDenominator(a: number, b: number): number {
-        if (a === 0) {
-            return b;
-        }
-
-        if (b === 1) {
-            return 1;
-        }
-
-        while (b !== 0) {
-            if (a > b) {
-                a -= b;
-            } else {
-                b -= a;
-            }
-        }
-
-        return a;
+  public toString(): string {
+    let result: string = this.numerator + "/" + this.denominator;
+    if (this.wholeValue != 0) {
+      result = this.wholeValue + " " + result;
     }
 
-    /**
-     *
-     * @param numerator
-     * @param denominator
-     * @param simplify - If simplify is true, then the fraction is simplified
-     *      to make both the numerator and denominator coprime, and less than maximumAllowedNumber.
-     */
-    constructor(numerator: number = 0, denominator: number = 1, simplify: boolean = true) {
-        this.numerator = numerator;
-        this.denominator = denominator;
-
-        if (simplify) { this.simplify(); }
-        this.setRealValue();
-    }
+    return result;
+  }
 
-    public toString(): string {
-        return this.numerator + "/" + this.denominator;
-    }
+  public clone(): Fraction {
+    return new Fraction(this.numerator, this.denominator, this.wholeValue, false);
+  }
 
-    public clone(): Fraction {
-        return new Fraction(this.numerator, this.denominator, false);
-    }
+  public get Numerator(): number {
+    return this.numerator;
+  }
 
-    public get Numerator(): number {
-        return this.numerator;
+  public set Numerator(value: number) {
+    if (this.numerator !== value) {
+      this.numerator = value;
+      this.simplify();
+      this.setRealValue();
     }
+  }
 
-    public set Numerator(value: number) {
-        if (this.numerator !== value) {
-            this.numerator = value;
-            this.simplify();
-            this.setRealValue();
-        }
-    }
+  public get Denominator(): number {
+    return this.denominator;
+  }
 
-    public get Denominator(): number {
-        return this.denominator;
+  public set Denominator(value: number) {
+    if (this.denominator !== value) {
+      this.denominator = value;
+      if (this.numerator !== 0) {
+        this.simplify();
+      }
+      this.setRealValue();
     }
+  }
 
-    public set Denominator(value: number) {
-        if (this.denominator !== value) {
-            this.denominator = value;
-            if (this.numerator !== 0) {
-                this.simplify();
-            }
-            this.setRealValue();
-        }
-    }
+  public get WholeValue(): number {
+    return this.wholeValue;
+  }
 
-    public get RealValue(): number {
-        return this.realValue;
+  public set WholeValue(value: number) {
+    if (this.wholeValue !== value) {
+      this.wholeValue = value;
+      this.setRealValue();
     }
-
-    public multiplyWithFactor(factor: number): void {
-        this.numerator *= factor;
-        this.denominator *= factor;
+  }
+
+  public GetExpandedNumerator(): number {
+    return this.wholeValue * this.denominator + this.numerator;
+  }
+
+  public IsNegative(): boolean {
+    return this.realValue < 0;
+  }
+
+  public get RealValue(): number {
+    return this.realValue;
+  }
+
+  public expand(expansionValue: number): void {
+    this.numerator *= expansionValue;
+    this.denominator *= expansionValue;
+    if(this.wholeValue != 0)
+    {
+      this.numerator += this.wholeValue * this.denominator;
+      this.wholeValue = 0;
     }
-
-    public multiplyDenominatorWithFactor(factor: number): void {
-        this.denominator *= factor;
-        this.setRealValue();
+  }
+
+  // public multiplyDenominatorWithFactor(factor: number): void {
+  //   this.denominator *= factor;
+  //   this.setRealValue();
+  // }
+
+  public Add(fraction: Fraction): void {
+    this.numerator = (this.wholeValue * this.denominator + this.numerator) * fraction.denominator + (fraction.wholeValue * fraction.denominator + fraction.numerator) * this.denominator;
+    this.denominator = this.denominator * fraction.denominator;
+    this.wholeValue = 0;
+    this.simplify();
+    this.setRealValue();
+  }
+
+  public Sub(fraction: Fraction): void {
+    this.numerator = (this.wholeValue * this.denominator + this.numerator) * fraction.denominator - (fraction.wholeValue * fraction.denominator + fraction.numerator) * this.denominator;
+    this.denominator = this.denominator * fraction.denominator;
+    this.wholeValue = 0;
+    this.simplify();
+    this.setRealValue();
+  }
+
+  public Quantize(maxAllowedDenominator: number): Fraction {
+    if (this.denominator <= maxAllowedDenominator) {
+      return this;
     }
 
-    public Add(fraction: Fraction): void {
-        this.numerator = this.numerator * fraction.denominator + fraction.numerator * this.denominator;
-        this.denominator = this.denominator * fraction.denominator;
-        this.simplify();
-        this.setRealValue();
+    let upTestFraction: Fraction = new Fraction(this.numerator + 1, this.denominator, this.wholeValue);
 
+    while (upTestFraction.Denominator > maxAllowedDenominator) {
+      upTestFraction.Numerator++;
     }
 
-    public Sub(fraction: Fraction): void {
-        this.numerator = this.numerator * fraction.denominator - fraction.numerator * this.denominator;
-        this.denominator = this.denominator * fraction.denominator;
-        this.simplify();
-        this.setRealValue();
-    }
+    if (this.numerator > this.denominator) {
+      let downTestFraction: Fraction = new Fraction(this.numerator - 1, this.denominator, this.wholeValue);
 
-    public Quantize(maxAllowedDenominator: number): Fraction {
-        if (this.denominator <= maxAllowedDenominator) {
-            return this;
-        }
+      while (downTestFraction.Denominator > maxAllowedDenominator) {
+        downTestFraction.Numerator--;
+      }
 
-        let upTestFraction: Fraction = new Fraction(this.numerator + 1, this.denominator);
-
-        while (upTestFraction.Denominator > maxAllowedDenominator) {
-            upTestFraction.Numerator++;
-        }
-
-        if (this.numerator > this.denominator) {
-            let downTestFraction: Fraction = new Fraction(this.numerator - 1, this.denominator);
-
-            while (downTestFraction.Denominator > maxAllowedDenominator) {
-                downTestFraction.Numerator--;
-            }
-
-            if (downTestFraction.Denominator < upTestFraction.Denominator) {
-                return downTestFraction;
-            }
-        }
-        return upTestFraction;
+      if (downTestFraction.Denominator < upTestFraction.Denominator) {
+        return downTestFraction;
+      }
     }
-
-    public Equals(obj: Fraction): boolean {
-        return this.RealValue === obj.RealValue;
-    }
-
-    public CompareTo(obj: Fraction): number {
-        let diff: number = this.numerator * obj.Denominator - this.denominator * obj.Numerator;
-        // Return the sign of diff
-        return diff ? diff < 0 ? -1 : 1 : 0;
+    return upTestFraction;
+  }
+
+  public Equals(obj: Fraction): boolean {
+    return this.realValue === obj.realValue;
+  }
+
+  public CompareTo(obj: Fraction): number {
+    let diff: number = this.realValue - obj.realValue;
+    // Return the sign of diff
+    return diff ? diff < 0 ? -1 : 1 : 0;
+  }
+
+  public lt(frac: Fraction): boolean {
+    return this.realValue < frac.realValue;
+  }
+
+  public lte(frac: Fraction): boolean {
+    return this.realValue <= frac.realValue;
+  }
+
+  //public Equals(f: Fraction): boolean {
+  //    if (ReferenceEquals(this, f))
+  //        return true;
+  //    if (ReferenceEquals(f, undefined))
+  //        return false;
+  //    return <number>this.numerator * f.denominator === <number>f.numerator * this.denominator;
+  //}
+
+  private setRealValue(): void {
+    this.realValue = this.wholeValue + this.numerator / this.denominator;
+  }
+
+  private simplify(): void {
+    if (this.numerator === 0) {
+      this.denominator = 1;
+      return;
     }
 
-    public lt(frac: Fraction): boolean {
-        return (this.numerator * frac.Denominator - this.denominator * frac.Numerator) < 0;
-    }
-    public lte(frac: Fraction): boolean {
-        return (this.numerator * frac.Denominator - this.denominator * frac.Numerator) <= 0;
-    }
+    let i: number = Fraction.greatestCommonDenominator(Math.abs(this.numerator), Math.abs(this.denominator));
 
-    //public Equals(f: Fraction): boolean {
-    //    if (ReferenceEquals(this, f))
-    //        return true;
-    //    if (ReferenceEquals(f, undefined))
-    //        return false;
-    //    return <number>this.numerator * f.denominator === <number>f.numerator * this.denominator;
-    //}
+    this.numerator /= i;
+    this.denominator /= i;
 
-    public GetInversion(): Fraction {
-        return new Fraction(this.denominator, this.numerator);
+    let whole:number = Math.floor(this.numerator / this.denominator);
+    if(whole != 0)
+    {
+      this.wholeValue += whole;
+      this.numerator -= whole * this.denominator;
+      if(this.numerator == 0)
+      {
+        this.denominator = 1;
+      }
     }
-
-    private setRealValue(): void {
-        this.realValue = this.numerator / this.denominator;
+    if(this.denominator > Fraction.maximumAllowedNumber)
+    {
+      let factor:number = <number>this.denominator / Fraction.maximumAllowedNumber;
+      this.numerator = <number>Math.round(this.numerator / factor);
+      this.denominator = <number>Math.round(this.denominator / factor);
     }
-
-    private simplify(): void {
-        if (this.numerator === 0) {
-            this.denominator = 1;
-            return;
-        }
-
-        let i: number = Fraction.greatestCommonDenominator(Math.abs(this.numerator), Math.abs(this.denominator));
-
-        this.numerator /= i;
-        this.denominator /= i;
-
-        if (this.denominator > Fraction.maximumAllowedNumber) {
-            let factor: number = this.denominator / Fraction.maximumAllowedNumber;
-            this.numerator = Math.round(this.numerator / factor);
-            this.denominator = Math.round(this.denominator / factor);
-        }
-        if (this.numerator > Fraction.maximumAllowedNumber) {
-            let factor: number = this.numerator / Fraction.maximumAllowedNumber;
-            this.numerator = Math.round(this.numerator / factor);
-            this.denominator = Math.round(this.denominator / factor);
-        }
+    if(this.numerator > Fraction.maximumAllowedNumber)
+    {
+      let factor:number = <number>this.numerator / Fraction.maximumAllowedNumber;
+      this.numerator = <number>Math.round(this.numerator / factor);
+      this.denominator = <number>Math.round(this.denominator / factor);
     }
-
-
-
-    //private static equals(f1: Fraction, f2: Fraction): boolean {
-    //    return <number>f1.numerator * f2.denominator === <number>f2.numerator * f1.denominator;
-    //}
-    //
-    //public static ApproximateFractionFromValue(value: number, epsilonForPrecision: number): Fraction {
-    //    let n: number = 1;
-    //    let d: number = 1;
-    //    let fraction: number = n / d;
-    //    while (Math.abs(fraction - value) > epsilonForPrecision) {
-    //        if (fraction < value) {
-    //            n++;
-    //        }
-    //        else {
-    //            d++;
-    //            n = <number>Math.round(value * d);
-    //        }
-    //        fraction = n / <number>d;
-    //    }
-    //    return new Fraction(n, d);
-    //}
-    //public static GetEarlierTimestamp(m1: Fraction, m2: Fraction): Fraction {
-    //    if (m1 < m2)
-    //        return m1;
-    //    else return m2;
-    //}
-
-    //public static getFraction(value: number, denominatorPrecision: number): Fraction {
-    //    let numerator: number = <number>Math.round(value / (1.0 / denominatorPrecision));
-    //    return new Fraction(numerator, denominatorPrecision);
-    //}
-    //public static fractionMin(f1: Fraction, f2: Fraction): Fraction {
-    //    if (f1 < f2)
-    //        return f1;
-    //    else return f2;
-    //}
-
-    //public static GetMaxValue(): Fraction {
-    //    return new Fraction(Fraction.maximumAllowedNumber, 1);
-    //}
-    //public static get MaxAllowedNumerator(): number {
-    //    return Fraction.maximumAllowedNumber;
-    //}
-    //public static get MaxAllowedDenominator(): number {
-    //    return Fraction.maximumAllowedNumber;
-    //}
-    //public ToFloatingString(): string {
-    //    return this.RealValue.ToString();
-    //}
-    //public Compare(x: Fraction, y: Fraction): number {
-    //    if (x > y)
-    //        return 1;
-    //    if (x < y)
-    //        return -1;
-    //    return 0;
-    //}
-
-    //#region operators
-    //
-    //    // operator overloads must always come in pairs
-    //    // operator overload +
-    //    public static Fraction operator + (Fraction f1, Fraction f2)
-    //{
-    //    Fraction sum = new Fraction(f1);
-    //    sum.Add(f2);
-    //    return sum;
-    //}
-    //
-    //// operator overload -
-    //public static Fraction operator - (Fraction f1, Fraction f2)
-    //{
-    //    Fraction diff = new Fraction(f1);
-    //    diff.Sub(f2);
-    //    return diff;
-    //}
-    //
-    //// operator overloads must always come in pairs
-    //// operator overload >
-    //public static bool operator > (Fraction f1, Fraction f2)
-    //{
-    //    //return (long) f1.Numerator*f2._denominator > (long) f2._numerator*f1._denominator;
-    //    return f1.RealValue > f2.RealValue;
-    //}
-    //
-    //// operator overload <
-    //public static bool operator < (Fraction f1, Fraction f2)
-    //{
-    //    //return (long) f1._numerator*f2._denominator < (long) f2._numerator*f1._denominator;
-    //    return f1.RealValue < f2.RealValue;
-    //}
-    //
-    //// operator overload ==
-    //public static bool operator === (Fraction f1, Fraction f2)
-    //{
-    //    // code enhanced for performance
-    //    // System.Object.ReferenceEquals(f1, undefined) is better than if (f1 === undefined)
-    //    // and comparisons between booleans are quick
-    //    bool f1IsNull = System.Object.ReferenceEquals(f1, undefined);
-    //    bool f2IsNull = System.Object.ReferenceEquals(f2, undefined);
-    //
-    //    // method returns true when both are undefined, false when only the first is undefined, otherwise the result of equals
-    //    if (f1IsNull !== f2IsNull)
-    //        return false;
-    //
-    //    if (f1IsNull /*&& f2IsNull*/)
-    //        return true;
-    //
-    //    return equals(f1, f2);
-    //}
-    //
-    //// operator overload !=
-    //public static bool operator !== (Fraction f1, Fraction f2)
-    //{
-    //    return (!(f1 === f2));
-    //}
-    //
-    //// operator overload >=
-    //public static bool operator >= (Fraction f1, Fraction f2)
-    //{
-    //    return (!(f1 < f2));
-    //}
-    //
-    //// operator overload <=
-    //public static bool operator <= (Fraction f1,Fraction f2)
-    //{
-    //    return (!(f1 > f2));
-    //}
-    //
-    //public static Fraction operator / (Fraction f, int i)
-    //{
-    //    return new Fraction(f._numerator, f._denominator *= i);
-    //}
-    //
-    //public static Fraction operator / (Fraction f1, Fraction f2)
-    //{
-    //    let res = new Fraction(f1.Numerator*f2.Denominator, f1.Denominator*f2.Numerator);
-    //    return res.Denominator === 0 ? new Fraction(0, 1) : res;
-    //}
-    //
-    //public static Fraction operator * (Fraction f1, Fraction f2)
-    //{
-    //    return new Fraction(f1.Numerator*f2.Numerator, f1.Denominator*f2.Denominator);
-    //}
-    //
-    //public static Fraction operator % (Fraction f1, Fraction f2)
-    //{
-    //    let a = f1/f2;
-    //    return new Fraction(a.Numerator%a.Denominator, a.Denominator)*f2;
-    //}
-    //
-    //#endregion operators
+  }
+
+
+  //private static equals(f1: Fraction, f2: Fraction): boolean {
+  //    return <number>f1.numerator * f2.denominator === <number>f2.numerator * f1.denominator;
+  //}
+  //
+  //public static ApproximateFractionFromValue(value: number, epsilonForPrecision: number): Fraction {
+  //    let n: number = 1;
+  //    let d: number = 1;
+  //    let fraction: number = n / d;
+  //    while (Math.abs(fraction - value) > epsilonForPrecision) {
+  //        if (fraction < value) {
+  //            n++;
+  //        }
+  //        else {
+  //            d++;
+  //            n = <number>Math.round(value * d);
+  //        }
+  //        fraction = n / <number>d;
+  //    }
+  //    return new Fraction(n, d);
+  //}
+  //public static GetEarlierTimestamp(m1: Fraction, m2: Fraction): Fraction {
+  //    if (m1 < m2)
+  //        return m1;
+  //    else return m2;
+  //}
+
+  //public static getFraction(value: number, denominatorPrecision: number): Fraction {
+  //    let numerator: number = <number>Math.round(value / (1.0 / denominatorPrecision));
+  //    return new Fraction(numerator, denominatorPrecision);
+  //}
+  //public static fractionMin(f1: Fraction, f2: Fraction): Fraction {
+  //    if (f1 < f2)
+  //        return f1;
+  //    else return f2;
+  //}
+
+  //public static GetMaxValue(): Fraction {
+  //    return new Fraction(Fraction.maximumAllowedNumber, 1);
+  //}
+  //public static get MaxAllowedNumerator(): number {
+  //    return Fraction.maximumAllowedNumber;
+  //}
+  //public static get MaxAllowedDenominator(): number {
+  //    return Fraction.maximumAllowedNumber;
+  //}
+  //public ToFloatingString(): string {
+  //    return this.RealValue.ToString();
+  //}
+  //public Compare(x: Fraction, y: Fraction): number {
+  //    if (x > y)
+  //        return 1;
+  //    if (x < y)
+  //        return -1;
+  //    return 0;
+  //}
+
+  //#region operators
+  //
+  //    // operator overloads must always come in pairs
+  //    // operator overload +
+  //    public static Fraction operator + (Fraction f1, Fraction f2)
+  //{
+  //    Fraction sum = new Fraction(f1);
+  //    sum.Add(f2);
+  //    return sum;
+  //}
+  //
+  //// operator overload -
+  //public static Fraction operator - (Fraction f1, Fraction f2)
+  //{
+  //    Fraction diff = new Fraction(f1);
+  //    diff.Sub(f2);
+  //    return diff;
+  //}
+  //
+  //// operator overloads must always come in pairs
+  //// operator overload >
+  //public static bool operator > (Fraction f1, Fraction f2)
+  //{
+  //    //return (long) f1.Numerator*f2._denominator > (long) f2._numerator*f1._denominator;
+  //    return f1.RealValue > f2.RealValue;
+  //}
+  //
+  //// operator overload <
+  //public static bool operator < (Fraction f1, Fraction f2)
+  //{
+  //    //return (long) f1._numerator*f2._denominator < (long) f2._numerator*f1._denominator;
+  //    return f1.RealValue < f2.RealValue;
+  //}
+  //
+  //// operator overload ==
+  //public static bool operator === (Fraction f1, Fraction f2)
+  //{
+  //    // code enhanced for performance
+  //    // System.Object.ReferenceEquals(f1, undefined) is better than if (f1 === undefined)
+  //    // and comparisons between booleans are quick
+  //    bool f1IsNull = System.Object.ReferenceEquals(f1, undefined);
+  //    bool f2IsNull = System.Object.ReferenceEquals(f2, undefined);
+  //
+  //    // method returns true when both are undefined, false when only the first is undefined, otherwise the result of equals
+  //    if (f1IsNull !== f2IsNull)
+  //        return false;
+  //
+  //    if (f1IsNull /*&& f2IsNull*/)
+  //        return true;
+  //
+  //    return equals(f1, f2);
+  //}
+  //
+  //// operator overload !=
+  //public static bool operator !== (Fraction f1, Fraction f2)
+  //{
+  //    return (!(f1 === f2));
+  //}
+  //
+  //// operator overload >=
+  //public static bool operator >= (Fraction f1, Fraction f2)
+  //{
+  //    return (!(f1 < f2));
+  //}
+  //
+  //// operator overload <=
+  //public static bool operator <= (Fraction f1,Fraction f2)
+  //{
+  //    return (!(f1 > f2));
+  //}
+  //
+  //public static Fraction operator / (Fraction f, int i)
+  //{
+  //    return new Fraction(f._numerator, f._denominator *= i);
+  //}
+  //
+  //public static Fraction operator / (Fraction f1, Fraction f2)
+  //{
+  //    let res = new Fraction(f1.Numerator*f2.Denominator, f1.Denominator*f2.Numerator);
+  //    return res.Denominator === 0 ? new Fraction(0, 1) : res;
+  //}
+  //
+  //public static Fraction operator * (Fraction f1, Fraction f2)
+  //{
+  //    return new Fraction(f1.Numerator*f2.Numerator, f1.Denominator*f2.Denominator);
+  //}
+  //
+  //public static Fraction operator % (Fraction f1, Fraction f2)
+  //{
+  //    let a = f1/f2;
+  //    return new Fraction(a.Numerator%a.Denominator, a.Denominator)*f2;
+  //}
+  //
+  //#endregion operators
 }

+ 9 - 4
src/MusicalScore/Graphical/GraphicalNote.ts

@@ -60,10 +60,15 @@ export class GraphicalNote extends GraphicalObject {
      * @returns {number}
      */
     private calculateNumberOfNeededDots(fraction: Fraction): number {
-        let dotCount: number = 0;
-        if (this.sourceNote === undefined || this.sourceNote.NoteTuplet === undefined) {
-            dotCount = Math.floor(Math.log(fraction.Numerator) / Math.LN2);
+      let number: number = 1;
+      let product: number = 2;
+      let expandedNumerator: number = fraction.GetExpandedNumerator();
+      if (this.sourceNote === undefined || this.sourceNote.NoteTuplet === undefined) {
+        while (product < expandedNumerator) {
+          number++;
+          product = <number>Math.pow(2, number);
         }
-        return Math.min(3, dotCount);
+      }
+      return Math.min(3, number - 1);
     }
 }

+ 2 - 2
src/MusicalScore/Graphical/GraphicalStaffEntry.ts

@@ -253,7 +253,7 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
             for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
                 let graphicalNote: GraphicalNote = graphicalNotes[idx2];
                 let calNoteLen: Fraction = graphicalNote.graphicalNoteLength;
-                if (calNoteLen.lt(minLength) && calNoteLen.Numerator > 0) {
+                if (calNoteLen.lt(minLength) && calNoteLen.GetExpandedNumerator() > 0) {
                     minLength = calNoteLen;
                 }
             }
@@ -268,7 +268,7 @@ export abstract class GraphicalStaffEntry extends GraphicalObject {
             for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
                 let graphicalNote: GraphicalNote = graphicalNotes[idx2];
                 let calNoteLen: Fraction = graphicalNote.graphicalNoteLength;
-                if (maxLength.lt(calNoteLen)  && calNoteLen.Numerator > 0) {
+                if (maxLength.lt(calNoteLen)  && calNoteLen.GetExpandedNumerator() > 0) {
                     maxLength = calNoteLen;
                 }
             }

+ 1 - 1
src/MusicalScore/MusicSheet.ts

@@ -33,7 +33,7 @@ export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet
         this.rules = EngravingRules.Rules;
         this.playbackSettings = new PlaybackSettings();
         // FIXME?
-        this.playbackSettings.rhythm = new Fraction(4, 4, false);
+        this.playbackSettings.rhythm = new Fraction(4, 4, 0, false);
         this.userStartTempoInBPM = 100;
         this.pageWidth = 120;
         this.MusicPartManager = new MusicPartManager(this);

+ 7 - 8
src/MusicalScore/ScoreIO/InstrumentReader.ts

@@ -226,15 +226,14 @@ export class InstrumentReader {
                     }
                     if (isTuplet) {
                         this.currentVoiceGenerator.read(
-                            xmlNode, noteDuration.Numerator,
-                            noteDuration.Denominator, restNote, isGraceNote,
+                            xmlNode, noteDuration, restNote, isGraceNote,
                             this.currentStaffEntry, this.currentMeasure,
                             measureStartAbsoluteTimestamp,
                             this.maxTieNoteFraction, isChord, guitarPro
                         );
                     } else {
                         this.currentVoiceGenerator.read(
-                            xmlNode, noteDivisions, 4 * this.divisions,
+                            xmlNode, new Fraction(noteDivisions, 4*this.divisions),
                             restNote, isGraceNote, this.currentStaffEntry,
                             this.currentMeasure, measureStartAbsoluteTimestamp,
                             this.maxTieNoteFraction, isChord, guitarPro
@@ -299,11 +298,11 @@ export class InstrumentReader {
                 } else if (xmlNode.name === "backup") {
                     let backFraction: number = parseInt(xmlNode.element("duration").value, 10);
                     currentFraction.Sub(new Fraction(backFraction, 4 * this.divisions));
-                    if (currentFraction.Numerator < 0) {
+                    if (currentFraction.IsNegative()) {
                         currentFraction = new Fraction(0, 1);
                     }
                     previousFraction.Sub(new Fraction(backFraction, 4 * this.divisions));
-                    if (previousFraction.Numerator < 0) {
+                    if (previousFraction.IsNegative()) {
                         previousFraction = new Fraction(0, 1);
                     }
                 } else if (xmlNode.name === "direction") {
@@ -738,7 +737,7 @@ export class InstrumentReader {
                             }
                             d = parseInt(typeList[i].value, 10);
                             maxDenom = Math.max(maxDenom, d);
-                            fractions[i] = new Fraction(n, d, false);
+                            fractions[i] = new Fraction(n, d, 0, false);
                         }
                         for (let i: number = 0; i < length; i++) {
                             if (fractions[i].Denominator === maxDenom) {
@@ -764,10 +763,10 @@ export class InstrumentReader {
                     symbolEnum = RhythmSymbolEnum.NONE;
                 }
                 this.abstractInstructions.push([1, new RhythmInstruction(
-                    new Fraction(num, denom, false), num, denom, symbolEnum
+                    new Fraction(num, denom, 0, false), symbolEnum
                 )]);
             } else {
-                this.abstractInstructions.push([1, new RhythmInstruction(new Fraction(4, 4, false), 4, 4, RhythmSymbolEnum.NONE)]);
+                this.abstractInstructions.push([1, new RhythmInstruction(new Fraction(4, 4, 0, false), RhythmSymbolEnum.NONE)]);
             }
         }
     }

+ 3 - 3
src/MusicalScore/ScoreIO/MusicSheetReader.ts

@@ -279,7 +279,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
             }
         }
         if (rhythmInstructions.length === 0 && this.currentMeasure === this.musicSheet.SourceMeasures[0]) {
-            let rhythmInstruction: RhythmInstruction = new RhythmInstruction(new Fraction(4, 4, false), 4, 4, RhythmSymbolEnum.NONE);
+            let rhythmInstruction: RhythmInstruction = new RhythmInstruction(new Fraction(4, 4, 0, false), RhythmSymbolEnum.NONE);
             for (let i: number = 0; i < this.completeNumberOfStaves; i++) {
                 if (this.currentMeasure.FirstInstructionsStaffEntries[i] === undefined) {
                     this.currentMeasure.FirstInstructionsStaffEntries[i] = new SourceStaffEntry(undefined, undefined);
@@ -345,7 +345,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
             instrumentsMaxTieNoteFractions.push(instrumentReader.MaxTieNoteFraction);
             let activeRythmMeasure: Fraction = instrumentReader.ActiveRhythm.Rhythm;
             if (activeRhythm.lt(activeRythmMeasure)) {
-                activeRhythm = new Fraction(activeRythmMeasure.Numerator, activeRythmMeasure.Denominator, false);
+                activeRhythm = new Fraction(activeRythmMeasure.Numerator, activeRythmMeasure.Denominator, 0, false);
             }
         }
         let instrumentsDurations: Fraction[] = this.currentMeasure.calculateInstrumentsDuration(this.musicSheet, instrumentsMaxTieNoteFractions);
@@ -400,7 +400,7 @@ export class MusicSheetReader /*implements IMusicSheetReader*/ {
     private checkFractionsForEquivalence(maxInstrumentDuration: Fraction, activeRhythm: Fraction): void {
         if (activeRhythm.Denominator > maxInstrumentDuration.Denominator) {
             let factor: number = activeRhythm.Denominator / maxInstrumentDuration.Denominator;
-            maxInstrumentDuration.multiplyWithFactor(factor);
+            maxInstrumentDuration.expand(factor);
         }
     }
 

+ 7 - 7
src/MusicalScore/ScoreIO/VoiceGenerator.ts

@@ -106,7 +106,7 @@ export class VoiceGenerator {
      * @returns {Note}
      */
     public read(
-        noteNode: IXmlElement, noteDuration: number, divisions: number, restNote: boolean, graceNote: boolean,
+        noteNode: IXmlElement, noteDuration: Fraction, restNote: boolean, graceNote: boolean,
         parentStaffEntry: SourceStaffEntry, parentMeasure: SourceMeasure,
         measureStartAbsoluteTimestamp: Fraction, maxTieNoteFraction: Fraction, chord: boolean, guitarPro: boolean
     ): Note {
@@ -115,8 +115,8 @@ export class VoiceGenerator {
         //Logging.debug("read called:", restNote);
         try {
             this.currentNote = restNote
-                ? this.addRestNote(noteDuration, divisions)
-                : this.addSingleNote(noteNode, noteDuration, divisions, graceNote, chord, guitarPro);
+                ? this.addRestNote(noteDuration)
+                : this.addSingleNote(noteNode, noteDuration, graceNote, chord, guitarPro);
             // (*)
             //if (this.lyricsReader !== undefined && noteNode.element("lyric") !== undefined) {
             //    this.lyricsReader.addLyricEntry(noteNode, this.currentVoiceEntry);
@@ -321,7 +321,7 @@ export class VoiceGenerator {
      * @returns {Note}
      */
     private addSingleNote(
-        node: IXmlElement, noteDuration: number, divisions: number, graceNote: boolean, chord: boolean, guitarPro: boolean
+        node: IXmlElement, noteDuration: Fraction, graceNote: boolean, chord: boolean, guitarPro: boolean
     ): Note {
         //Logging.debug("addSingleNote called");
         let noteAlter: AccidentalEnum = AccidentalEnum.NONE;
@@ -396,7 +396,7 @@ export class VoiceGenerator {
 
         noteOctave -= Pitch.OctaveXmlDifference;
         let pitch: Pitch = new Pitch(noteStep, noteOctave, noteAlter);
-        let noteLength: Fraction = new Fraction(noteDuration, divisions);
+        let noteLength: Fraction = Fraction.createFromFraction(noteDuration);
         let note: Note = new Note(this.currentVoiceEntry, this.currentStaffEntry, noteLength, pitch);
         note.PlaybackInstrumentId = playbackInstrumentId;
         if (!graceNote) {
@@ -416,8 +416,8 @@ export class VoiceGenerator {
      * @param divisions
      * @returns {Note}
      */
-    private addRestNote(noteDuration: number, divisions: number): Note {
-        let restFraction: Fraction = new Fraction(noteDuration, divisions);
+    private addRestNote(noteDuration: Fraction): Note {
+        let restFraction: Fraction = Fraction.createFromFraction(noteDuration);
         let restNote: Note = new Note(this.currentVoiceEntry, this.currentStaffEntry, restFraction, undefined);
         this.currentVoiceEntry.Notes.push(restNote);
         if (this.openBeam !== undefined) {

+ 4 - 4
src/MusicalScore/VoiceData/Instructions/RhythmInstruction.ts

@@ -5,11 +5,11 @@ import {Fraction} from "../../../Common/DataObjects/Fraction";
  * A [[RhythmInstruction]] is the time signature which specifies the number of beats in each bar, and the value of one beat.
  */
 export class RhythmInstruction extends AbstractNotationInstruction {
-    constructor(rhythm: Fraction, numerator: number, denominator: number, rhythmSymbolEnum: RhythmSymbolEnum) {
+    constructor(rhythm: Fraction, rhythmSymbolEnum: RhythmSymbolEnum) {
         super(undefined); // FIXME no parent SourceStaffEntry
         this.rhythm = rhythm;
-        this.numerator = numerator;
-        this.denominator = denominator;
+        this.numerator = rhythm.Numerator;
+        this.denominator = rhythm.Denominator;
         this.symbolEnum = rhythmSymbolEnum;
     }
 
@@ -35,7 +35,7 @@ export class RhythmInstruction extends AbstractNotationInstruction {
     }
 
     public clone(): RhythmInstruction {
-        return new RhythmInstruction(this.rhythm.clone(), this.numerator, this.denominator, this.symbolEnum);
+        return new RhythmInstruction(this.rhythm.clone(), this.symbolEnum);
     }
 
     public OperatorEquals(rhythm2: RhythmInstruction): boolean {

+ 8 - 8
src/OSMD/Cursor.ts

@@ -54,24 +54,24 @@ export class Cursor {
         }
         this.graphic.Cursors.length = 0;
         let iterator: MusicPartManagerIterator = this.iterator;
-        if  (iterator.EndReached || iterator.CurrentVoiceEntries === undefined) {
+        if  (iterator.EndReached || iterator.CurrentVoiceEntries === undefined || iterator.CurrentVoiceEntries.length === 0) {
             return;
         }
         let x: number = 0, y: number = 0, height: number = 0;
-        for (let idx: number = 0, len: number = iterator.CurrentVoiceEntries.length; idx < len; ++idx) {
-            let voiceEntry: VoiceEntry = iterator.CurrentVoiceEntries[idx];
+
+            let voiceEntry: VoiceEntry = iterator.CurrentVoiceEntries[0];
             let measureIndex: number = voiceEntry.ParentSourceStaffEntry.VerticalContainerParent.ParentMeasure.measureListIndex;
             let staffIndex: number = voiceEntry.ParentSourceStaffEntry.ParentStaff.idInMusicSheet;
             let gse: VexFlowStaffEntry =
                 <VexFlowStaffEntry>this.graphic.findGraphicalStaffEntryFromMeasureList(staffIndex, measureIndex, voiceEntry.ParentSourceStaffEntry);
-            if (idx === 0) {
-                x = gse.getX();
+
+                x = gse.PositionAndShape.AbsolutePosition.x;
                 let musicSystem: MusicSystem = gse.parentMeasure.parentMusicSystem;
                 y = musicSystem.PositionAndShape.AbsolutePosition.y + musicSystem.StaffLines[0].PositionAndShape.RelativePosition.y;
                 let endY: number = musicSystem.PositionAndShape.AbsolutePosition.y +
                     musicSystem.StaffLines[musicSystem.StaffLines.length - 1].PositionAndShape.RelativePosition.y + 4.0;
                 height = endY - y;
-            }
+
             // The following code is not necessary (for now, but it could come useful later):
             // it highlights the notes under the cursor.
             //let vfNotes: { [voiceID: number]: Vex.Flow.StaveNote; } = gse.vfNotes;
@@ -83,7 +83,7 @@ export class Cursor {
             //        });
             //    }
             //}
-        }
+
         // Update the graphical cursor
         // The following is the legacy cursor rendered on the canvas:
         // // let cursor: GraphicalLine = new GraphicalLine(new PointF2D(x, y), new PointF2D(x, y + height), 3, OutlineAndFillStyleEnum.PlaybackCursor);
@@ -129,7 +129,7 @@ export class Cursor {
     }
 
     /**
-     * Go to next entry
+     * reset cursor to start
      */
     public reset(): void {
         this.iterator = this.manager.getIterator();