spire.math.Rational.scala Maven / Gradle / Ivy
The newest version!
package spire
package math
import scala.math.{ScalaNumber, ScalaNumericConversions}
import java.math.{BigDecimal => JBigDecimal, BigInteger, MathContext, RoundingMode}
import spire.algebra.{Field, IsRational, NRoot, Sign}
import spire.algebra.Sign.{ Positive, Zero, Negative }
import spire.macros.Checked
import spire.std.long.LongAlgebra
import spire.std.double._
import spire.syntax.nroot._
import spire.util.Opt
//scalastyle:off equals.hash.code
sealed abstract class Rational extends ScalaNumber with ScalaNumericConversions with Ordered[Rational] { lhs =>
def numerator: SafeLong
def denominator: SafeLong
def numeratorAsLong: Long
def denominatorAsLong: Long
def isValidLong: Boolean
def isWhole: Boolean
def longValue: Long = toBigInt.longValue
def intValue: Int = longValue.intValue
def floatValue: Float = doubleValue.toFloat
// ugh, ScalaNumber and ScalaNumericConversions in 2.10 require this hack
override def underlying: Object = this
def abs: Rational = if (signum < 0) -this else this
def inverse: Rational = reciprocal
def reciprocal: Rational
def signum: Int
def isZero: Boolean
def isOne: Boolean
def unary_-(): Rational
def +(rhs: Rational): Rational
def -(rhs: Rational): Rational
def *(rhs: Rational): Rational
def /(rhs: Rational): Rational
def /~(rhs: Rational): Rational = Rational(SafeLong((this / rhs).toBigInt), SafeLong.one)
def %(rhs: Rational): Rational = this - (this /~ rhs) * rhs
def /%(rhs: Rational): (Rational, Rational) = {
val q = this /~ rhs
(q, this - q * rhs)
}
def gcd(rhs: Rational): Rational
def toReal: Real = Real(this)
def toAlgebraic: Algebraic = Algebraic(this)
def toBigDecimal(scale: Int, mode: RoundingMode): BigDecimal = {
val n = new JBigDecimal(numerator.toBigInteger)
val d = new JBigDecimal(denominator.toBigInteger)
BigDecimal(n.divide(d, scale, mode))
}
def toBigDecimal(mc: MathContext): BigDecimal = {
val n = new JBigDecimal(numerator.toBigInteger)
val d = new JBigDecimal(denominator.toBigInteger)
BigDecimal(n.divide(d, mc))
}
def toSafeLong: SafeLong
final def toBigInt: BigInt = toSafeLong.toBigInt
override def shortValue: Short = longValue.toShort
override def byteValue: Byte = longValue.toByte
def floor: Rational
def ceil: Rational
def round: Rational
def roundTo(denom: SafeLong): Rational = (this * denom).round / denom
def pow(exp: Int): Rational
def sign: Sign = Sign(signum)
def compareToOne: Int
def min(rhs: Rational): Rational =
if ((lhs compare rhs) < 0) lhs else rhs
def max(rhs: Rational): Rational =
if ((lhs compare rhs) > 0) lhs else rhs
/**
* Returns a `Rational` whose numerator and denominator both fit in an `Int`.
*/
def limitToInt: Rational = limitTo(BigInt(Int.MaxValue))
/**
* Returns a `Rational` whose numerator and denominator both fit in a `Long`.
*/
def limitToLong: Rational = limitTo(BigInt(Long.MaxValue))
/**
* Returns a `Rational` whose denominator and numerator are no larger than
* `max` and whose value is close to the original. This applies, even if, for
* example, this `Rational` is greater than `max`. In that case,
* `Rational(max, 1)` is returned.
*
* @param max A positive integer.
*/
def limitTo(max: SafeLong): Rational = if (this.signum < 0) {
-((-this).limitTo(max))
} else {
require(max.signum > 0, "Limit must be a positive integer.")
val half = max >> 1
val floor = SafeLong(this.toBigInt)
if (floor >= max) {
Rational(max)
} else if (floor >= (max >> 1)) {
Rational(floor.toLong)
} else if (this.compareToOne < 0) {
limitDenominatorTo(max)
} else {
limitDenominatorTo(max * denominator / numerator)
}
}
/**
* Finds the closest `Rational` to this `Rational` whose denominator is no
* larger than `limit`.
*
* See [[http://en.wikipedia.org/wiki/Stern%E2%80%93Brocot_tree#Mediants_and_binary_search]]
*/
def limitDenominatorTo(limit: SafeLong): Rational = {
require(limit.signum > 0, "Cannot limit denominator to non-positive number.")
def nextK(curr: Opt[SafeLong]): Opt[SafeLong] =
if (curr.isEmpty) Opt(SafeLong(2)) else Opt(curr.get * 2)
// This implements the basic mediant algorithm. However, we adapt it in 1
// way - by allowing an exponentially growing multiplier for either the
// lower bound or upper bound that we use when calculating the mediant.
// This fixes the cases where the algorithm is slow to converge. This
// happens when the lower bound or upper bound are made up of small
// numbers, thus requiring us to add it to the other bound many times, in
// succession in order to make a significant enough change to flip the
// direction of the search or find the mediant. For example, instead of
// adding u=1/2 n times to the mediant, we would instead try to add 1/2,
// 2/4, 4/8, 8/16, etc, until we either pass the limit or our bound no
// longer contains this Rational. Instead of requiring n additions, we only
// need log n.
@tailrec
def closest(l: Rational, u: Rational, lk: Opt[SafeLong], rk: Opt[SafeLong]): Rational = {
val mediant = (lk.nonEmpty, rk.nonEmpty) match {
case (true, false) => Rational(lk.get * l.numerator + u.numerator, lk.get * l.denominator + u.denominator)
case (false, true) => Rational(l.numerator + rk.get * u.numerator, l.denominator + rk.get * u.denominator)
case _ => Rational(l.numerator + u.numerator, l.denominator + u.denominator)
}
if (mediant.denominator > limit) {
if (lk.nonEmpty || rk.nonEmpty) closest(l, u, Opt.empty, Opt.empty)
else if ((this - l).abs > (u - this).abs) u
else l
} else if (mediant == this) {
mediant
} else if (mediant < this) {
closest(mediant, u, Opt.empty, nextK(rk))
} else {
closest(l, mediant, nextK(lk), Opt.empty)
}
}
// note: a rational with a zero denominator is not allowed. But for this algorithm it is quite convenient. So we
// bypass the factory method and call the constructor directly.
import Rational.LongRational
this.sign match {
case Zero => this
case Positive => closest(Rational(this.toBigInt), new LongRational(1, 0), Opt.empty, Opt.empty)
case Negative => closest(new LongRational(-1, 0), Rational(this.toBigInt), Opt.empty, Opt.empty)
}
}
override def equals(that: Any): Boolean = that match {
case that: Real => this == that.toRational
case that: Algebraic => that == this
case that: BigInt => isWhole && toBigInt == that
case that: BigDecimal => try { toBigDecimal(that.mc) == that } catch { case ae: ArithmeticException => false }
case that: SafeLong => SafeLong(toBigInt) == that
case that: Number => Number(this) == that
case that: Natural => isWhole && this == Rational(that.toBigInt)
case that: Complex[_] => that == this
case that: Quaternion[_] => that == this
case that: Long => isValidLong && toLong == that
case that => unifiedPrimitiveEquals(that)
}
}
object Rational extends RationalInstances {
private val RationalString = """^(-?\d+)/(-?\d+)$""".r
private val IntegerString = """^(-?\d+)$""".r
val zero: Rational = longRational(0L, 1L)
val one: Rational = longRational(1L, 1L)
private[math] def toDouble(n: SafeLong, d: SafeLong): Double = n.signum match {
case 0 => 0.0
case -1 => -toDouble(-n, d)
case 1 =>
// We basically just shift n so that integer division gives us 54 bits of
// accuracy. We use the last bit for rounding, so end w/ 53 bits total.
val sharedLength = java.lang.Math.min(n.bitLength, d.bitLength)
val dLowerLength = d.bitLength - sharedLength
val nShared = n >> (n.bitLength - sharedLength)
val dShared = d >> dLowerLength
val addBit = if (nShared < dShared || (nShared == dShared && d.toBigInteger.getLowestSetBit < dLowerLength)) {
1
} else {
0
}
val e = d.bitLength - n.bitLength + addBit
val ln = n << (53 + e) // We add 1 bit for rounding.
val lm = (ln / d).toLong
val m = ((lm >> 1) + (lm & 1)) & 0x000fffffffffffffL
val bits = (m | ((1023L - e) << 52))
java.lang.Double.longBitsToDouble(bits)
}
def apply(n: BigInt, d: BigInt): Rational =
apply(SafeLong(n), SafeLong(d))
def apply(n: Long, d: Long): Rational = {
def build0(n: Long, d: Long) = if (n == 0L) zero else {
val divisor = spire.math.gcd(n, d)
if (divisor == 1L)
longRational(n, d)
else
longRational(n / divisor, d / divisor)
}
if (d == 0) throw new IllegalArgumentException("0 denominator")
else if (d > 0) build0(n, d)
else if (n == Long.MinValue || d == Long.MinValue) Rational(-BigInt(n), -BigInt(d))
else build0(-n, -d)
}
private[math] def buildWithDiv(num: Long, ngcd: Long, rd: Long, lden: Long): Rational = {
val n = num / ngcd
val d = rd / ngcd
Checked.tryOrReturn {
apply(n, lden * d)
} {
Rational(SafeLong(n), SafeLong(lden) * d)
}
}
def apply(n: SafeLong, d: SafeLong): Rational = {
if (d.isZero) throw new IllegalArgumentException("0 denominator")
else if (n.isValidLong && d.isValidLong) apply(n.toLong, d.toLong)
else if (d.signum < 0) return apply(-n, -d)
else {
val g = n gcd d
n / g match {
case SafeLongLong(x) => (d / g) match {
case SafeLongLong(y) => longRational(x, y)
case y: SafeLongBigInteger => bigRational(x, y)
}
case x: SafeLongBigInteger => bigRational(x, d / g)
}
}
}
implicit def apply(x: Int): Rational = if(x == 0) Rational.zero else longRational(x, 1L)
implicit def apply(x: Long): Rational = if(x == 0L) Rational.zero else longRational(x, 1L)
implicit def apply(x: BigInt): Rational = apply(x, SafeLong.one)
implicit def apply(x: Float): Rational = apply(x.toDouble)
implicit def apply(x: Double): Rational = if (x == 0D) {
zero
} else {
val bits = java.lang.Double.doubleToLongBits(x)
val value = if ((bits >> 63) < 0) -(bits & 0x000FFFFFFFFFFFFFL | 0x0010000000000000L)
else (bits & 0x000FFFFFFFFFFFFFL | 0x0010000000000000L)
val exp = ((bits >> 52) & 0x7FF).toInt - 1075 // 1023 + 52
if (exp > 10) {
apply(SafeLong(value) << exp, SafeLong.one)
} else if (exp >= 0) {
apply(value << exp, 1L)
} else if (exp >= -52 && (~((-1L) << (-exp)) & value) == 0L) {
apply(value >> (-exp), 1L)
} else {
apply(SafeLong(value), SafeLong.one << (-exp))
}
}
implicit def apply(x:BigDecimal): Rational = {
if (x.ulp >= 1) {
apply(x.toBigInt, 1)
} else {
val n = (x / x.ulp).toBigInt
val d = (BigDecimal(1.0) / x.ulp).toBigInt
apply(n, d)
}
}
def apply(r: String): Rational = r match {
case RationalString(n, d) => Rational(SafeLong(n), SafeLong(d))
case IntegerString(n) => Rational(SafeLong(n))
case s => try {
Rational(BigDecimal(s))
} catch {
case nfe: NumberFormatException => throw new NumberFormatException("For input string: " + s)
}
}
implicit def apply(n: SafeLong): Rational =
n match {
case SafeLongLong(x) => if (x == 0) Rational.zero else longRational(x, 1L)
case x:SafeLongBigInteger => bigRational(x, SafeLong.one)
}
implicit def apply(x: Number): Rational = x match {
case RationalNumber(n) => n
case IntNumber(n) => apply(n)
case FloatNumber(n) => apply(n)
case DecimalNumber(n) => apply(n)
}
@SerialVersionUID(0L)
private final class LongRational(val n: Long, val d: Long) extends Rational with Serializable {
def numerator: SafeLong = SafeLong(n)
def denominator: SafeLong = SafeLong(d)
def numeratorAsLong: Long = n
def denominatorAsLong: Long = d
def reciprocal: Rational =
if (n == 0L) throw new ArithmeticException("reciprocal called on 0/1")
else if (n > 0L) longRational(d, n)
else if (n == Long.MinValue || d == Long.MinValue) bigRational(-SafeLong(d), -SafeLong(n))
else longRational(-d, -n)
override def signum: Int = java.lang.Long.signum(n)
override def isWhole: Boolean = d == 1L
override def isZero: Boolean = n == 0L
override def isOne: Boolean = d == 1L && n == 1L
override def isValidChar: Boolean = isWhole && new scala.runtime.RichLong(n).isValidChar
override def isValidByte: Boolean = isWhole && new scala.runtime.RichLong(n).isValidByte
override def isValidShort: Boolean = isWhole && new scala.runtime.RichLong(n).isValidShort
override def isValidInt: Boolean = isWhole && new scala.runtime.RichLong(n).isValidInt
override def isValidLong: Boolean = isWhole
override def toSafeLong: SafeLong = SafeLong(n / d)
override def doubleValue: Double = Rational.toDouble(n, d)
override def unary_-(): Rational =
if (n == Long.MinValue) bigRational(SafeLong.safe64, SafeLong(d))
else longRational(-n, d)
def +(r: Rational): Rational = r match {
case r: LongRational =>
val dgcd: Long = spire.math.gcd(d, r.d)
if (dgcd == 1L) {
Checked.tryOrReturn[Rational] {
Rational(n * r.d + r.n * d, d * r.d)
} {
Rational(SafeLong(n) * r.d + SafeLong(r.n) * d, SafeLong(d) * r.d)
}
} else {
val lden: Long = d / dgcd
val rden: Long = r.d / dgcd
Checked.tryOrReturn {
val num: Long = n * rden + r.n * lden
val ngcd: Long = spire.math.gcd(num, dgcd)
if (ngcd == 1L)
Rational(num, lden * r.d)
else
Rational.buildWithDiv(num, ngcd, r.d, lden)
} {
val num: SafeLong = SafeLong(n) * rden + SafeLong(r.n) * lden
val ngcd: Long = spire.math.gcd(dgcd, (num % dgcd).toLong)
if (ngcd == 1L)
Rational(num, SafeLong(lden) * r.d)
else
Rational(num / ngcd, SafeLong(lden) * (r.d / ngcd))
}
}
case r: BigRational =>
val dgcd: Long = spire.math.gcd(d, (r.d % d).toLong)
if (dgcd == 1L) {
val num = r.d * n + r.n * d
val den = r.d * d
Rational(num, den)
} else {
val lden: Long = d / dgcd
val rden: SafeLong = r.d / dgcd
val num: SafeLong = rden * n + r.n * lden
val ngcd: Long = num match {
case SafeLongLong(x) => spire.math.gcd(x, dgcd)
case SafeLongBigInteger(x) => spire.math.gcd(dgcd, (x mod BigInteger.valueOf(dgcd)).longValue)
}
if (ngcd == 1L)
Rational(num, SafeLong(lden) * r.d)
else
Rational(num / ngcd, r.d / ngcd * lden)
}
}
def -(r: Rational): Rational = r match {
case r: LongRational =>
val dgcd: Long = spire.math.gcd(d, r.d)
if (dgcd == 1L) {
Checked.tryOrReturn[Rational] {
Rational(n * r.d - r.n * d, d * r.d)
} {
Rational(SafeLong(n) * r.d - SafeLong(r.n) * d, SafeLong(d) * r.d)
}
} else {
val lden: Long = d / dgcd
val rden: Long = r.d / dgcd
Checked.tryOrReturn {
val num: Long = n * rden - r.n * lden
val ngcd: Long = spire.math.gcd(num, dgcd)
if (ngcd == 1L)
Rational(num, lden * r.d)
else
Rational.buildWithDiv(num, ngcd, r.d, lden)
} {
val num: SafeLong = SafeLong(n) * rden - SafeLong(r.n) * lden
val ngcd: Long = spire.math.gcd(dgcd, (num % dgcd).toLong)
if (ngcd == 1L)
Rational(num, SafeLong(lden) * r.d)
else
Rational(num / ngcd, SafeLong(lden) * (r.d / ngcd))
}
}
case r: BigRational =>
val dgcd: Long = spire.math.gcd(d, (r.d % d).toLong)
if (dgcd == 1L) {
val num = r.d * n - r.n * d
val den = r.d * d
Rational(num, den)
} else {
val lden: Long = d / dgcd
val rden: SafeLong = r.d / dgcd
val num: SafeLong = rden * n - r.n * lden
val ngcd: Long = num match {
case SafeLongLong(x) => spire.math.gcd(x, dgcd)
case SafeLongBigInteger(x) => spire.math.gcd(dgcd, (x mod BigInteger.valueOf(dgcd)).longValue)
}
if (ngcd == 1L)
Rational(num, SafeLong(lden) * r.d)
else
Rational(num / ngcd, r.d / ngcd * lden)
}
}
def *(r: Rational): Rational = if (n == 0L) Rational.zero else r match {
case r: LongRational =>
val a = spire.math.gcd(n, r.d)
val b = spire.math.gcd(d, r.n)
// this does not have to happen within the checked block, since the divisions are guaranteed to work
val n1 = n / a
val n2 = r.n / b
val d1 = d / b
val d2 = r.d / a
Checked.tryOrReturn[Rational] {
longRational(n1 * n2, d1 * d2)
} {
// we know that the result does not fit into a LongRational, and also that the denominators are positive.
// so we can just call BigRational.apply directly
bigRational(SafeLong(n1) * n2, SafeLong(d1) * d2)
}
case r: BigRational =>
val a = spire.math.gcd(n, (r.d % n).toLong)
val b = spire.math.gcd(d, (r.n % d).toLong)
Rational(SafeLong(n / a) * (r.n / b), SafeLong(d / b) * (r.d / a))
}
def /(r: Rational): Rational = r match {
case r: LongRational =>
// we only have to do this check in the long branch, since 0 is always represented as LongRational(0,1)
if (r.n == 0L) throw new ArithmeticException("divide (/) by 0")
if (n == 0L) return this
val a = spire.math.gcd(n, r.n)
val b = spire.math.gcd(d, r.d)
// this does not have to happen within the checked block, since the divisions are guaranteed to work
val n1 = n / a
val n2 = r.n / a
var d1 = d / b
var d2 = r.d / b
// this is to make sure that the denominator of the result is positive
if (n2 < 0) {
// denominators are always positive, so negating them can not yield an overflow
d1 = -d1
d2 = -d2
}
Checked.tryOrReturn[Rational] {
longRational(n1 * d2, d1 * n2)
} {
// we know that the result does not fit into a LongRational, and we have made sure that the product of d1
// and n2 is positive. So we can just call BigRational.apply directly
bigRational(SafeLong(n1) * d2, SafeLong(d1) * n2)
}
case r: BigRational =>
if (n == 0L) return this
val a = spire.math.gcd(n, (r.n % n).toLong)
val b = spire.math.gcd(d, (r.d % d).toLong)
val num = SafeLong(n / a) * (r.d / b)
val den = SafeLong(d / b) * (r.n / a)
if (den.signum < 0) Rational(-num, -den) else Rational(num, den)
}
def gcd(r: Rational): Rational = if(isZero) r.abs else if(isOne) this else r match {
case r: LongRational =>
val dgcd: Long = spire.math.gcd(d, r.d)
val n0 = spire.math.abs(n)
val n1 = spire.math.abs(r.n)
if (dgcd == 1L) {
Rational(spire.math.gcd(n0, n1), SafeLong(d) * r.d)
} else {
val lm = d / dgcd
val rm = r.d / dgcd
Rational((SafeLong(n0) * rm) gcd (SafeLong(n1) * lm), SafeLong(dgcd) * lm * rm)
}
case r: BigRational =>
val dgcd: Long = spire.math.gcd(d, (r.d % d).toLong)
if (dgcd == 1L) {
Rational(spire.math.gcd(spire.math.abs(n), spire.math.abs((r.n % n).toLong)),
SafeLong(d) * r.d)
} else {
val lm = d / dgcd
val rm = r.d / dgcd
Rational((SafeLong(spire.math.abs(n)) * rm) gcd (r.n.abs * lm),
SafeLong(dgcd) * lm * rm)
}
}
def floor: Rational =
if (d == 1L) this
else if (n >= 0) Rational(n / d, 1L)
else Rational(n / d - 1L, 1L)
def ceil: Rational =
if (d == 1L) this
else if (n >= 0) Rational(n / d + 1L, 1L)
else Rational(n / d, 1L)
def round: Rational =
if (n >= 0) {
val m = (n % d)
if (m >= (d - m)) Rational(n / d + 1) else Rational(n / d)
} else {
val m = -(n % d)
if (m >= (d - m)) Rational(n / d - 1) else Rational(n / d)
}
def pow(exp: Int): Rational = if (exp == 0)
Rational.one
else if (exp < 0)
reciprocal.pow(-exp)
else
Rational(SafeLong(n).pow(exp), SafeLong(d).pow(exp))
def compareToOne: Int = n compare d
def compare(r: Rational): Int = r match {
case r: LongRational =>
Checked.tryOrElse {
LongAlgebra.compare(n * r.d, r.n * d)
} {
val dgcd = spire.math.gcd(d, r.d)
if (dgcd == 1L)
(SafeLong(n) * r.d) compare (SafeLong(r.n) * d)
else
(SafeLong(n) * (r.d / dgcd)) compare (SafeLong(r.n) * (d / dgcd))
}
case r: BigRational =>
val dgcd = spire.math.gcd(d, (r.d % d).toLong)
if (dgcd == 1L)
(SafeLong(n) * r.d) compare (r.n * d)
else
(SafeLong(n) * (r.d / dgcd)) compare (r.n * (d / dgcd))
}
override def longValue: Long =
if(d == 1L) n
else n / d
override def equals(that: Any): Boolean = that match {
case that: LongRational => this.n == that.n && this.d == that.d
case _ => super.equals(that)
}
override def hashCode: Int =
if (d == 1L) unifiedPrimitiveHashcode
else 29 * (37 * n.## + d.##)
override def toString: String = if(isWhole) n.toString else s"$n/$d"
}
private def longRational(n: Long, d: Long): LongRational = new LongRational(n, d)
@SerialVersionUID(0L)
private final class BigRational(val n: SafeLong, val d: SafeLong) extends Rational with Serializable {
def numerator: SafeLong = n
def denominator: SafeLong = d
def numeratorAsLong: Long = n.toLong
def denominatorAsLong: Long = d.toLong
def reciprocal: Rational = if (signum < 0)
Rational(-d, -n)
else
bigRational(d, n)
override def signum: Int = n.signum
override def isWhole: Boolean = d.isOne
override def isZero: Boolean = false
override def isOne: Boolean = false
override def isValidChar: Boolean = false
override def isValidByte: Boolean = false
override def isValidShort: Boolean = false
override def isValidInt: Boolean = false
override def isValidLong: Boolean = false
override def toSafeLong: SafeLong = n / d
override def doubleValue: Double = Rational.toDouble(n, d)
override def unary_-(): Rational = Rational(-n, d)
def +(r: Rational): Rational = r match {
case r: LongRational => r + this
case r: BigRational =>
val dgcd: SafeLong = d.gcd(r.d)
if (dgcd.isOne) {
Rational(r.d * n + r.n * d, r.d * d)
} else {
val lden: SafeLong = d / dgcd
val rden: SafeLong = r.d / dgcd
val num: SafeLong = rden * n + r.n * lden
val ngcd: SafeLong = num.gcd(dgcd)
if (ngcd.isOne)
Rational(num, lden * r.d)
else
Rational(num / ngcd, (r.d / ngcd) * lden)
}
}
def -(r: Rational): Rational = r match {
case r: LongRational => (-r) + this
case r: BigRational =>
val dgcd: SafeLong = d.gcd(r.d)
if (dgcd.isOne) {
Rational(r.d * n - r.n * d, r.d * d)
} else {
val lden: SafeLong = d / dgcd
val rden: SafeLong = r.d / dgcd
val num: SafeLong = rden * n - r.n * lden
val ngcd: SafeLong = num.gcd(dgcd)
if (ngcd.isOne)
Rational(num, lden * r.d)
else
Rational(num / ngcd, (r.d / ngcd) * lden)
}
}
def *(r: Rational): Rational = r match {
case r: LongRational => r * this
case r: BigRational =>
val a = n.gcd(r.d)
val b = d.gcd(r.n)
Rational((n / a) * (r.n / b), (d / b) * (r.d / a))
}
def /(r: Rational): Rational = r match {
case r: LongRational => r.inverse * this
case r: BigRational =>
val a = n.gcd(r.n)
val b = d.gcd(r.d)
val num = (n / a) * (r.d / b)
val den = (d / b) * (r.n / a)
if (den.signum < 0) Rational(-num, -den) else Rational(num, den)
}
def gcd(r: Rational): Rational = r match {
case r: LongRational => r gcd this
case r: BigRational =>
val dgcd: SafeLong = d.gcd(r.d)
if (dgcd.isOne) {
Rational(n.abs gcd r.n.abs, d * r.d)
} else {
val lm = d / dgcd
val rm = r.d / dgcd
Rational((n * rm).abs gcd (r.n * lm).abs, dgcd * lm * rm)
}
}
def floor: Rational =
if (isWhole) this
else if (n.signum >= 0) Rational(n / d, SafeLong.one)
else Rational(n / d - 1, SafeLong.one)
def ceil: Rational =
if (isWhole) this
else if (n.signum >= 0) Rational(n / d + 1, SafeLong.one)
else Rational(n / d, SafeLong.one)
def round: Rational =
if (n.signum >= 0) {
val m = n % d
if (m >= (d - m)) Rational(n / d + 1) else Rational(n / d)
} else {
val m = -(n % d)
if (m >= (d - m)) Rational(n / d - 1) else Rational(n / d)
}
def pow(exp: Int): Rational = if (exp == 0)
Rational.one
else if (exp < 0)
Rational(d pow -exp, n pow -exp)
else
Rational(n pow exp, d pow exp)
def compareToOne: Int = n compare d
def compare(r: Rational): Int = r match {
case r: LongRational =>
val dgcd = spire.math.gcd(r.d, (d % r.d).toLong)
if (dgcd == 1L)
(n * r.d) compare (SafeLong(r.n) * d)
else
(n * (r.d / dgcd)) compare (SafeLong(r.n) * (d / dgcd))
case r: BigRational =>
val dgcd = d.gcd(r.d)
if (dgcd.isOne)
(n * r.d) compare (r.n * d)
else
((r.d / dgcd) * n) compare ((d / dgcd) * r.n)
}
override def equals(that: Any): Boolean = that match {
case that: BigRational => this.n == that.n && this.d == that.d
case _ => super.equals(that)
}
override def hashCode: Int =
29 * (37 * n.## + d.##)
override def toString: String = if(isWhole) n.toString else s"$n/$d"
}
private def bigRational(n: SafeLong, d: SafeLong): BigRational = new BigRational(n, if(d.isOne) SafeLong.one else d)
}
trait RationalInstances {
implicit final val RationalAlgebra = new RationalAlgebra
import NumberTag._
implicit final val RationalTag = new LargeTag[Rational](Exact, Rational.zero)
}
/**
* Used for Fractional[Rational] and Numeric[Rational] to provide
* approximate sqrt and nroot implementations.
*
* These operations occur at Double precision.
*/
private[math] trait RationalApproximateNRoot extends NRoot[Rational] {
def nroot(n: Rational, k: Int): Rational =
Rational(n.toDouble nroot k)
def fpow(n: Rational, k: Rational): Rational =
Rational(n.toDouble ** k.toDouble)
}
private[math] trait RationalIsField extends Field[Rational] {
override def minus(a:Rational, b:Rational): Rational = a - b
def negate(a:Rational): Rational = -a
def one: Rational = Rational.one
def plus(a:Rational, b:Rational): Rational = a + b
override def pow(a:Rational, b:Int): Rational = a.pow(b)
override def times(a:Rational, b:Rational): Rational = a * b
def zero: Rational = Rational.zero
def quot(a:Rational, b:Rational): Rational = a /~ b
def mod(a:Rational, b:Rational): Rational = a % b
override def quotmod(a:Rational, b:Rational): (Rational, Rational) = a /% b
def gcd(a:Rational, b:Rational): Rational = a gcd b
override def fromInt(n: Int): Rational = Rational(n)
override def fromDouble(n: Double): Rational = Rational(n)
def div(a:Rational, b:Rational): Rational = a / b
}
private[math] trait RationalIsReal extends IsRational[Rational] {
override def eqv(x:Rational, y:Rational): Boolean = x == y
override def neqv(x:Rational, y:Rational): Boolean = x != y
override def gt(x: Rational, y: Rational): Boolean = x > y
override def gteqv(x: Rational, y: Rational): Boolean = x >= y
override def lt(x: Rational, y: Rational): Boolean = x < y
override def lteqv(x: Rational, y: Rational): Boolean = x <= y
def compare(x: Rational, y: Rational): Int = x compare y
override def sign(a: Rational): Sign = a.sign
def signum(a: Rational): Int = a.signum
def abs(a: Rational): Rational = a.abs
def toDouble(r: Rational): Double = r.toDouble
def ceil(a:Rational): Rational = a.ceil
def floor(a:Rational): Rational = a.floor
def round(a:Rational): Rational = a.round
def toRational(a: Rational): Rational = a
def isWhole(a:Rational): Boolean = a.isWhole
}
@SerialVersionUID(1L)
class RationalAlgebra extends RationalIsField with RationalIsReal with Serializable
© 2015 - 2025 Weber Informatics LLC | Privacy Policy