public void Main() { Fraction frac = new Fraction(); // we'll get NaN Assert.AreEqual(frac, Fraction.NaN); Assert.AreEqual(frac.ToString(), NumberFormatInfo.CurrentInfo.NaNSymbol); frac = new Fraction(1, 5); // we'll get 1/5 Assert.AreEqual(frac.ToString(), "1/5"); frac = new Fraction(25); // we'll get 25 Assert.AreEqual(frac.ToString(), "25"); frac = new Fraction(0.0); // we'll get 0 Assert.AreEqual(frac.ToString(), "0"); frac = new Fraction(0.25); // we'll get 1/4 Assert.AreEqual(frac.ToString(), "1/4"); frac = new Fraction(9.25); // we'll get 37/4 Assert.AreEqual(frac.ToString(), "37/4"); frac = new Fraction(long.MaxValue, 1); string compareTo = string.Format("{0}", long.MaxValue); Assert.AreEqual(frac.ToString(), compareTo); frac = new Fraction(1, long.MaxValue); compareTo = string.Format("1/{0}", long.MaxValue); Assert.AreEqual(frac.ToString(), compareTo); frac = new Fraction(long.MaxValue, long.MaxValue); Assert.AreEqual(frac.ToString(), "1"); // the plus-one issue is because of twos-complement representing one more negtive value than // positive frac = new Fraction(long.MinValue + 1, 1); compareTo = string.Format("{0}", long.MinValue + 1); Assert.AreEqual(frac.ToString(), compareTo); frac = new Fraction(1, long.MinValue + 1); compareTo = string.Format("-1/{0}", Math.Abs(long.MinValue + 1)); Assert.AreEqual(frac.ToString(), compareTo); frac = new Fraction(long.MinValue + 1, long.MinValue + 1); Assert.AreEqual(frac.ToString(), "1"); frac = new Fraction(long.MaxValue, long.MinValue + 1); Assert.AreEqual(frac.ToString(), "-1"); frac = new Fraction(long.MinValue + 1, long.MaxValue); Assert.AreEqual(frac.ToString(), "-1"); frac = new Fraction(0.025); // we'll get 1/40 Assert.AreEqual(frac.ToString(), "1/40"); frac = new Fraction(1 / 2.0); // we'll get 1/2 Assert.AreEqual(frac.ToString(), "1/2"); frac = new Fraction(1 / 3.0); // we'll get 1/3 Assert.AreEqual(frac.ToString(), "1/3"); frac = new Fraction(1 / 4.0); // we'll get 1/4 Assert.AreEqual(frac.ToString(), "1/4"); frac = new Fraction(1 / 5.0); // we'll get 1/5 Assert.AreEqual(frac.ToString(), "1/5"); frac = new Fraction(1 / 6.0); // we'll get 1/6 Assert.AreEqual(frac.ToString(), "1/6"); frac = new Fraction(1 / 7.0); // we'll get 1/7 Assert.AreEqual(frac.ToString(), "1/7"); frac = new Fraction(1 / 8.0); // we'll get 1/8 Assert.AreEqual(frac.ToString(), "1/8"); frac = new Fraction(1 / 9.0); // we'll get 1/9 Assert.AreEqual(frac.ToString(), "1/9"); frac = new Fraction(1 / 10.0); // we'll get 1/10 Assert.AreEqual(frac.ToString(), "1/10"); frac = new Fraction(1 / 49.0); // we'll get 1/49 Assert.AreEqual(frac.ToString(), "1/49"); frac = new Fraction(6); Assert.AreEqual(frac.ToString(), "6"); Fraction divisor = new Fraction(4); Assert.AreEqual(divisor.ToString(), "4"); frac %= divisor; Assert.AreEqual(frac.ToString(), "2"); frac = new Fraction(9, 4); Assert.AreEqual(frac.ToString(), "9/4"); divisor = new Fraction(2); Assert.AreEqual(divisor.ToString(), "2"); frac %= divisor; Assert.AreEqual(frac.ToString(), "1/4"); frac = new Fraction(5, 12); Assert.AreEqual(frac.ToString(), "5/12"); divisor = new Fraction(1, 4); Assert.AreEqual(divisor.ToString(), "1/4"); frac %= divisor; Assert.AreEqual(frac.ToString(), "1/6"); frac = new Fraction(1.0); // we'll get 1 Assert.AreEqual(frac.ToString(), "1"); frac = new Fraction(2.0); // we'll get 2 Assert.AreEqual(frac.ToString(), "2"); frac = new Fraction(-2.0); // we'll get -2 Assert.AreEqual(frac.ToString(), "-2"); frac = new Fraction(-1.0); // we'll get -1 Assert.AreEqual(frac.ToString(), "-1"); frac = new Fraction(0.5); // we'll get 1/2 Assert.AreEqual(frac.ToString(), "1/2"); frac = new Fraction(1.5); // we'll get 3/2 Assert.AreEqual(frac.ToString(), "3/2"); Console.WriteLine("Doing the loop check"); for (int numerator = -100; numerator < 100; numerator++) { Console.Write("{0} ", numerator); for (int denominator = -100; denominator < 100; denominator++) { Fraction frac1 = new Fraction(numerator, denominator); double dbl = (double)numerator / (double)denominator; Fraction frac2 = new Fraction(dbl); Assert.AreEqual(frac1, frac2); } } Console.WriteLine(); frac = new Fraction("6.25"); // we'll get 25/4 Assert.AreEqual(frac.ToString(), "25/4"); frac = 0; Assert.AreEqual(frac.ToString(), "0"); frac = 1; Assert.AreEqual(frac.ToString(), "1"); frac /= new Fraction(0); Assert.AreEqual(frac, Fraction.PositiveInfinity); Assert.AreEqual(frac.ToString(), NumberFormatInfo.CurrentInfo.PositiveInfinitySymbol); frac = -1; Assert.AreEqual(frac.ToString(), "-1"); frac /= new Fraction(0); Assert.AreEqual(frac, Fraction.NegativeInfinity); Assert.AreEqual(frac.ToString(), NumberFormatInfo.CurrentInfo.NegativeInfinitySymbol); // we can enter anything like "213" or // "23/3" or "4.27" Console.Write("Enter a Fraction: "); frac = new Fraction(System.Console.ReadLine()); Console.WriteLine(frac); // displays the current value of frac object; frac = new Fraction("1/2"); // initialize a fraction with 1/2 Assert.AreEqual(frac.ToString(), "1/2"); Console.WriteLine(frac + 2.5); // will display 3 Assert.AreEqual((frac + 2.5).ToString(), "3"); frac = "1/2"; // implicit cast from string to Assert.AreEqual(frac.ToString(), "1/2"); frac = "22.5"; // implicit cast from string to fraction Assert.AreEqual(frac.ToString(), "45/2"); frac = 10.25; // implicit cast from double to fraction Assert.AreEqual(frac.ToString(), "41/4"); frac = 15; // implicit cast from integer/long to fraction Assert.AreEqual(frac.ToString(), "15"); frac = 0.5; // initialize frac=1/2 Assert.AreEqual(frac.ToString(), "1/2"); Console.WriteLine(frac - 0.25); // Yes, you are right. "1/4" is displayed Assert.AreEqual((frac - 0.25).ToString(), "1/4"); Console.WriteLine(frac + "1/4"); Assert.AreEqual((frac + "1/4").ToString(), "3/4"); if (frac.Equals(0.5)) Console.WriteLine("seems that frac == 0.5"); frac += 0.5; Assert.AreEqual(frac.ToString(), "1"); if (frac.Equals(1)) Console.WriteLine("seems that now frac == 1"); frac = double.NaN; Console.WriteLine(frac.ToString()); Assert.AreEqual(frac.ToString(), NumberFormatInfo.CurrentInfo.NaNSymbol); frac = double.PositiveInfinity; Console.WriteLine(frac.ToString()); Assert.AreEqual(frac.ToString(), NumberFormatInfo.CurrentInfo.PositiveInfinitySymbol); frac = double.NegativeInfinity; Console.WriteLine(frac.ToString()); Assert.AreEqual(frac.ToString(), NumberFormatInfo.CurrentInfo.NegativeInfinitySymbol); frac = "33"; frac += "1/3"; Console.WriteLine(frac.ToString()); frac *= 3; Console.WriteLine(frac.ToString()); Console.Write("Any key to quit"); Console.ReadLine(); }
/// <summary> /// Compares this Fraction to another Fraction /// </summary> /// <param name="right">The Fraction 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(Fraction 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 Fraction localThis = new Fraction(this); Fraction localRight = new Fraction(right); CrossReducePair(localThis, localRight); try { checked { long leftScale = localThis.m_Numerator * localRight.m_Denominator; long rightScale = localThis.m_Denominator * localRight.m_Numerator; if (leftScale < rightScale) return -1; else if (leftScale > rightScale) return 1; else return 0; } } catch (Exception e) { throw new FractionException(string.Format("CompareTo({0}, {1}) error", this, right), e); } }
/// <summary> /// Inverts a Fraction /// </summary> /// <returns>The inverted Fraction (with Denominator over Numerator)</returns> /// <remarks>Does NOT throw for zero Numerators as later use of the fraction will catch the error.</remarks> public Fraction Inverse() { // don't use the obvious constructor because we do not want it normalized at this time Fraction frac = new Fraction(); frac.m_Numerator = this.m_Denominator; frac.m_Denominator = this.m_Numerator; return frac; }
/// <summary> /// Creates an inverted Fraction /// </summary> /// <returns>The inverted Fraction (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 Fraction Inverted(double value) { Fraction frac = new Fraction(value); return frac.Inverse(); }
/// <summary> /// Reduces (simplifies) a Fraction by dividing down to lowest possible denominator (via GCD) /// </summary> /// <param name="frac">The Fraction 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(Fraction 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; } }
/// <summary> /// Construct a Fraction from another fraction /// </summary> public Fraction(Fraction f) { this.m_Numerator = f.Numerator; this.m_Denominator = f.Denominator; }
/* /// <summary> /// Reduces (simplifies) a Fraction by dividing down to lowest possible denominator (via GCD) /// </summary> /// <param name="frac">The Fraction to be reduced</param> /// <returns>Will normalize the NaN and infinites representation. /// Will set Denominator to 1 for any zero numerator. /// Moves sign to the Numerator.</returns> /// <example>2/4 will be reduced to 1/2</example> public static Fraction ReduceFraction(Fraction frac) { Fraction reducedFraction = new Fraction(frac); // clean up the NaNs and infinites if (frac.m_Denominator == 0) { reducedFraction.m_Numerator = (long)NormalizeIndeterminate(frac.m_Numerator); return reducedFraction; } // all forms of zero are alike. if (frac.m_Numerator == 0) { reducedFraction.m_Denominator = 1; return reducedFraction; } long iGCD = GCD(frac.m_Numerator, frac.m_Denominator); reducedFraction.m_Numerator /= iGCD; reducedFraction.m_Denominator /= iGCD; // if negative sign in denominator if (frac.m_Denominator < 0) { //move negative sign to numerator reducedFraction.m_Numerator = -frac.m_Numerator; reducedFraction.m_Denominator = -frac.m_Denominator; } return reducedFraction; } */ /// <summary> /// Cross-reduces a pair of Fractions so that we have the best GCD-reduced values for multiplication /// </summary> /// <param name="frac1">The first Fraction [WILL BE MODIFIED IN PLACE]</param> /// <param name="frac2">The second Fraction [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(Fraction frac1, Fraction 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> /// Negates the Fraction /// </summary> /// <param name="frac">Value to negate</param> /// <returns>A new Fraction that is sign-flipped from the input</returns> private static Fraction Negate(Fraction frac) { // for a NaN, it's still a NaN return new Fraction(-frac.m_Numerator, frac.m_Denominator); }
/// <summary> /// Compares for equality the current Fraction to the value passed. /// </summary> /// <param name="right">A Fraction 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(Fraction right, bool notEqualCheck) { // insure we're normalized first ReduceFraction(this); // now normalize the comperand ReduceFraction(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> /// Multiplies two Fractions /// </summary> /// <param name="left">A Fraction</param> /// <param name="right">Another Fraction</param> /// <returns>Product of the Fractions. Returns NaN if either Fraction is a NaN.</returns> /// <exception cref="FractionException">Will throw if an overflow occurs. Does a cross-reduce to /// insure only the unavoidable overflows occur.</exception> private static Fraction Multiply(Fraction left, Fraction right) { if (left.IsNaN() || right.IsNaN()) return NaN; Fraction localLeft = new Fraction(left); Fraction localRight = new Fraction(right); CrossReducePair(localLeft, localRight); try { checked { long numerator = localLeft.m_Numerator * localRight.m_Numerator; long denominator = localLeft.m_Denominator * localRight.m_Denominator; return new Fraction(numerator, denominator); } } catch (Exception e) { throw new FractionException("Multiply error", e); } }
/// <summary> /// Returns the modulus (remainder after dividing) two Fractions /// </summary> /// <param name="left">A Fraction</param> /// <param name="right">Another Fraction</param> /// <returns>Modulus of the Fractions. Returns NaN if either Fraction is a NaN.</returns> /// <exception cref="FractionException">Will throw if an overflow occurs. Does a cross-reduce to /// insure only the unavoidable overflows occur.</exception> private static Fraction Modulus(Fraction left, Fraction right) { if (left.IsNaN() || right.IsNaN()) return NaN; try { checked { // this will discard any fractional places... Int64 quotient = (Int64)(left / right); Fraction whole = new Fraction(quotient * right.m_Numerator, right.m_Denominator); return left - whole; } } catch (Exception e) { throw new FractionException("Modulus error", e); } }
/// <summary> /// Determines how this Fraction, of an indeterminate type, compares to another Fraction /// </summary> /// <param name="leftType">What kind of indeterminate</param> /// <param name="right">The other Fraction 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, Fraction 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> /// Adds two Fractions /// </summary> /// <param name="left">A Fraction</param> /// <param name="right">Another Fraction</param> /// <returns>Sum of the Fractions. Returns NaN if either Fraction is a NaN.</returns> /// <exception cref="FractionException">Will throw if an overflow occurs when computing the /// GCD-normalized values.</exception> private static Fraction Add(Fraction left, Fraction 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 Fraction(numerator, denominator); } } catch (Exception e) { throw new FractionException("Add error", e); } }
private static List<Ingredient> ExtractIngredients(HtmlNode ingredientsNode) { List<Ingredient> ingredients = new List<Ingredient>(); string[] ingredientLines = ingredientsNode.InnerHtml.Split(new string[] { "<br>" }, StringSplitOptions.RemoveEmptyEntries); foreach (string ingredientLine in ingredientLines) { string ingredientName = ingredientLine; Fraction incredientAmountFraction = Fraction.Zero; double incredientWeight = 1; string[] ingredientParsed = null; string measureUnit = GetMeasureUnit(ingredientLine); if (measureUnit != null) { ingredientParsed = ingredientLine.Split(new string[] { measureUnit }, StringSplitOptions.RemoveEmptyEntries); } else { ingredientParsed = ingredientLine.Split(null as string[], 2, StringSplitOptions.RemoveEmptyEntries); } try { if (ingredientParsed.Length == 1) { incredientAmountFraction = new Fraction(1); ingredientName = ingredientParsed[0].Trim(' ', '-'); } else { string incredientAmount = ingredientParsed[0] .Trim(' ', '-') .Replace("חצי", "1/2").Replace("½", "1/2") .Replace("שליש", "1/3") .Replace("רבע", "1/4"); incredientAmountFraction = new Fraction(incredientAmount); ingredientName = ingredientParsed[1].Trim(' ', '-'); if (measureUnit != null) { incredientWeight = incredientAmountFraction.ToDouble() * IncredientAmount.MEASURE_UNITS[measureUnit]; } } } catch (Exception e) { log.WarnFormat("Failed to parse ingredient: {0} {1}", ingredientLine, e); } ProductBasicData product = FindProductByName(ingredientName); string productId = null, productName = null; NutritionFacts nutFacts = new NutritionFacts(); if (product != null) { productId = product.ProductId; productName = product.ProductName; if (product.ExtendedData != null && product.ExtendedData.NutritionTable != null) { nutFacts = new NutritionFacts(product.ExtendedData.NutritionTable); } } Ingredient ingredient = new Ingredient() { Name = ingredientName, ProductId = productId, ProductName = productName, NutritionFacts = nutFacts, Amount = new IncredientAmount() { MeasureUnit = measureUnit, Amount = incredientAmountFraction, Weight = incredientWeight } }; ingredients.Add(ingredient); } return ingredients; }