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

com.mchange.sc.v2.math.package.scala Maven / Gradle / Ivy

The newest version!
package com.mchange.sc.v2;

import scala.annotation.tailrec;

package object math {
  private final val ProductLogMin = -1/scala.math.E

  // straight outta Wikipedia... Halley's method
  // https://en.wikipedia.org/wiki/Lambert_W_function
  //
  // TODO: test more systematically than a couple of examples
  def productLog( z : Double, seed : Double = 0, precision : Double = 0.00001, maxIterations : Int = 10000 ) : Double = {
    require( z > ProductLogMin && !z.isInfinite, s"productLog is not defined for z <= -1/e (${ProductLogMin}) or infinite z, so not defined for z = ${z}" )

    def computeNext( wj : Double ) : Double = {
      val ewj  = scala.math.exp( wj )
      val wjewj = wj * ewj
      val rhs = (wjewj - z) / ((ewj * (wj+1)) - (((wj+2)*(wjewj-z))/((2*wj) + 2)))
      wj - rhs
    }
    @tailrec
    def iterate( wj : Double, count : Int = 0 ) : Double = {
      val next = computeNext( wj );
      if ( scala.math.abs( next - wj ) < precision ) next
      else if ( count > maxIterations ) throw new Exception( s"productLog( ${z} ) failed to converge to a precision of ${precision} within ${maxIterations} iterations." )
      else iterate( next, count + 1 )
    }
    iterate( seed )
  }
  def optimalPrice( p0 : Double, p1 : Double, cost : Double, referencePrice : Double ) : Double = {
    val plArg = scala.math.exp( -1 + p0 + ((cost / referencePrice) * p1) )
    (-referencePrice + (cost * p1) - (referencePrice * productLog( plArg ))) / p1
  }
  def logitCdf2( p0 : Double, p1 : Double, x : Double ) : Double = {
    val exponent = -( p0 + (p1 * x) )
    1 / (1 + scala.math.exp( exponent ))
  }

  def lambertW( z : Double, seed : Double = 0, precision : Double = 0.00001, maxIterations : Int = 10000 ) : Double = {
    productLog( z, seed, precision, maxIterations )
  }

  implicit class RichLong( val long : Long ) extends AnyVal {
    def toIntExact : Int = {
      if ( long.isValidInt ) {
        long.toInt
      } else {
        throw new ArithmeticException( s"${long}L cannot be converted to Int (not in range [${Integer.MIN_VALUE},${Integer.MAX_VALUE})" )
      }
    }
  }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy