1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | Next→
//========================================================================================
//  controller.js
//  Copyright (C) 2018 Makoto Kamada
//========================================================================================





//========================================================================================
//
//  Array
//
//========================================================================================



//------------------------------------------------------------------------
//a = aappend (a, ...b)
//  配列破壊追加
//  Arrayは括弧を1段外してそれ以外はそのままpushする
function aappend (a, ...b) {
  for (let i = 0; i < b.length; i++) {
    let x = b[i];
    if (x instanceof Array) {
      Array.prototype.push.apply (a, x);
    } else {
      a.push (x);
    }
  }
  return a;
}

//------------------------------------------------------------------------
//a = apush (a, ...b)
function apush (a, ...b) {
  Array.prototype.push.apply (a, b);
  return a;
}

//------------------------------------------------------------------------
//i = ainsert (a, x, l, r, c)
//  インサート
//  コンパレータcについて昇順にソートされた配列aのインデックスl~r-1でxよりも大きい最小の要素の直前にxを挿入してそのインデックスを返す
//  xと等しい要素が既にあるときはその後ろに挿入される
//    等しい要素を大量に挿入するとき先頭への挿入を繰り返すと順序が逆になってしまい効率も悪いのでasearchと条件を変えてある
//    等しい要素の手前に挿入したいときはa.splice(asearch(a,x,l,r,c),0,x)とすること
//  すべての要素がxよりも大きいときはlの位置に挿入する
//  すべての要素がxよりも小さいときはrの位置に挿入する
//  lを省略したときは0とみなす
//  rを省略したときはa.lengthとみなす
//  cを省略したときは自然順序比較のcmpを使用する
function ainsert (a, x, l, r, c) {
  l || (l = 0);  //l==0の場合もl=0が実行されるが問題ない
  r !== undefined || (r = a.length);   //r==0の場合があるので!rは不可
  c || (c = cmp);
  while (l < r) {
    let m = (l + r) >> 1;
    c (a[m], x) <= 0 ? l = m + 1 : r = m;  //asearchと条件が異なることに注意
  }
  a.splice (l, 0, x);
  return l;
}

//------------------------------------------------------------------------
//i = asearch (a, x, l, r, c)
//  バイナリサーチ
//  コンパレータcについて昇順にソートされた配列aのインデックスl~r-1でxと等しいかxよりも大きい最小の要素のインデックスを返す
//  xと等しい要素が既にあるときは先頭のインデックスを返す
//  すべての要素がxよりも大きいときはlを返す
//  すべての要素がxよりも小さいときはrを返す
//  lを省略したときは0とみなす
//  rを省略したときはa.lengthとみなす
//  cを省略したときは自然順序比較のcmpを使用する
function asearch (a, x, l, r, c) {
  l || (l = 0);  //l==0の場合もl=0が実行されるが問題ない
  r !== undefined || (r = a.length);   //r==0の場合があるので!rは不可
  c || (c = cmp);
  while (l < r) {
    let m = (l + r) >> 1;
    c (a[m], x) < 0 ? l = m + 1 : r = m;  //ainsertと条件が異なることに注意
  }
  return l;
}



