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

de.sciss.fscape.graph.BinaryOp.scala Maven / Gradle / Ivy

/*
 *  BinaryOp.scala
 *  (FScape)
 *
 *  Copyright (c) 2001-2016 Hanns Holger Rutz. All rights reserved.
 *
 *  This software is published under the GNU General Public License v2+
 *
 *
 *  For further information, please contact Hanns Holger Rutz at
 *  [email protected]
 */

package de.sciss.fscape
package graph

import de.sciss.fscape.stream.{StreamIn, StreamOut}
import de.sciss.numbers.{DoubleFunctions => rd, DoubleFunctions2 => rd2}

import scala.annotation.switch
import scala.collection.immutable.{IndexedSeq => Vec}

object BinaryOp {
  object Op {
    def apply(id: Int): Op = (id: @switch) match {
      case Plus     .id => Plus
      case Minus    .id => Minus
      case Times    .id => Times
      case Div      .id => Div
      case Mod      .id => Mod
      case Eq       .id => Eq
      case Neq      .id => Neq
      case Lt       .id => Lt
      case Gt       .id => Gt
      case Leq      .id => Leq
      case Geq      .id => Geq
      case Min      .id => Min
      case Max      .id => Max
      //      case BitAnd   .id => BitAnd
      //      case BitOr    .id => BitOr
      //      case BitXor   .id => BitXor
      case RoundTo  .id => RoundTo
      case RoundUpTo.id => RoundUpTo
      case Trunc    .id => Trunc
      case Atan2    .id => Atan2
      case Hypot    .id => Hypot
      case Hypotx   .id => Hypotx
      case Pow      .id => Pow
      case Ring1    .id => Ring1
      case Ring2    .id => Ring2
      case Ring3    .id => Ring3
      case Ring4    .id => Ring4
      case Difsqr   .id => Difsqr
      case Sumsqr   .id => Sumsqr
      case Sqrsum   .id => Sqrsum
      case Sqrdif   .id => Sqrdif
      case Absdif   .id => Absdif
      case Thresh   .id => Thresh
      case Amclip   .id => Amclip
      case Scaleneg .id => Scaleneg
      case Clip2    .id => Clip2
      case Excess   .id => Excess
      case Fold2    .id => Fold2
      case Wrap2    .id => Wrap2
      //      case Firstarg .id => Firstarg
      case SecondArg.id => SecondArg
    }
  }

  sealed trait Op {
    op =>

    def id: Int

    def apply(a: Double, b: Double): Double

    /** The default converts to `Double`, but specific operators
      * may better preserve semantics and precision for other types such as `Int` and `Long`.
      */
    def apply(a: Constant, b: Constant): Constant = ConstantD(apply(a.doubleValue, b.doubleValue))

    def name: String = plainName.capitalize

    def make(a: GE, b: GE): GE = (a, b) match {
      case (av: Constant, bv: Constant) => apply(av, bv)
      case _ => BinaryOp(op.id, a, b)
    }

    private def plainName: String = {
      val cn = getClass.getName
      val sz = cn.length
      val i  = cn.indexOf('$') + 1
      cn.substring(i, if (cn.charAt(sz - 1) == '$') sz - 1 else sz)
    }
  }

  private def mkIntOrLong(n: Long): Constant =
    if (n >= Int.MinValue && n <= Int.MaxValue)
      ConstantI(n.toInt)
    else
      ConstantL(n)

  case object Plus extends Op {
    final val id = 0
    override val name = "+"

    def apply(a: Double, b: Double) = rd.+(a, b)

    override def apply(a: Constant, b: Constant): Constant = (a, b) match {
      case (ConstantD(_), _)  => super.apply(a, b)
      case (_, ConstantD(_))  => super.apply(a, b)
      case (ConstantL(an), _) => ConstantL(an + b.longValue)
      case (_, ConstantL(bn)) => ConstantL(a.longValue + bn)
      case _ =>
        val an = a.longValue
        val bn = b.longValue
        val n  = an + bn
        mkIntOrLong(n)
    }
  }

  case object Minus extends Op {
    final val id = 1
    override val name = "-"

    def apply(a: Double, b: Double) = rd.-(a, b)

    override def apply(a: Constant, b: Constant): Constant = (a, b) match {
      case (ConstantD(_), _)  => super.apply(a, b)
      case (_, ConstantD(_))  => super.apply(a, b)
      case (ConstantL(an), _) => ConstantL(an - b.longValue)
      case (_, ConstantL(bn)) => ConstantL(a.longValue - bn)
      case _ =>
        val an = a.longValue
        val bn = b.longValue
        val n  = an - bn
        mkIntOrLong(n)
    }
  }

  case object Times extends Op {
    final val id = 2
    override val name = "*"

    override def make(a: GE, b: GE): GE =
      (a, b) match {
      case (Constant(0), _)  => a
      case (_, Constant(0))  => b
      case (Constant(1), _)  => b
      case (_, Constant(1))  => a
      case (Constant(-1), _) => UnaryOp.Neg.make(b) // -b
      case (_, Constant(-1)) => UnaryOp.Neg.make(a) // -a
      case _                 => super.make(a, b)
    }

    def apply(a: Double, b: Double) = rd.*(a, b)

    override def apply(a: Constant, b: Constant): Constant = (a, b) match {
      case (ConstantD(_), _)  => super.apply(a, b)
      case (_, ConstantD(_))  => super.apply(a, b)
      case (ConstantL(an), _) => ConstantL(an * b.longValue)  // XXX TODO --- check overflow
      case (_, ConstantL(bn)) => ConstantL(a.longValue * bn)  // XXX TODO --- check overflow
      case _ =>
        val an = a.longValue
        val bn = b.longValue
        val n  = an * bn
        mkIntOrLong(n)
    }
  }

  // case object IDiv           extends Op(  3 )
  case object Div extends Op {
    final val id = 4
    override val name = "/"

    def apply(a: Double, b: Double) = rd./(a, b)

    override def apply(a: Constant, b: Constant): Constant = (a, b) match {
      case (ConstantD(_), _)  => super.apply(a, b)
      case (_, ConstantD(_))  => super.apply(a, b)
      case (_: ConstantL, _) | (_, _: ConstantL) =>
        val an = a.longValue
        val bn = b.longValue
        if (an % bn == 0)
          ConstantL(an / bn)
        else
          ConstantD(an.toDouble / bn.toDouble)

      case _ =>
        val ai = a.intValue
        val bi = b.intValue
        if (ai % bi == 0)
          ConstantI(ai / bi)
        else
          ConstantD(ai.toDouble / bi.toDouble)
    }
  }

  case object Mod extends Op {
    final val id = 5
    override val name = "%"

    def apply(a: Double, b: Double): Double = rd.%(a, b)

    override def apply(a: Constant, b: Constant): Constant = (a, b) match {
      case (ConstantD(_), _)  => super.apply(a, b)
      case (_, ConstantD(_))  => super.apply(a, b)
      case (_: ConstantL, _) | (_, _: ConstantL) =>
        val an = a.longValue
        val bn = b.longValue
        ConstantL(an % bn)
      case _ =>
        val ai = a.intValue
        val bi = b.intValue
        ConstantI(ai % bi)
    }
  }

  case object Eq extends Op {
    final val id = 6
    override val name = "sig_=="

    def apply(a: Double, b: Double): Double = if (a == b) 1 else 0
    override def apply(a: Constant, b: Constant): Constant = if (a.value == b.value) 1 else 0
  }

  case object Neq extends Op {
    final val id = 7
    override val name = "sig_!="

    def apply(a: Double, b: Double): Double = if (a != b) 1 else 0
    override def apply(a: Constant, b: Constant): Constant = if (a.value != b.value) 1 else 0
  }

