All Downloads are FREE. Search and download functionalities are using the official Maven repository.

spire.std.bigDecimal.scala Maven / Gradle / Ivy

package spire.std

import java.lang.Math
import java.math.MathContext

import scala.annotation.tailrec

import BigDecimal.RoundingMode.{CEILING, FLOOR, HALF_UP}

import spire.algebra.{Field, IsRational, NRoot, Order, Signed, Trig}
import spire.math.Rational

trait BigDecimalIsField extends Field[BigDecimal] {
  override def minus(a: BigDecimal, b: BigDecimal): BigDecimal = a - b
  def negate(a: BigDecimal): BigDecimal = -a
  val one: BigDecimal = BigDecimal(1.0)
  def plus(a: BigDecimal, b: BigDecimal): BigDecimal = a + b
  override def pow(a: BigDecimal, b: Int): BigDecimal = a.pow(b)
  override def times(a: BigDecimal, b: BigDecimal): BigDecimal = a * b
  val zero: BigDecimal = BigDecimal(0.0)

  override def fromInt(n: Int): BigDecimal = BigDecimal(n)

  def quot(a: BigDecimal, b: BigDecimal): BigDecimal = a.quot(b)
  def mod(a: BigDecimal, b: BigDecimal): BigDecimal = a % b
  override def quotmod(a: BigDecimal, b: BigDecimal): (BigDecimal, BigDecimal) = a /% b
  def gcd(a: BigDecimal, b: BigDecimal): BigDecimal = {
    import java.math.BigInteger

    val Two = BigInteger.valueOf(2)
    val Five = BigInteger.valueOf(5)
    val Ten = BigInteger.TEN

    @tailrec
    def reduce0(n: BigInteger, prime: BigInteger, shift: Int = 0): (Int, BigInteger) = {
      val Array(div, rem) = n.divideAndRemainder(prime)
      if (n == BigInteger.ZERO || rem != BigInteger.ZERO) {
        (shift, n)
      } else {
        reduce0(div, prime, shift + 1)
      }
    }

    def reduce(n: BigInteger): (Int, Int, BigInteger) = {
      val (shift10, n0) = reduce0(n, Ten)
      val (shift5, n1) = reduce0(n0, Five)
      val (shift2, n2) = reduce0(n1, Two)
      (shift2 + shift10, shift5 + shift10, n2)
    }

    def gcd0(val0: BigInteger, exp0: Int, val1: BigInteger, exp1: Int): BigDecimal = {
      val (shiftTwo0, shiftFive0, shifted0) = reduce(val0)
      val (shiftTwo1, shiftFive1, shifted1) = reduce(val1)
      val sharedTwo = spire.math.min(shiftTwo0, shiftTwo1 + exp1 - exp0)
      val sharedFive = spire.math.min(shiftFive0, shiftFive1 + exp1 - exp0)
      val reshift = Two.pow(sharedTwo).multiply(Five.pow(sharedFive))
      val n = (shifted0 gcd shifted1).multiply(reshift)
      BigDecimal(new java.math.BigDecimal(n, -exp0))
    }

    val aJbd = a.bigDecimal
    val aVal = aJbd.unscaledValue.abs
    val aExp = -aJbd.scale

    val bJbd = b.bigDecimal
    val bVal = bJbd.unscaledValue.abs
    val bExp = -bJbd.scale

    if (aExp < bExp) gcd0(aVal, aExp, bVal, bExp) else gcd0(bVal, bExp, aVal, aExp)
  }

  override def fromDouble(n: Double): BigDecimal = BigDecimal(n, MathContext.UNLIMITED)
  def div(a: BigDecimal, b: BigDecimal): BigDecimal = a / b
}

trait BigDecimalIsNRoot extends NRoot[BigDecimal] {
  def nroot(a: BigDecimal, k: Int): BigDecimal = {
    if (a.mc.getPrecision <= 0)
      throw new ArithmeticException("Cannot find the nroot of a BigDecimal with unlimited precision.")
    NRoot.nroot(a, k, a.mc)
  }

  private[this] val two = BigDecimal(2)

  // this is newton's method
  override def sqrt(n: BigDecimal): BigDecimal = {
    if (n.mc.getPrecision <= 0)
      throw new ArithmeticException("Cannot find the sqrt of a BigDecimal with unlimited precision.")

    def approxSqrt(x: BigDecimal): BigDecimal =
      if (x < Double.MaxValue)
        BigDecimal(Math.sqrt(x.toDouble), x.mc)
      else
        approxSqrt(x / Double.MaxValue) * BigDecimal(Math.sqrt(Double.MaxValue), x.mc)

    @tailrec def loop(x: BigDecimal, y: BigDecimal): BigDecimal =
      if (x == y) y else loop(y, ((n / y) + y) / two)

    loop(BigDecimal(0, n.mc), approxSqrt(n))
  }

