//----------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All Rights Reserved. // //----------------------------------------------------------------------------- using System; using System.Diagnostics.Contracts; namespace Microsoft.Basetypes { /// /// The representation of a rational number. /// public struct Rational { public static readonly Rational ZERO = Rational.FromInts(0, 1); public static readonly Rational ONE = Rational.FromInts(1, 1); public static readonly Rational MINUS_ONE = Rational.FromInts(-1, 1); private BigNum numerator, denominator; // int numerator; // int denominator; // invariant: 0 < denominator || (numerator == 0 && denominator == 0); // invariant: numerator != 0 ==> gcd(abs(numerator),denominator) == 1; // invariant: numerator == 0 ==> denominator == 1 || denominator == 0; public static Rational FromInt(int x) { return FromBignum(BigNum.FromInt(x)); } public static Rational FromBignum(BigNum n) { return new Rational(n, BigNum.ONE); } private Rational(BigNum num, BigNum den) { Contract.Assert(den.Signum > 0); Contract.Assert(num == BigNum.ZERO || num.Gcd(den) == BigNum.ONE); numerator = num; denominator = den; } public static Rational FromBignums(BigNum num, BigNum den) { Contract.Assert(!den.IsZero); if (num == BigNum.ZERO) return ZERO; if (den.Signum < 0) { den = -den; num = -num; } if (den == BigNum.ONE) return new Rational(num, den); var gcd = num.Gcd(den); if (gcd == BigNum.ONE) return new Rational(num, den); return new Rational(num / gcd, den / gcd); } public static Rational FromInts(int num, int den) { return FromBignums(BigNum.FromInt(num), BigNum.FromInt(den)); } /// /// Returns the absolute value of the rational. /// public Rational Abs() { Contract.Ensures(Contract.Result().IsNonNegative); if (IsNonNegative) { return this; } else { return -this; } } /// /// Returns a rational whose numerator and denominator, resepctively, are the Gcd /// of the numerators and denominators of r and s. If one of r and s is 0, the absolute /// value of the other is returned. If both are 0, 1 is returned. /// public static Rational Gcd(Rational r, Rational s) { Contract.Ensures(Contract.Result().IsPositive); if (r.IsZero) { if (s.IsZero) { return ONE; } else { return s.Abs(); } } else if (s.IsZero) { return r.Abs(); } else { return new Rational(r.Numerator.Gcd(s.Numerator), r.Denominator.Gcd(s.Denominator)); } } public BigNum Numerator { get { return numerator; } } public BigNum Denominator { get { return denominator == BigNum.ZERO ? BigNum.ONE : denominator; } } public override string/*!*/ ToString() { Contract.Ensures(Contract.Result() != null); return String.Format("{0}/{1}", Numerator, Denominator); } public static bool operator ==(Rational r, Rational s) { return r.Numerator == s.Numerator && r.Denominator == s.Denominator; } public static bool operator !=(Rational r, Rational s) { return !(r == s); } public override bool Equals(object obj) { if (obj == null) return false; return obj is Rational && (Rational)obj == this; } public override int GetHashCode() { return this.Numerator.GetHashCode() * 13 + this.Denominator.GetHashCode(); } public int Signum { get { return this.Numerator.Signum; } } public bool IsZero { get { return Signum == 0; } } public bool IsNonZero { get { return Signum != 0; } } public bool IsIntegral { get { return Denominator == BigNum.ONE; } } [Pure] [Reads(ReadsAttribute.Reads.Nothing)] public bool HasValue(int n) { return this == FromInt(n); } /// /// Returns the rational as an integer. Requires the rational to be integral. /// public int AsInteger { get { Contract.Assert(this.IsIntegral); return Numerator.ToIntSafe; } } public BigNum AsBigNum { get { Contract.Assert(this.IsIntegral); return Numerator; } } public double AsDouble { [Pure] get { if (this.IsZero) { return 0.0; } else { return (double)Numerator.ToIntSafe / (double)Denominator.ToIntSafe; } } } public bool IsNegative { [Pure] get { return Signum < 0; } } public bool IsPositive { [Pure] get { return 0 < Signum; } } public bool IsNonNegative { [Pure] get { return 0 <= Signum; } } public static Rational operator -(Rational r) { return new Rational(-r.Numerator, r.Denominator); } public static Rational operator /(Rational r, Rational s) { return FromBignums(r.Numerator * s.Denominator, r.Denominator * s.Numerator); } public static Rational operator -(Rational r, Rational s) { return r + (-s); } public static Rational operator +(Rational r, Rational s) { return FromBignums(r.Numerator * s.Denominator + s.Numerator * r.Denominator, r.Denominator * s.Denominator); } public static Rational operator *(Rational r, Rational s) { return FromBignums(r.Numerator * s.Numerator, r.Denominator * s.Denominator); } public static bool operator <(Rational r, Rational s) { return (r - s).Signum < 0; } public static bool operator <=(Rational r, Rational s) { return !(r > s); } public static bool operator >=(Rational r, Rational s) { return !(r < s); } public static bool operator >(Rational r, Rational s) { return s < r; } } }