  case object Lt extends Op {
    final val id = 8
    override val name = "<"

    def apply(a: Double, b: Double): Double = if (a < b) 1 else 0 // NOT rd.< !

    override def apply(a: Constant, b: Constant): Constant = {
      val res: Boolean = (a, b) match {
        case (ConstantD(ad), ConstantD(bd)) => ad < bd
        case (ConstantD(ad), ConstantL(bn)) => ad < bn
        case (ConstantD(ad), ConstantI(bi)) => ad < bi
        case (ConstantL(an), ConstantD(bd)) => an < bd
        case (ConstantL(an), ConstantL(bn)) => an < bn
        case (ConstantL(an), ConstantI(bi)) => an < bi
        case (ConstantI(ai), ConstantD(bd)) => ai < bd
        case (ConstantI(ai), ConstantL(bn)) => ai < bn
        case (ConstantI(ai), ConstantI(bi)) => ai < bi
      }
      if (res) 1 else 0
    }
  }

  case object Gt extends Op {
    final val id = 9
    override val name = ">"

    def apply(a: Double, b: Double): Double = if (a > b) 1 else 0 // NOT rd.> !

    override def apply(a: Constant, b: Constant): Constant = {
      val res: Boolean = (a, b) match {
        case (ConstantD(ad), ConstantD(bd)) => ad > bd
        case (ConstantD(ad), ConstantL(bn)) => ad > bn
        case (ConstantD(ad), ConstantI(bi)) => ad > bi
        case (ConstantL(an), ConstantD(bd)) => an > bd
        case (ConstantL(an), ConstantL(bn)) => an > bn
        case (ConstantL(an), ConstantI(bi)) => an > bi
        case (ConstantI(ai), ConstantD(bd)) => ai > bd
        case (ConstantI(ai), ConstantL(bn)) => ai > bn
        case (ConstantI(ai), ConstantI(bi)) => ai > bi
      }
      if (res) 1 else 0
    }
  }

  case object Leq extends Op {
    final val id = 10
    override val name = "<="

    def apply(a: Double, b: Double): Double = if (a <= b) 1 else 0 // NOT rd.<= !

    override def apply(a: Constant, b: Constant): Constant = {
      val res: Boolean = (a, b) match {
        case (ConstantD(ad), ConstantD(bd)) => ad <= bd
        case (ConstantD(ad), ConstantL(bn)) => ad <= bn
        case (ConstantD(ad), ConstantI(bi)) => ad <= bi
        case (ConstantL(an), ConstantD(bd)) => an <= bd
        case (ConstantL(an), ConstantL(bn)) => an <= bn
        case (ConstantL(an), ConstantI(bi)) => an <= bi
        case (ConstantI(ai), ConstantD(bd)) => ai <= bd
        case (ConstantI(ai), ConstantL(bn)) => ai <= bn
        case (ConstantI(ai), ConstantI(bi)) => ai <= bi
      }
      if (res) 1 else 0
    }
  }

  case object Geq extends Op {
    final val id = 11
    override val name = ">="

    def apply(a: Double, b: Double): Double = if (a >= b) 1 else 0 // NOT rd.>= !

    override def apply(a: Constant, b: Constant): Constant = {
      val res: Boolean = (a, b) match {
        case (ConstantD(ad), ConstantD(bd)) => ad >= bd
        case (ConstantD(ad), ConstantL(bn)) => ad >= bn
        case (ConstantD(ad), ConstantI(bi)) => ad >= bi
        case (ConstantL(an), ConstantD(bd)) => an >= bd
        case (ConstantL(an), ConstantL(bn)) => an >= bn
        case (ConstantL(an), ConstantI(bi)) => an >= bi
        case (ConstantI(ai), ConstantD(bd)) => ai >= bd
        case (ConstantI(ai), ConstantL(bn)) => ai >= bn
        case (ConstantI(ai), ConstantI(bi)) => ai >= bi
      }
      if (res) 1 else 0
    }
  }

  case object Min extends Op {
    final val id = 12
    def apply(a: Double, b: Double): Double = rd.min(a, b)

    override def apply(a: Constant, b: Constant): Constant = (a, b) match {
      case (ConstantD(_), _)  => super.apply(a, b)
      case (_, ConstantD(_))  => super.apply(a, b)
      case (ConstantL(an), _) => math.min(an, b.longValue)
      case (_, ConstantL(bn)) => math.min(a.longValue, bn)
      case _                  => math.min(a.intValue, b.intValue)
    }
  }

