/* Copyright 2010 Aaron J. Radke Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package cc.drx import scala.util.hashing.{MurmurHash3 => MM3} object Repeat{ @tailrec def apply(n:Long)(f: => Unit):Unit = if(n > 0){f;apply(n-1)(f)} @tailrec def iterate[T](n:Long,a:T)(f:T => T):T = if(n > 0) iterate(n-1,f(a))(f) else a @tailrec def scanRight[T](n:Long, a:List[T])(f:T => T):List[T] = if(n > 0) scanRight(n-1,f(a.head)::a)(f) else a // def iterator[A](n:Long, f: => A):Iterator[A] = Iterator.fill[A](n)(f) } /**https://www.wikiwand.com/en/Rational_number*/ object Ratio{ /* val `½` = Ratio(1,2) val `⅓` = Ratio(1,3) val `⅔` = Ratio(2,3) val `¼` = Ratio(1,4) val `¾` = Ratio(3,4) val `⅒` = Ratio(1,10) */ val golden = Ratio(2971215073L,1836311903L) def apply(num:Long):Ratio = Ratio(num,1L) def apply(d:Double):Ratio = { val i = d.toLong //TODO implement this reduction with some logarithmic values val f = d - i; val p = 100000000L (Ratio((f*p).round, p) + i).reduce } //Note: the golden ratio is exceptionally hard to represent as a rational type (Ratio) //val golden = 5.sqrt.next/2 // (1 + sqrt(5)) / 2 implicit object ParsableRatio extends Parsable[Ratio]{ private val basicChars = "0123456789/ ".toSet private def isBasic(cs:String) = cs forall basicChars.contains def apply(v:String):Ratio = { val ns = v split "/" if(ns.size == 2 && isBasic(v)) Ratio(Parse[Long](ns(0)) , Parse[Long](ns(1))) else Ratio(Parse[Double](v)) } } /* //TODO mix in Numeric type class work with the Ratio and Scala numbers //TODO make a Long based Ratio that has num and den based on Int and is an AnyVal implicit object NumericRatio extends scala.math.Numeric[Ratio]{ def compare(a:Ratio,b:Ratio):Int = (a.n*b.d) compare (b.n*a.d) def fromInt(x:Int) = Ratio(x,1) def times(a:Ratio,b:Ratio) = Ratio(a.n*b.n, a.d*b.d).reduce def div(a:Ratio,b:Ratio) = Ratio(a.n*b.d, a.d*b.n).reduce def plus(a:Ratio,b:Ratio) = Ratio(a.n*b.d + b.n*a.d, a.d*b.d).reduce def minus(a:Ratio,b:Ratio) = Ratio(a.n*b.d - b.n*a.d, a.d*b.d).reduce def negate(a:Ratio) = Ratio(-a.n,a.d) def toDouble(x:Ratio) = x.n.toDouble/x.d def toFloat(x:Ratio) = x.n.toFloat/x.d def toInt(x:Ratio) = (x.n/x.d).toInt def toLong(x:Ratio) = x.n/x.d } */ } // trait DrxNumeric[T] extends scala.math.Fractional[T]{ // //TODO mix in required methods to provide common drx numeric types instead of hand copying them to each numeric type // } case class Ratio(n:Long,d:Long=1) extends Ordered[Ratio]{ def *(i:Long) = Ratio(this.n*i, this.d).reduce def /(i:Long) = Ratio(this.n, this.d*i).reduce def +(i:Long) = Ratio(this.n + i*this.d, this.d).reduce // i + n/d => id/d + n/d => (id+n / d) def -(i:Long) = Ratio(this.n - i*this.d, this.d).reduce def *(that:Ratio) = Ratio(this.n*that.n, this.d*that.d).reduce def /(that:Ratio) = Ratio(this.n*that.d, this.d*that.n).reduce def +(that:Ratio) = Ratio(this.n*that.d + that.n*this.d, this.d*that.d).reduce /**subtraction a/b - c/d => a*d / b*d - c*b / b*d => (a*d - c*b) / b*d */ def -(that:Ratio) = Ratio(this.n*that.d - that.n*this.d, this.d*that.d).reduce lazy val gcd = n gcd d def reduce = if(gcd == 0) this else Ratio(n/gcd,d/gcd) def diff(that:Ratio):Ratio = (this-that).abs def compare(that:Ratio):Int = (this.n*that.d) compare (that.n*this.d) def inv = Ratio(d,n) //TODO unary negative def neg = Ratio(-n,d)//numerator def abs = Ratio(n.abs, d.abs) def num = n //numerator def den = d //denominator //overrided hashcode to allow for reduced comparisons override def hashCode:Int = { val r = this.reduce (41*(41+r.n) + r.d).toInt } override def equals(other:Any):Boolean = other match { case that:Ratio => this == that case _ => false } def ==(that:Ratio):Boolean = { val a = this.reduce val b = that.reduce a.n == b.n && a.d == b.d } def ==(i:Long):Boolean = { val a = this.reduce a.n == i && a.d == 1 } def toDouble = n.toDouble/d def log(b:Double):Double = n.log(b) - d.log(b) def log:Double = n.log - d.log def logE:Double = n.log - d.log def log10:Double = n.log10 - d.log10 def todB:Double = 10*math.log10(toDouble) //amplitude ratio to decibel value def dB:Double = 10d**(toDouble/10d) //decibel to amplitude ratio /**1-n/d => (d - n)/d*/ def not:Ratio = Ratio(d-n,d) def percentString = f"${toDouble*100}%3.0f%%" } //--# Constructor companions //-- integer types object DrxInt{ val bound = Bound(Int.MinValue, Int.MaxValue) /**msb first*/ def fromBits(bits:Array[Boolean]):Int = bits.reverse.zipWithIndex.foldLeft(0){case (l,(b,i)) => if(b) l | 1 << i else l} def fromByteArray(bytes:Array[Byte],offset:Int=0):Int = { def b(i:Int):Int = bytes(i+offset) & 0xFF (b(0) << 24) | (b(1) << 16) | (b(2) << 8) | b(3) } val sqrt2 = math.sqrt(2) } object DrxLong{ val bound = Bound(Long.MinValue, Long.MaxValue) /**msb first*/ def fromBits(bits:Array[Boolean]):Long = bits.reverse.zipWithIndex.foldLeft(0L){case (l,(b,i)) => if(b) l | 1 << i else l} def fromByteArray(bytes:Array[Byte],offset:Int=0):Long = { def b(i:Int):Long = bytes(i+offset) & 0xFFL (b(0) << 56) | (b(1) << 48) | (b(2) << 40) | (b(3) << 32) | (b(4) << 24) | (b(5) << 16) | (b(6) << 8) | b(7) } } //-- floating-point types object DrxFloat{ @inline def fromBits(i:Int):Float = java.lang.Float.intBitsToFloat(i) @inline def fromByteArray(bytes:Array[Byte],offset:Int=0):Float = fromBits(DrxInt.fromByteArray(bytes,offset)) } object DrxDouble{ @inline def fromBits(i:Long):Double = java.lang.Double.longBitsToDouble(i) @inline def fromByteArray(bytes:Array[Byte],offset:Int=0):Double = fromBits(DrxLong.fromByteArray(bytes,offset)) } //--# Numeric extension methods class DrxLong(val v: Long) extends AnyVal{ //internal helper functions private def vd:Double = v.toDouble def factorial:Long = (2L to v).foldLeft(1L){_*_} def gamma:Long = v.prev.factorial def sat(min:Long, max:Long):Long = if(v < min) min else if(v > max) max else v def sat(max:Long):Long = this.sat(-max,max) def lerp(b:Long)(amt:Double):Long = ((b-v)*amt + v).round def pow(e:Double):Double = math.pow(vd,e) def **(e:Double):Double = math.pow(vd,e) def **(e:Int):Long = if(v == 2) 1L << e else math.pow(vd,e).toLong //require(e >= 0 && e <= 64, "assumes positive for generation to Long") def toBig:BigDecimal = BigDecimal(v) def *[A](other:BaseValue[A]):A = other*v def *(vec:Vec):Vec = vec*vd def *(c:Complex):Complex = c*vd def *(r:Ratio):Ratio = r*v def diff(w:Long):Long = (v-w).abs ////--binary named ops //def add(k:Long) = v+k //def sub(k:Long) = v-k //def mul(k:Long) = v*k //def div(k:Long) = v/k //def over(k:Long) = v/k /**consider alternative exponent function for drx exponents that matches eponential as higher precidence than / or * */ 2017-07-30("just use **, don't duplicate operator symbols","0.2.13") def #^(e:Double):Double = math.pow(vd,e) 2017-07-30("just use **, don't duplicate operator symbols","0.2.13") def #^(e:Int):Long = math.pow(vd,e).toLong def ||(w:Double):Double = v*w/(v+w) def mean(w:Double):Double = (v+w)/2 def %/(w:Double):Double = v.toDouble %/ w def divMod(k:Long) = (v/k, v%k) def divFloor(k:Long):Long = v/k def divCeil(k:Long):Long = v/k + (if(v % k == 0) 0 else 1) //x log b => y | b**y == x def log(b:Double) = b match { case 10 => math.log10(vd); case E => math.log(vd); case _ => math.log(vd)/math.log(b) } def ln = math.log(vd) def log = math.log(vd) def logE = math.log(vd) def log10 = math.log10(vd) def todB:Double = 10*math.log10(vd) //decibel to ratio def dB:Double = 10d**(v/10d) //ratio to decibel def log2 = math.log(vd)/math.log(2) def exp = math.exp(vd) def expE = math.exp(vd) //math.pow(E,v) def exp10 = math.pow(10d,vd) def exp2 = math.pow(2d,vd) /**nice way to flip things around rather than using base.pow(vd); useful for chained equation descriptions */ def exp(base:Double) = math.pow(base,vd) def sigmoid:Double = 1d/(1d+math.exp(-vd)) //the logistic function is the most common sigmoid def sqrt = math.sqrt(vd) def sq = v*v def sgn:Long = math.signum(v) def neg:Long = -v def inv:Double = 1.0/vd def prev:Long = v - 1 def next:Long = v + 1 def half:Long = v/2 def double:Long = v*2 def %?(w:Long):Boolean = v % w == 0 def even:Boolean = v % 2 == 0 def odd:Boolean = v % 2 != 0 def modOption(w:Long):Option[Long] = if(w == 0) None else Some(v % w) /**returns true or false if a bit is set where i=0 is the LSB*/ def bit(i:Int):Boolean = (v & (1L << i)) != 0 def bitNot(i:Int):Boolean = !bit(i) 2017-07-30("use title case method name bitSet", "v0.2.15") def bitset(i:Int,b:Boolean):Long = bitSet(i,b) def bitSet(i:Int,b:Boolean):Long = if(b) v | (1L << i) else v & ~(1L << i) def bitFlip(i:Int):Long = v ^ (1L << i) //xor with a shifted bit // bitSet(i,!bit(i)) def toByteArray:Array[Byte] = Array((v >> 56).toByte, (v>>48).toByte, (v>>40).toByte, (v>>32).toByte, (v>>24).toByte, (v>>16).toByte, (v>>8).toByte, v.toByte) // def hi:Int = (v>>32).toInt //note these are implemented in the U64 class // def lo:Int = v.toInt def bshift(n:Int):Long = if(n > 0) v << n else v >> -n //efficient scaling positive and negative direction /*depth must be an integer from 0 to 63*/ def twosComp(depth:Int):Long = {val shift = 64-depth; v << shift >> shift} /**greatest common divisor*/ def gcd(z:Long):Long = if(z == 0) v.abs else z gcd (v%z) /**return a Ratio (Rational) type reduced*/ def over(z:Long):Ratio = Ratio(v,z)//.reduce def outOf(z:Long):Ratio = over(z) def #/(z:Long):Ratio = over(z).reduce //reduced 2017-07-30("this latex form is not infix anyways","di") def frac(z:Long):Ratio = over(z) //warning will through an error if outside the range and incorrect for interpolated values def sigma:Ratio = { // (x/2.sqrt).erf.not.inv //0 to 7.0 by 1 sigma val freqOutside = "0 3 22 371 15_787 1_744278 506_797346 390_682_215445" split " " Ratio(1, Parse[Long](freqOutside(v.toInt.abs))) } // def iterator[A](f: => A):Iterator[A] = Repeat.iterator(n,f) def times(f: => Unit):Unit = Repeat(v)(f) def iterate[T](acc:T)(f:T => T):T = Repeat.iterate(v,acc)(f) 2017-07-30("use foldLeft instead in scala-2.13","dp") def /:[T](acc:T)(f:T => T):T = Repeat.iterate(v,acc)(f) def foldLeft[T](acc:T)(f:T => T):T = Repeat.iterate(v,acc)(f) def base(radix:Int):String = if(radix <= 36) java.lang.Long.toString(v,radix) else base(Data.Radix36 take radix) def base(radix:Int,padZeroToWidth:Int=1):String = (v base radix).fit(padZeroToWidth,Style.Right,"0") def base(charset:IndexedSeq[Char]):String= { val N =charset.size @tailrec def crunch(x:Long,s:String=""):(Long,String) = { if(x > 0) crunch(x / N, charset((x % N).toInt).toString + s) else (x, s) } crunch(v)._2 } //def hex:String = "0x%016x" format v // def format(spec:String):String = ("%"+dspec+"d").format(v) //warning this could break at run time def plusOrMinus(w:Double):Bound[Double] = Bound(v-w,v+w) def +-(w:Double):Bound[Double] = Bound(v-w,v+w) def ~(w:Double):Bound[Double] = Bound(vd,w) def ~>(w:Double):Bound[Double] = Bound(vd,v+w) def take(n:Int) = Bound(0d,v.toDouble).take(n) //--common units def m = Length(vd) def g0 = Acceleration(v*0.10197162129) def ft = Length(v*0.3048) def km = Length(v*1000d) def k:Long = v*1000 def M:Long = v*1000000 //note over 9.2E12 will be an overflow error def G:Long = v*1000000000L //note over 9.2E9 will be an input error def s = Time(vd) def ms = Time(v*1E-03) def ns = Time(v*1E-09) // def min = Time(v*60.0) def minute = Time(v*60.0) def month = Time.month*v def day = Time.day*v def yr = Time.yr*v def hr = Time(v*3600.0) def deg = Angle(v*deg2rad) def rad = Angle(vd) //2017-07-30("use Hz case instead","dk") def hz = Frequency(vd) def Hz = Frequency(vd) def px = Style.Weight(vd) //pixel def x = Vec(vd,0) def y = Vec(0,vd) def z = Vec(0,0,vd) def xy = Vec(vd,vd,0) def xyz = Vec(vd,vd,vd) /**imaginary number notation*/ def i = Complex(0,vd) /**ratio construction by percent*/ def percent:Ratio = Ratio(v,100) def uniformHash:Int = { val m0 = 0x7ce7488e //int seed from => scala.util.Random.nextInt.hex => val m1 = MM3.mix(m0, (v >> 32).toInt ) val m2 = MM3.mixLast(m1, (v & 0xFFFFFFFF).toInt) MM3.finalizeHash(m2, 2) } } class DrxInt(val v: Int) extends AnyVal{ def toWord:String = Data.IntegerWords.getOrElse(v,s"$v unknown") def factorial:Long = v.toLong.factorial def gamma:Long = v.toLong.gamma def sat(min:Int, max:Int):Int = if(v < min) min else if(v > max) max else v def sat(max:Int):Int = this.sat(-max,max) def lerp(b:Int)(amt:Double):Int = ((b-v)*amt + v).round.toInt def pow(e:Double):Double = math.pow(v,e) def **(e:Double):Double = math.pow(v,e) def **(e:Int):Long = { require(e >= 0 && e <= 64, "assumes positive for generation to Long") if(v == 2) 1L << e else math.pow(v,e).toLong } def toBig:BigDecimal = BigDecimal(v) def *[A](other:BaseValue[A]):A = other*v def *(vec:Vec):Vec = vec*v def *(c:Complex):Complex = c*v def *(r:Ratio):Ratio = r*v def diff(w:Int):Int = (v-w).abs /**consider alternative exponent function for drx exponents that matches eponential as higher precidence than / or * */ 2017-07-30("just use **, don't duplicate operator symbols","0.2.13") def #^(e:Double):Double = math.pow(v,e) 2017-07-30("just use **, don't duplicate operator symbols","0.2.13") def #^(e:Int):Int = math.pow(v,e).toInt def ||(w:Double):Double = v*w/(v+w) def mean(w:Double):Double = (v+w)/2 def %/(w:Double):Double = v.toDouble %/ w def divMod(k:Int) = (v/k, v%k) def divFloor(k:Int):Long = v/k def divCeil(k:Int):Long = v/k + (if(v % k == 0) 0 else 1) def isEven:Boolean = v % 2 == 0 def isOdd:Boolean = v % 2 != 0 /**binomial coefficients, pascals triangle (n \choose k) = n^k/k! */ def choose(k:Int):Int = if(k > v || k < 0) 0 else if(k == v || k == 0) 1 else (1 to k.min(v-k)).foldLeft(1){(a,i) => a*(v+1-i)/i} //min for triangle symmetry /**returns true or false if a bit is set where i=0 is the LSB*/ def bit(i:Int):Boolean = (v & (1 << i)) != 0 def bitNot(i:Int):Boolean = !bit(i) 2017-07-30("use title case method name bitSet", "v0.2.15") def bitset(i:Int,b:Boolean):Int = bitSet(i,b) def bitSet(i:Int,b:Boolean):Int = if(b) v | (1 << i) else v & ~(1 << i) def bitFlip(i:Int):Int = v ^ (1 << i) //xor with a shifted bit // bitSet(i,!bit(i)) def toByteArray:Array[Byte] = Array((v>>24).toByte, (v>>16).toByte, (v>>8).toByte, v.toByte) // def hi:Short = (v>>16).toShort //note these are implemented in the U32 class for Int // def lo:Short = v.toShort def unsigned:Long = v & 0x00000000ffffffffL; /*depth must be an integer from 0 to 31*/ def twosComp(depth:Int):Long = {val shift = 64-depth; v << shift >> shift} // def iterator[A](f: => A):Iterator[A] = v.toLong.iterator(f) def times(f: => Unit):Unit = v.toLong times f def iterate[T](acc:T)(f:T => T):T = v.toLong.iterate(acc)(f) 2017-07-30("use foldLeft instead in scala-2.13","dp") def /:[T](acc:T)(f:T => T):T = iterate(acc)(f) def foldLeft[T](acc:T)(f:T => T):T = iterate(acc)(f) def scanRight[T](acc:T)(f:T => T) = Repeat.scanRight(v,List(acc))(f) def scan[T](acc:T)(f:T => T) = scanRight(acc)(f).reverse def base(radix:Int):String = v.toLong base radix def base(radix:Int,padZeroToWidth:Int=1):String = v.toLong.base(radix,padZeroToWidth) def base(charset:IndexedSeq[Char]):String = v.toLong base charset //def hex:String = "0x%08x" format v // def format(spec:String):String = ("%"+dspec+"d").format(v) //warning this could break at run time /**prime factorization, not efficient but super simple and readable for one line of code and small primes**/ def factors:List[Int] = (2 to v.sqrt.toInt).find{v % _ == 0}.map{i => i :: (v/i).factors}.getOrElse(List(v)) def isPrime:Boolean = factors.size == 1 /**greatest common divisor*/ def gcd(z:Int):Int = if(z == 0) v.abs else z gcd (v%z) /**return a Ratio (Rational) type reduced*/ def over(z:Int):Ratio = Ratio(v,z)//.reduce def outOf(z:Int):Ratio = over(z) def #/(z:Int):Ratio = over(z).reduce //reduced def ln = math.log(v) def log = math.log(v) def logE = math.log(v) def log(b:Double) = math.log(v)/math.log(b) def log10 = math.log10(v) def log2 = math.log(v)/math.log(2) def todB:Double = 10*math.log10(v) //ratio to a db value def dB:Double = 10d**(v/10d) //decibel expressed as a ratio def exp = math.exp(v) def expE = math.exp(v) //math.pow(E,v) def exp10 = math.pow(10d,v) def exp2 = math.pow(2d,v) /**nice way to flip things around rather than using base.pow(v); useful for chained equation descriptions */ def exp(base:Double) = math.pow(base,v) def sigmoid:Double = 1d/(1d+math.exp(-v)) //the logistic function is the most common sigmoid def sqrt = if(v == 2) DrxInt.sqrt2 else math.sqrt(v) def sq = v*v def sgn:Int = math.signum(v) def neg:Int = -v def inv:Double = 1.0/v def not:Int = 1 - v def prev:Int = v - 1 def next:Int = v + 1 def half:Int = v/2 def double:Int = v*2 def %?(w:Int):Boolean = v % w == 0 def even:Boolean = v % 2 == 0 def odd:Boolean = v % 2 != 0 def modOption(w:Int):Option[Int] = if(w == 0) None else Some(v % w) def plusOrMinus(w:Double):Bound[Double] = Bound(v-w,v+w) def +-(w:Double):Bound[Double] = Bound(v-w,v+w) def ~(w:Double):Bound[Double] = Bound(v,w) def ~>(w:Double):Bound[Double] = Bound(v,v+w) def take(n:Int) = Bound(0d,v.toDouble).take(n) def /(t:Time):Frequency = Frequency(v/t.s) //--common units def m = Length(v) def g0 = Acceleration(v*0.10197162129) def ft = Length(v*0.3048) def km = Length(v*1000) def k:Int = v*1000 def M:Int = v*1000000 //note over 2000 will be an overflow error def G:Long = v*1000000000L //note over 9.2E9 will be an input error def s = Time(v) def ms = Time(v*1E-03) def ns = Time(v*1E-09) // def min = Time(v*60.0) def minute = Time(v*60.0) def month = Time.month*v def day = Time.day*v def yr = Time.yr*v def hr = Time(v*3600.0) def deg = Angle(v*deg2rad) def rad = Angle(v) def hz = Frequency(v) def px = Style.Weight(v) def x = Vec(v,0) def y = Vec(0,v) def z = Vec(0,0,v) def xy = Vec(v,v,0) def xyz = Vec(v,v,v) /**imaginary number notation*/ def i = Complex(0,v) /**ratio construction by percent*/ def percent:Ratio = Ratio(v,100) def uniformHash:Int = MM3.finalizeHash(MM3.mixLast(0xa8e7ba6a,v),1) //int seed from => scala.util.Random.nextInt.hex => } class DrxShort(val v: Short) extends AnyVal{ def unsigned:Int = v & 0x0000ffff; // def hi:Byte = (v>>8).toByte // def lo:Byte = v.toByte def toByteArray:Array[Byte] = Array((v >> 8).toByte, v.toByte) //Array(v.hi,v.lo) // def hex:String = "0x%04x" format v //Note: this method is provided in U16 } object DrxByte{ private lazy val noCharAscii = "NUL SOH STX ETX EOT ENQ ACK BEL BS TAB LF VT FF CR SO SI DLE DC1 DC2 DC3 NAK SYN ETB CAN EM SUB ESC FS GS RS US" split " " def asciiName(v:Byte):String = { if(v == 127) "DEL" else if(v >= 0 && v <= 32) noCharAscii(v) else v.unsigned.toChar.toString } } class DrxByte(val v: Byte) extends AnyVal{ def unsigned:Short = (v & 0x00ff).toShort; def toByteArray:Array[Byte] = Array(v) // def hex:String = "0x%02x" format v //Note: this method is provided in U8 // def format(spec:String):String = ("%"+dspec+"d").format(v) //warning this could break at run time def asciiName:String = DrxByte.asciiName(v) } class DrxFloat(val v: Float) extends AnyVal{ def toByteArray:Array[Byte] = toBits.toByteArray def toBits:Int = java.lang.Float.floatToIntBits(v) //note: the rest of the nice methods here are not implemented here since double is assumed the default floating point type } //https://en.wikipedia.org/wiki/Double-precision_floating-point_format class DrxDouble(val v: Double) extends AnyVal{ /* def ieeeSign:Boolean = (toBits ^ 0x8000000000000000L) == 0L def ieeeExponent:Int = ((toBits >> 52) & 0x7FFL).toInt def ieeeFraction:Long = toBits & 0x000FFFFFFFFFFFFFL */ // def format(spec:String):String = ("%"+dspec+"f").format(v) //warning this could break at run time // // deprecated("experimentally try the apply on the double for math notation, breaks chaining results with ambiguity","2019-01-01") // def apply(k:Double) = v*k //--binary named ops def add(k:Double) = v+k def sub(k:Double) = v-k def mul(k:Double) = v*k def div(k:Double) = v/k def sin = math.sin(v) def cos = math.cos(v) def tan = math.tan(v) def sin2 = {val t = math.sin(v); t*t} def cos2 = {val t = math.cos(v); t*t} def tan2 = {val t = math.tan(v); t*t} def asin = math.asin(v) def acos = math.acos(v) def atan = math.atan(v) /**(e^x - e^-x)/2*/ def sinh = math.sinh(v) /**(e^x + e^-x)/2*/ def cosh = math.cosh(v) /**sinh/cosh => (e^2x - 1)/(e^2x + 1)*/ def tanh = math.tanh(v) /**ln(x + sqrt(x^2 + 1))*/ def asinh = math.log(v + math.sqrt(v*v + 1.0)) /**ln(x + sqrt(x^2 - 1))*/ def acosh = math.log(v + math.sqrt(v*v - 1.0)) /**ln((1+v)/(1-v))/2*/ def atanh = math.log((1.0+v)/(1.0-v))/2.0 def atan2(a:Double) = math.atan2(v,a) def hypot(a:Double) = math.hypot(v,a) /**Sine assuming degrees instead of radians: sin(x*pi/180)*/ def sind = math.sin(v*deg2rad) /**Cosine assuming degrees instead of radians: cos(x*pi/180)*/ def cosd = math.cos(v*deg2rad) /**Tan assuming degrees instead of radians: tan(x*pi/180)*/ def tand = math.tan(v*deg2rad) /**ArcSine returning degrees instead of radians: asin(x)*180/pi */ def asind = math.asin(v)*rad2deg /**ArcCosine returning degrees instead of radians: acos(x)*180/pi */ def acosd = math.acos(v)*rad2deg /**ArcTan returning degrees instead of radians: atan(x)*180/pi */ def atand = math.atan(v)*rad2deg //--unary flips def sgn:Double = math.signum(v) def sgnOf(w:Double):Double = if(w < 0) -v.abs else v.abs def inv:Double = 1d/v def neg:Double = -v def not:Double = 1d - v //this assumes the double is a probability between 0 and 1 def prev:Double = v - 1d def next:Double = v + 1d def half:Double = v/2 def double:Double = v*2 // def sgn:Double = math.signum(v) // /**apply an operation to the absolute value and return the double with the original sgn. -1.2.mapAbs(_.ceil) => -2.0 ; -1.2.ceil => 1.0 */ def mapAbs(f:Double=>Double):Double = f(v.abs).abs*v.sgn //--tests def isUnreal = v.isNaN || v.isInfinite def isReal = !isUnreal def even:Boolean = v % 2 == 0 def odd:Boolean = v % 2 != 0 //--special/safe gets (working with nan and infinite numbers representations) def get:Option[Double] = if(isReal) Some(v) else None def modOption(w:Double):Option[Double] = if(w == 0) None else Some(v % w) def divOption(w:Double):Option[Double] = if(w == 0) None else Some(v / w) def ln = math.log(v) def log = math.log(v) def logE = math.log(v) def log(b:Double) = math.log(v)/math.log(b) def log10 = math.log10(v) def log2 = math.log(v)/math.log(2) def todB:Double = 10*math.log10(v) //decibel to ratio /**note this assumes a power based ratio, a root-power (field) quanity needs to be multiplied by 2*/ def dB:Double = 10d**(v/10d) //ratio to decibel def exp = math.exp(v) def expE = math.exp(v) //math.pow(E,v) def exp10 = math.pow(10d,v) def exp2 = math.pow(2d,v) /**nice way to flip things around rather than using base.pow(v); useful for chained equation descriptions */ def exp(base:Double) = math.pow(base,v) def sigmoid:Double = 1d/(1d+math.exp(-v)) //the logistic function is the most common sigmoid //useful methods for chaining def sqrt = math.sqrt(v) def sq = v*v def ierf:Double = { val a = 0.147 val q = v.sq.not.log val qp = 2/pi/a + q/2 ((qp.sq - q/a).sqrt - qp).sqrt*v.sgn //TODO add more precision } /** erf --max error: 1.5×10−7 https://en.wikipedia.org/wiki/Error_function#Numerical_approximations A+S */ def erf:Double = { val t = (v.abs*0.3275911).next.inv val c = Polynomial(0d, 0.254829592, -0.284496736, 1.421413741, -1.453152027, 1.061405429).apply(t) val e = ( v.sq.neg.exp * c).not e.sgnOf(v) //use the fact that erf(x) is an odd function } /**gamma --max-error 1E-14 https://en.wikipedia.org/wiki/Lanczos_approximation https://rosettacode.org/wiki/Gamma_function#Scala */ def gamma:Double = if(v < 0.5) pi / ( (pi*v).sin * v.not.gamma ) else { val t = v + 6.5 val ps = List(676.5203681218851,-1259.1392167224028,771.32342877765313,-176.61502916214059,12.507343278686905,-0.13857109526572012,9.9843695780195716e-6,1.5056327351493116e-7) val a = ps.zipWithIndex.foldLeft(0.99999999999980993){ case (a,(p,i)) => a + p/(v + i)} t**(v-0.5) * tau.sqrt * t.neg.exp * a } def times(f: => Unit):Unit = v.round.toLong times f def iterate[T](acc:T)(f:T => T):T = v.toLong.iterate(acc)(f) 2017-07-30("use foldLeft instead in scala-2.13","dp") def /:[T](acc:T)(f:T => T):T = iterate(acc)(f) def foldLeft[T](acc:T)(f:T => T):T = iterate(acc)(f) def sat(min:Double, max:Double):Double = if(v < min) min else if(v > max) max else v def sat(max:Double):Double = this.sat(-max,max) //def round:Int = math.round(v).toInt //def round:Long = math.round(v) //def min(t:Double):Double = if(v < t) v else t //def max(t:Double):Double = if(v > t) v else t //TODO make this operator have implicit use to define what the close eps value is. And make it work for different types //def near(w:Double, eps:Double=0.00001):Boolean = (v - b).abs <= eps def plusOrMinus(w:Double):Bound[Double] = Bound(v-w,v+w) def +-(w:Double):Bound[Double] = Bound(v-w,v+w) def ~(w:Double):Bound[Double] = Bound(v,w) def ~>(w:Double):Bound[Double] = Bound(v,v+w) def take(n:Int) = Bound(0d,v).take(n) def lerp(b:Double)(amt:Double):Double= (b-v)*amt + v 2017-07-30("just use **, don't duplicate operator symbols","0.2.13") def #^(e:Double):Double = math.pow(v,e) def pow(e:Double):Double = math.pow(v,e) def **(e:Double):Double = math.pow(v,e) //def **(e:Int):Double = math.pow(v,e) //TODO possibly add speedups for special int case def toBig:BigDecimal = BigDecimal(v) def *[A](other:BaseValue[A]):A = other*v def *(vec:Vec):Vec = vec*v def *(c:Complex):Complex = c*v // def *(r:Ratio):Ratio = r*v //Note this op should not compile/valid for doubles def diff(w:Double):Double = (v-w).abs def ||(w:Double):Double = v*w/(v+w) //TODO include on int and long def mean(w:Double):Double = (v+w)/2 /**alias to [[relativeChange]] percent operator where the divisor is the reference*/ def %/(ref:Double):Double = relativeChange(ref) /**relativeChange percent operator where the divisor is the reference * https://en.wikipedia.org/wiki/Approximation_error * https://en.wikipedia.org/wiki/Relative_change_and_difference */ def relativeChange(ref:Double):Double = if(v == 0 && ref == 0) 0 else (v-ref)/ref //(11-10)/10 => 0.1 def percentError(ref:Double):Double = relativeChange(ref) * 100 def relativeDifference(w:Double):Double = (v-w).abs.divOption( (v+w).abs/2 ).getOrElse(0d) def ~=(w:Double)(implicit eps:Eps):Boolean = math.abs(v-w) < eps.v def toByteArray:Array[Byte] = toBits.toByteArray def toBits:Long = java.lang.Double.doubleToLongBits(v) def toRatio:Ratio = Ratio(v) def divFloor(k:Double):Long = (v/k).floor.toLong def divCeil(k:Double):Long = (v/k).ceil.toLong //--common units def m = Length(v) def g0 = Acceleration(v*0.10197162129) def ft = Length(v*0.3048) def km = Length(v*1000) def k:Double = v*1000d def M:Double = v*1000000d def G:Double = v*1000000000d def s = Time(v) def ms = Time(v*1E-03) def ns = Time(v*1E-09) // def min = Time(v*60.0) def minute = Time(v*60.0) def month = Time.month*v def yr = Time.yr*v def day = Time.day*v def hr = Time(v*3600.0) def deg = Angle(v*deg2rad) def rad = Angle(v) def hz = Frequency(v) def px = Style.Weight(v) def x = Vec(v,0,0) def y = Vec(0,v,0) def z = Vec(0,0,v) def xy = Vec(v,v,0) def xyz = Vec(v,v,v) /**imaginary number notation*/ def i = Complex(0,v) /**ratio construction by percent*/ def percent:Ratio = Ratio(v/100) // /**Bound construction since the Range constructor is deprecated*/ // def to(w:Double) = Bound(v,w) } /*16bit unsigned utf character*/ class DrxChar(val v: Char) extends AnyVal{ // def isPrintable:Boolean = v >= 0x20 && v <= 0x7E //TODO make this extend to not just ascii/ansi chars }