/// <summary> /// Creates an inverted NuGenFraction /// </summary> /// <returns>The inverted NuGenFraction (with Denominator over Numerator)</returns> /// <remarks>Does NOT throw for zero Numerators as later use of the fraction will catch the error.</remarks> public static NuGenFraction Inverted(double value) { NuGenFraction frac = new NuGenFraction(value); return frac.Inverse(); }
/// <summary> /// Compares this NuGenFraction to another NuGenFraction /// </summary> /// <param name="right">The NuGenFraction to compare against</param> /// <returns>-1 if this is less than <paramref name="right"></paramref>, /// 0 if they are equal, /// 1 if this is greater than <paramref name="right"></paramref></returns> public int CompareTo(NuGenFraction right) { // if left is an indeterminate, punt to the helper... if (this.m_Denominator == 0) { return IndeterminantCompare(NormalizeIndeterminate(this.m_Numerator), right); } // if right is an indeterminate, punt to the helper... if (right.m_Denominator == 0) { // note sign-flip... return - IndeterminantCompare(NormalizeIndeterminate(right.m_Numerator), this); } // they're both normal Fractions CrossReducePair(ref this, ref right); try { checked { long leftScale = this.m_Numerator * right.m_Denominator; long rightScale = this.m_Denominator * right.m_Numerator; if (leftScale < rightScale) return -1; else if (leftScale > rightScale) return 1; else return 0; } } catch (Exception e) { throw new NuGenFractionException(string.Format("CompareTo({0}, {1}) error", this, right), e); } }
/// <summary> /// Returns the modulus (remainder after dividing) two Fractions /// </summary> /// <param name="left">A NuGenFraction</param> /// <param name="right">Another NuGenFraction</param> /// <returns>Modulus of the Fractions. Returns NaN if either NuGenFraction is a NaN.</returns> /// <exception cref="NuGenFractionException">Will throw if an overflow occurs. Does a cross-reduce to /// insure only the unavoidable overflows occur.</exception> private static NuGenFraction Modulus(NuGenFraction left, NuGenFraction right) { if (left.IsNaN() || right.IsNaN()) return NaN; try { checked { // this will discard any fractional places... Int64 quotient = (Int64)(left / right); NuGenFraction whole = new NuGenFraction(quotient * right.m_Numerator, right.m_Denominator); return left - whole; } } catch (Exception e) { throw new NuGenFractionException("Modulus error", e); } }
/// <summary> /// Inverts a NuGenFraction /// </summary> /// <returns>The inverted NuGenFraction (with Denominator over Numerator)</returns> /// <remarks>Does NOT throw for zero Numerators as later use of the fraction will catch the error.</remarks> public NuGenFraction Inverse() { // don't use the obvious constructor because we do not want it normalized at this time NuGenFraction frac = new NuGenFraction(); frac.m_Numerator = this.m_Denominator; frac.m_Denominator = this.m_Numerator; return frac; }
/// <summary> /// Adds two Fractions /// </summary> /// <param name="left">A NuGenFraction</param> /// <param name="right">Another NuGenFraction</param> /// <returns>Sum of the Fractions. Returns NaN if either NuGenFraction is a NaN.</returns> /// <exception cref="NuGenFractionException">Will throw if an overflow occurs when computing the /// GCD-normalized values.</exception> private static NuGenFraction Add(NuGenFraction left, NuGenFraction right) { if (left.IsNaN() || right.IsNaN()) return NaN; long gcd = GCD(left.m_Denominator, right.m_Denominator); // cannot return less than 1 long leftDenominator = left.m_Denominator / gcd; long rightDenominator = right.m_Denominator / gcd; try { checked { long numerator = left.m_Numerator * rightDenominator + right.m_Numerator * leftDenominator; long denominator = leftDenominator * rightDenominator * gcd; return new NuGenFraction(numerator, denominator); } } catch (Exception e) { throw new NuGenFractionException("Add error", e); } }
/// <summary> /// Multiplies two Fractions /// </summary> /// <param name="left">A NuGenFraction</param> /// <param name="right">Another NuGenFraction</param> /// <returns>Product of the Fractions. Returns NaN if either NuGenFraction is a NaN.</returns> /// <exception cref="NuGenFractionException">Will throw if an overflow occurs. Does a cross-reduce to /// insure only the unavoidable overflows occur.</exception> private static NuGenFraction Multiply(NuGenFraction left, NuGenFraction right) { if (left.IsNaN() || right.IsNaN()) return NaN; // this would be unsafe if we were not a ValueType, because we would be changing the // caller's values. If we change back to a class, must use temporaries CrossReducePair(ref left, ref right); try { checked { long numerator = left.m_Numerator * right.m_Numerator; long denominator = left.m_Denominator * right.m_Denominator; return new NuGenFraction(numerator, denominator); } } catch (Exception e) { throw new NuGenFractionException("Multiply error", e); } }
/// <summary> /// Negates the NuGenFraction /// </summary> /// <param name="frac">Value to negate</param> /// <returns>A new NuGenFraction that is sign-flipped from the input</returns> private static NuGenFraction Negate(NuGenFraction frac) { // for a NaN, it's still a NaN return new NuGenFraction( - frac.m_Numerator, frac.m_Denominator); }
/// <summary> /// Determines how this NuGenFraction, of an indeterminate type, compares to another NuGenFraction /// </summary> /// <param name="leftType">What kind of indeterminate</param> /// <param name="right">The other NuGenFraction to compare against</param> /// <returns>-1 if this is less than <paramref name="right"></paramref>, /// 0 if they are equal, /// 1 if this is greater than <paramref name="right"></paramref></returns> /// <remarks>NaN is less than anything except NaN and Negative Infinity. Negative Infinity is less /// than anything except Negative Infinity. Positive Infinity is greater than anything except /// Positive Infinity.</remarks> private static int IndeterminantCompare(Indeterminates leftType, NuGenFraction right) { switch (leftType) { case Indeterminates.NaN: // A NaN is... if (right.IsNaN()) return 0; // equal to a NaN else if (right.IsNegativeInfinity()) return 1; // great than Negative Infinity else return -1; // less than anything else case Indeterminates.NegativeInfinity: // Negative Infinity is... if (right.IsNegativeInfinity()) return 0; // equal to Negative Infinity else return -1; // less than anything else case Indeterminates.PositiveInfinity: if (right.IsPositiveInfinity()) return 0; // equal to Positive Infinity else return 1; // greater than anything else default: // this CAN'T happen, something VERY wrong is going on... return 0; } }
/// <summary> /// Compares for equality the current NuGenFraction to the value passed. /// </summary> /// <param name="right">A NuGenFraction to compare against</param> /// <param name="notEqualCheck">If true, we're looking for not-equal</param> /// <returns>True if the <paramref name="right"></paramref> equals the current /// fraction, false otherwise. If comparing two NaNs, they are always equal AND /// not-equal.</returns> private bool CompareEquality(NuGenFraction right, bool notEqualCheck) { // insure we're normalized first ReduceFraction(ref this); // now normalize the comperand ReduceFraction(ref right); if (this.m_Numerator == right.m_Numerator && this.m_Denominator == right.m_Denominator) { // special-case rule, two NaNs are always both equal if (notEqualCheck && this.IsNaN()) return true; else return ! notEqualCheck; } else { return notEqualCheck; } }
/// <summary> /// Cross-reduces a pair of Fractions so that we have the best GCD-reduced values for multiplication /// </summary> /// <param name="frac1">The first NuGenFraction [WILL BE MODIFIED IN PLACE]</param> /// <param name="frac2">The second NuGenFraction [WILL BE MODIFIED IN PLACE]</param> /// <remarks>Modifies the input arguments in-place!</remarks> /// <example>(3/4, 5/9) = (1/4, 5/3)</example> public static void CrossReducePair(ref NuGenFraction frac1, ref NuGenFraction frac2) { // leave the indeterminates alone! if (frac1.m_Denominator == 0 || frac2.m_Denominator == 0) return; long gcdTop = GCD(frac1.m_Numerator, frac2.m_Denominator); frac1.m_Numerator = frac1.m_Numerator / gcdTop; frac2.m_Denominator = frac2.m_Denominator / gcdTop; long gcdBottom = GCD(frac1.m_Denominator, frac2.m_Numerator); frac2.m_Numerator = frac2.m_Numerator / gcdBottom; frac1.m_Denominator = frac1.m_Denominator / gcdBottom; }
/// <summary> /// Reduces (simplifies) a NuGenFraction by dividing down to lowest possible denominator (via GCD) /// </summary> /// <param name="frac">The NuGenFraction to be reduced [WILL BE MODIFIED IN PLACE]</param> /// <remarks>Modifies the input arguments in-place! Will normalize the NaN and infinites /// representation. Will set Denominator to 1 for any zero numerator. Moves sign to the /// Numerator.</remarks> /// <example>2/4 will be reduced to 1/2</example> public static void ReduceFraction(ref NuGenFraction frac) { // clean up the NaNs and infinites if (frac.m_Denominator == 0) { frac.m_Numerator = (long)NormalizeIndeterminate(frac.m_Numerator); return; } // all forms of zero are alike. if (frac.m_Numerator == 0) { frac.m_Denominator = 1; return; } long iGCD = GCD(frac.m_Numerator, frac.m_Denominator); frac.m_Numerator /= iGCD; frac.m_Denominator /= iGCD; // if negative sign in denominator if ( frac.m_Denominator < 0 ) { //move negative sign to numerator frac.m_Numerator = - frac.m_Numerator; frac.m_Denominator = - frac.m_Denominator; } }