  case object Max extends Op {
    final val id = 13
    def apply(a: Double, b: Double): Double = rd.max(a, b)

    override def apply(a: Constant, b: Constant): Constant = (a, b) match {
      case (ConstantD(_), _)  => super.apply(a, b)
      case (_, ConstantD(_))  => super.apply(a, b)
      case (ConstantL(an), _) => math.max(an, b.longValue)
      case (_, ConstantL(bn)) => math.max(a.longValue, bn)
      case _                  => math.max(a.intValue, b.intValue)
    }
  }

  case object BitAnd extends Op {
    final val id = 14
    override val name = "&"

    def apply(a: Double, b: Double): Double = a.toLong & b.toLong

    override def apply(a: Constant, b: Constant): Constant = (a, b) match {
      case (ConstantD(_), _)  => super.apply(a, b)
      case (_, ConstantD(_))  => super.apply(a, b)
      case (ConstantL(an), _) => an & b.longValue
      case (_, ConstantL(bn)) => a.longValue & bn
      case _                  => a.intValue & b.intValue
    }
  }

  case object BitOr extends Op {
    final val id = 15
    override val name = "|"

    def apply(a: Double, b: Double): Double = a.toLong | b.toLong

    override def apply(a: Constant, b: Constant): Constant = (a, b) match {
      case (ConstantD(_), _)  => super.apply(a, b)
      case (_, ConstantD(_))  => super.apply(a, b)
      case (ConstantL(an), _) => an | b.longValue
      case (_, ConstantL(bn)) => a.longValue | bn
      case _                  => a.intValue | b.intValue
    }
  }

  case object BitXor extends Op {
    final val id = 16
    override val name = "^"

    def apply(a: Double, b: Double): Double = a.toLong ^ b.toLong

    override def apply(a: Constant, b: Constant): Constant = (a, b) match {
      case (ConstantD(_), _)  => super.apply(a, b)
      case (_, ConstantD(_))  => super.apply(a, b)
      case (ConstantL(an), _) => an ^ b.longValue
      case (_, ConstantL(bn)) => a.longValue ^ bn
      case _                  => a.intValue ^ b.intValue
    }
  }

  // case object Lcm            extends Op( 17 )
  // case object Gcd            extends Op( 18 )
  case object RoundTo extends Op {
    final val id = 19
    def apply(a: Double, b: Double) = rd.roundTo(a, b)
  }

  case object RoundUpTo extends Op {
    final val id = 20
    def apply(a: Double, b: Double): Double = rd.roundUpTo(a, b)
  }

  case object Trunc extends Op {
    final val id = 21
    def apply(a: Double, b: Double): Double = rd.trunc(a, b)
  }

  case object Atan2 extends Op {
    final val id = 22
    def apply(a: Double, b: Double): Double = rd.atan2(a, b)
  }

  case object Hypot extends Op {
    final val id = 23
    def apply(a: Double, b: Double): Double = rd.hypot(a, b)
  }

  case object Hypotx extends Op {
    final val id = 24
    def apply(a: Double, b: Double): Double = rd.hypotx(a, b)
  }

  /** '''Warning:''' Unlike a normal power operation, the signum of the
    * left operand is always preserved. I.e. `DC.kr(-0.5).pow(2)` will
    * not output `0.25` but `-0.25`. This is to avoid problems with
    * floating point noise and negative input numbers, so
    * `DC.kr(-0.5).pow(2.001)` does not result in a `NaN`, for example.
    */
  case object Pow extends Op {
    final val id = 25
    def apply(a: Double, b: Double): Double = rd.pow(a, b)
  }

  // case object <<             extends Op( 26 )
  // case object >>             extends Op( 27 )
  // case object UnsgnRghtShft  extends Op( 28 )
  // case object Fill           extends Op( 29 )
  case object Ring1 extends Op {
    final val id = 30
    def apply(a: Double, b: Double): Double = rd2.ring1(a, b)
  }

