//-----------------------------------------------------------------------------
//
// 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;
}
}
}