//------------------------------------------------------------------------
//void inferentialSort (array, incompleteComparator)
//  推論ソート
//  1=aが大きい,-1=bが大きい,0=不明 を返す不完全なコンパレータで配列をソートする
//  コンパレータは矛盾する結果を返してはならない
//  常にincompleteComparator(b,a)=-incompleteComparator(a,b)でなければならない
let inferentialSortWork1 = null;
let inferentialSortWork2 = null;
function inferentialSort (array, incompleteComparator) {
  let n = array.length;
  //cprint ("n=" + n);
  if (n <= 1) {
    return;
  }
  //比較する
  if (inferentialSortWork1 == null || inferentialSortWork1.length < n * n) {
    inferentialSortWork1 = new Int8Array (n * n);  //足りないときだけ新しく作る
  }
  let f = inferentialSortWork1;
  for (let a = 0; a < n; a++) {
    for (let b = 0; b < n; b++) {
      f[n * a + b] = a == b ? 0 : 2;  //f[n*a+b]:1=[a]>[b],-1=[a]<[b],0=不明,2=未テスト
    }
  }
  //let compared = 0;
  for (let b0 = 1; b0 < n; b0++) {
    for (let a0 = 0; a0 < b0; a0++) {  //すべてのa0<b0の組み合わせについて
      if (f[n * a0 + b0] === 2) {  //未テスト
        //[a0]と[b0]を比較する
        let h = incompleteComparator (array[a0], array[b0]);
        //compared++;
        if (h !== 0) {  //確定
          //[a]<[b]にする
          let a;
          let b;
          if (h < 0) {  //[a0]<[b0]
            a = a0;
            b = b0;
          } else {  //[b0]<[a0]
            a = b0;
            b = a0;
          }
          //[a]は[b]より小さい
          f[n * a + b] = -1;
          f[n * b + a] = 1;
          for (let c = 0; c < n; c++) {
            if (f[n * b + c] === -1) {  //[b]<[c]
              //[a]は[b]より大きいすべての要素より小さい
              f[n * a + c] = -1;  //[a]<[b]<[c]
              f[n * c + a] = 1;
            }
            if (f[n * c + a] === -1) {  //[c]<[a]
              //[a]より小さいすべての要素は[b]より小さい
              f[n * c + b] = -1;  //[c]<[a]<[b]
              f[n * b + c] = 1;
              //以下は推論のためにあったほうがよいと思われるが、比較回数はほとんど減らない
              for (let d = 0; d < n; d++) {
                if (f[n * b + d] === -1) {  //[b]<[d]
                  //[a]より小さいすべての要素は[b]より大きいすべての要素より小さい
                  f[n * c + d] = -1;  //[c]<[a]<[b]<[d]
                  f[n * d + c] = 1;
                }
              }
            }
          }
        }  //if 確定
      }  //if 未テスト
    }  //for a
  }  //for b
  //cprint ("compared=" + compared);
  //並べ替える
  if (inferentialSortWork2 == null || inferentialSortWork2.length < n) {
    inferentialSortWork2 = new Int32Array (n);  //足りないときだけ新しく作る
  }
  let g = inferentialSortWork2;
  for (let i = 0; i < n; i++) {
    g[i] = i;
  }
  for (let j = 1; j < n; j++) {
    let b = g[j];
    //g[0..j-1]にb=g[j]を挿入する
    //g[0..j-1]でb=g[j]より大きい最初のa=g[i]を探す
    let i = 0;
    while (i < j) {
      let a = g[i];
      if (f[n * a + b] === 1) {  //[a]>[b]
        break;
      }
      i++;
    }
    if (i < j) {  //g[0..j-1]にb=g[j]より大きいa=g[i]があるとき
      //g[i+1..j-1]にb=g[j]より小さいc=g[k]があればa=g[i]までずらしてiを増やす
      //  c=g[k]はg[0..j-1]と比較できずb=g[j]と比較できる要素
      for (let k = i + 1; k < j; k++) {
        let c = g[k];
        if (f[n * c + b] === -1) {  //[c]<[b]
          let e = array[k];
          for (let l = k; i < l; l--) {
            g[l] = g[l - 1];
            array[l] = array[l - 1];
          }
          g[i] = c;
          array[i] = e;
          i++;
        }
      }
      //b=g[j]をa=g[i]までずらす
      if (i < j) {
        let e = array[j];
        for (let l = j; i < l; l--) {
          g[l] = g[l - 1];
          array[l] = array[l - 1];
        }
        g[i] = b;
        array[i] = e;
      }
    }
  }
}  //inferentialSort





//========================================================================================
//
//  DOM
//
//========================================================================================

//------------------------------------------------------------------------
//o = getElementById (id)
//  Object o
//  String id
function getElementById (id) {
  return document.getElementById (id);
}

//------------------------------------------------------------------------
//o = createNode (t)
//  Object o
//  String t
//  DOM要素tを作る
function createNode (t) {
  return document.createElement (t);
}

//------------------------------------------------------------------------
//o = nsCreateNode (n, t)
//  Object o
//  String n
//  String t
//  名前空間nでDOM要素tを作る
//  IE7,IE8にはdocument.createElementNSがない
function nsCreateNode (n, t) {
  return document.createElementNS (n, t);
}

