//-----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All Rights Reserved.
//
//-----------------------------------------------------------------------------
using System;
using Microsoft.Contracts;
namespace Microsoft.Basetypes
{
using BNM = Microsoft.FSharp.Math.BigRational;
///
/// The representation of a rational number.
///
public struct Rational
{
public static readonly Rational ZERO = new Rational ((!)BNM.Zero);
public static readonly Rational ONE = new Rational ((!)BNM.One);
public static readonly Rational MINUS_ONE = new Rational ((!)(-BNM.One));
private readonly Microsoft.FSharp.Math.BigRational _val;
private Microsoft.FSharp.Math.BigRational! val {
get {
if (_val == null)
return (!)BNM.Zero;
else
return _val;
}
}
// 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;
private Rational(int x)
{
this._val = BNM.FromInt(x);
}
public static Rational FromInt(int x) {
return new Rational(x);
}
private Rational(Microsoft.FSharp.Math.BigRational! val) {
this._val = val;
}
public Rational(BigNum num, BigNum den) {
System.Diagnostics.Debug.Assert(!den.IsZero);
this._val = BNM.FromBigInt(num.val) / BNM.FromBigInt(den.val);
}
public static Rational FromInts(int num, int den) {
return new Rational(BigNum.FromInt(num), BigNum.FromInt(den));
}
///
/// Returns the absolute value of the rational.
///
public Rational Abs()
ensures 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)
ensures 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
{
// Better way to obtain the numerator?
// The FSharp library does not seem to offer any methods for this
string! lin = (!)val.ToString();
int i = lin.IndexOf('/');
if (i == -1)
return new BigNum(BNM.ToBigInt(this.val));
else
return BigNum.FromString(lin.Substring(0, i));
}
}
public BigNum Denominator
{
get
{
// Better way to obtain the numerator?
// The FSharp library does not seem to offer any methods for this
string! lin = (!)val.ToString();
int i = lin.IndexOf('/');
if (i == -1)
return BigNum.ONE;
else
return BigNum.FromString(lin.Substring(i+1));
}
}
public override string! ToString()
{
return String.Format("{0}/{1}", Numerator, Denominator);
}
public static bool operator==(Rational r, Rational s)
{
return (r.val == s.val);
}
public static bool operator!=(Rational r, Rational s)
{
return !(r.val == s.val);
}
public override bool Equals(object obj)
{
if (obj == null) return false;
return obj is Rational && (Rational)obj == this;
}
public override int GetHashCode()
{
return this.val.GetHashCode();
}
public int Signum {
get { return this.val.Sign; }
}
public bool IsZero
{
get
{
return Signum == 0;
}
}
public bool IsNonZero
{
get
{
return Signum != 0;
}
}
public bool IsIntegral
{
get
{
// better way?
return !((!)this.val.ToString()).Contains("/");
}
}
[Pure][Reads(ReadsAttribute.Reads.Nothing)]
public bool HasValue (int n) { return this == new Rational(n); }
///
/// Returns the rational as an integer. Requires the rational to be integral.
///
public int AsInteger
{
get
{
System.Diagnostics.Debug.Assert(this.IsIntegral);
return Numerator.ToIntSafe;
}
}
public BigNum AsBigNum {
get
{
System.Diagnostics.Debug.Assert(this.IsIntegral);
return new BigNum (BNM.ToBigInt(this.val));
}
}
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 ((!)(BNM.Zero -r.val)); // for whatever reason, the Spec# compiler refuses to accept -r.val (unary negation)
}
public static Rational operator+(Rational r, Rational s)
{
return new Rational ((!)(r.val + s.val));
}
public static Rational operator-(Rational r, Rational s)
{
return new Rational ((!)(r.val - s.val));
}
public static Rational operator*(Rational r, Rational s)
{
return new Rational ((!)(r.val * s.val));
}
public static Rational operator/(Rational r, Rational s)
{
return new Rational ((!)(r.val / s.val));
}
public static bool operator<=(Rational r, Rational s)
{
return (r.val <= s.val);
}
public static bool operator>=(Rational r, Rational s)
{
return (r.val >= s.val);
}
public static bool operator<(Rational r, Rational s)
{
return (r.val < s.val);
}
public static bool operator>(Rational r, Rational s)
{
return (r.val > s.val);
}
}
}