Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.Analysis.AlgorithmBehavior/HeuristicLab.Analysis.AlgorithmBehavior.Analyzers/3.3/FractionClass.cs @ 10286

Last change on this file since 10286 was 10286, checked in by ascheibe, 11 years ago

#1886 added support for calculating big volumes

File size: 43.8 KB
Line 
1//ascheibe: adapted to use BigInteger instead of long
2
3/*
4* Author: Syed Mehroz Alam
5* Email: smehrozalam@yahoo.com
6* URL: Programming Home "http://www.geocities.com/smehrozalam/"
7*
8*/
9
10using System;
11using System.Globalization;
12using System.Numerics;
13using System.Runtime.InteropServices;
14
15namespace Mehroz {
16  /// <summary>
17  /// Classes Contained:
18  ///     Fraction
19  ///     FractionException
20  /// </summary>
21
22  /// Class name: Fraction
23  /// Developed by: Syed Mehroz Alam
24  /// Email: mailto:smehrozalam@yahoo.com
25  /// URL: http://www.geocities.com/smehrozalam/
26  /// Changes: Marc C. Brooks  mailto:IDisposable@gmail.com
27  ///          Jeffery Sax    http://www.extremeoptimization.com
28  /// Version: 2.2
29  ///
30  /// What's new in version 2.0:
31  ///     *   Changed Numerator and Denominator from Int32(integer) to Int64(long) for increased range
32  ///     *   renamed ConvertToString() to (overloaded) ToString()
33  ///     *   added the capability of detecting/raising overflow exceptions
34  ///     *   Fixed the bug that very small numbers e.g. 0.00000001 could not be converted to fraction
35  ///     *   Other minor bugs fixed
36  ///
37  /// What's new in version 2.1
38  ///     *   overloaded user-defined conversions to/from Fractions
39  ///
40  /// What's new in version 2.2 - Marc C. Brooks   mailto:IDisposable@gmail.com
41  ///     *   less overflows by finding the GCD for Add [Jeffery Sax] and Multiply [Marc C. Brooks]
42  ///     *   understands and handles NaN, PositiveInfinity, NegativeInfinity just like double [Marc C. Brooks]
43  ///     *   fixed several uses of int where long was correct [Marc C. Brooks]
44  ///     *   made value-type (struct) [Jeffery Sax]
45  ///     *   added ToInt32(), ToInt64() which throw for invalid (NaN, PositiveInfinity, NegativeInfinity) [Marc C. Brooks]
46  ///     *   removed redundant Value property [Jeffery Sax]
47  ///     *   added explicit conversion to Int32 and Int64 [Marc C. Brooks]
48  ///     *   better handling of exceptions [Marc C. Brooks]
49  ///     *   reorganize code, add XML doc and regions [Marc C. Brooks]
50  ///     *   proper implementations of Equals [Marc C. Brooks, Jeffery Sax]
51  ///     *   uses Math.Log(xx,2) and Math.Pow(xx,2) to get the best accuracy possible when converting doubles [Marc C. Brooks, Jeffery Sax]
52  ///
53  /// What's new in version 2.3 - Marc C. Brooks   mailto:IDisposable@gmail.com   01/10/2005
54  ///     *   fixed double-to-fraction logic to use continued fraction rules to get best possible precistion [bug fix for Syed Mehroz Alam, idea from  Jeffery Sax]
55  ///     * added static readonly values for NaN, PositiveInfinity, NegativeInfinity [idea from Jeffery Sax]
56  ///     * moved comparisons into an implementation of IComparer [idea from Jeffery Sax]
57  ///     * no longer throws for NaN(s) involved in Add, Subtract, Multiply, Divide operations [idea from Jeffery Sax]
58  ///     *   added static readonly values for Zero, MinValue, MaxValue, Epsilon to better mirror double
59  ///     *   added IsInfinity to better mirror double.
60  ///     *   added Modulus and % operators
61  ///
62  /// Properties:
63  ///     Numerator: Set/Get value for Numerator
64  ///     Denominator:  Set/Get value for Numerator
65  ///     [Note: If you Set either Property, the Fraction should be passed to ReduceFraction at some point.]
66  ///
67  /// Constructors:
68  ///     no arguments:   initializes fraction as 0/0 = NaN, so don't do that!
69  ///     (Numerator, Denominator): initializes fraction with the given numerator and denominator
70  ///                               values and reduces
71  ///     (long): initializes fraction with the given long value
72  ///     (double):   initializes fraction with the given double value
73  ///     (string):   initializes fraction with the given string value
74  ///                 the string can be an in the form of and integer, double or fraction.
75  ///                 e.g it can be like "123" or "123.321" or "123/456"
76  ///
77  /// Public Methods (Description is given with respective methods' definitions)
78  ///     Fraction ToFraction(long)
79  ///     Fraction ToFraction(double)
80  ///     Fraction ToFraction(string)
81  ///     Int32 ToInt32()
82  ///     Int64 ToInt64()
83  ///     double ToDouble()
84  ///     (override) string ToString()
85  ///     Fraction Inverse()
86  ///     Fraction Inverted(long)
87  ///     Fraction Inverted(double)
88  ///     ReduceFraction(ref Fraction)
89  ///     CrossReducePair(ref Fraction, ref Fraction)
90  ///     (override) Equals(object)
91  ///     (override) GetHashCode()
92  ///
93  /// Overloaded Operators (overloaded for Fractions, long and double)
94  ///     Unary: -
95  ///     Binary: +,-,*,/
96  ///     Relational and Logical Operators: ==,!=,<,>,<=,>= (only == and != for long and doubles)
97  ///
98  /// Overloaded user-defined conversions
99  ///     Implicit:   From long/double/string to Fraction
100  ///     Explicit:   From Fraction to long/double/string
101  /// </summary>
102  [Serializable, StructLayout(LayoutKind.Sequential)]
103  public struct Fraction : IComparable, IFormattable {
104    #region Constructors
105    /// <summary>
106    /// Construct a Fraction from an integral value
107    /// </summary>
108    /// <param name="wholeNumber">The value (eventual numerator)</param>
109    /// <remarks>The denominator will be 1</remarks>
110    public Fraction(long wholeNumber) {
111      if (wholeNumber == long.MinValue)
112        wholeNumber++;  // prevent serious issues later..
113
114      m_Numerator = wholeNumber;
115      m_Denominator = 1;
116      // no reducing required, we're a whole number
117    }
118
119    /// <summary>
120    /// Construct a Fraction from a floating-point value
121    /// </summary>
122    /// <param name="floatingPointNumber">The value</param>
123    public Fraction(double floatingPointNumber) {
124      this = ToFraction(floatingPointNumber);
125    }
126
127    /// <summary>
128    /// Construct a Fraction from a string in any legal format
129    /// </summary>
130    /// <param name="inValue">A string with a legal fraction input format</param>
131    /// <remarks>Will reduce the fraction to smallest possible denominator</remarks>
132    /// <see>ToFraction(string strValue)</see>
133    public Fraction(string inValue) {
134      this = ToFraction(inValue);
135    }
136
137    /// <summary>
138    /// Construct a Fraction from a numerator, denominator pair
139    /// </summary>
140    /// <param name="numerator">The numerator (top number)</param>
141    /// <param name="denominator">The denominator (bottom number)</param>
142    /// <remarks>Will reduce the fraction to smallest possible denominator</remarks>
143    public Fraction(BigInteger numerator, BigInteger denominator) {
144      m_Numerator = numerator;
145      m_Denominator = denominator;
146      ReduceFraction(ref this);
147    }
148
149    /// <summary>
150    /// Private constructor to synthesize a Fraction for indeterminates (NaN and infinites)
151    /// </summary>
152    /// <param name="type">Kind of inderterminate</param>
153    private Fraction(Indeterminates type) {
154      m_Numerator = (long)type;
155      m_Denominator = 0;
156      // do NOT reduce, we're clean as can be!
157    }
158    #endregion
159
160    #region Properties
161    /// <summary>
162    /// The 'top' part of the fraction
163    /// </summary>
164    /// <example>For 3/4ths, this is the 3</example>
165    public BigInteger Numerator {
166      get {
167        return m_Numerator;
168      }
169      set {
170        m_Numerator = value;
171      }
172    }
173
174    /// <summary>
175    /// The 'bottom' part of the fraction
176    /// </summary>
177    /// <example>For 3/4ths, this is the 4</example>
178    public BigInteger Denominator {
179      get {
180        return m_Denominator;
181      }
182      set {
183        m_Denominator = value;
184      }
185    }
186    #endregion
187
188    #region Expose constants
189    public static readonly Fraction NaN = new Fraction(Indeterminates.NaN);
190    public static readonly Fraction PositiveInfinity = new Fraction(Indeterminates.PositiveInfinity);
191    public static readonly Fraction NegativeInfinity = new Fraction(Indeterminates.NegativeInfinity);
192    public static readonly Fraction Zero = new Fraction(0, 1);
193    public static readonly Fraction Epsilon = new Fraction(1, Int64.MaxValue);
194    private static readonly double EpsilonDouble = 1.0 / Int64.MaxValue;
195    public static readonly Fraction MaxValue = new Fraction(Int64.MaxValue, 1);
196    public static readonly Fraction MinValue = new Fraction(Int64.MinValue, 1);
197    #endregion
198
199    #region Explicit conversions
200    #region To primitives
201    /// <summary>
202    /// Get the integral value of the Fraction object as int/Int32
203    /// </summary>
204    /// <returns>The (approximate) integer value</returns>
205    /// <remarks>If the value is not a true integer, the fractional part is discarded
206    /// (truncated toward zero). If the valid exceeds the range of an Int32 and exception is thrown.</remarks>
207    /// <exception cref="FractionException">Will throw a FractionException for NaN, PositiveInfinity
208    /// or NegativeInfinity with the InnerException set to a System.NotFiniteNumberException.</exception>
209    /// <exception cref="OverflowException" Will throw a System.OverflowException if the value is too
210    /// large or small to be represented as an Int32.</exception>
211    public Int32 ToInt32() {
212      if (this.m_Denominator == 0) {
213        throw new FractionException(string.Format("Cannot convert {0} to Int32", IndeterminateTypeName(this.m_Numerator)), new System.NotFiniteNumberException());
214      }
215
216      BigInteger bestGuess = this.m_Numerator / this.m_Denominator;
217
218      if (bestGuess > Int32.MaxValue || bestGuess < Int32.MinValue) {
219        throw new FractionException("Cannot convert to Int32", new System.OverflowException());
220      }
221
222      return (Int32)bestGuess;
223    }
224
225    /// <summary>
226    /// Get the value of the Fraction object as double with full support for NaNs and infinities
227    /// </summary>
228    /// <returns>The decimal representation of the Fraction, or double.NaN, double.NegativeInfinity
229    /// or double.PositiveInfinity</returns>
230    public double ToDouble() {
231      if (this.m_Denominator == 1)
232        return (double)this.m_Numerator;
233      else if (this.m_Denominator == 0) {
234        switch (NormalizeIndeterminate(this.m_Numerator)) {
235          case Indeterminates.NegativeInfinity:
236            return double.NegativeInfinity;
237
238          case Indeterminates.PositiveInfinity:
239            return double.PositiveInfinity;
240
241          case Indeterminates.NaN:
242          default:    // this can't happen
243            return double.NaN;
244        }
245      } else {
246        ReduceFraction(ref this);
247        return (double)(this.m_Numerator / this.m_Denominator);
248      }
249    }
250
251    /// <summary>
252    /// Get the value of the Fraction as a string, with proper representation for NaNs and infinites
253    /// </summary>
254    /// <returns>The string representation of the Fraction, or the culture-specific representations of
255    /// NaN, PositiveInfinity or NegativeInfinity.</returns>
256    /// <remarks>The current culture determines the textual representation the Indeterminates</remarks>
257    public override string ToString() {
258      if (this.m_Denominator == 1) {
259        return this.m_Numerator.ToString();
260      } else if (this.m_Denominator == 0) {
261        return IndeterminateTypeName(this.m_Numerator);
262      } else {
263        return this.m_Numerator.ToString() + "/" + this.m_Denominator.ToString();
264      }
265    }
266    #endregion
267
268    #region From primitives
269    /// <summary>
270    /// Converts a long value to the exact Fraction
271    /// </summary>
272    /// <param name="inValue">The long to convert</param>
273    /// <returns>An exact representation of the value</returns>
274    public static Fraction ToFraction(long inValue) {
275      return new Fraction(inValue);
276    }
277
278    /// <summary>
279    /// Converts a double value to the approximate Fraction
280    /// </summary>
281    /// <param name="inValue">The double to convert</param>
282    /// <returns>A best-fit representation of the value</returns>
283    /// <remarks>Supports double.NaN, double.PositiveInfinity and double.NegativeInfinity</remarks>
284    public static Fraction ToFraction(double inValue) {
285      // it's one of the indeterminates... which?
286      if (double.IsNaN(inValue))
287        return NaN;
288      else if (double.IsNegativeInfinity(inValue))
289        return NegativeInfinity;
290      else if (double.IsPositiveInfinity(inValue))
291        return PositiveInfinity;
292      else if (inValue == 0.0d)
293        return Zero;
294
295      int sign = Math.Sign(inValue);
296      inValue = Math.Abs(inValue);
297
298      return ConvertPositiveDouble(sign, inValue);
299    }
300
301    /// <summary>
302    /// Converts a string to the corresponding reduced fraction
303    /// </summary>
304    /// <param name="inValue">The string representation of a fractional value</param>
305    /// <returns>The Fraction that represents the string</returns>
306    /// <remarks>Four forms are supported, as a plain integer, as a double, or as Numerator/Denominator
307    /// and the representations for NaN and the infinites</remarks>
308    /// <example>"123" = 123/1 and "1.25" = 5/4 and "10/36" = 5/13 and NaN = 0/0 and
309    /// PositiveInfinity = 1/0 and NegativeInfinity = -1/0</example>
310    public static Fraction ToFraction(string inValue) {
311      if (inValue == null || inValue == string.Empty)
312        throw new ArgumentNullException("inValue");
313
314      // could also be NumberFormatInfo.InvariantInfo
315      NumberFormatInfo info = NumberFormatInfo.CurrentInfo;
316
317      // Is it one of the special symbols for NaN and such...
318      string trimmedValue = inValue.Trim();
319
320      if (trimmedValue == info.NaNSymbol)
321        return NaN;
322      else if (trimmedValue == info.PositiveInfinitySymbol)
323        return PositiveInfinity;
324      else if (trimmedValue == info.NegativeInfinitySymbol)
325        return NegativeInfinity;
326      else {
327        // Not special, is it a Fraction?
328        int slashPos = inValue.IndexOf('/');
329
330        if (slashPos > -1) {
331          // string is in the form of Numerator/Denominator
332          long numerator = Convert.ToInt64(inValue.Substring(0, slashPos));
333          long denominator = Convert.ToInt64(inValue.Substring(slashPos + 1));
334
335          return new Fraction(numerator, denominator);
336        } else {
337          // the string is not in the form of a fraction
338          // hopefully it is double or integer, do we see a decimal point?
339          int decimalPos = inValue.IndexOf(info.CurrencyDecimalSeparator);
340
341          if (decimalPos > -1)
342            return new Fraction(Convert.ToDouble(inValue));
343          else
344            return new Fraction(Convert.ToInt64(inValue));
345        }
346      }
347    }
348    #endregion
349    #endregion
350
351    #region Indeterminate classifications
352    /// <summary>
353    /// Determines if a Fraction represents a Not-a-Number
354    /// </summary>
355    /// <returns>True if the Fraction is a NaN</returns>
356    public bool IsNaN() {
357      if (this.m_Denominator == 0
358        && NormalizeIndeterminate(this.m_Numerator) == Indeterminates.NaN)
359        return true;
360      else
361        return false;
362    }
363
364    /// <summary>
365    /// Determines if a Fraction represents Any Infinity
366    /// </summary>
367    /// <returns>True if the Fraction is Positive Infinity or Negative Infinity</returns>
368    public bool IsInfinity() {
369      if (this.m_Denominator == 0
370        && NormalizeIndeterminate(this.m_Numerator) != Indeterminates.NaN)
371        return true;
372      else
373        return false;
374    }
375
376    /// <summary>
377    /// Determines if a Fraction represents Positive Infinity
378    /// </summary>
379    /// <returns>True if the Fraction is Positive Infinity</returns>
380    public bool IsPositiveInfinity() {
381      if (this.m_Denominator == 0
382        && NormalizeIndeterminate(this.m_Numerator) == Indeterminates.PositiveInfinity)
383        return true;
384      else
385        return false;
386    }
387
388    /// <summary>
389    /// Determines if a Fraction represents Negative Infinity
390    /// </summary>
391    /// <returns>True if the Fraction is Negative Infinity</returns>
392    public bool IsNegativeInfinity() {
393      if (this.m_Denominator == 0
394        && NormalizeIndeterminate(this.m_Numerator) == Indeterminates.NegativeInfinity)
395        return true;
396      else
397        return false;
398    }
399    #endregion
400
401    #region Inversion
402    /// <summary>
403    /// Inverts a Fraction
404    /// </summary>
405    /// <returns>The inverted Fraction (with Denominator over Numerator)</returns>
406    /// <remarks>Does NOT throw for zero Numerators as later use of the fraction will catch the error.</remarks>
407    public Fraction Inverse() {
408      // don't use the obvious constructor because we do not want it normalized at this time
409      Fraction frac = new Fraction();
410
411      frac.m_Numerator = this.m_Denominator;
412      frac.m_Denominator = this.m_Numerator;
413      return frac;
414    }
415
416    /// <summary>
417    /// Creates an inverted Fraction
418    /// </summary>
419    /// <returns>The inverted Fraction (with Denominator over Numerator)</returns>
420    /// <remarks>Does NOT throw for zero Numerators as later use of the fraction will catch the error.</remarks>
421    public static Fraction Inverted(long value) {
422      Fraction frac = new Fraction(value);
423      return frac.Inverse();
424    }
425
426    /// <summary>
427    /// Creates an inverted Fraction
428    /// </summary>
429    /// <returns>The inverted Fraction (with Denominator over Numerator)</returns>
430    /// <remarks>Does NOT throw for zero Numerators as later use of the fraction will catch the error.</remarks>
431    public static Fraction Inverted(double value) {
432      Fraction frac = new Fraction(value);
433      return frac.Inverse();
434    }
435    #endregion
436
437    #region Operators
438    #region Unary Negation operator
439    /// <summary>
440    /// Negates the Fraction
441    /// </summary>
442    /// <param name="left">The Fraction to negate</param>
443    /// <returns>The negative version of the Fraction</returns>
444    public static Fraction operator -(Fraction left) {
445      return Negate(left);
446    }
447    #endregion
448
449    #region Addition operators
450    public static Fraction operator +(Fraction left, Fraction right) {
451      return Add(left, right);
452    }
453
454    public static Fraction operator +(long left, Fraction right) {
455      return Add(new Fraction(left), right);
456    }
457
458    public static Fraction operator +(Fraction left, long right) {
459      return Add(left, new Fraction(right));
460    }
461
462    public static Fraction operator +(double left, Fraction right) {
463      return Add(ToFraction(left), right);
464    }
465
466    public static Fraction operator +(Fraction left, double right) {
467      return Add(left, ToFraction(right));
468    }
469    #endregion
470
471    #region Subtraction operators
472    public static Fraction operator -(Fraction left, Fraction right) {
473      return Add(left, -right);
474    }
475
476    public static Fraction operator -(long left, Fraction right) {
477      return Add(new Fraction(left), -right);
478    }
479
480    public static Fraction operator -(Fraction left, long right) {
481      return Add(left, new Fraction(-right));
482    }
483
484    public static Fraction operator -(double left, Fraction right) {
485      return Add(ToFraction(left), -right);
486    }
487
488    public static Fraction operator -(Fraction left, double right) {
489      return Add(left, ToFraction(-right));
490    }
491    #endregion
492
493    #region Multiplication operators
494    public static Fraction operator *(Fraction left, Fraction right) {
495      return Multiply(left, right);
496    }
497
498    public static Fraction operator *(long left, Fraction right) {
499      return Multiply(new Fraction(left), right);
500    }
501
502    public static Fraction operator *(Fraction left, long right) {
503      return Multiply(left, new Fraction(right));
504    }
505
506    public static Fraction operator *(double left, Fraction right) {
507      return Multiply(ToFraction(left), right);
508    }
509
510    public static Fraction operator *(Fraction left, double right) {
511      return Multiply(left, ToFraction(right));
512    }
513    #endregion
514
515    #region Division operators
516    public static Fraction operator /(Fraction left, Fraction right) {
517      return Multiply(left, right.Inverse());
518    }
519
520    public static Fraction operator /(long left, Fraction right) {
521      return Multiply(new Fraction(left), right.Inverse());
522    }
523
524    public static Fraction operator /(Fraction left, long right) {
525      return Multiply(left, Inverted(right));
526    }
527
528    public static Fraction operator /(double left, Fraction right) {
529      return Multiply(ToFraction(left), right.Inverse());
530    }
531
532    public static Fraction operator /(Fraction left, double right) {
533      return Multiply(left, Inverted(right));
534    }
535    #endregion
536
537    #region Modulus operators
538    public static Fraction operator %(Fraction left, Fraction right) {
539      return Modulus(left, right);
540    }
541
542    public static Fraction operator %(long left, Fraction right) {
543      return Modulus(new Fraction(left), right);
544    }
545
546    public static Fraction operator %(Fraction left, long right) {
547      return Modulus(left, right);
548    }
549
550    public static Fraction operator %(double left, Fraction right) {
551      return Modulus(ToFraction(left), right);
552    }
553
554    public static Fraction operator %(Fraction left, double right) {
555      return Modulus(left, right);
556    }
557    #endregion
558
559    #region Equal operators
560    public static bool operator ==(Fraction left, Fraction right) {
561      return left.CompareEquality(right, false);
562    }
563
564    public static bool operator ==(Fraction left, long right) {
565      return left.CompareEquality(new Fraction(right), false);
566    }
567
568    public static bool operator ==(Fraction left, double right) {
569      return left.CompareEquality(new Fraction(right), false);
570    }
571    #endregion
572
573    #region Not-equal operators
574    public static bool operator !=(Fraction left, Fraction right) {
575      return left.CompareEquality(right, true);
576    }
577
578    public static bool operator !=(Fraction left, long right) {
579      return left.CompareEquality(new Fraction(right), true);
580    }
581
582    public static bool operator !=(Fraction left, double right) {
583      return left.CompareEquality(new Fraction(right), true);
584    }
585    #endregion
586
587    #region Inequality operators
588    /// <summary>
589    /// Compares two Fractions to see if left is less than right
590    /// </summary>
591    /// <param name="left">The first Fraction</param>
592    /// <param name="right">The second Fraction</param>
593    /// <returns>True if <paramref name="left">left</paramref> is less
594    /// than <paramref name="right">right</paramref></returns>
595    /// <remarks>Special handling for indeterminates exists. <see>IndeterminateLess</see></remarks>
596    /// <exception cref="FractionException">Throws an error if overflows occur while computing the
597    /// difference with an InnerException of OverflowException</exception>
598    public static bool operator <(Fraction left, Fraction right) {
599      return left.CompareTo(right) < 0;
600    }
601
602    /// <summary>
603    /// Compares two Fractions to see if left is greater than right
604    /// </summary>
605    /// <param name="left">The first Fraction</param>
606    /// <param name="right">The second Fraction</param>
607    /// <returns>True if <paramref name="left">left</paramref> is greater
608    /// than <paramref name="right">right</paramref></returns>
609    /// <remarks>Special handling for indeterminates exists. <see>IndeterminateLess</see></remarks>
610    /// <exception cref="FractionException">Throws an error if overflows occur while computing the
611    /// difference with an InnerException of OverflowException</exception>
612    public static bool operator >(Fraction left, Fraction right) {
613      return left.CompareTo(right) > 0;
614    }
615
616    /// <summary>
617    /// Compares two Fractions to see if left is less than or equal to right
618    /// </summary>
619    /// <param name="left">The first Fraction</param>
620    /// <param name="right">The second Fraction</param>
621    /// <returns>True if <paramref name="left">left</paramref> is less than or
622    /// equal to <paramref name="right">right</paramref></returns>
623    /// <remarks>Special handling for indeterminates exists. <see>IndeterminateLessEqual</see></remarks>
624    /// <exception cref="FractionException">Throws an error if overflows occur while computing the
625    /// difference with an InnerException of OverflowException</exception>
626    public static bool operator <=(Fraction left, Fraction right) {
627      return left.CompareTo(right) <= 0;
628    }
629
630    /// <summary>
631    /// Compares two Fractions to see if left is greater than or equal to right
632    /// </summary>
633    /// <param name="left">The first Fraction</param>
634    /// <param name="right">The second Fraction</param>
635    /// <returns>True if <paramref name="left">left</paramref> is greater than or
636    /// equal to <paramref name="right">right</paramref></returns>
637    /// <remarks>Special handling for indeterminates exists. <see>IndeterminateLessEqual</see></remarks>
638    /// <exception cref="FractionException">Throws an error if overflows occur while computing the
639    /// difference with an InnerException of OverflowException</exception>
640    public static bool operator >=(Fraction left, Fraction right) {
641      return left.CompareTo(right) >= 0;
642    }
643    #endregion
644
645    #region Implict conversion from primitive operators
646    /// <summary>
647    /// Implicit conversion of a long integral value to a Fraction
648    /// </summary>
649    /// <param name="value">The long integral value to convert</param>
650    /// <returns>A Fraction whose denominator is 1</returns>
651    public static implicit operator Fraction(long value) {
652      return new Fraction(value);
653    }
654
655    /// <summary>
656    /// Implicit conversion of a double floating point value to a Fraction
657    /// </summary>
658    /// <param name="value">The double value to convert</param>
659    /// <returns>A reduced Fraction</returns>
660    public static implicit operator Fraction(double value) {
661      return new Fraction(value);
662    }
663
664    /// <summary>
665    /// Implicit conversion of a string to a Fraction
666    /// </summary>
667    /// <param name="value">The string to convert</param>
668    /// <returns>A reduced Fraction</returns>
669    public static implicit operator Fraction(string value) {
670      return new Fraction(value);
671    }
672    #endregion
673
674    #region Explicit converstion to primitive operators
675    /// <summary>
676    /// Explicit conversion from a Fraction to an integer
677    /// </summary>
678    /// <param name="frac">the Fraction to convert</param>
679    /// <returns>The integral representation of the Fraction</returns>
680    public static explicit operator int(Fraction frac) {
681      return frac.ToInt32();
682    }
683
684    /// <summary>
685    /// Explicit conversion from a Fraction to a double floating-point value
686    /// </summary>
687    /// <param name="frac">The Fraction to convert</param>
688    /// <returns>The double representation of the Fraction</returns>
689    public static explicit operator double(Fraction frac) {
690      return frac.ToDouble();
691    }
692
693    /// <summary>
694    /// Explicit conversion from a Fraction to a string
695    /// </summary>
696    /// <param name="frac">the Fraction to convert</param>
697    /// <returns>The string representation of the Fraction</returns>
698    public static implicit operator string(Fraction frac) {
699      return frac.ToString();
700    }
701    #endregion
702    #endregion
703
704    #region Equals and GetHashCode overrides
705    /// <summary>
706    /// Compares for equality the current Fraction to the value passed.
707    /// </summary>
708    /// <param name="obj">A  Fraction,</param>
709    /// <returns>True if the value equals the current fraction, false otherwise (including for
710    /// non-Fraction types or null object.</returns>
711    public override bool Equals(object obj) {
712      if (obj == null || !(obj is Fraction))
713        return false;
714
715      try {
716        Fraction right = (Fraction)obj;
717        return this.CompareEquality(right, false);
718      }
719      catch {
720        // can't throw in an Equals!
721        return false;
722      }
723    }
724
725    /// <summary>
726    /// Returns a hash code generated from the current Fraction
727    /// </summary>
728    /// <returns>The hash code</returns>
729    /// <remarks>Reduces (in-place) the Fraction first.</remarks>
730    public override int GetHashCode() {
731      // insure we're as close to normalized as possible first
732      ReduceFraction(ref this);
733
734      int numeratorHash = this.m_Numerator.GetHashCode();
735      int denominatorHash = this.m_Denominator.GetHashCode();
736
737      return (numeratorHash ^ denominatorHash);
738    }
739    #endregion
740
741    #region IComparable member and type-specific version
742    /// <summary>
743    /// Compares an object to this Fraction
744    /// </summary>
745    /// <param name="obj">The object to compare against (null is less than everything)</param>
746    /// <returns>-1 if this is less than <paramref name="obj"></paramref>,
747    ///  0 if they are equal,
748    ///  1 if this is greater than <paramref name="obj"></paramref></returns>
749    /// <remarks>Will convert an object from longs, doubles, and strings as this is a value-type.</remarks>
750    public int CompareTo(object obj) {
751      if (obj == null)
752        return 1; // null is less than anything
753
754      Fraction right;
755
756      if (obj is Fraction)
757        right = (Fraction)obj;
758      else if (obj is long)
759        right = (long)obj;
760      else if (obj is double)
761        right = (double)obj;
762      else if (obj is string)
763        right = (string)obj;
764      else
765        throw new ArgumentException("Must be convertible to Fraction", "obj");
766
767      return this.CompareTo(right);
768    }
769
770    /// <summary>
771    /// Compares this Fraction to another Fraction
772    /// </summary>
773    /// <param name="right">The Fraction to compare against</param>
774    /// <returns>-1 if this is less than <paramref name="right"></paramref>,
775    ///  0 if they are equal,
776    ///  1 if this is greater than <paramref name="right"></paramref></returns>
777    public int CompareTo(Fraction right) {
778      // if left is an indeterminate, punt to the helper...
779      if (this.m_Denominator == 0) {
780        return IndeterminantCompare(NormalizeIndeterminate(this.m_Numerator), right);
781      }
782
783      // if right is an indeterminate, punt to the helper...
784      if (right.m_Denominator == 0) {
785        // note sign-flip...
786        return -IndeterminantCompare(NormalizeIndeterminate(right.m_Numerator), this);
787      }
788
789      // they're both normal Fractions
790      CrossReducePair(ref this, ref right);
791
792      try {
793        checked {
794          BigInteger leftScale = this.m_Numerator * right.m_Denominator;
795          BigInteger rightScale = this.m_Denominator * right.m_Numerator;
796
797          if (leftScale < rightScale)
798            return -1;
799          else if (leftScale > rightScale)
800            return 1;
801          else
802            return 0;
803        }
804      }
805      catch (Exception e) {
806        throw new FractionException(string.Format("CompareTo({0}, {1}) error", this, right), e);
807      }
808    }
809    #endregion
810
811    #region IFormattable Members
812    string System.IFormattable.ToString(string format, IFormatProvider formatProvider) {
813      return this.m_Numerator.ToString(format, formatProvider) + "/" + this.m_Denominator.ToString(format, formatProvider);
814    }
815    #endregion
816
817    #region Reduction
818    /// <summary>
819    /// Reduces (simplifies) a Fraction by dividing down to lowest possible denominator (via GCD)
820    /// </summary>
821    /// <param name="frac">The Fraction to be reduced [WILL BE MODIFIED IN PLACE]</param>
822    /// <remarks>Modifies the input arguments in-place! Will normalize the NaN and infinites
823    /// representation. Will set Denominator to 1 for any zero numerator. Moves sign to the
824    /// Numerator.</remarks>
825    /// <example>2/4 will be reduced to 1/2</example>
826    public static void ReduceFraction(ref Fraction frac) {
827      // clean up the NaNs and infinites
828      if (frac.m_Denominator == 0) {
829        frac.m_Numerator = (long)NormalizeIndeterminate(frac.m_Numerator);
830        return;
831      }
832
833      // all forms of zero are alike.
834      if (frac.m_Numerator == 0) {
835        frac.m_Denominator = 1;
836        return;
837      }
838
839      BigInteger iGCD = BigInteger.GreatestCommonDivisor(frac.m_Numerator, frac.m_Denominator);
840      frac.m_Numerator /= iGCD;
841      frac.m_Denominator /= iGCD;
842
843      // if negative sign in denominator
844      if (frac.m_Denominator < 0) {
845        //move negative sign to numerator
846        frac.m_Numerator = -frac.m_Numerator;
847        frac.m_Denominator = -frac.m_Denominator;
848      }
849    }
850
851    /// <summary>
852    /// Cross-reduces a pair of Fractions so that we have the best GCD-reduced values for multiplication
853    /// </summary>
854    /// <param name="frac1">The first Fraction [WILL BE MODIFIED IN PLACE]</param>
855    /// <param name="frac2">The second Fraction [WILL BE MODIFIED IN PLACE]</param>
856    /// <remarks>Modifies the input arguments in-place!</remarks>
857    /// <example>(3/4, 5/9) = (1/4, 5/3)</example>
858    public static void CrossReducePair(ref Fraction frac1, ref Fraction frac2) {
859      // leave the indeterminates alone!
860      if (frac1.m_Denominator == 0 || frac2.m_Denominator == 0)
861        return;
862
863      BigInteger gcdTop = BigInteger.GreatestCommonDivisor(frac1.m_Numerator, frac2.m_Denominator);
864      frac1.m_Numerator = frac1.m_Numerator / gcdTop;
865      frac2.m_Denominator = frac2.m_Denominator / gcdTop;
866
867      BigInteger gcdBottom = BigInteger.GreatestCommonDivisor(frac1.m_Denominator, frac2.m_Numerator);
868      frac2.m_Numerator = frac2.m_Numerator / gcdBottom;
869      frac1.m_Denominator = frac1.m_Denominator / gcdBottom;
870    }
871    #endregion
872
873    #region Implementation
874    #region Convert a double to a fraction
875    private static Fraction ConvertPositiveDouble(int sign, double inValue) {
876      // Shamelessly stolen from http://homepage.smc.edu/kennedy_john/CONFRAC.PDF
877      // with AccuracyFactor == double.Episilon
878      long fractionNumerator = (long)inValue;
879      double fractionDenominator = 1;
880      double previousDenominator = 0;
881      double remainingDigits = inValue;
882      int maxIterations = 594;  // found at http://www.ozgrid.com/forum/archive/index.php/t-22530.html
883
884      while (remainingDigits != Math.Floor(remainingDigits)
885        && Math.Abs(inValue - (fractionNumerator / fractionDenominator)) > double.Epsilon) {
886        remainingDigits = 1.0 / (remainingDigits - Math.Floor(remainingDigits));
887
888        double scratch = fractionDenominator;
889
890        fractionDenominator = (Math.Floor(remainingDigits) * fractionDenominator) + previousDenominator;
891        fractionNumerator = (long)(inValue * fractionDenominator + 0.5);
892
893        previousDenominator = scratch;
894
895        if (maxIterations-- < 0)
896          break;
897      }
898
899      return new Fraction(fractionNumerator * sign, (long)fractionDenominator);
900    }
901    #endregion
902
903    #region Equality helper
904    /// <summary>
905    /// Compares for equality the current Fraction to the value passed.
906    /// </summary>
907    /// <param name="right">A Fraction to compare against</param>
908    /// <param name="notEqualCheck">If true, we're looking for not-equal</param>
909    /// <returns>True if the <paramref name="right"></paramref> equals the current
910    /// fraction, false otherwise. If comparing two NaNs, they are always equal AND
911    /// not-equal.</returns>
912    private bool CompareEquality(Fraction right, bool notEqualCheck) {
913      // insure we're normalized first
914      ReduceFraction(ref this);
915
916      // now normalize the comperand
917      ReduceFraction(ref right);
918
919      if (this.m_Numerator == right.m_Numerator && this.m_Denominator == right.m_Denominator) {
920        // special-case rule, two NaNs are always both equal
921        if (notEqualCheck && this.IsNaN())
922          return true;
923        else
924          return !notEqualCheck;
925      } else {
926        return notEqualCheck;
927      }
928    }
929    #endregion
930
931    #region Comparison helper
932    /// <summary>
933    /// Determines how this Fraction, of an indeterminate type, compares to another Fraction
934    /// </summary>
935    /// <param name="leftType">What kind of indeterminate</param>
936    /// <param name="right">The other Fraction to compare against</param>
937    /// <returns>-1 if this is less than <paramref name="right"></paramref>,
938    ///  0 if they are equal,
939    ///  1 if this is greater than <paramref name="right"></paramref></returns>
940    /// <remarks>NaN is less than anything except NaN and Negative Infinity. Negative Infinity is less
941    /// than anything except Negative Infinity. Positive Infinity is greater than anything except
942    /// Positive Infinity.</remarks>
943    private static int IndeterminantCompare(Indeterminates leftType, Fraction right) {
944      switch (leftType) {
945        case Indeterminates.NaN:
946          // A NaN is...
947          if (right.IsNaN())
948            return 0; // equal to a NaN
949          else if (right.IsNegativeInfinity())
950            return 1; // great than Negative Infinity
951          else
952            return -1;  // less than anything else
953
954        case Indeterminates.NegativeInfinity:
955          // Negative Infinity is...
956          if (right.IsNegativeInfinity())
957            return 0; // equal to Negative Infinity
958          else
959            return -1;  // less than anything else
960
961        case Indeterminates.PositiveInfinity:
962          if (right.IsPositiveInfinity())
963            return 0; // equal to Positive Infinity
964          else
965            return 1; // greater than anything else
966
967        default:
968          // this CAN'T happen, something VERY wrong is going on...
969          return 0;
970      }
971    }
972    #endregion
973
974    #region Math helpers
975    /// <summary>
976    /// Negates the Fraction
977    /// </summary>
978    /// <param name="frac">Value to negate</param>
979    /// <returns>A new Fraction that is sign-flipped from the input</returns>
980    private static Fraction Negate(Fraction frac) {
981      // for a NaN, it's still a NaN
982      return new Fraction(-frac.m_Numerator, frac.m_Denominator);
983    }
984
985    /// <summary>
986    /// Adds two Fractions
987    /// </summary>
988    /// <param name="left">A Fraction</param>
989    /// <param name="right">Another Fraction</param>
990    /// <returns>Sum of the Fractions. Returns NaN if either Fraction is a NaN.</returns>
991    /// <exception cref="FractionException">Will throw if an overflow occurs when computing the
992    /// GCD-normalized values.</exception>
993    private static Fraction Add(Fraction left, Fraction right) {
994      if (left.IsNaN() || right.IsNaN())
995        return NaN;
996
997      BigInteger gcd = BigInteger.GreatestCommonDivisor(left.m_Denominator, right.m_Denominator); // cannot return less than 1
998      BigInteger leftDenominator = left.m_Denominator / gcd;
999      BigInteger rightDenominator = right.m_Denominator / gcd;
1000
1001      try {
1002        checked {
1003          BigInteger numerator = left.m_Numerator * rightDenominator + right.m_Numerator * leftDenominator;
1004          BigInteger denominator = leftDenominator * rightDenominator * gcd;
1005
1006          return new Fraction(numerator, denominator);
1007        }
1008      }
1009      catch (Exception e) {
1010        throw new FractionException("Add error", e);
1011      }
1012    }
1013
1014    /// <summary>
1015    /// Multiplies two Fractions
1016    /// </summary>
1017    /// <param name="left">A Fraction</param>
1018    /// <param name="right">Another Fraction</param>
1019    /// <returns>Product of the Fractions. Returns NaN if either Fraction is a NaN.</returns>
1020    /// <exception cref="FractionException">Will throw if an overflow occurs. Does a cross-reduce to
1021    /// insure only the unavoidable overflows occur.</exception>
1022    private static Fraction Multiply(Fraction left, Fraction right) {
1023      if (left.IsNaN() || right.IsNaN())
1024        return NaN;
1025
1026      // this would be unsafe if we were not a ValueType, because we would be changing the
1027      // caller's values.  If we change back to a class, must use temporaries
1028      CrossReducePair(ref left, ref right);
1029
1030      try {
1031        checked {
1032          BigInteger numerator = left.m_Numerator * right.m_Numerator;
1033          BigInteger denominator = left.m_Denominator * right.m_Denominator;
1034
1035          return new Fraction(numerator, denominator);
1036        }
1037      }
1038      catch (Exception e) {
1039        throw new FractionException("Multiply error", e);
1040      }
1041    }
1042
1043    /// <summary>
1044    /// Returns the modulus (remainder after dividing) two Fractions
1045    /// </summary>
1046    /// <param name="left">A Fraction</param>
1047    /// <param name="right">Another Fraction</param>
1048    /// <returns>Modulus of the Fractions. Returns NaN if either Fraction is a NaN.</returns>
1049    /// <exception cref="FractionException">Will throw if an overflow occurs. Does a cross-reduce to
1050    /// insure only the unavoidable overflows occur.</exception>
1051    private static Fraction Modulus(Fraction left, Fraction right) {
1052      if (left.IsNaN() || right.IsNaN())
1053        return NaN;
1054
1055      try {
1056        checked {
1057          // this will discard any fractional places...
1058          Int64 quotient = (Int64)(left / right);
1059          Fraction whole = new Fraction(quotient * right.m_Numerator, right.m_Denominator);
1060          return left - whole;
1061        }
1062      }
1063      catch (Exception e) {
1064        throw new FractionException("Modulus error", e);
1065      }
1066    }
1067    #endregion
1068
1069    #region Indeterminate helpers
1070    /// <summary>
1071    /// Gives the culture-related representation of the indeterminate types NaN, PositiveInfinity
1072    /// and NegativeInfinity
1073    /// </summary>
1074    /// <param name="numerator">The value in the numerator</param>
1075    /// <returns>The culture-specific string representation of the implied value</returns>
1076    /// <remarks>Only the sign and zero/non-zero information is relevant.</remarks>
1077    private static string IndeterminateTypeName(BigInteger numerator) {
1078      // could also be NumberFormatInfo.InvariantInfo
1079      System.Globalization.NumberFormatInfo info = NumberFormatInfo.CurrentInfo;
1080
1081      switch (NormalizeIndeterminate(numerator)) {
1082        case Indeterminates.PositiveInfinity:
1083          return info.PositiveInfinitySymbol;
1084
1085        case Indeterminates.NegativeInfinity:
1086          return info.NegativeInfinitySymbol;
1087
1088        default:    // if this happens, something VERY wrong is going on...
1089        case Indeterminates.NaN:
1090          return info.NaNSymbol;
1091      }
1092    }
1093
1094    /// <summary>
1095    /// Gives the normalize representation of the indeterminate types NaN, PositiveInfinity
1096    /// and NegativeInfinity
1097    /// </summary>
1098    /// <param name="numerator">The value in the numerator</param>
1099    /// <returns>The normalized version of the indeterminate type</returns>
1100    /// <remarks>Only the sign and zero/non-zero information is relevant.</remarks>
1101    private static Indeterminates NormalizeIndeterminate(BigInteger numerator) {
1102      switch (numerator.Sign) {
1103        case 1:
1104          return Indeterminates.PositiveInfinity;
1105
1106        case -1:
1107          return Indeterminates.NegativeInfinity;
1108
1109        default:    // if this happens, your Math.Sign function is BROKEN!
1110        case 0:
1111          return Indeterminates.NaN;
1112      }
1113    }
1114
1115    // These are used to represent the indeterminate with a Denominator of zero
1116    private enum Indeterminates {
1117      NaN = 0
1118      ,
1119      PositiveInfinity = 1
1120        , NegativeInfinity = -1
1121    }
1122    #endregion
1123
1124    #region Member variables
1125    private BigInteger m_Numerator;
1126    private BigInteger m_Denominator;
1127    #endregion
1128    #endregion
1129  }   //end class Fraction
1130
1131  /// <summary>
1132  /// Exception class for Fraction, derived from System.Exception
1133  /// </summary>
1134  public class FractionException : Exception {
1135    /// <summary>
1136    /// Constructs a FractionException
1137    /// </summary>
1138    /// <param name="Message">String associated with the error message</param>
1139    /// <param name="InnerException">Actual inner exception caught</param>
1140    public FractionException(string Message, Exception InnerException)
1141      : base(Message, InnerException) {
1142    }
1143  }   //end class FractionException
1144}   //end namespace Mehroz
Note: See TracBrowser for help on using the repository browser.