//------------------------------------------------------------------------
//o = createNodeXXX ()
//  Object o
//  DOM要素を作る
//  function名が圧縮されてコードが短くなることを期待している
function createNodeA () { return createNode ("a"); }
function createNodeAbbr () { return createNode ("abbr"); }
function createNodeAddress () { return createNode ("address"); }
function createNodeApplet () { return createNode ("applet"); }
function createNodeArea () { return createNode ("area"); }
function createNodeArticle () { return createNode ("article"); }
function createNodeAside () { return createNode ("aside"); }
function createNodeAudio () { return createNode ("audio"); }
function createNodeB () { return createNode ("b"); }
function createNodeBase () { return createNode ("base"); }
function createNodeBdo () { return createNode ("bdo"); }
function createNodeBlockquote () { return createNode ("blockquote"); }
function createNodeBody () { return createNode ("body"); }
function createNodeBr () { return createNode ("br"); }
function createNodeButton () { return createNode ("button"); }
function createNodeCanvas () { return createNode ("canvas"); }
function createNodeCaption () { return createNode ("caption"); }
function createNodeCite () { return createNode ("cite"); }
function createNodeCode () { return createNode ("code"); }
function createNodeCol () { return createNode ("col"); }
function createNodeColgroup () { return createNode ("colgroup"); }
function createNodeCommand () { return createNode ("command"); }
function createNodeDatalist () { return createNode ("datalist"); }
function createNodeDel () { return createNode ("del"); }
function createNodeDetails () { return createNode ("details"); }
function createNodeDfn () { return createNode ("dfn"); }
function createNodeDiv () { return createNode ("div"); }
function createNodeDd () { return createNode ("dd"); }
function createNodeDl () { return createNode ("dl"); }
function createNodeDt () { return createNode ("dt"); }
function createNodeEm () { return createNode ("em"); }
function createNodeEmbed () { return createNode ("embed"); }
function createNodeFieldset () { return createNode ("fieldset"); }
function createNodeFigcaption () { return createNode ("figcaption"); }
function createNodeFigure () { return createNode ("figure"); }
function createNodeFooter () { return createNode ("footer"); }
function createNodeForm () { return createNode ("form"); }
function createNodeH1 () { return createNode ("h1"); }
function createNodeH2 () { return createNode ("h2"); }
function createNodeH3 () { return createNode ("h3"); }
function createNodeH4 () { return createNode ("h4"); }
function createNodeH5 () { return createNode ("h5"); }
function createNodeH6 () { return createNode ("h6"); }
function createNodeHead () { return createNode ("head"); }
function createNodeHeader () { return createNode ("header"); }
function createNodeHr () { return createNode ("hr"); }
function createNodeHtml () { return createNode ("html"); }
function createNodeI () { return createNode ("i"); }
function createNodeIframe () { return createNode ("iframe"); }
function createNodeImg () { return createNode ("img"); }
function createNodeInput () { return createNode ("input"); }
function createNodeIns () { return createNode ("ins"); }
function createNodeKbd () { return createNode ("kbd"); }
function createNodeKeygen () { return createNode ("keygen"); }
function createNodeLabel () { return createNode ("label"); }
function createNodeLegend () { return createNode ("legend"); }
function createNodeLi () { return createNode ("li"); }
function createNodeLink () { return createNode ("link"); }
function createNodeMap () { return createNode ("map"); }
function createNodeMark () { return createNode ("mark"); }
function createNodeMenu () { return createNode ("menu"); }
function createNodeMeta () { return createNode ("meta"); }
function createNodeMeter () { return createNode ("meter"); }
function createNodeNav () { return createNode ("nav"); }
function createNodeNoscript () { return createNode ("noscript"); }
function createNodeObject () { return createNode ("object"); }
function createNodeOl () { return createNode ("ol"); }
function createNodeOptgroup () { return createNode ("optgroup"); }
function createNodeOption () { return createNode ("option"); }
function createNodeOutput () { return createNode ("output"); }
function createNodeP () { return createNode ("p"); }
function createNodeParam () { return createNode ("param"); }
function createNodeProgress () { return createNode ("progress"); }
function createNodePre () { return createNode ("pre"); }
function createNodeQ () { return createNode ("q"); }
function createNodeRp () { return createNode ("rp"); }
function createNodeRt () { return createNode ("rt"); }
function createNodeRuby () { return createNode ("ruby"); }
function createNodeS () { return createNode ("s"); }
function createNodeSamp () { return createNode ("samp"); }
function createNodeScript () { return createNode ("script"); }
function createNodeSection () { return createNode ("section"); }
function createNodeSelect () { return createNode ("select"); }
function createNodeSmall () { return createNode ("small"); }
function createNodeSource () { return createNode ("source"); }
function createNodeSpan () { return createNode ("span"); }
function createNodeStrong () { return createNode ("strong"); }
function createNodeStyle () { return createNode ("style"); }
function createNodeSub () { return createNode ("sub"); }
function createNodeSummary () { return createNode ("summary"); }
function createNodeSup () { return createNode ("sup"); }
function createNodeTable () { return createNode ("table"); }
function createNodeTbody () { return createNode ("tbody"); }
function createNodeTd () { return createNode ("td"); }
function createNodeTextarea () { return createNode ("textarea"); }
function createNodeTfoot () { return createNode ("tfoot"); }
function createNodeTh () { return createNode ("th"); }
function createNodeThead () { return createNode ("thead"); }
function createNodeTime () { return createNode ("time"); }
function createNodeTitle () { return createNode ("title"); }
function createNodeTr () { return createNode ("tr"); }
function createNodeU () { return createNode ("u"); }
function createNodeUl () { return createNode ("ul"); }
function createNodeVar () { return createNode ("var"); }
function createNodeVideo () { return createNode ("video"); }
function createNodeWbr () { return createNode ("wbr"); }

//------------------------------------------------------------------------
//o = createTextNode (t)
//  Object o
//  Object t
//  テキストノードを作る
//  string以外を直接documentcreateTextNodeに渡すとエラーになるので""を連結してstringに変換している
//  t.toStringはnullやobjectでないものを変換できないので不可
function createTextNode (t) {
  return document.createTextNode ("" + t);
}

//------------------------------------------------------------------------
//o = setStyle (o, k0, v0, ...)
//  Object o
//  DOM要素oのスタイルkiに値viを与える
//  いくつかの環境依存のスタイルに対応する
function setStyle () {
  let o = arguments[0];  //Object
  let s = o.style;  //Object
  let l = arguments.length;  //int
  for (let i = 1; i + 1 < l; i += 2) {  //int
    let k = arguments[i];  //String
    if (k) {  //false,null,undefined,0,NaN,""は無視する
      let v = arguments[i + 1];  //String
      s[k] = v;
      if (k === "borderRadius") {
        s.WebkitBorderRadius = v;  //Chrome,Safari5
      } else if (k === "boxSizing") {
        s.MozBoxSizing = v;  //Firefox4
        s.WebkitBoxSizing = v;  //Chrome,Safari5
        s.msBoxSizing = v;  //IE8
      } else if (k === "cssFloat") {
        s.styleFloat = v;  //IE9,Opera11
      } else if (k === "transform") {
        s.msTransform = v;  //IE9
      }
    }
  }
  return o;
}

//------------------------------------------------------------------------
//o = setAttr (o, a0, v0, ...)
//  Object o
//  DOM要素oの属性名aiに属性値viを与える
//  属性名がfalse,null,undefined,0,NaN,""のときはその属性値を設定しない
function setAttr () {
  let o = arguments[0];  //Object
  let l = arguments.length;  //int
  for (let i = 1; i + 1 < l; i += 2) {  //int
    let a = arguments[i];  //String
    if (a) {
      o[a] = arguments[i + 1];
    }
  }
  return o;
}

//------------------------------------------------------------------------
//o = nsSetAttr (o, n, a0, v0, ...)
//  Object o
//  名前空間nでDOM要素oの属性aiに値viを与える
//  属性がfalse,null,undefined,0,NaN,""のときその属性を設定しない
function nsSetAttr () {
  let o = arguments[0];  //Object
  let n = arguments[1];  //String
  let l = arguments.length;  //int
  for (let i = 2; i + 1 < l; i += 2) {  //int
    let a = arguments[i];  //String
    if (a) {
      o.setAttributeNS (n, a, arguments[i + 1]);
    }
  }
  return o;
}

//------------------------------------------------------------------------
//o = appendNode (o, p0, ...)
//  Object o
//  DOM要素oにテキストまたはDOM要素piを追加する
//  stringはテキストノードになる
function appendNode () {
  let o = arguments[0];  //Object
  let l = arguments.length;  //int
  for (let i = 1; i < l; i++) {  //int
    let p = arguments[i];  //Object
    if (typeof (p) !== "object") {  //boolean(falseを含む),number(0とNaNを含む),string(""を含む),undefined
      o.appendChild (createTextNode (p));
    } else if (p) {  //nullを除くfunction,object,xml
      o.appendChild (p);
    }
  }
  return o;
}

//------------------------------------------------------------------------
//o = removeNode (o, p0, ...)
//  Object o
//  要素oから要素piを削除する
function removeNode () {
  let o = arguments[0];  //Object
  let l = arguments.length;  //int
  for (let i = 1; i < l; i++) {  //int
    let p = arguments[i];  //Object
    if (p) {  //removeChildはnullを無視するはずだが念のため
      o.removeChild (p);
    }
  }
  return o;
}

//------------------------------------------------------------------------
//element = addDragEvent (element, dragStart, dragMove, dragEnd)
//  Object element
//  Function dragStart
//    dragStart (x, y)
//    int x
//    int y
//  Function dragMove
//    dragMove (x, y)
//    int x
//    int y
//  Function dragEnd
//    dragEnd ()
//  要素にドラッグイベントを追加する
//
//  clientX,clientYはビューポートの左上から
//  MouseEvent.offsetX,MouseEvent.offsetYは要素の左上から
//    Google Chromeは動かしたい頂点の上にテキストが重なっているとうまく動かせない
//  Touch.offsetXは存在しない
//  Touch.pageX,Touch.pageYはページの左上から
//  WaterfoxはsvgElement.offsetLeftがない
//  window.scrollX + e.clientX - e.pageX == 0  Chrome,Waterfox。EdgeはNaN
function addDragEvent (element, dragStart, dragMove, dragEnd) {
  if (dragStart) {
    addEvent (element, "mousedown", function (e) {  //Error
      dragStart (e.clientX, e.clientY);
      consumeEvent (e);
    });
    addEvent (element, "touchstart", function (e) {  //Error
      let touch = e.touches[0];  //Object
      dragStart (touch.clientX, touch.clientY);
      consumeEvent (e);
    });
  }
  if (dragMove) {
    addEvent (element, "mousemove", function (e) {  //Error
      dragMove (e.clientX, e.clientY);
      consumeEvent (e);
    });
    addEvent (element, "touchmove", function (e) {  //Error
      let touch = e.touches[0];  //Object
      dragMove (touch.clientX, touch.clientY);
      consumeEvent (e);
    });
  }
  if (dragEnd) {
    addEvent (element, "mouseup", function (e) {  //Error
      dragEnd ();
      consumeEvent (e);
    });
    addEvent (element, "touchend", function (e) {  //Error
      dragEnd ();
      consumeEvent (e);
    });
  }
  return element;
}

//------------------------------------------------------------------------
//element = addDropEvent (element, dragover, drop)
//  Object element
//  Function drogover
//    drogover (files)
//    FileList files
//  Function drop
//    drop (files)
//    FileList files
//  要素にドロップイベントを追加する
function addDropEvent (element, dragover, drop) {
  if (dragover) {
    addEvent (element, "dragover", function (e) {
      dragover (e);
      consumeEvent (e);
    });
  }
  if (drop) {
    addEvent (element, "drop", function (e) {
      drop (e);
      consumeEvent (e);
    });
  }
  return element;
}



//========================================================================
//window

//------------------------------------------------------------------------
//width = getWindowWidth ()
//  int width
function getWindowWidth () {
  return window.innerWidth || document.documentElement.offsetWidth || 0;
}

//------------------------------------------------------------------------
//width = getWindowHeight ()
//  int height
function getWindowHeight () {
  return window.innerHeight || document.documentElement.offsetHeight || 0;
}

//------------------------------------------------------------------------
//scrollX = getWindowScrollX ()
//  int scrollX
function getWindowScrollX () {
  return (defined (window.pageXOffset) ? window.pageXOffset :
          (document.documentElement || document.body.parentNode || document.body).scrollLeft);
}

//------------------------------------------------------------------------
//scrollY = getWindowScrollY ()
//  int scrollY
function getWindowScrollY () {
  return (defined (window.pageYOffset) ? window.pageYOffset :
          (document.documentElement || document.body.parentNode || document.body).scrollTop);
}





//========================================================================================
//
//  Math
//
//========================================================================================

//------------------------------------------------------------------------
//数学定数
let E = Math.E;
let LN10 = Math.LN10;
let LN2 = Math.LN2;
let LOG10E = Math.LOG10E;
let LOG2E = Math.LOG2E;
let PI = Math.PI;
let SQRT1_2 = Math.SQRT1_2;
let SQRT2 = Math.SQRT2;
let ONE_3 = 1 / 3;
let TWO_3 = 2 / 3;
let FOUR_3 = 4 / 3;
let TWOPI = 2 * PI;
let PI_2 = 0.5 * PI;
let PI_4 = 0.25 * PI;
let DEG_RAD = 180 / PI;
let RAD_DEG = PI / 180;

//------------------------------------------------------------------------
//y = abs (x)
//  絶対値関数 absolute value function アブソリュートバリューファンクション
let abs = Math.abs;

//------------------------------------------------------------------------
//y = acos (x)
//  逆余弦 inverse cosine インバースコサイン
let acos = Math.acos;

//------------------------------------------------------------------------
//y = acosh (x)
//  逆双曲線余弦 inverse hyperbolic cosine インバースハイパボリックコサイン
//
function builtin_acosh (x) {
  return log (x + sqrt (x * x - 1.0));
}
let acosh = Math.acosh || builtin_acosh;  //ECMAScrpit 6

//------------------------------------------------------------------------
//y = acot (x)
//  逆余接 inverse cotangent インバースコタンジェント
function acot (x) {
  return atan (1.0 / x);
}

//------------------------------------------------------------------------
//y = acoth (x)
//  逆双曲線余接 inverse hyperbolic cotangent インバースハイパボリックコタンジェント
function acoth (x) {
  return 0.5 * sgn (x) * log1p (2.0 / (abs (x) - 1.0));
}

//------------------------------------------------------------------------
//y = acsc (x)
//  逆余割 inverse csc インバースコセカント
function acsc (x) {
  return asin (1.0 / x);
}

//------------------------------------------------------------------------
//y = acsch (x)
//  逆双曲線余割 inverse hyperbolic csc インバースハイパボリックコセカント
function acsch (x) {
  return asinh (1.0 / x);
}

//------------------------------------------------------------------------
//y = asec (x)
//  逆正割 inverse secant インバースセカント
function asec (x) {
  return atan2 (sqrt (x * x - 1), sgn (x));
}

//------------------------------------------------------------------------
//y = asech (x)
//  逆双曲線正割 inverse hyperbolic secant インバースハイパボリックセカント
function asech (x) {
  return acosh (1.0 / x);
}

//------------------------------------------------------------------------
//y = asin (x)
//  逆正弦 inverse sine インバースサイン
let asin = Math.asin;

//------------------------------------------------------------------------
//y = asinh (x)
//  逆双曲線正弦 inverse hyperbolic sine インバースハイパボリックサイン
function builtin_asinh (x) {
  return sgn (x) * log (abs (x) + sqrt (x * x + 1.0));
}
let asinh = Math.asinh || builtin_asinh;

//------------------------------------------------------------------------
//y = atan (x)
//  逆正接 inverse tangent インバースタンジェント
let atan = Math.atan;

//------------------------------------------------------------------------
//y = atan2 (y, x)
//  逆正接 inverse tangent インバースタンジェント
let atan2 = Math.atan2;

//------------------------------------------------------------------------
//y = atanh (x)
//  逆双曲線正接 inverse hyperbolic tangent インバースハイパボリックタンジェント
function builtin_atanh (x) {
  return x == 0.0 ? x : 0.5 * log ((1.0 + x) / (1.0 - x));
}
let atanh = Math.atanh || builtin_atanh;

//------------------------------------------------------------------------
//y = cbrt (x)
//  1/3乗(立方根)
function builtin_cbrt (x) {
  return sgn (x) * pow (abs (x), ONE_3);
}
let cbrt = Math.cbrt || builtin_cbrt;

//------------------------------------------------------------------------
//y = ceil (x)
//  天井関数 ceiling function
let ceil = Math.ceil;

//------------------------------------------------------------------------
//n = clz32 (x)
//  count leading zeros
function builtin_clz32 (x) {
  let n = ((x >>> 16) - 1) >> 12 & 16;
  n += ((x >>> (24 - n)) - 1) >> 5 & 8;
  n += ((x >>> (28 - n)) - 1) >> 2 & 4;
  x >>>= 28 - n;
  return n + ((0x55af >>> (x << 1) & 3) - ((x - 1) >> 4));
}
let clz32 = Math.clz32 || builtin_clz32;

//------------------------------------------------------------------------
//s = cmp (x, y)
//  自然順序比較
function cmp (x, y) {
  return y < x ? 1.0 : x < y ? -1.0 : 0.0;
}

//------------------------------------------------------------------------
//s = cmpnum (x, y)
//  数値比較 spaceship operator
function cmpnum (x, y) {
  x = +x;
  y = +y;
  return isNaN (x) || isNaN (y) || x === y ? 0.0 : y < x ? 1.0 : -1.0;
}