  case object Ring2 extends Op {
    final val id = 31
    def apply(a: Double, b: Double): Double = rd2.ring2(a, b)
  }

  case object Ring3 extends Op {
    final val id = 32
    def apply(a: Double, b: Double) = rd2.ring3(a, b)
  }

  case object Ring4 extends Op {
    final val id = 33
    def apply(a: Double, b: Double): Double = rd2.ring4(a, b)
  }

  case object Difsqr extends Op {
    final val id = 34
    def apply(a: Double, b: Double): Double = rd.difsqr(a, b)
  }

  case object Sumsqr extends Op {
    final val id = 35
    def apply(a: Double, b: Double): Double = rd.sumsqr(a, b)
  }

  case object Sqrsum extends Op {
    final val id = 36
    def apply(a: Double, b: Double): Double = rd.sqrsum(a, b)
  }

  case object Sqrdif extends Op {
    final val id = 37
    def apply(a: Double, b: Double): Double = rd.sqrdif(a, b)
  }

  case object Absdif extends Op {
    final val id = 38
    def apply(a: Double, b: Double): Double = rd.absdif(a, b)

    override def apply(a: Constant, b: Constant): Constant = UnaryOp.Abs(Minus(a, b))
  }

  case object Thresh extends Op {
    final val id = 39
    def apply(a: Double, b: Double): Double = rd2.thresh(a, b)
  }

  case object Amclip extends Op {
    final val id = 40
    def apply(a: Double, b: Double): Double = rd2.amclip(a, b)
  }

  case object Scaleneg extends Op {
    final val id = 41
    def apply(a: Double, b: Double): Double = rd2.scaleneg(a, b)
  }

  // XXX TODO --- refined apply
  case object Clip2 extends Op {
    final val id = 42
    def apply(a: Double, b: Double): Double = rd.clip2(a, b)
  }

  case object Excess extends Op {
    final val id = 43
    def apply(a: Double, b: Double): Double = rd.excess(a, b)
  }

  // XXX TODO --- refined apply
  case object Fold2 extends Op {
    final val id = 44
    def apply(a: Double, b: Double): Double = rd.fold2(a, b)
  }

  // XXX TODO --- refined apply
  case object Wrap2 extends Op {
    final val id = 45
    def apply(a: Double, b: Double): Double = rd.wrap2(a, b)
  }

  //  case object Firstarg extends Op {
  //    final val id = 46
  //
  //    def apply(a: Double, b: Double) = a
  //  }

  // case object Rrand          extends Op( 47 )
  // case object ExpRRand       extends Op( 48 )

  case object SecondArg extends Op {
    final val id = 100
    def apply(a: Double, b: Double): Double = b

    override def apply(a: Constant, b: Constant): Constant = b
  }
}

/** A binary operator UGen, for example two sum or multiply two signals.
  * The left or `a` input is "hot", i.e. it keeps the UGen running,
  * while the right or `b` input may close early, and the last value will
  * be remembered.
  *
  * @param op   the identifier of the operator (e.g. `BinaryOp.Times.id`)
  * @param a    the left operand which determines how long the UGen computes
  * @param b    the right operand.
  */
final case class BinaryOp(op: Int, a: GE, b: GE) extends UGenSource.SingleOut {

  protected def makeUGens(implicit builder: UGenGraph.Builder): UGenInLike =
    unwrap(Vector(a.expand, b.expand))

  protected def makeUGen(args: Vec[UGenIn])(implicit builder: UGenGraph.Builder): UGenInLike =
    UGen.SingleOut(this, inputs = args, rest = op)

  private[fscape] def makeStream(args: Vec[StreamIn])(implicit b: stream.Builder): StreamOut = {
    val Vec(in1, in2) = args
    val op0 = BinaryOp.Op(op)
    stream.BinaryOp(op = op0, in1 = in1.toDouble, in2 = in2.toDouble)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy