mohayonao/SCScript

View on GitHub
src/sc/classlib/Math/SimpleNumber.js

Summary

Maintainability
F
3 days
Test Coverage
SCScript.install(function(sc) {
  "use strict";

  require("./Number");

  var $ = sc.lang.$;
  var $nil  = $.nil;
  var $int0 = $.int0;
  var $int1 = $.int1;
  var random = sc.libs.random;
  var strlib = sc.libs.strlib;

  var SCArray = $("Array");
  var SCRoutine = $("Routine");

  function prOpSimpleNumber(selector, func) {
    return function($aNumber, $adverb) {
      var tag = $aNumber.__tag;

      switch (tag) {
      case sc.TAG_INT:
      case sc.TAG_FLOAT:
        return $.Boolean(func(this._, $aNumber._));
      }

      if ($aNumber.isSequenceableCollection().__bool__()) {
        return $aNumber.performBinaryOpOnSimpleNumber(
          $.Symbol(selector), this, $adverb
        );
      }

      return $.False();
    };
  }

  sc.lang.klass.refine("SimpleNumber", function(builder) {
    builder.addMethod("__newFrom__", $.Float);

    builder.addMethod("__bool__", function() {
      return this._ !== 0;
    });

    builder.addMethod("__dec__", function() {
      return this.__newFrom__(this._ - 1);
    });

    builder.addMethod("__inc__", function() {
      return this.__newFrom__(this._ + 1);
    });

    builder.addMethod("__int__", function() {
      if (!isFinite(this._)) {
        return this._;
      }
      return this._|0;
    });

    builder.addMethod("__num__", function() {
      return this._;
    });

    builder.shouldUseLiterals("new");

    builder.addMethod("isValidUGenInput", function() {
      return $.Boolean(!isNaN(this._));
    });

    builder.addMethod("numChannels", function() {
      return $int1;
    });

    builder.addMethod("magnitude", function() {
      return this.abs();
    });

    builder.addMethod("angle", function() {
      return $.Float(this._ >= 0 ? 0 : Math.PI);
    });

    builder.addMethod("neg", function() {
      return this.__newFrom__(-this._);
    });

    // bitNot: implemented by subclass

    builder.addMethod("abs", function() {
      return this.__newFrom__(Math.abs(this._));
    });

    builder.addMethod("ceil", function() {
      return this.__newFrom__(Math.ceil(this._));
    });

    builder.addMethod("floor", function() {
      return this.__newFrom__(Math.floor(this._));
    });

    builder.addMethod("frac", function() {
      var a = this._;

      if (a < 0) {
        return this.__newFrom__(1 + (a - (a|0)));
      }
      return this.__newFrom__(a - (a|0));
    });

    builder.addMethod("sign", function() {
      var a = this._;
      return this.__newFrom__(
        a > 0 ? 1 : a === 0 ? 0 : -1
      );
    });

    builder.addMethod("squared", function() {
      return this.__newFrom__(this._ * this._);
    });

    builder.addMethod("cubed", function() {
      return this.__newFrom__(this._ * this._ * this._);
    });

    builder.addMethod("sqrt", function() {
      return $.Float(Math.sqrt(this._));
    });

    builder.addMethod("exp", function() {
      return $.Float(Math.exp(this._));
    });

    builder.addMethod("reciprocal", function() {
      return $.Float(1 / this._);
    });

    builder.addMethod("midicps", function() {
      return $.Float(
        440 * Math.pow(2, (this._ - 69) * 1 / 12)
      );
    });

    builder.addMethod("cpsmidi", function() {
      return $.Float(
        Math.log(Math.abs(this._) * 1 / 440) * Math.LOG2E * 12 + 69
      );
    });

    builder.addMethod("midiratio", function() {
      return $.Float(
        Math.pow(2, this._ * 1 / 12)
      );
    });

    builder.addMethod("ratiomidi", function() {
      return $.Float(
        Math.log(Math.abs(this._)) * Math.LOG2E * 12
      );
    });

    builder.addMethod("ampdb", function() {
      return $.Float(
        Math.log(this._) * Math.LOG10E * 20
      );
    });

    builder.addMethod("dbamp", function() {
      return $.Float(
        Math.pow(10, this._ * 0.05)
      );
    });

    builder.addMethod("octcps", function() {
      return $.Float(
        440 * Math.pow(2, this._ - 4.75)
      );
    });

    builder.addMethod("cpsoct", function() {
      return $.Float(
        Math.log(Math.abs(this._) * 1 / 440) * Math.LOG2E + 4.75
      );
    });

    builder.addMethod("log", function() {
      return $.Float(Math.log(this._));
    });

    builder.addMethod("log2", function() {
      return $.Float(Math.log(Math.abs(this._)) * Math.LOG2E);
    });

    builder.addMethod("log10", function() {
      return $.Float(Math.log(this._) * Math.LOG10E);
    });

    builder.addMethod("sin", function() {
      return $.Float(Math.sin(this._));
    });

    builder.addMethod("cos", function() {
      return $.Float(Math.cos(this._));
    });

    builder.addMethod("tan", function() {
      return $.Float(Math.tan(this._));
    });

    builder.addMethod("asin", function() {
      return $.Float(Math.asin(this._));
    });

    builder.addMethod("acos", function() {
      return $.Float(Math.acos(this._));
    });

    builder.addMethod("atan", function() {
      return $.Float(Math.atan(this._));
    });

    function _sinh(a) {
      return (Math.pow(Math.E, a) - Math.pow(Math.E, -a)) * 0.5;
    }

    builder.addMethod("sinh", function() {
      return $.Float(_sinh(this._));
    });

    function _cosh(a) {
      return (Math.pow(Math.E, a) + Math.pow(Math.E, -a)) * 0.5;
    }

    builder.addMethod("cosh", function() {
      return $.Float(_cosh(this._));
    });

    builder.addMethod("tanh", function() {
      return $.Float(_sinh(this._) / _cosh(this._));
    });

    builder.addMethod("rand", function() {
      return this.__newFrom__(
        random.next() * this._
      );
    });

    builder.addMethod("rand2", function() {
      return this.__newFrom__(
        (random.next() * 2 - 1) * this._
      );
    });

    builder.addMethod("linrand", function() {
      return this.__newFrom__(
        Math.min(random.next(), random.next()) * this._
      );
    });

    builder.addMethod("bilinrand", function() {
      return this.__newFrom__(
        (random.next() - random.next()) * this._
      );
    });

    builder.addMethod("sum3rand", function() {
      return this.__newFrom__(
        (random.next() + random.next() + random.next() - 1.5) * 2 / 3 * this._
      );
    });

    builder.addMethod("distort", function() {
      return $.Float(
        this._ / (1 + Math.abs(this._))
      );
    });

    builder.addMethod("softclip", function() {
      var a = this._, abs = Math.abs(a);
      return $.Float(abs <= 0.5 ? a : (abs - 0.25) / a);
    });

    builder.addMethod("coin", function() {
      return $.Boolean(random.next() < this._);
    });

    builder.addMethod("isPositive", function() {
      return $.Boolean(this._ >= 0);
    });

    builder.addMethod("isNegative", function() {
      return $.Boolean(this._ < 0);
    });

    builder.addMethod("isStrictlyPositive", function() {
      return $.Boolean(this._ > 0);
    });

    builder.addMethod("isNaN", function() {
      return $.Boolean(isNaN(this._));
    });

    builder.addMethod("asBoolean", function() {
      return $.Boolean(this._ > 0);
    });

    builder.addMethod("booleanValue", function() {
      return $.Boolean(this._ > 0);
    });

    builder.addMethod("binaryValue", function() {
      return this._ > 0 ? $int1 : $int0;
    });

    builder.addMethod("rectWindow", function() {
      var a = this._;
      if (a < 0 || 1 < a) {
        return $.Float(0);
      }
      return $.Float(1);
    });

    builder.addMethod("hanWindow", function() {
      var a = this._;
      if (a < 0 || 1 < a) {
        return $.Float(0);
      }
      return $.Float(0.5 - 0.5 * Math.cos(a * 2 * Math.PI));
    });

    builder.addMethod("welWindow", function() {
      var a = this._;
      if (a < 0 || 1 < a) {
        return $.Float(0);
      }
      return $.Float(Math.sin(a * Math.PI));
    });

    builder.addMethod("triWindow", function() {
      var a = this._;
      if (a < 0 || 1 < a) {
        return $.Float(0);
      }
      if (a < 0.5) {
        return $.Float(2 * a);
      }
      return $.Float(-2 * a + 2);
    });

    builder.addMethod("scurve", function() {
      var a = this._;
      if (a <= 0) {
        return $.Float(0);
      }
      if (1 <= a) {
        return $.Float(1);
      }
      return $.Float(a * a * (3 - 2 * a));
    });

    builder.addMethod("ramp", function() {
      var a = this._;
      if (a <= 0) {
        return $.Float(0);
      }
      if (1 <= a) {
        return $.Float(1);
      }
      return $.Float(a);
    });

    // +: implemented by subclass
    // -: implemented by subclass
    // *: implemented by subclass
    // /: implemented by subclass
    // mod: implemented by subclass
    // div: implemented by subclass
    // pow: implemented by subclass
    // min: implemented by subclass
    // max: implemented by subclass
    // bitAnd: implemented by subclass
    // bitOr : implemented by subclass
    // bitXor: implemented by subclass

    builder.addMethod("bitTest", function($bit) {
      return $.Boolean(
        this.bitAnd($int1.leftShift($bit)).__num__() !== 0
      );
    });

    // lcm     : implemented by subclass
    // gcd     : implemented by subclass
    // round   : implemented by subclass
    // roundUp : implemented by subclass
    // trunc   : implemented by subclass
    // atan2   : implemented by subclass
    // hypot   : implemented by subclass
    // hypotApx: implemented by subclass
    // leftShift         : implemented by subclass
    // rightShift        : implemented by subclass
    // unsignedRightShift: implemented by subclass
    // ring1 : implemented by subclass
    // ring2 : implemented by subclass
    // ring3 : implemented by subclass
    // ring4 : implemented by subclass
    // difsqr: implemented by subclass
    // sumsqr: implemented by subclass
    // sqrsum: implemented by subclass
    // sqrdif: implemented by subclass
    // absdif: implemented by subclass
    // thresh: implemented by subclass
    // amclip: implemented by subclass
    // clip2 : implemented by subclass
    // fold2 : implemented by subclass
    // wrap2 : implemented by subclass
    // excess: implemented by subclass
    // firstArg: implemented by subclass
    // rrand   : implemented by subclass
    // exprand : implemented by subclass

    builder.addMethod("==", function($aNumber) {
      return $.Boolean(this._ === $aNumber._);
    });

    builder.addMethod("!=", function($aNumber) {
      return $.Boolean(this._ !== $aNumber._);
    });

    builder.addMethod("<", prOpSimpleNumber("<", function(a, b) {
      return a < b;
    }));

    builder.addMethod(">", prOpSimpleNumber(">", function(a, b) {
      return a > b;
    }));

    builder.addMethod("<=", prOpSimpleNumber("<=", function(a, b) {
      return a <= b;
    }));

    builder.addMethod(">=", prOpSimpleNumber(">=", function(a, b) {
      return a >= b;
    }));

    builder.addMethod("equalWithPrecision", {
      args: "that; precision=0.0001"
    }, function($that, $precision) {
      return this.absdif($that) ["<"] ($precision);
    });

    builder.addMethod("asInteger", function() {
      return $.Integer(this._);
    });

    builder.addMethod("asFloat", function() {
      return $.Float(this._);
    });

    // TODO: implements asComplex
    // TODO: implements asRect

    builder.addMethod("degrad", function() {
      return $.Float(this._ * Math.PI / 180);
    });

    builder.addMethod("raddeg", function() {
      return $.Float(this._ * 180 / Math.PI);
    });

    builder.addMethod("performBinaryOpOnSimpleNumber", function($aSelector) {
      throw new Error(strlib.format("binary operator '#{0}' failed", $aSelector.__sym__()));
    });

    // TODO: implements performBinaryOpOnComplex

    builder.addMethod("performBinaryOpOnSignal", function($aSelector) {
      throw new Error(strlib.format("binary operator '#{0}' failed", $aSelector.__sym__()));
    });

    builder.addMethod("nextPowerOfTwo", function() {
      return $.Float(
        Math.pow(2, Math.ceil(Math.log(this._) / Math.log(2)))
      );
    });

    builder.addMethod("nextPowerOf", {
      args: "base"
    }, function($base) {
      return $base.pow(
        (this.log() ["/"] ($base.$("log"))).ceil()
      );
    });

    builder.addMethod("nextPowerOfThree", function() {
      return $.Float(
        Math.pow(3, Math.ceil(Math.log(this._) / Math.log(3)))
      );
    });

    builder.addMethod("previousPowerOf", {
      args: "base"
    }, function($base) {
      return $base.pow(
        (this.log() ["/"] ($base.$("log"))).ceil().__dec__()
      );
    });

    builder.addMethod("quantize", {
      args: "quantum=1.0; tolerance=0.05; strength=1.0"
    }, function($quantum, $tolerance, $strength) {
      var $round, $diff;

      $round = this.round($quantum);
      $diff = $round ["-"] (this);

      if ($diff.abs() < $tolerance) {
        return this ["+"] ($strength.$("*", [ $diff ]));
      }

      return this;
    });

    builder.addMethod("linlin", {
      args: "inMin; inMax; outMin; outMax; clip=\\minmax"
    }, function($inMin, $inMax, $outMin, $outMax, $clip) {
      var $res = null;

      $res = getClippedValue(this, $inMin, $inMax, $outMin, $outMax, $clip);

      if ($res === null) {
        // (this-inMin)/(inMax-inMin) * (outMax-outMin) + outMin;
        $res = ((this ["-"] ($inMin)) ["/"] ($inMax ["-"] ($inMin))
              ["*"] ($outMax ["-"] ($outMin)) ["+"] ($outMin));
      }

      return $res;
    });

    builder.addMethod("linexp", {
      args: "inMin; inMax; outMin; outMax; clip=\\minmax"
    }, function($inMin, $inMax, $outMin, $outMax, $clip) {
      var $res = null;

      $res = getClippedValue(this, $inMin, $inMax, $outMin, $outMax, $clip);

      if ($res === null) {
        // Math.pow(outMax/outMin, (this-inMin)/(inMax-inMin)) * outMin;
        $res = $outMax ["/"] ($outMin).pow(
          (this ["-"] ($inMin)) ["/"] ($inMax ["-"] ($inMin))
        ) ["*"] ($outMin);
      }

      return $res;
    });

    builder.addMethod("explin", {
      args: "inMin; inMax; outMin; outMax; clip=\\minmax"
    }, function($inMin, $inMax, $outMin, $outMax, $clip) {
      var $res = null;

      $res = getClippedValue(this, $inMin, $inMax, $outMin, $outMax, $clip);

      if ($res === null) {
        // (((Math.log(this/inMin)) / (Math.log(inMax/inMin))) * (outMax-outMin)) + outMin;
        $res = ((this ["/"] ($inMin).log() ["/"] ($inMax ["/"] ($inMin).log())
                 ["*"] ($outMax ["-"] ($outMin))) ["+"] ($outMin));
      }

      return $res;
    });

    builder.addMethod("expexp", {
      args: "inMin; inMax; outMin; outMax; clip=\\minmax"
    }, function($inMin, $inMax, $outMin, $outMax, $clip) {
      var $res = null;

      $res = getClippedValue(this, $inMin, $inMax, $outMin, $outMax, $clip);

      if ($res === null) {
        // Math.pow(outMax/outMin, Math.log(this/inMin) / Math.log(inMax/inMin)) * outMin;
        $res = $outMax ["/"] ($outMin).pow(
          this ["/"] ($inMin).log() ["/"] ($inMax ["/"] ($inMin).log())
        ) ["*"] ($outMin);
      }

      return $res;
    });

    builder.addMethod("lincurve", {
      args: "inMin=0; inMax=1; outMin=0; outMax=1; curve=-4; clip=\\minmax"
    }, function($inMin, $inMax, $outMin, $outMax, $curve, $clip) {
      var $res = null, $grow, $a, $b, $scaled;

      $res = getClippedValue(this, $inMin, $inMax, $outMin, $outMax, $clip);

      if ($res === null) {
        if (Math.abs($curve.__num__()) < 0.001) {
          $res = this.linlin($inMin, $inMax, $outMin, $outMax);
        } else {
          $grow = $curve.exp();
          $a = $outMax ["-"] ($outMin) ["/"] ($.Float(1.0) ["-"] ($grow));
          $b = $outMin ["+"] ($a);
          $scaled = (this ["-"] ($inMin)) ["/"] ($inMax ["-"] ($inMin));

          $res = $b ["-"] ($a ["*"] ($grow.pow($scaled)));
        }
      }

      return $res;
    });

    builder.addMethod("curvelin", {
      args: "inMin=0; inMax=1; outMin=0; outMax=1; curve=-4; clip=\\minmax"
    },function($inMin, $inMax, $outMin, $outMax, $curve, $clip) {
      var $res = null, $grow, $a, $b;

      $res = getClippedValue(this, $inMin, $inMax, $outMin, $outMax, $clip);

      if ($res === null) {
        if (Math.abs($curve.__num__()) < 0.001) {
          $res = this.linlin($inMin, $inMax, $outMin, $outMax);
        } else {
          $grow = $curve.exp();
          $a = $inMax ["-"] ($inMin) ["/"] ($.Float(1.0) ["-"] ($grow));
          $b = $inMin ["+"] ($a);

          $res = ((($b ["-"] (this)) ["/"] ($a)).log()
                  ["*"] ($outMax ["-"] ($outMin)) ["/"] ($curve) ["+"] ($outMin));
        }
      }

      return $res;
    });

    builder.addMethod("bilin", {
      args: "inCenter; inMin; inMax; outCenter; outMin; outMax; clip=\\minmax"
    }, function($inCenter, $inMin, $inMax, $outCenter, $outMin, $outMax, $clip) {
      var $res = null;

      $res = getClippedValue(this, $inMin, $inMax, $outMin, $outMax, $clip);

      if ($res === null) {
        if (this >= $inCenter) {
          $res = this.linlin($inCenter, $inMax, $outCenter, $outMax, $.Symbol("none"));
        } else {
          $res = this.linlin($inMin, $inCenter, $outMin, $outCenter, $.Symbol("none"));
        }
      }

      return $res;
    });

    builder.addMethod("biexp", {
      args: "inCenter; inMin; inMax; outCenter; outMin; outMax; clip=\\minmax"
    }, function($inCenter, $inMin, $inMax, $outCenter, $outMin, $outMax, $clip) {
      var $res = null;

      $res = getClippedValue(this, $inMin, $inMax, $outMin, $outMax, $clip);

      if ($res === null) {
        if (this >= $inCenter) {
          $res = this.explin($inCenter, $inMax, $outCenter, $outMax, $.Symbol("none"));
        } else {
          $res = this.explin($inMin, $inCenter, $outMin, $outCenter, $.Symbol("none"));
        }
      }

      return $res;
    });

    builder.addMethod("moddif", {
      args: "aNumber=0.0; mod=1.0"
    }, function($aNumber, $mod) {
      var $diff, $modhalf;

      $diff = this.absdif($aNumber) ["%"] ($mod);
      $modhalf = $mod.$("*", [ $.Float(0.5) ]);

      return $modhalf.$("-", [ $diff.absdif($modhalf) ]);
    });

    builder.addMethod("lcurve", {
      args: "a=1.0; m=0.0; n=1.0; tau=1.0"
    }, function($a, $m, $n, $tau) {
      var $rTau, $x;

      $x = this.neg();

      if ($tau.__num__() === 1.0) {
        // a * (m * exp(x) + 1) / (n * exp(x) + 1)
        return $a.$("*", [
          $m.$("*", [ $x.exp() ]).__inc__()
        ]).$("/", [
          $n.$("*", [ $x.exp() ]).__inc__()
        ]);
      } else {
        $rTau = $tau.reciprocal();
        return $a.$("*", [
          $m.$("*", [ $x.exp() ]) ["*"] ($rTau).__inc__()
        ]).$("/", [
          $n.$("*", [ $x.exp() ]) ["*"] ($rTau).__inc__()
        ]);
      }
    });

    builder.addMethod("gauss", {
      args: "standardDeviation"
    }, function($standardDeviation) {
      // ^((((-2*log(1.0.rand)).sqrt * sin(2pi.rand)) * standardDeviation) + this)
      return ($.Float(-2.0) ["*"] ($.Float(1.0).rand().log()).sqrt() ["*"] (
        $.Float(2 * Math.PI).rand().sin()
      ) ["*"] ($standardDeviation)) ["+"] (this);
    });

    builder.addMethod("gaussCurve", {
      args: "a=1.0; b=0.0; c=1.0"
    }, function($a, $b, $c) {
      // ^a * (exp(squared(this - b) / (-2.0 * squared(c))))
      return $a.$("*", [ ((
        (this ["-"] ($b).squared()) ["/"] ($.Float(-2.0) ["*"] ($c.$("squared")))
      ).exp()) ]);
    });

    // TODO: implements asPoint
    // TODO: implements asWarp

    builder.addMethod("wait", function() {
      return this.yield();
    });

    // TODO: implements waitUntil
    // TODO: implements sleep
    // TODO: implements printOn
    // TODO: implements storeOn

    builder.addMethod("rate", function() {
      return $.Symbol("scalar");
    });

    builder.addMethod("asAudioRateInput", function() {
      if (this._ === 0) {
        return $("Silent").ar();
      }
      return $("DC").ar(this);
    });

    builder.addMethod("madd", {
      args: "mul; add"
    }, function($mul, $add) {
      return (this ["*"] ($mul)) ["+"] ($add);
    });

    builder.addMethod("lag");
    builder.addMethod("lag2");
    builder.addMethod("lag3");
    builder.addMethod("lagud");
    builder.addMethod("lag2ud");
    builder.addMethod("lag3ud");
    builder.addMethod("varlag");
    builder.addMethod("slew");

    // TODO: implements writeInputSpec

    builder.addMethod("series", {
      args: "second; last"
    }, function($second, $last) {
      var $step;
      var last, step, size;

      if ($second === $nil) {
        if (this.__num__() < $last.__num__()) {
          $second = this.__inc__();
        } else {
          $second = this.__dec__();
        }
      }
      $step = $second ["-"] (this);

      last = $last.__num__();
      step = $step.__num__();
      size = (Math.floor((last - this._) / step + 0.001)|0) + 1;

      return SCArray.series($.Integer(size), this, $step);
    });

    builder.addMethod("seriesIter", {
      args: "second; last"
    }, function($second, $last) {
      var first, second, last, step;
      var $newFrom = this.__newFrom__;

      first = this.__num__();
      if ($second === $nil) {
        last = ($last !== $nil) ? $last.__num__() : Infinity;
        step = first < last ? 1 : -1;
      } else {
        second = $second.__num__();
        last = ($last !== $nil) ? $last.__num__() : (
          $second < first ? -Infinity : Infinity
        );
        step = second - first;
      }
      return SCRoutine.new($.Function(function() {
        var val, $cond;
        $cond = $.Func(step < 0 ? function() {
          return $.Boolean(val >= last);
        } : function() {
          return $.Boolean(val <= last);
        });
        return [ function() {
          val = first;
          return $cond.while($.Func(function() {
            $newFrom(val).yield();
            val += step;
            return $nil;
          }));
        } ];
      }, null, null));
    });

    builder.addMethod("degreeToKey", {
      args: "scale; stepsPerOctave=12"
    }, function($scale, $stepsPerOctave) {
      var $scaleDegree, $accidental;
      $scaleDegree = this.round($int1).asInteger();
      $accidental  = (this ["-"] ($scaleDegree)) ["*"] ($.Float(10.0));
      return $scale.performDegreeToKey($scaleDegree, $stepsPerOctave, $accidental);
    });

    builder.addMethod("keyToDegree", {
      args: "scale; stepsPerOctave=12"
    }, function($scale, $stepsPerOctave) {
      return $scale.performKeyToDegree(this, $stepsPerOctave);
    });

    builder.addMethod("nearestInList", {
      args: "list"
    }, function($list) {
      return $list.performNearestInList(this);
    });

    builder.addMethod("nearestInScale", {
      args: "scale; stepsPerOctave=12"
    }, function($scale, $stepsPerOctave) {
      return $scale.performNearestInScale(this, $stepsPerOctave);
    });

    builder.addMethod("partition", {
      args: "parts=2; min=1"
    }, function($parts, $min) {
      var $n = this ["-"] ($min.__dec__() ["*"] ($parts));
      return $int1.series(null, $n.__dec__()).scramble().keep($parts.__dec__())
        .sort().add($n).differentiate() ["+"] ($min.__dec__());
    });

    builder.addMethod("nextTimeOnGrid", {
      args: "clock"
    }, function($clock) {
      return $clock.nextTimeOnGrid(this, $int0);
    });

    builder.addMethod("playAndDelta");

    builder.addMethod("asQuant", function() {
      return $("Quant").new(this);
    });

    // TODO: implements asTimeString
    // TODO: implements asFraction
    // TODO: implements asBufWithValues
    // TODO: implements schedBundleArrayOnClock

    builder.addMethod("shallowCopy");
  });

  function getClippedValue($this, $inMin, $inMax, $outMin, $outMax, $clip) {
    switch ($clip.__sym__()) {
    case "minmax":
      if ($this <= $inMin) {
        return $outMin;
      }
      if ($this >= $inMax) {
        return $outMax;
      }
      break;
    case "min":
      if ($this <= $inMin) {
        return $outMin;
      }
      break;
    case "max":
      if ($this >= $inMax) {
        return $outMax;
      }
      break;
    }

    return null;
  }
});