//------------------------------------------------------------------------
//z = copysign (x, y)
//  符号複写
function copysign (x, y) {
  x = abs (x);
  return y < 0.0 || y == 0.0 && 1.0 / y < 0.0 ? -x : x;  //1/+0=+Infinity,1/-0=-Infinity。-NaNは作れない
}

//------------------------------------------------------------------------
//y = cos (x)
//  余弦 cosine コサイン
let cos = Math.cos;

//------------------------------------------------------------------------
//y = cosh (x)
//  双曲線余弦 hyperbolic cosine ハイパボリックコサイン
function builtin_cosh (x) {
  x = exp (x);
  return 0.5 * (x + 1.0 / x);
}
let cosh = Math.cosh || builtin_cosh;

//------------------------------------------------------------------------
//y = cot (x)
//  余接 cotangent コタンジェント
function cot (x) {
  return cos (x) / sin (x);
}

//------------------------------------------------------------------------
//y = coth (x)
//  双曲線余接 hyperbolic cotangent ハイパボリックコタンジェント
function coth (x) {
  return cosh (x) / sinh (x);
}

//------------------------------------------------------------------------
//y = csc (x)
//  余割 csc コセカント
function csc (x) {
  return 1.0 / sin (x);
}

//------------------------------------------------------------------------
//y = csch (x)
//  双曲線余割 hyperbolic csc ハイパボリックコセカント
function csch (x) {
  return 1.0 / sinh (x);
}

//------------------------------------------------------------------------
//y = ctz32 (x)
//  count trailing zeros
const CTZ32_HASH = [0, 1, 2, 5, 3, 9, 6, 11, 15, 4, 8, 10, 14, 7, 13, 12];
function builtin_ctz32 (x) {
  x >>>= 0;  //32bit符号なし整数にする
  if (0 < x) {  //1以上かつNaNではないとき
    let n = ((x & 65535) - 1) >> 12 & 16;  //x<<16?0:16
    x >>>= n;
    //  xの下位16bitは0ではない
    //  (1)x&-xで最下位の1<<0..15を取り出す
    //  (2)最小完全ハッシュ関数x*2479>>>12&15で1<<0..15を0..15に変換する
    //  (3)配列参照で0..15を1<<0..15のbit番号に変換する
    //  配列参照のコストによっては見た目ほど速くないかも知れない
    return n + CTZ32_HASH[(x & -x) * 2479 >>> 12 & 15];
  } else {
    return 32;
  }
}
let ctz32 = Math.ctz32 || builtin_ctz32;

//------------------------------------------------------------------------
//y = cub (x)
//  3乗
function cub (x) {
  return x * x * x;
}

//------------------------------------------------------------------------
//x = cubic (a, b, c, d)
//  3次方程式
function cubic (a, b, c, d) {
  return (a != 0.0 ? (a = 1.0 / a,
                      b *= a * ONE_3,
                      c *= a,
                      d *= a,
                      a = b * b - c * ONE_3,
                      c = 0.5 * (b * c - 2.0 * b * b * b - d),
                      d = c * c - a * a * a,
                      (0.0 <= d ? (d = sqrt (d),
                                   cbrt (c - d) + cbrt (c + d)) :
                       2.0 * sqrt (a) * cos (atan2 (sqrt (-d), c) * ONE_3)) - b) :
          b != 0.0 ? (b = 1.0 / b,
                      c *= 0.5 * b,
                      d = c * c - b * d,
                      0.0 <= d ? sqrt (d) - c : NaN) :
          c != 0.0 ? -d / c :
          d != 0.0 ? NaN :
          0.0);
}

//------------------------------------------------------------------------
//b = defined (x)
//  x !== undefined
function defined (x) {
  return x !== undefined;
}

//------------------------------------------------------------------------
//y = deg (x)
//  180/pi倍(ラジアン)
function deg (x) {
  return DEG_RAD * x;
}

//------------------------------------------------------------------------
//y = exp (x)
//  指数関数 exponential
let exp = Math.exp;

//------------------------------------------------------------------------
//y = exp10 (x)
//  底が10の指数関数 exponential with base 10
function exp10 (x) {
  return exp (LN10 * x);
}

//------------------------------------------------------------------------
//y = exp2 (x)
//  底が2の指数関数 exponential with base 2
function exp2 (x) {
  return exp (LN2 * x);
}

//------------------------------------------------------------------------
//y = exp2m1 (x)
//  0に近い数の底が2の指数関数 exponential of number being close to 0 with base 2
function exp2m1 (x) {
  return exp2 (x) - 1.0;
}