  def fpow(a: BigDecimal, b: BigDecimal): BigDecimal = spire.math.pow(a, b)
}

@SerialVersionUID(1L)
class BigDecimalIsTrig(mc: MathContext = BigDecimal.defaultMathContext) extends Trig[BigDecimal] with Serializable {
  import spire.math.Real

  val bits = Real.digitsToBits(mc.getPrecision + 1)

  def fromReal(r: Real): BigDecimal =
    BigDecimal(r(bits).toBigInt) / BigDecimal(BigInt(2).pow(bits))

  lazy val e: BigDecimal = fromReal(Real.e)
  lazy val pi: BigDecimal = fromReal(Real.pi)

  def exp(x: BigDecimal): BigDecimal = fromReal(Real.exp(Real(x)))
  def expm1(x: BigDecimal): BigDecimal = fromReal(Real.exp(Real(x)) - Real.one)
  def log(a: BigDecimal): BigDecimal = fromReal(Real.log(Real(a)))
  def log1p(a: BigDecimal): BigDecimal = fromReal(Real.log(Real(a) + Real.one))

  lazy val degreesPerRadian = fromReal(Real(360) / (Real.pi * Real(2)))
  def toRadians(a: BigDecimal): BigDecimal = a / degreesPerRadian
  def toDegrees(a: BigDecimal): BigDecimal = a * degreesPerRadian

  def sin(a: BigDecimal): BigDecimal = fromReal(Real.sin(Real(a)))
  def cos(a: BigDecimal): BigDecimal = fromReal(Real.cos(Real(a)))
  def tan(a: BigDecimal): BigDecimal = fromReal(Real.tan(Real(a)))

  def asin(a: BigDecimal): BigDecimal = fromReal(Real.asin(Real(a)))
  def acos(a: BigDecimal): BigDecimal = fromReal(Real.acos(Real(a)))
  def atan(a: BigDecimal): BigDecimal = fromReal(Real.atan(Real(a)))

  def atan2(y: BigDecimal, x: BigDecimal): BigDecimal =
    fromReal(Real.atan2(Real(y), Real(x)))

  def sinh(a: BigDecimal): BigDecimal = fromReal(Real.sinh(Real(a)))
  def cosh(a: BigDecimal): BigDecimal = fromReal(Real.cosh(Real(a)))
  def tanh(a: BigDecimal): BigDecimal = fromReal(Real.tanh(Real(a)))
}

trait BigDecimalOrder extends Order[BigDecimal] {
  override def eqv(x: BigDecimal, y: BigDecimal): Boolean = x == y
  override def neqv(x: BigDecimal, y: BigDecimal): Boolean = x != y
  override def gt(x: BigDecimal, y: BigDecimal): Boolean = x > y
  override def gteqv(x: BigDecimal, y: BigDecimal): Boolean = x >= y
  override def lt(x: BigDecimal, y: BigDecimal): Boolean = x < y
  override def lteqv(x: BigDecimal, y: BigDecimal): Boolean = x <= y
  override def min(x: BigDecimal, y: BigDecimal): BigDecimal = x.min(y)
  override def max(x: BigDecimal, y: BigDecimal): BigDecimal = x.max(y)
  // Scala compareTo has no guarantee to return only -1, 0 or 1 as per Spire compare contract,
  // so we call Java's compareTo which does
  def compare(x: BigDecimal, y: BigDecimal): Int = x.bigDecimal.compareTo(y.bigDecimal)
}

trait BigDecimalIsSigned extends Signed[BigDecimal] {
  def signum(a: BigDecimal): Int = a.signum
  def abs(a: BigDecimal): BigDecimal = a.abs
}

trait BigDecimalIsReal extends IsRational[BigDecimal] with BigDecimalOrder with BigDecimalIsSigned {
  def toDouble(x: BigDecimal): Double = x.toDouble
  def ceil(a: BigDecimal): BigDecimal = a.setScale(0, CEILING)
  def floor(a: BigDecimal): BigDecimal = a.setScale(0, FLOOR)
  def round(a: BigDecimal): BigDecimal = a.setScale(0, HALF_UP)
  def isWhole(a: BigDecimal): Boolean = a % 1.0 == 0.0
  def toRational(a:BigDecimal): Rational = Rational(a)
}

@SerialVersionUID(0L)
class BigDecimalAlgebra extends BigDecimalIsField with BigDecimalIsNRoot with BigDecimalIsReal with Serializable

trait BigDecimalInstances {
  import BigDecimal.defaultMathContext

  implicit final val BigDecimalAlgebra = new BigDecimalAlgebra
  implicit def BigDecimalIsTrig(implicit mc: MathContext = defaultMathContext): BigDecimalIsTrig =
    new BigDecimalIsTrig(mc)

  import spire.math.NumberTag._
  implicit final val BigDecimalTag = new LargeTag[BigDecimal](Approximate, BigDecimal(0))
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy