// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Rational numbers

package bignum

import "fmt"


// Rational represents a quotient a/b of arbitrary precision.
//
type Rational struct {
	a	*Integer;	// numerator
	b	Natural;	// denominator
}


// MakeRat makes a rational number given a numerator a and a denominator b.
//
func MakeRat(a *Integer, b Natural) *Rational {
	f := a.mant.Gcd(b);	// f > 0
	if f.Cmp(Nat(1)) != 0 {
		a = MakeInt(a.sign, a.mant.Div(f));
		b = b.Div(f);
	}
	return &Rational{a, b};
}


// Rat creates a small rational number with value a0/b0.
//
func Rat(a0 int64, b0 int64) *Rational {
	a, b := Int(a0), Int(b0);
	if b.sign {
		a = a.Neg()
	}
	return MakeRat(a, b.mant);
}


// Value returns the numerator and denominator of x.
//
func (x *Rational) Value() (numerator *Integer, denominator Natural) {
	return x.a, x.b
}


// Predicates

// IsZero returns true iff x == 0.
//
func (x *Rational) IsZero() bool	{ return x.a.IsZero() }


// IsNeg returns true iff x < 0.
//
func (x *Rational) IsNeg() bool	{ return x.a.IsNeg() }


// IsPos returns true iff x > 0.
//
func (x *Rational) IsPos() bool	{ return x.a.IsPos() }


// IsInt returns true iff x can be written with a denominator 1
// in the form x == x'/1; i.e., if x is an integer value.
//
func (x *Rational) IsInt() bool	{ return x.b.Cmp(Nat(1)) == 0 }


// Operations

// Neg returns the negated value of x.
//
func (x *Rational) Neg() *Rational	{ return MakeRat(x.a.Neg(), x.b) }


// Add returns the sum x + y.
//
func (x *Rational) Add(y *Rational) *Rational {
	return MakeRat((x.a.MulNat(y.b)).Add(y.a.MulNat(x.b)), x.b.Mul(y.b))
}


// Sub returns the difference x - y.
//
func (x *Rational) Sub(y *Rational) *Rational {
	return MakeRat((x.a.MulNat(y.b)).Sub(y.a.MulNat(x.b)), x.b.Mul(y.b))
}


// Mul returns the product x * y.
//
func (x *Rational) Mul(y *Rational) *Rational	{ return MakeRat(x.a.Mul(y.a), x.b.Mul(y.b)) }


// Quo returns the quotient x / y for y != 0.
// If y == 0, a division-by-zero run-time error occurs.
//
func (x *Rational) Quo(y *Rational) *Rational {
	a := x.a.MulNat(y.b);
	b := y.a.MulNat(x.b);
	if b.IsNeg() {
		a = a.Neg()
	}
	return MakeRat(a, b.mant);
}


// Cmp compares x and y. The result is an int value
//
//   <  0 if x <  y
//   == 0 if x == y
//   >  0 if x >  y
//
func (x *Rational) Cmp(y *Rational) int	{ return (x.a.MulNat(y.b)).Cmp(y.a.MulNat(x.b)) }


// ToString converts x to a string for a given base, with 2 <= base <= 16.
// The string representation is of the form "n" if x is an integer; otherwise
// it is of form "n/d".
//
func (x *Rational) ToString(base uint) string {
	s := x.a.ToString(base);
	if !x.IsInt() {
		s += "/" + x.b.ToString(base)
	}
	return s;
}


// String converts x to its decimal string representation.
// x.String() is the same as x.ToString(10).
//
func (x *Rational) String() string	{ return x.ToString(10) }


// Format is a support routine for fmt.Formatter. It accepts
// the formats 'b' (binary), 'o' (octal), and 'x' (hexadecimal).
//
func (x *Rational) Format(h fmt.State, c int)	{ fmt.Fprintf(h, "%s", x.ToString(fmtbase(c))) }


// RatFromString returns the rational number corresponding to the
// longest possible prefix of s representing a rational number in a
// given conversion base, the actual conversion base used, and the
// prefix length. The syntax of a rational number is:
//
//	rational = mantissa [exponent] .
//	mantissa = integer ('/' natural | '.' natural) .
//	exponent = ('e'|'E') integer .
//
// If the base argument is 0, the string prefix determines the actual
// conversion base for the mantissa. A prefix of ``0x'' or ``0X'' selects
// base 16; the ``0'' prefix selects base 8. Otherwise the selected base is 10.
// If the mantissa is represented via a division, both the numerator and
// denominator may have different base prefixes; in that case the base of
// of the numerator is returned. If the mantissa contains a decimal point,
// the base for the fractional part is the same as for the part before the
// decimal point and the fractional part does not accept a base prefix.
// The base for the exponent is always 10.
//
func RatFromString(s string, base uint) (*Rational, uint, int) {
	// read numerator
	a, abase, alen := IntFromString(s, base);
	b := Nat(1);

	// read denominator or fraction, if any
	var blen int;
	if alen < len(s) {
		ch := s[alen];
		if ch == '/' {
			alen++;
			b, base, blen = NatFromString(s[alen:len(s)], base);
		} else if ch == '.' {
			alen++;
			b, base, blen = NatFromString(s[alen:len(s)], abase);
			assert(base == abase);
			f := Nat(uint64(base)).Pow(uint(blen));
			a = MakeInt(a.sign, a.mant.Mul(f).Add(b));
			b = f;
		}
	}

	// read exponent, if any
	rlen := alen + blen;
	if rlen < len(s) {
		ch := s[rlen];
		if ch == 'e' || ch == 'E' {
			rlen++;
			e, _, elen := IntFromString(s[rlen:len(s)], 10);
			rlen += elen;
			m := Nat(10).Pow(uint(e.mant.Value()));
			if e.sign {
				b = b.Mul(m)
			} else {
				a = a.MulNat(m)
			}
		}
	}

	return MakeRat(a, b), base, rlen;
}
