Free cookie consent management tool by TermsFeed Policy Generator

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

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

#1886 adapted convex hull modifier to use fractions for big volumes

File size: 44.5 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    public double ToDouble(int digits) {
252      if (this.m_Denominator == 1)
253        return (double)this.m_Numerator;
254      else if (this.m_Denominator == 0) {
255        switch (NormalizeIndeterminate(this.m_Numerator)) {
256          case Indeterminates.NegativeInfinity:
257            return double.NegativeInfinity;
258
259          case Indeterminates.PositiveInfinity:
260            return double.PositiveInfinity;
261
262          case Indeterminates.NaN:
263          default:    // this can't happen
264            return double.NaN;
265        }
266      } else {
267        ReduceFraction(ref this);
268        int factor = (int)Math.Pow(10, digits);
269        return ((double)((this.m_Numerator * factor) / this.m_Denominator)) / factor;
270      }
271    }
272
273    /// <summary>
274    /// Get the value of the Fraction as a string, with proper representation for NaNs and infinites
275    /// </summary>
276    /// <returns>The string representation of the Fraction, or the culture-specific representations of
277    /// NaN, PositiveInfinity or NegativeInfinity.</returns>
278    /// <remarks>The current culture determines the textual representation the Indeterminates</remarks>
279    public override string ToString() {
280      if (this.m_Denominator == 1) {
281        return this.m_Numerator.ToString();
282      } else if (this.m_Denominator == 0) {
283        return IndeterminateTypeName(this.m_Numerator);
284      } else {
285        return this.m_Numerator.ToString() + "/" + this.m_Denominator.ToString();
286      }
287    }
288    #endregion
289
290    #region From primitives
291    /// <summary>
292    /// Converts a long value to the exact Fraction
293    /// </summary>
294    /// <param name="inValue">The long to convert</param>
295    /// <returns>An exact representation of the value</returns>
296    public static Fraction ToFraction(long inValue) {
297      return new Fraction(inValue);
298    }
299
300    /// <summary>
301    /// Converts a double value to the approximate Fraction
302    /// </summary>
303    /// <param name="inValue">The double to convert</param>
304    /// <returns>A best-fit representation of the value</returns>
305    /// <remarks>Supports double.NaN, double.PositiveInfinity and double.NegativeInfinity</remarks>
306    public static Fraction ToFraction(double inValue) {
307      // it's one of the indeterminates... which?
308      if (double.IsNaN(inValue))
309        return NaN;
310      else if (double.IsNegativeInfinity(inValue))
311        return NegativeInfinity;
312      else if (double.IsPositiveInfinity(inValue))
313        return PositiveInfinity;
314      else if (inValue == 0.0d)
315        return Zero;
316
317      int sign = Math.Sign(inValue);
318      inValue = Math.Abs(inValue);
319
320      return ConvertPositiveDouble(sign, inValue);
321    }
322
323    /// <summary>
324    /// Converts a string to the corresponding reduced fraction
325    /// </summary>
326    /// <param name="inValue">The string representation of a fractional value</param>
327    /// <returns>The Fraction that represents the string</returns>
328    /// <remarks>Four forms are supported, as a plain integer, as a double, or as Numerator/Denominator
329    /// and the representations for NaN and the infinites</remarks>
330    /// <example>"123" = 123/1 and "1.25" = 5/4 and "10/36" = 5/13 and NaN = 0/0 and
331    /// PositiveInfinity = 1/0 and NegativeInfinity = -1/0</example>
332    public static Fraction ToFraction(string inValue) {
333      if (inValue == null || inValue == string.Empty)
334        throw new ArgumentNullException("inValue");
335
336      // could also be NumberFormatInfo.InvariantInfo
337      NumberFormatInfo info = NumberFormatInfo.CurrentInfo;
338
339      // Is it one of the special symbols for NaN and such...
340      string trimmedValue = inValue.Trim();
341
342      if (trimmedValue == info.NaNSymbol)
343        return NaN;
344      else if (trimmedValue == info.PositiveInfinitySymbol)
345        return PositiveInfinity;
346      else if (trimmedValue == info.NegativeInfinitySymbol)
347        return NegativeInfinity;
348      else {
349        // Not special, is it a Fraction?
350        int slashPos = inValue.IndexOf('/');
351
352        if (slashPos > -1) {
353          // string is in the form of Numerator/Denominator
354          long numerator = Convert.ToInt64(inValue.Substring(0, slashPos));
355          long denominator = Convert.ToInt64(inValue.Substring(slashPos + 1));
356
357          return new Fraction(numerator, denominator);
358        } else {
359          // the string is not in the form of a fraction
360          // hopefully it is double or integer, do we see a decimal point?
361          int decimalPos = inValue.IndexOf(info.CurrencyDecimalSeparator);
362
363          if (decimalPos > -1)
364            return new Fraction(Convert.ToDouble(inValue));
365          else
366            return new Fraction(Convert.ToInt64(inValue));
367        }
368      }
369    }
370    #endregion
371    #endregion
372
373    #region Indeterminate classifications
374    /// <summary>
375    /// Determines if a Fraction represents a Not-a-Number
376    /// </summary>
377    /// <returns>True if the Fraction is a NaN</returns>
378    public bool IsNaN() {
379      if (this.m_Denominator == 0
380        && NormalizeIndeterminate(this.m_Numerator) == Indeterminates.NaN)
381        return true;
382      else
383        return false;
384    }
385
386    /// <summary>
387    /// Determines if a Fraction represents Any Infinity
388    /// </summary>
389    /// <returns>True if the Fraction is Positive Infinity or Negative Infinity</returns>
390    public bool IsInfinity() {
391      if (this.m_Denominator == 0
392        && NormalizeIndeterminate(this.m_Numerator) != Indeterminates.NaN)
393        return true;
394      else
395        return false;
396    }
397
398    /// <summary>
399    /// Determines if a Fraction represents Positive Infinity
400    /// </summary>
401    /// <returns>True if the Fraction is Positive Infinity</returns>
402    public bool IsPositiveInfinity() {
403      if (this.m_Denominator == 0
404        && NormalizeIndeterminate(this.m_Numerator) == Indeterminates.PositiveInfinity)
405        return true;
406      else
407        return false;
408    }
409
410    /// <summary>
411    /// Determines if a Fraction represents Negative Infinity
412    /// </summary>
413    /// <returns>True if the Fraction is Negative Infinity</returns>
414    public bool IsNegativeInfinity() {
415      if (this.m_Denominator == 0
416        && NormalizeIndeterminate(this.m_Numerator) == Indeterminates.NegativeInfinity)
417        return true;
418      else
419        return false;
420    }
421    #endregion
422
423    #region Inversion
424    /// <summary>
425    /// Inverts a Fraction
426    /// </summary>
427    /// <returns>The inverted Fraction (with Denominator over Numerator)</returns>
428    /// <remarks>Does NOT throw for zero Numerators as later use of the fraction will catch the error.</remarks>
429    public Fraction Inverse() {
430      // don't use the obvious constructor because we do not want it normalized at this time
431      Fraction frac = new Fraction();
432
433      frac.m_Numerator = this.m_Denominator;
434      frac.m_Denominator = this.m_Numerator;
435      return frac;
436    }
437
438    /// <summary>
439    /// Creates an inverted Fraction
440    /// </summary>
441    /// <returns>The inverted Fraction (with Denominator over Numerator)</returns>
442    /// <remarks>Does NOT throw for zero Numerators as later use of the fraction will catch the error.</remarks>
443    public static Fraction Inverted(long value) {
444      Fraction frac = new Fraction(value);
445      return frac.Inverse();
446    }
447
448    /// <summary>
449    /// Creates an inverted Fraction
450    /// </summary>
451    /// <returns>The inverted Fraction (with Denominator over Numerator)</returns>
452    /// <remarks>Does NOT throw for zero Numerators as later use of the fraction will catch the error.</remarks>
453    public static Fraction Inverted(double value) {
454      Fraction frac = new Fraction(value);
455      return frac.Inverse();
456    }
457    #endregion
458
459    #region Operators
460    #region Unary Negation operator
461    /// <summary>
462    /// Negates the Fraction
463    /// </summary>
464    /// <param name="left">The Fraction to negate</param>
465    /// <returns>The negative version of the Fraction</returns>
466    public static Fraction operator -(Fraction left) {
467      return Negate(left);
468    }
469    #endregion
470
471    #region Addition 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 Subtraction operators
494    public static Fraction operator -(Fraction left, Fraction right) {
495      return Add(left, -right);
496    }
497
498    public static Fraction operator -(long left, Fraction right) {
499      return Add(new Fraction(left), -right);
500    }
501
502    public static Fraction operator -(Fraction left, long right) {
503      return Add(left, new Fraction(-right));
504    }
505
506    public static Fraction operator -(double left, Fraction right) {
507      return Add(ToFraction(left), -right);
508    }
509
510    public static Fraction operator -(Fraction left, double right) {
511      return Add(left, ToFraction(-right));
512    }
513    #endregion
514
515    #region Multiplication operators
516    public static Fraction operator *(Fraction left, Fraction right) {
517      return Multiply(left, right);
518    }
519
520    public static Fraction operator *(long left, Fraction right) {
521      return Multiply(new Fraction(left), right);
522    }
523
524    public static Fraction operator *(Fraction left, long right) {
525      return Multiply(left, new Fraction(right));
526    }
527
528    public static Fraction operator *(double left, Fraction right) {
529      return Multiply(ToFraction(left), right);
530    }
531
532    public static Fraction operator *(Fraction left, double right) {
533      return Multiply(left, ToFraction(right));
534    }
535    #endregion
536
537    #region Division operators
538    public static Fraction operator /(Fraction left, Fraction right) {
539      return Multiply(left, right.Inverse());
540    }
541
542    public static Fraction operator /(long left, Fraction right) {
543      return Multiply(new Fraction(left), right.Inverse());
544    }
545
546    public static Fraction operator /(Fraction left, long right) {
547      return Multiply(left, Inverted(right));
548    }
549
550    public static Fraction operator /(double left, Fraction right) {
551      return Multiply(ToFraction(left), right.Inverse());
552    }
553
554    public static Fraction operator /(Fraction left, double right) {
555      return Multiply(left, Inverted(right));
556    }
557    #endregion
558
559    #region Modulus operators
560    public static Fraction operator %(Fraction left, Fraction right) {
561      return Modulus(left, right);
562    }
563
564    public static Fraction operator %(long left, Fraction right) {
565      return Modulus(new Fraction(left), right);
566    }
567
568    public static Fraction operator %(Fraction left, long right) {
569      return Modulus(left, right);
570    }
571
572    public static Fraction operator %(double left, Fraction right) {
573      return Modulus(ToFraction(left), right);
574    }
575
576    public static Fraction operator %(Fraction left, double right) {
577      return Modulus(left, right);
578    }
579    #endregion
580
581    #region Equal operators
582    public static bool operator ==(Fraction left, Fraction right) {
583      return left.CompareEquality(right, false);
584    }
585
586    public static bool operator ==(Fraction left, long right) {
587      return left.CompareEquality(new Fraction(right), false);
588    }
589
590    public static bool operator ==(Fraction left, double right) {
591      return left.CompareEquality(new Fraction(right), false);
592    }
593    #endregion
594
595    #region Not-equal operators
596    public static bool operator !=(Fraction left, Fraction right) {
597      return left.CompareEquality(right, true);
598    }
599
600    public static bool operator !=(Fraction left, long right) {
601      return left.CompareEquality(new Fraction(right), true);
602    }
603
604    public static bool operator !=(Fraction left, double right) {
605      return left.CompareEquality(new Fraction(right), true);
606    }
607    #endregion
608
609    #region Inequality operators
610    /// <summary>
611    /// Compares two Fractions to see if left is less than right
612    /// </summary>
613    /// <param name="left">The first Fraction</param>
614    /// <param name="right">The second Fraction</param>
615    /// <returns>True if <paramref name="left">left</paramref> is less
616    /// than <paramref name="right">right</paramref></returns>
617    /// <remarks>Special handling for indeterminates exists. <see>IndeterminateLess</see></remarks>
618    /// <exception cref="FractionException">Throws an error if overflows occur while computing the
619    /// difference with an InnerException of OverflowException</exception>
620    public static bool operator <(Fraction left, Fraction right) {
621      return left.CompareTo(right) < 0;
622    }
623
624    /// <summary>
625    /// Compares two Fractions to see if left is greater than right
626    /// </summary>
627    /// <param name="left">The first Fraction</param>
628    /// <param name="right">The second Fraction</param>
629    /// <returns>True if <paramref name="left">left</paramref> is greater
630    /// than <paramref name="right">right</paramref></returns>
631    /// <remarks>Special handling for indeterminates exists. <see>IndeterminateLess</see></remarks>
632    /// <exception cref="FractionException">Throws an error if overflows occur while computing the
633    /// difference with an InnerException of OverflowException</exception>
634    public static bool operator >(Fraction left, Fraction right) {
635      return left.CompareTo(right) > 0;
636    }
637
638    /// <summary>
639    /// Compares two Fractions to see if left is less than or equal to right
640    /// </summary>
641    /// <param name="left">The first Fraction</param>
642    /// <param name="right">The second Fraction</param>
643    /// <returns>True if <paramref name="left">left</paramref> is less than or
644    /// equal to <paramref name="right">right</paramref></returns>
645    /// <remarks>Special handling for indeterminates exists. <see>IndeterminateLessEqual</see></remarks>
646    /// <exception cref="FractionException">Throws an error if overflows occur while computing the
647    /// difference with an InnerException of OverflowException</exception>
648    public static bool operator <=(Fraction left, Fraction right) {
649      return left.CompareTo(right) <= 0;
650    }
651
652    /// <summary>
653    /// Compares two Fractions to see if left is greater than or equal to right
654    /// </summary>
655    /// <param name="left">The first Fraction</param>
656    /// <param name="right">The second Fraction</param>
657    /// <returns>True if <paramref name="left">left</paramref> is greater than or
658    /// equal to <paramref name="right">right</paramref></returns>
659    /// <remarks>Special handling for indeterminates exists. <see>IndeterminateLessEqual</see></remarks>
660    /// <exception cref="FractionException">Throws an error if overflows occur while computing the
661    /// difference with an InnerException of OverflowException</exception>
662    public static bool operator >=(Fraction left, Fraction right) {
663      return left.CompareTo(right) >= 0;
664    }
665    #endregion
666
667    #region Implict conversion from primitive operators
668    /// <summary>
669    /// Implicit conversion of a long integral value to a Fraction
670    /// </summary>
671    /// <param name="value">The long integral value to convert</param>
672    /// <returns>A Fraction whose denominator is 1</returns>
673    public static implicit operator Fraction(long value) {
674      return new Fraction(value);
675    }
676
677    /// <summary>
678    /// Implicit conversion of a double floating point value to a Fraction
679    /// </summary>
680    /// <param name="value">The double value to convert</param>
681    /// <returns>A reduced Fraction</returns>
682    public static implicit operator Fraction(double value) {
683      return new Fraction(value);
684    }
685
686    /// <summary>
687    /// Implicit conversion of a string to a Fraction
688    /// </summary>
689    /// <param name="value">The string to convert</param>
690    /// <returns>A reduced Fraction</returns>
691    public static implicit operator Fraction(string value) {
692      return new Fraction(value);
693    }
694    #endregion
695
696    #region Explicit converstion to primitive operators
697    /// <summary>
698    /// Explicit conversion from a Fraction to an integer
699    /// </summary>
700    /// <param name="frac">the Fraction to convert</param>
701    /// <returns>The integral representation of the Fraction</returns>
702    public static explicit operator int(Fraction frac) {
703      return frac.ToInt32();
704    }
705
706    /// <summary>
707    /// Explicit conversion from a Fraction to a double floating-point value
708    /// </summary>
709    /// <param name="frac">The Fraction to convert</param>
710    /// <returns>The double representation of the Fraction</returns>
711    public static explicit operator double(Fraction frac) {
712      return frac.ToDouble();
713    }
714
715    /// <summary>
716    /// Explicit conversion from a Fraction to a string
717    /// </summary>
718    /// <param name="frac">the Fraction to convert</param>
719    /// <returns>The string representation of the Fraction</returns>
720    public static implicit operator string(Fraction frac) {
721      return frac.ToString();
722    }
723    #endregion
724    #endregion
725
726    #region Equals and GetHashCode overrides
727    /// <summary>
728    /// Compares for equality the current Fraction to the value passed.
729    /// </summary>
730    /// <param name="obj">A  Fraction,</param>
731    /// <returns>True if the value equals the current fraction, false otherwise (including for
732    /// non-Fraction types or null object.</returns>
733    public override bool Equals(object obj) {
734      if (obj == null || !(obj is Fraction))
735        return false;
736
737      try {
738        Fraction right = (Fraction)obj;
739        return this.CompareEquality(right, false);
740      }
741      catch {
742        // can't throw in an Equals!
743        return false;
744      }
745    }
746
747    /// <summary>
748    /// Returns a hash code generated from the current Fraction
749    /// </summary>
750    /// <returns>The hash code</returns>
751    /// <remarks>Reduces (in-place) the Fraction first.</remarks>
752    public override int GetHashCode() {
753      // insure we're as close to normalized as possible first
754      ReduceFraction(ref this);
755
756      int numeratorHash = this.m_Numerator.GetHashCode();
757      int denominatorHash = this.m_Denominator.GetHashCode();
758
759      return (numeratorHash ^ denominatorHash);
760    }
761    #endregion
762
763    #region IComparable member and type-specific version
764    /// <summary>
765    /// Compares an object to this Fraction
766    /// </summary>
767    /// <param name="obj">The object to compare against (null is less than everything)</param>
768    /// <returns>-1 if this is less than <paramref name="obj"></paramref>,
769    ///  0 if they are equal,
770    ///  1 if this is greater than <paramref name="obj"></paramref></returns>
771    /// <remarks>Will convert an object from longs, doubles, and strings as this is a value-type.</remarks>
772    public int CompareTo(object obj) {
773      if (obj == null)
774        return 1; // null is less than anything
775
776      Fraction right;
777
778      if (obj is Fraction)
779        right = (Fraction)obj;
780      else if (obj is long)
781        right = (long)obj;
782      else if (obj is double)
783        right = (double)obj;
784      else if (obj is string)
785        right = (string)obj;
786      else
787        throw new ArgumentException("Must be convertible to Fraction", "obj");
788
789      return this.CompareTo(right);
790    }
791
792    /// <summary>
793    /// Compares this Fraction to another Fraction
794    /// </summary>
795    /// <param name="right">The Fraction to compare against</param>
796    /// <returns>-1 if this is less than <paramref name="right"></paramref>,
797    ///  0 if they are equal,
798    ///  1 if this is greater than <paramref name="right"></paramref></returns>
799    public int CompareTo(Fraction right) {
800      // if left is an indeterminate, punt to the helper...
801      if (this.m_Denominator == 0) {
802        return IndeterminantCompare(NormalizeIndeterminate(this.m_Numerator), right);
803      }
804
805      // if right is an indeterminate, punt to the helper...
806      if (right.m_Denominator == 0) {
807        // note sign-flip...
808        return -IndeterminantCompare(NormalizeIndeterminate(right.m_Numerator), this);
809      }
810
811      // they're both normal Fractions
812      CrossReducePair(ref this, ref right);
813
814      try {
815        checked {
816          BigInteger leftScale = this.m_Numerator * right.m_Denominator;
817          BigInteger rightScale = this.m_Denominator * right.m_Numerator;
818
819          if (leftScale < rightScale)
820            return -1;
821          else if (leftScale > rightScale)
822            return 1;
823          else
824            return 0;
825        }
826      }
827      catch (Exception e) {
828        throw new FractionException(string.Format("CompareTo({0}, {1}) error", this, right), e);
829      }
830    }
831    #endregion
832
833    #region IFormattable Members
834    string System.IFormattable.ToString(string format, IFormatProvider formatProvider) {
835      return this.m_Numerator.ToString(format, formatProvider) + "/" + this.m_Denominator.ToString(format, formatProvider);
836    }
837    #endregion
838
839    #region Reduction
840    /// <summary>
841    /// Reduces (simplifies) a Fraction by dividing down to lowest possible denominator (via GCD)
842    /// </summary>
843    /// <param name="frac">The Fraction to be reduced [WILL BE MODIFIED IN PLACE]</param>
844    /// <remarks>Modifies the input arguments in-place! Will normalize the NaN and infinites
845    /// representation. Will set Denominator to 1 for any zero numerator. Moves sign to the
846    /// Numerator.</remarks>
847    /// <example>2/4 will be reduced to 1/2</example>
848    public static void ReduceFraction(ref Fraction frac) {
849      // clean up the NaNs and infinites
850      if (frac.m_Denominator == 0) {
851        frac.m_Numerator = (long)NormalizeIndeterminate(frac.m_Numerator);
852        return;
853      }
854
855      // all forms of zero are alike.
856      if (frac.m_Numerator == 0) {
857        frac.m_Denominator = 1;
858        return;
859      }
860
861      BigInteger iGCD = BigInteger.GreatestCommonDivisor(frac.m_Numerator, frac.m_Denominator);
862      frac.m_Numerator /= iGCD;
863      frac.m_Denominator /= iGCD;
864
865      // if negative sign in denominator
866      if (frac.m_Denominator < 0) {
867        //move negative sign to numerator
868        frac.m_Numerator = -frac.m_Numerator;
869        frac.m_Denominator = -frac.m_Denominator;
870      }
871    }
872
873    /// <summary>
874    /// Cross-reduces a pair of Fractions so that we have the best GCD-reduced values for multiplication
875    /// </summary>
876    /// <param name="frac1">The first Fraction [WILL BE MODIFIED IN PLACE]</param>
877    /// <param name="frac2">The second Fraction [WILL BE MODIFIED IN PLACE]</param>
878    /// <remarks>Modifies the input arguments in-place!</remarks>
879    /// <example>(3/4, 5/9) = (1/4, 5/3)</example>
880    public static void CrossReducePair(ref Fraction frac1, ref Fraction frac2) {
881      // leave the indeterminates alone!
882      if (frac1.m_Denominator == 0 || frac2.m_Denominator == 0)
883        return;
884
885      BigInteger gcdTop = BigInteger.GreatestCommonDivisor(frac1.m_Numerator, frac2.m_Denominator);
886      frac1.m_Numerator = frac1.m_Numerator / gcdTop;
887      frac2.m_Denominator = frac2.m_Denominator / gcdTop;
888
889      BigInteger gcdBottom = BigInteger.GreatestCommonDivisor(frac1.m_Denominator, frac2.m_Numerator);
890      frac2.m_Numerator = frac2.m_Numerator / gcdBottom;
891      frac1.m_Denominator = frac1.m_Denominator / gcdBottom;
892    }
893    #endregion
894
895    #region Implementation
896    #region Convert a double to a fraction
897    private static Fraction ConvertPositiveDouble(int sign, double inValue) {
898      // Shamelessly stolen from http://homepage.smc.edu/kennedy_john/CONFRAC.PDF
899      // with AccuracyFactor == double.Episilon
900      long fractionNumerator = (long)inValue;
901      double fractionDenominator = 1;
902      double previousDenominator = 0;
903      double remainingDigits = inValue;
904      int maxIterations = 594;  // found at http://www.ozgrid.com/forum/archive/index.php/t-22530.html
905
906      while (remainingDigits != Math.Floor(remainingDigits)
907        && Math.Abs(inValue - (fractionNumerator / fractionDenominator)) > double.Epsilon) {
908        remainingDigits = 1.0 / (remainingDigits - Math.Floor(remainingDigits));
909
910        double scratch = fractionDenominator;
911
912        fractionDenominator = (Math.Floor(remainingDigits) * fractionDenominator) + previousDenominator;
913        fractionNumerator = (long)(inValue * fractionDenominator + 0.5);
914
915        previousDenominator = scratch;
916
917        if (maxIterations-- < 0)
918          break;
919      }
920
921      return new Fraction(fractionNumerator * sign, (long)fractionDenominator);
922    }
923    #endregion
924
925    #region Equality helper
926    /// <summary>
927    /// Compares for equality the current Fraction to the value passed.
928    /// </summary>
929    /// <param name="right">A Fraction to compare against</param>
930    /// <param name="notEqualCheck">If true, we're looking for not-equal</param>
931    /// <returns>True if the <paramref name="right"></paramref> equals the current
932    /// fraction, false otherwise. If comparing two NaNs, they are always equal AND
933    /// not-equal.</returns>
934    private bool CompareEquality(Fraction right, bool notEqualCheck) {
935      // insure we're normalized first
936      ReduceFraction(ref this);
937
938      // now normalize the comperand
939      ReduceFraction(ref right);
940
941      if (this.m_Numerator == right.m_Numerator && this.m_Denominator == right.m_Denominator) {
942        // special-case rule, two NaNs are always both equal
943        if (notEqualCheck && this.IsNaN())
944          return true;
945        else
946          return !notEqualCheck;
947      } else {
948        return notEqualCheck;
949      }
950    }
951    #endregion
952
953    #region Comparison helper
954    /// <summary>
955    /// Determines how this Fraction, of an indeterminate type, compares to another Fraction
956    /// </summary>
957    /// <param name="leftType">What kind of indeterminate</param>
958    /// <param name="right">The other Fraction to compare against</param>
959    /// <returns>-1 if this is less than <paramref name="right"></paramref>,
960    ///  0 if they are equal,
961    ///  1 if this is greater than <paramref name="right"></paramref></returns>
962    /// <remarks>NaN is less than anything except NaN and Negative Infinity. Negative Infinity is less
963    /// than anything except Negative Infinity. Positive Infinity is greater than anything except
964    /// Positive Infinity.</remarks>
965    private static int IndeterminantCompare(Indeterminates leftType, Fraction right) {
966      switch (leftType) {
967        case Indeterminates.NaN:
968          // A NaN is...
969          if (right.IsNaN())
970            return 0; // equal to a NaN
971          else if (right.IsNegativeInfinity())
972            return 1; // great than Negative Infinity
973          else
974            return -1;  // less than anything else
975
976        case Indeterminates.NegativeInfinity:
977          // Negative Infinity is...
978          if (right.IsNegativeInfinity())
979            return 0; // equal to Negative Infinity
980          else
981            return -1;  // less than anything else
982
983        case Indeterminates.PositiveInfinity:
984          if (right.IsPositiveInfinity())
985            return 0; // equal to Positive Infinity
986          else
987            return 1; // greater than anything else
988
989        default:
990          // this CAN'T happen, something VERY wrong is going on...
991          return 0;
992      }
993    }
994    #endregion
995
996    #region Math helpers
997    /// <summary>
998    /// Negates the Fraction
999    /// </summary>
1000    /// <param name="frac">Value to negate</param>
1001    /// <returns>A new Fraction that is sign-flipped from the input</returns>
1002    private static Fraction Negate(Fraction frac) {
1003      // for a NaN, it's still a NaN
1004      return new Fraction(-frac.m_Numerator, frac.m_Denominator);
1005    }
1006
1007    /// <summary>
1008    /// Adds two Fractions
1009    /// </summary>
1010    /// <param name="left">A Fraction</param>
1011    /// <param name="right">Another Fraction</param>
1012    /// <returns>Sum of the Fractions. Returns NaN if either Fraction is a NaN.</returns>
1013    /// <exception cref="FractionException">Will throw if an overflow occurs when computing the
1014    /// GCD-normalized values.</exception>
1015    private static Fraction Add(Fraction left, Fraction right) {
1016      if (left.IsNaN() || right.IsNaN())
1017        return NaN;
1018
1019      BigInteger gcd = BigInteger.GreatestCommonDivisor(left.m_Denominator, right.m_Denominator); // cannot return less than 1
1020      BigInteger leftDenominator = left.m_Denominator / gcd;
1021      BigInteger rightDenominator = right.m_Denominator / gcd;
1022
1023      try {
1024        checked {
1025          BigInteger numerator = left.m_Numerator * rightDenominator + right.m_Numerator * leftDenominator;
1026          BigInteger denominator = leftDenominator * rightDenominator * gcd;
1027
1028          return new Fraction(numerator, denominator);
1029        }
1030      }
1031      catch (Exception e) {
1032        throw new FractionException("Add error", e);
1033      }
1034    }
1035
1036    /// <summary>
1037    /// Multiplies two Fractions
1038    /// </summary>
1039    /// <param name="left">A Fraction</param>
1040    /// <param name="right">Another Fraction</param>
1041    /// <returns>Product of the Fractions. Returns NaN if either Fraction is a NaN.</returns>
1042    /// <exception cref="FractionException">Will throw if an overflow occurs. Does a cross-reduce to
1043    /// insure only the unavoidable overflows occur.</exception>
1044    private static Fraction Multiply(Fraction left, Fraction right) {
1045      if (left.IsNaN() || right.IsNaN())
1046        return NaN;
1047
1048      // this would be unsafe if we were not a ValueType, because we would be changing the
1049      // caller's values.  If we change back to a class, must use temporaries
1050      CrossReducePair(ref left, ref right);
1051
1052      try {
1053        checked {
1054          BigInteger numerator = left.m_Numerator * right.m_Numerator;
1055          BigInteger denominator = left.m_Denominator * right.m_Denominator;
1056
1057          return new Fraction(numerator, denominator);
1058        }
1059      }
1060      catch (Exception e) {
1061        throw new FractionException("Multiply error", e);
1062      }
1063    }
1064
1065    /// <summary>
1066    /// Returns the modulus (remainder after dividing) two Fractions
1067    /// </summary>
1068    /// <param name="left">A Fraction</param>
1069    /// <param name="right">Another Fraction</param>
1070    /// <returns>Modulus of the Fractions. Returns NaN if either Fraction is a NaN.</returns>
1071    /// <exception cref="FractionException">Will throw if an overflow occurs. Does a cross-reduce to
1072    /// insure only the unavoidable overflows occur.</exception>
1073    private static Fraction Modulus(Fraction left, Fraction right) {
1074      if (left.IsNaN() || right.IsNaN())
1075        return NaN;
1076
1077      try {
1078        checked {
1079          // this will discard any fractional places...
1080          Int64 quotient = (Int64)(left / right);
1081          Fraction whole = new Fraction(quotient * right.m_Numerator, right.m_Denominator);
1082          return left - whole;
1083        }
1084      }
1085      catch (Exception e) {
1086        throw new FractionException("Modulus error", e);
1087      }
1088    }
1089    #endregion
1090
1091    #region Indeterminate helpers
1092    /// <summary>
1093    /// Gives the culture-related representation of the indeterminate types NaN, PositiveInfinity
1094    /// and NegativeInfinity
1095    /// </summary>
1096    /// <param name="numerator">The value in the numerator</param>
1097    /// <returns>The culture-specific string representation of the implied value</returns>
1098    /// <remarks>Only the sign and zero/non-zero information is relevant.</remarks>
1099    private static string IndeterminateTypeName(BigInteger numerator) {
1100      // could also be NumberFormatInfo.InvariantInfo
1101      System.Globalization.NumberFormatInfo info = NumberFormatInfo.CurrentInfo;
1102
1103      switch (NormalizeIndeterminate(numerator)) {
1104        case Indeterminates.PositiveInfinity:
1105          return info.PositiveInfinitySymbol;
1106
1107        case Indeterminates.NegativeInfinity:
1108          return info.NegativeInfinitySymbol;
1109
1110        default:    // if this happens, something VERY wrong is going on...
1111        case Indeterminates.NaN:
1112          return info.NaNSymbol;
1113      }
1114    }
1115
1116    /// <summary>
1117    /// Gives the normalize representation of the indeterminate types NaN, PositiveInfinity
1118    /// and NegativeInfinity
1119    /// </summary>
1120    /// <param name="numerator">The value in the numerator</param>
1121    /// <returns>The normalized version of the indeterminate type</returns>
1122    /// <remarks>Only the sign and zero/non-zero information is relevant.</remarks>
1123    private static Indeterminates NormalizeIndeterminate(BigInteger numerator) {
1124      switch (numerator.Sign) {
1125        case 1:
1126          return Indeterminates.PositiveInfinity;
1127
1128        case -1:
1129          return Indeterminates.NegativeInfinity;
1130
1131        default:    // if this happens, your Math.Sign function is BROKEN!
1132        case 0:
1133          return Indeterminates.NaN;
1134      }
1135    }
1136
1137    // These are used to represent the indeterminate with a Denominator of zero
1138    private enum Indeterminates {
1139      NaN = 0
1140      ,
1141      PositiveInfinity = 1
1142        , NegativeInfinity = -1
1143    }
1144    #endregion
1145
1146    #region Member variables
1147    private BigInteger m_Numerator;
1148    private BigInteger m_Denominator;
1149    #endregion
1150    #endregion
1151  }   //end class Fraction
1152
1153  /// <summary>
1154  /// Exception class for Fraction, derived from System.Exception
1155  /// </summary>
1156  public class FractionException : Exception {
1157    /// <summary>
1158    /// Constructs a FractionException
1159    /// </summary>
1160    /// <param name="Message">String associated with the error message</param>
1161    /// <param name="InnerException">Actual inner exception caught</param>
1162    public FractionException(string Message, Exception InnerException)
1163      : base(Message, InnerException) {
1164    }
1165  }   //end class FractionException
1166}   //end namespace Mehroz
Note: See TracBrowser for help on using the repository browser.