//------------------------------------------------------------------------
//y = expm1 (x)
//  0に近い数の指数関数 exponential of number being close to 0
function builtin_expm1 (x) {
  return x == 0 ? x : exp (x) - 1.0;
}
let expm1 = Math.expm1 || builtin_expm1;

//------------------------------------------------------------------------
//y = ext16s (x)
//  符号拡張(16bit符号あり整数)
//  小数点以下を切り捨ててから2^16で割った余りを-2^15..2^15-1の範囲で返す
function ext16s (x) {
  x = trunc (x) + 2 ** 15;  //0x1p+15
  return x - floor (x * 2 ** -16) * 2 ** 16 - 2 ** 15;  //0x1p-16,0x1p+16,0x1p+15
}

//------------------------------------------------------------------------
//y = ext16u (x)
//  ゼロ拡張(16bit符号なし整数)
//  小数点以下を切り捨ててから2^16で割った余りを0..2^16-1の範囲で返す
function ext16u (x) {
  x = trunc (x);
  return x - floor (x * 2 ** -16) * 2 ** 16;  //0x1p-16,0x1p+16
}

//------------------------------------------------------------------------
//y = ext32s (x)
//  符号拡張(32bit符号あり整数)
//  小数点以下を切り捨ててから2^32で割った余りを-2^31..2^31-1の範囲で返す
function ext32s (x) {
  x = trunc (x) + 2 ** 31;  //0x1p+31
  return x - floor (x * 2 ** -32) * 2 ** 32 - 2 ** 31;  //0x1p-32,0x1p+32,0x1p+31
}

//------------------------------------------------------------------------
//y = ext32u (x)
//  ゼロ拡張(32bit符号なし整数)
//  小数点以下を切り捨ててから2^32で割った余りを0..2^32-1の範囲で返す
function ext32u (x) {
  x = trunc (x);
  return x - floor (x * 2 ** -32) * 2 ** 32;  //0x1p-32,0x1p+32
}

//------------------------------------------------------------------------
//y = ext64s (x)
//  符号拡張(64bit符号あり整数)
//  小数点以下を切り捨ててから2^64で割った余りを-2^63..2^63-1の範囲で返す
function ext64s (x) {
  x = trunc (x) + 2 ** 63;  //0x1p+63
  return x - floor (x * 2 ** -64) * 2 ** 64 - 2 ** 63;  //0x1p-64,0x1p+64,0x1p+63
}

//------------------------------------------------------------------------
//y = ext64u (x)
//  ゼロ拡張(64bit符号なし整数)
//  小数点以下を切り捨ててから2^64で割った余りを0..2^64-1の範囲で返す
function ext64u (x) {
  x = trunc (x);
  return x - floor (x * 2 ** -64) * 2 ** 64;  //0x1p-64,0x1p+64
}

//------------------------------------------------------------------------
//y = ext8s (x)
//  符号拡張(8bit符号あり整数)
//  小数点以下を切り捨ててから2^8で割った余りを-2^7..2^7-1の範囲で返す
function ext8s (x) {
  x = trunc (x) + 2 ** 7;  //0x1p+7
  return x - floor (x * 2 ** -8) * 2 ** 8 - 2 ** 7;  //0x1p-8,0x1p+8,0x1p+7
}

//------------------------------------------------------------------------
//y = ext8u (x)
//  ゼロ拡張(8bit符号なし整数)
//  小数点以下を切り捨ててから2^8で割った余りを0..2^8-1の範囲で返す
function ext8u (x) {
  x = trunc (x);
  return x - floor (x * 2 ** -8) * 2 ** 8;  //0x1p-8,0x1p+8
}

//------------------------------------------------------------------------
//z = fdiv (x, y)
//  floor除算の商
function fdiv (x, y) {
  return floor (x / y);
}

//------------------------------------------------------------------------
//y = floor (x)
//  床関数 floor function
let floor = Math.floor;

//------------------------------------------------------------------------
//z = floor2 (x, y)
//  床倍数
function floor2 (x, y) {
  return floor (x / y) * y;
}

//------------------------------------------------------------------------
//y = fracf (x)
//  床小数部 fractional part (floor)
function fracf (x) {
  return x - floor (x);
}

//------------------------------------------------------------------------
//y = fract (x)
//  幹小数部 fractional part (trunc)
function fract (x) {
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | Next→