context: /*
   * Author: Syed Mehroz Alam
   * Email: smehrozalam@yahoo.com
   * URL: Programming Home "http://www.geocities.com/smehrozalam/" 
   * Date: 6/15/2004
   * Time: 10:54 AM
   *
   */
  using System;
  using System.Globalization;
  
  namespace ECalc.IronPythonEngine.Types
  {
      /// <summary>
      /// Classes Contained:
      /// 	Fraction
      /// 	FractionException
      /// </summary>
  
      ///<summary>
      /// Class name: Fraction
      /// Developed by: Syed Mehroz Alam
      /// Email: smehrozalam@yahoo.com
      /// URL: Programming Home "http://www.geocities.com/smehrozalam/"
      /// Version: 2.0
      /// 
      /// What's new in version 2.0:
      /// 	*	Changed Numerator and Denominator from Int32(integer) to Int64(long) for increased range
      /// 	*	renamed ConvertToString() to (overloaded) ToString()
      /// 	*	added the capability of detecting/raising overflow exceptions
      /// 	*	Fixed the bug that very small numbers e.g. 0.00000001 could not be converted to fraction
      /// 	*	Other minor bugs fixed
      /// 
      /// What's new in version 2.1
      /// 	*	overloaded user-defined conversions to/from Fractions
      /// 	
      /// 
      /// Properties:
      /// 	Numerator: Set/Get value for Numerator
      /// 	Denominator:  Set/Get value for Numerator
      /// 	Value:  Set an integer value for the fraction
      /// 
      /// Constructors:
      /// 	no arguments:	initializes fraction as 0/1
      /// 	(Numerator, Denominator): initializes fraction with the given numerator and denominator values
      /// 	(integer):	initializes fraction with the given integer value
      /// 	(long):	initializes fraction with the given long value
      /// 	(double):	initializes fraction with the given double value
      /// 	(string):	initializes fraction with the given string value
      /// 				the string can be an in the form of and integer, double or fraction.
      /// 				e.g it can be like "123" or "123.321" or "123/456"
      /// 
      /// Public Methods (Description is given with respective methods' definitions)
      /// 	(override) string ToString(Fraction)
      /// 	Fraction ToFraction(string)
      /// 	Fraction ToFraction(double)
      /// 	double ToDouble(Fraction)
      /// 	Fraction Duplicate()
      /// 	Fraction Inverse(integer)
      /// 	Fraction Inverse(Fraction)
      /// 	ReduceFraction(Fraction)
      /// 	Equals(object)
      /// 	GetHashCode()
      /// 
      ///	Private Methods (Description is given with respective methods' definitions)
      /// 	Initialize(Numerator, Denominator)
      /// 	Fraction Negate(Fraction)
      /// 	Fraction Add(Fraction1, Fraction2)
      /// 
      /// Overloaded Operators (overloaded for Fractions, Integers and Doubles)
      /// 	Unary: -
      /// 	Binary: +,-,*,/ 
      /// 	Relational and Logical Operators
      /// 
      /// Overloaded user-defined conversions
      /// 	Implicit:	From double/long/string to Fraction
      /// 	Explicit:	From Fraction to double/string
      /// </summary>
      public class Fraction: IEquatable<Fraction>
      {
          /// <summary>
          /// Class attributes/members
          /// </summary>
          long m_iNumerator;
          long m_iDenominator;
          
          /// <summary>
          /// Constructors
          /// </summary>
          public Fraction()
          {
              Initialize(0,1);
          }
      
          public Fraction(long iWholeNumber)
          {
              Initialize(iWholeNumber, 1);
          }
      
          public Fraction(double dDecimalValue)
          {
              Fraction temp=ToFraction(dDecimalValue);
              Initialize(temp.Numerator, temp.Denominator);
          }
          
          public Fraction(string strValue)
          {
              Fraction temp=ToFraction(strValue);
              Initialize(temp.Numerator, temp.Denominator);
          }
          
          public Fraction(long iNumerator, long iDenominator)
          {
              Initialize(iNumerator, iDenominator);
          }
          
          /// <summary>
          /// Internal function for constructors
          /// </summary>
          private void Initialize(long iNumerator, long iDenominator)
          {
              Numerator=iNumerator;
              Denominator=iDenominator;
              ReduceFraction(this);
          }
      
          /// <summary>
          /// Properites
          /// </summary>
          public long Denominator
          {
              get
              {	return m_iDenominator;	}
              set
              {
                  if (value!=0)
                      m_iDenominator=value;
                  else
                      throw new FractionException("Denominator cannot be assigned a ZERO Value");
              }
          }
      
          public long Numerator
          {
              get	
              {	return m_iNumerator;	}
              set
              {	m_iNumerator=value;	}
          }
      
          public long Value
          {
              set
              {	m_iNumerator=value;
                  m_iDenominator=1;	}
          }
      
          /// <summary>
          /// The function returns the current Fraction object as double
          /// </summary>
          public double ToDouble()
          {
              return ( (double)this.Numerator/this.Denominator );
          }
  
          /// <summary>
          /// The function returns the current Fraction object as a string
          /// </summary>
          public override string ToString()
          {
              string str;
              if ( this.Denominator==1 )
                  str=this.Numerator.ToString();
              else
                  str=this.Numerator + "/" + this.Denominator;
              return str;
          }
          /// <summary>
          /// The function takes an string as an argument and returns its corresponding reduced fraction
          /// the string can be an in the form of and integer, double or fraction.
          /// e.g it can be like "123" or "123.321" or "123/456"
          /// </summary>
          public static Fraction ToFraction(string strValue)
          {
              int i;
              for (i=0;i<strValue.Length;i++)
                  if (strValue[i]=='/')
                      break;
              
              if (i==strValue.Length)		// if string is not in the form of a fraction
                  // then it is double or integer
                  return ( Convert.ToDouble(strValue));
                  //return ( ToFraction( Convert.ToDouble(strValue) ) );
              
              // else string is in the form of Numerator/Denominator
              long iNumerator=Convert.ToInt64(strValue.Substring(0,i));
              long iDenominator=Convert.ToInt64(strValue.Substring(i+1));
              return new Fraction(iNumerator, iDenominator);
          }
          
          
          /// <summary>
          /// The function takes a floating point number as an argument 
          /// and returns its corresponding reduced fraction
          /// </summary>
          public static Fraction ToFraction(double dValue)
          {
              try
              {
                  checked
                  {
                      Fraction frac;
                      if (dValue%1==0)	// if whole number
                      {
                          frac=new Fraction( (long) dValue );
                      }
                      else
                      {
                          double dTemp=dValue;
                          long iMultiple=1;
                          string strTemp=dValue.ToString(CultureInfo.InvariantCulture);
                          while ( strTemp.IndexOf("E")>0 )	// if in the form like 12E-9
                          {
                              dTemp*=10;
                              iMultiple*=10;
                              strTemp=dTemp.ToString(CultureInfo.InvariantCulture);
                          }
                          int i=0;
                          while ( strTemp[i]!='.')
                              i++;
                          int iDigitsAfterDecimal=strTemp.Length-i-1;
                          while ( iDigitsAfterDecimal>0  )
                          {
                              dTemp*=10;
                              iMultiple*=10;
                              iDigitsAfterDecimal--;
                          }
                          frac=new Fraction( (int)Math.Round(dTemp) , iMultiple );
                      }
                      return frac;
                  }
              }
              catch(OverflowException)
              {
                  throw new FractionException("Conversion not possible due to overflow");
              }
              catch(Exception)
              {
  
gt:                 throw new FractionException("Conversion not possible");