/*
   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
}