matrixlib-js
Version:
MatrixLib.js is a JavaScript simple matrix calculation library.
1,093 lines (1,018 loc) • 28.1 kB
JavaScript
/**
* # matrixLib.js - v0.3.1
*
* Copyright (c) 2018 Kohei Katada.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
const matrixlibJs = () => {
'use strict';
/*
# util
*/
const util = {
/**
* # 引数がnullかundefinedではない事を確認する関数
* @param x
* @returns {boolean}
*/
existy(x) {
return x != null;
},
/**
* # 0と''もtrueとみなす真偽値を返す
* @param x
* @returns {boolean|*}
*/
truthy(x) {
return (x !== false) && util.existy(x);
},
/**
* # 数値かどうかの真偽値を返す
* @param num
* @returns {boolean}
*/
isNumber(num) {
return typeof num === 'number' && isFinite(num);
},
/**
* # 配列かどうかの真偽値を返す
* @param arr
* @returns {boolean}
*/
isArray(arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
},
/**
* # 関数かどうかの真偽値を返す
* @param fn
* @returns {boolean}
*/
isFunction(fn, notArrow) {
return Object.prototype.toString.call(fn) === '[object Function]' && (!notArrow || 'prototype' in fn);
},
/**
* # 配列の不要な要素を削除する関数
* @param arr
* @param deleteValue
* @returns {*}
*/
cleanArray(arr, deleteValue) {
for (let i = 0; i < arr.length; i += 1) {
if (arr[i] === deleteValue) {
arr.splice(i, 1);
i--;
}
}
return arr;
},
/**
* # 正規乱数生成
* @param m
* @param s
* @returns {*}
*/
rnorm(m, s) {
const a = 1 - Math.random();
const b = 1 - Math.random();
const c = Math.sqrt(-2 * Math.log(a));
if(0.5 - Math.random() > 0) {
return c * Math.sin(Math.PI * 2 * b) * s + m;
}else{
return c * Math.cos(Math.PI * 2 * b) * s + m;
}
}
};
/*
# math
*/
const math = {
/*
# math.util
*/
util: {
/**
* # 計算をループ処理する関数
*/
caluclate(x, y, f) {
/**
* # f を実行する関数
* @param f
* @param x
* @param y
* @returns {*}
*/
const execFunc = (f, x, y) => {
return f(x, y);
};
/**
* # x を yArr に合わせてブロードキャストする関数
* @param x
* @param yArr
* @returns {*[]}
*/
function broadCastX(x, yArr) {
if (!util.isArray(yArr)) {
return [x, yArr];
}
let bcX = [];
yArr.forEach((val, idx, arr) => {
if (util.isArray(val)) {
bcX[idx] = [];
val.forEach((_val, _idx, _arr) => {
bcX[idx][_idx] = broadCastX(x, _val)[0];
});
} else {
bcX[idx] = x;
}
});
return [bcX, yArr];
}
/**
* # y を xArr に合わせてブロードキャストする関数
* @param xArr
* @param y
* @returns {*[]}
*/
function broadCastY(xArr, y) {
if (!util.isArray(xArr)) {
return [xArr, y];
}
let bcY = [];
xArr.forEach((val, idx, arr) => {
if (util.isArray(val)) {
bcY[idx] = [];
val.forEach((_val, _idx, _arr) => {
bcY[idx][_idx] = broadCastY(_val, y)[1];
});
} else {
bcY[idx] = y;
}
});
return [xArr, bcY];
}
// 計算を実行
if (!util.isArray(x) && !util.isArray(y)) { // x, y がスカラー値だった場合
return execFunc(f, x, y);
}
if (!util.isArray(x) && util.isArray(y)) { // x がスカラー値の場合
const bc = broadCastX(x, y);
return this.caluclate(bc[0], bc[1], f);
} else if (!util.isArray(y) && util.isArray(x)) { // y がスカラー値の場合
const bc = broadCastY(x, y);
return this.caluclate(bc[0], bc[1], f);
}
if (math.util.shape(x)[1] === 0 && math.util.shape(y)[1] === 0) { // xとyが1次元配列だった場合
let r = [];
x.forEach((val, idx, arr) => {
r[idx] = execFunc(f, x[idx], y[idx]);
});
return r;
}
if (math.util.shape(x)[1] === 0 && math.util.shape(y)[1] !== 0) { // xが1次元でyが2次元以上なら、
const bc = [];
y.forEach((val, idx, arr) => {
bc[idx] = x;
});
return this.caluclate(bc, y, f);
}
if (math.util.shape(y)[1] === 0 && math.util.shape(x)[1] !== 0) { // yが1次元でxが2次元以上なら、
const bc = [];
x.forEach((val, idx, arr) => {
bc[idx] = y;
});
return this.caluclate(x, bc, f);
}
if (math.util.shape(x)[1] !== 0 && math.util.shape(y)[1] !== 0 &&x.length === y.length) { // xとyの要素数が同じでxもyも2次元なら、
let r = [];
x.forEach((val, idx, arr) => {
r[idx] = this.caluclate(x[idx], y[idx], f);
});
return r;
} else {
throw new Error('計算不可能です.');
}
},
/**
* # n次元配列の形状を返す
* @param arr
* @returns {[*,*]}
*/
shape(arr) {
let row, col;
if (!util.isArray(arr)) {
return -1;
} else if (util.isArray(arr[0])) {
row = arr.length;
col = arr[0].length;
} else if (util.isArray(arr)) {
row = arr.length;
col = 0;
} else {
throw new Error(arr + 'が2次元以上の配列なのでshape()は使えません.');
}
return [row, col];
}
},
/*
# math.calc
*/
calc: {
/**
* # 足し算
* @param x
* @param y
* @returns {*}
*/
plus(x, y) {
return x + y;
},
/**
* # 引き算
* @param x
* @param y
* @returns {*}
*/
minus(x, y) {
return x - y;
},
/**
* # 掛け算
* @param x
* @param y
* @returns {*}
*/
multi(x, y) {
return x * y;
},
/**
* # 割り算
* @param x
* @param y
* @returns {*}
*/
div(x, y) {
return x / y;
},
/**
* # 大なり
* @param x
* @param y
* @returns {boolean}
*/
more(x, y) {
return x > y;
},
/**
* # 小なり
* @param x
* @param y
* @returns {boolean}
*/
less(x, y) {
return x < y;
},
/**
* # 大なりイコール
* @param x
* @param y
* @returns {boolean}
*/
moreEq(x, y) {
return x >= y;
},
/**
* # 小なりイコール
* @param x
* @param y
* @returns {boolean}
*/
lessEq(x, y) {
return x <= y;
},
/**
* # 同値演算
* @param x
* @param y
* @returns {boolean}
*/
equal(x, y) {
return x === y;
},
/**
* # 非同値演算
* @param x
* @param y
* @returns {boolean}
*/
notEqual(x, y) {
return x !== y;
},
/**
* # 累乗する
* @param x
* @param n
* @returns {*}
*/
pow(x, n) {
return Math.pow(x, n);
},
/**
* # Ex を返す.(自然対数の底であるネイピア数(オイラー数))
* @param x
* @returns {*}
*/
exp(x) {
return Math.exp(x);
},
/**
* # 自然対数 (底は e) を返す.
* @param x
* @returns {*}
*/
log(x) {
return Math.log(x);
},
/**
* # 引数、x, y を比べて大きい方の値を返す
* @param x
* @param y
* @returns {number}
*/
max(x, y) {
return Math.max(x, y);
}
},
/*
# math.arr
*/
arr: {
/*
# math.arr.util
*/
util: {
/**
* # 1次元配列生成
* @param x {Number}
* @param v {*} # 配列の各要素を初期化する値(関数だった場合は関数の戻り値で初期化)
* @returns {Array}
*/
create(x, v) {
let r = [];
for (let i=0,l=x; i<l; i+=1) {
r[i] = util.isFunction(v) ? v() : v;
}
return r;
},
/**
* # step単位の、minSizeからmaxSizeまでの数の1次元配列を生成
* @param minSize
* @param maxSize
* @param step
* @param callback # 出来上がった配列に適用可能な関数
* @returns {Array}
*/
arange(minSize, maxSize, step, callback) {
// 初期化
let arr = [];
// step引数がないのに、maxSize引数がある場合
if (!step && !!maxSize) {
throw new Error('第三引数のステップ数が指定されていないため計算できません.');
}
// 引数がminSizeだけの場合
if (!step && !maxSize && !!minSize) {
// 引数がminSizeだけで、負の値だった場合
if (minSize < 0) {
throw new Error('負の値を指定するときは第二引数に範囲の最大値、第三引数にステップ数を指定してください.');
}
// 引数がminSizeだけで、正の値だった場合
for (let i=0,l=minSize; i<l; i+=1) {
arr[i] = i;
}
}
// 引数が全てある場合
if (!!step) {
if (maxSize <= minSize) {
throw new Error('第二引数の値は第一引数の値より大きい値を指定してください.');
}
let len = maxSize - minSize;
for (let i=0,l=len/step; i<l; i+=1) {
arr[i] = minSize + (step*i);
}
}
// コールバック関数がある場合
if (!!callback) {
for (let i=0,l=arr.length; i<l; i+=1) {
arr[i] = callback(arr[i]);
}
return arr;
}
return arr;
}
}
},
/*
# math.matrix
*/
matrix: {
/*
# math.matrix.util
*/
util: {
/**
* # 行列を生成
* @param x {Array|Number}
* @param y {Number}
* @param v {*} # 行列の各要素を初期化する値(関数だった場合は関数の戻り値で初期化)
* @returns {*}
*/
create(x, y, v) {
let r1;
if (util.isArray(x)) {
if (!util.isArray(x[0])) {
r1 = [x];
} else {
r1 = x;
}
}
let r2 = [];
for (let i=0,l=x; i<l; i+=1) {
r2[i] = (() => {
return math.arr.util.create(y, v);
})();
}
let r;
if (util.truthy(r1)) {
r = r1;
} else {
r = r2;
}
return r;
},
/**
* # 行列の列を行に変換
* @param matrix
* @returns {Array}
*/
colToRow(matrix) {
if (math.util.shape(matrix)[1] === 0) {
return matrix;
}
let r = [];
for (let i=0,l=matrix[0].length; i<l; i+=1) {
r[i] = [];
for (let j=0,m=matrix.length; j<m; j+=1) {
r[i][j] = matrix[j][i];
}
}
return r;
}
}
},
/**
* # n次元配列同士の足し算
* @param x
* @param y
* @returns {Array}
*/
plus(x, y) {
return math.util.caluclate(x, y, math.calc.plus);
},
/**
* # n次元配列同士の引き算 (x - y)
* @param x
* @param y
* @returns {Array}
*/
minus(x, y) {
return math.util.caluclate(x, y, math.calc.minus);
},
/**
* # n次元配列同士の掛け算
* @param x
* @param y
* @returns {Array}
*/
multi(x, y) {
return math.util.caluclate(x, y, math.calc.multi);
},
/**
* # n次元配列同士の割り算 (x / y)
* @param x
* @param y
* @returns {Array}
*/
div(x, y) {
return math.util.caluclate(x, y, math.calc.div);
},
/**
* # n次元配列同士の大なり (x > y)
* @param x
* @param y
* @returns {*}
*/
more(x, y) {
return math.util.caluclate(x, y, math.calc.more);
},
/**
* # n次元配列同士の小なり (x < y)
* @param x
* @param y
* @returns {*}
*/
less(x, y) {
return math.util.caluclate(x, y, math.calc.less);
},
/**
* # n次元配列同士の大なりイコール (x >= y)
* @param x
* @param y
* @returns {*}
*/
moreEq(x, y) {
return math.util.caluclate(x, y, math.calc.moreEq);
},
/**
* # n次元配列同士の小なりイコール (x <= y)
* @param x
* @param y
* @returns {*}
*/
lessEq(x, y) {
return math.util.caluclate(x, y, math.calc.lessEq);
},
/**
* # n次元配列同士の同値演算 (x === y)
* @param x
* @param y
* @returns {*}
*/
equal(x, y) {
return math.util.caluclate(x, y, math.calc.equal);
},
/**
* # n次元配列同士の非同値演算 (x !== y)
* @param x
* @param y
* @returns {*}
*/
notEqual(x, y) {
return math.util.caluclate(x, y, math.calc.notEqual);
},
/**
* n次元配列の総和を求める
* @param xArr
* @param axis
* @returns {*}
*/
sum(xArr, axis) {
if (!util.isArray(xArr)) { // スカラー値だった場合
return xArr;
}
if (!util.isArray(xArr[0])) { // 1次元配列だった場合
return xArr.reduce((pre, val, idx, arr) => {
return math.calc.plus(pre, val);
}, 0);
} else if (!util.isArray(xArr[0][0])) { // 2次元配列だった場合
if (!util.truthy(axis)) {
return this.sum(math.flatten(xArr));
} else if (axis === 0) {
xArr = math.matrix.util.colToRow(xArr);
let r = [];
xArr.forEach((val, idx, arr) => {
r[idx] = this.sum(val);
});
return r;
} else if (axis === 1) {
let r = [];
xArr.forEach((val, idx, arr) => {
r[idx] = this.sum(val);
});
return r;
}
} else { // 3次元以上の配列だった場合
if (!util.truthy(axis)) {
return this.sum(math.flatten(xArr));
} else if (axis === 0) {
xArr = math.matrix.util.colToRow(xArr);
let r = [];
xArr.forEach((val, idx, arr) => {
r[idx] = this.sum(val, 0);
});
return r;
} else if (axis === 1) {
let r = [];
xArr.forEach((val, idx, arr) => {
val = math.matrix.util.colToRow(val);
r[idx] = this.sum(val, 1);
});
return r;
} else if (axis === 2) {
let r = [];
xArr.forEach((val, idx, arr) => {
r[idx] = this.sum(val, 1);
});
return r;
}
}
},
/**
* # n次元配列中の最大値のインデックスを取得
* @param xArr
* @param axis
* @returns {*}
*/
maxIdx(xArr, axis) {
if (!util.isArray(xArr[0])) { // 1次元配列だった場合
return xArr.indexOf(Math.max.apply(null, xArr));
} else if (!util.isArray(xArr[0][0])) { // 2次元配列だった場合
if (!util.truthy(axis)) {
return this.maxIdx(math.flatten(xArr));
} else if (axis === 0) {
xArr = math.matrix.util.colToRow(xArr);
let r = [];
xArr.forEach((val, idx, arr) => {
r[idx] = this.maxIdx(val);
});
return r;
} else if (axis === 1) {
let r = [];
xArr.forEach((val, idx, arr) => {
r[idx] = this.maxIdx(val);
});
return r;
}
} else { // 3次元以上の配列だった場合
if (!util.truthy(axis)) {
return this.maxIdx(math.flatten(xArr));
} else if (axis === 0) {
xArr = math.matrix.util.colToRow(xArr);
let r = [];
xArr.forEach((val, idx, arr) => {
r[idx] = this.maxIdx(val, 0);
});
return r;
} else if (axis === 1) {
let r = [];
xArr.forEach((val, idx, arr) => {
val = math.matrix.util.colToRow(val);
r[idx] = this.maxIdx(val, 1);
});
return r;
} else if (axis === 2) {
let r = [];
xArr.forEach((val, idx, arr) => {
r[idx] = this.maxIdx(val, 1);
});
return r;
}
}
},
/**
* # 2つの配列の同じインデックスの要素の値が同じだった場合に、そのカウント数を取得する
* @param x
* @param y
* @returns {number}
*/
matchCount(x, y) {
if (!util.isArray(x[0])) { // 1次元配列だった場合
let count = 0;
x.forEach((val, idx, arr) => {
if (val === y[idx]) {
count += 1;
}
});
return count;
} else { // 2次元以上の配列だった場合
let r = [];
x.forEach((val, idx, arr) => {
r[idx] = this.matchCount(val, y[idx]);
});
return r;
}
},
/**
* # 真偽値を数値1か0に変換する
* @param x
* @returns {Array}
*/
boolToInt(x) {
if (!util.isArray(x)) {
return !util.truthy(x) ? 0 : 1;
}
if (!util.isArray(x[0])) { // 1次元配列だった場合
let r = [];
x.forEach((val, idx, arr) => {
if (!util.truthy(val)) {
r[idx] = 0;
} else {
r[idx] = 1;
}
});
return r;
} else { // 2次元以上の配列だった場合
let r = [];
x.forEach((val, idx, arr) => {
r[idx] = this.boolToInt(val);
});
return r;
}
},
/**
* # 真偽値を任意の値に変換する
* @param x
* @param callback
* @param y
* @returns {Array}
*/
valToAnyVal(x, callback, y) {
if (!util.isArray(x)) {
return callback(x, util.truthy(y) && y);
}
if (!util.isArray(x[0])) { // 1次元配列だった場合
let r = [];
x.forEach((val, idx, arr) => {
r[idx] = callback(val, util.truthy(y) && util.truthy(y[idx]) && y[idx], idx, arr);
});
return r;
} else { // 2次元以上の配列だった場合
let r = [];
x.forEach((val, idx, arr) => {
r[idx] = this.valToAnyVal(val, callback, util.truthy(y) && util.truthy(y[idx]) && y[idx]);
});
return r;
}
},
/**
* # 累乗する
* @param x
* @param n
* @returns {*}
*/
pow(x, n) {
return math.util.caluclate(x, n, math.calc.pow);
},
/**
* # Ex を返す.(自然対数の底であるネイピア数(オイラー数))
* @param x
* @returns {*}
*/
exp(x) {
if (!util.isArray(x)) { // スカラー値の場合
return math.calc.exp(x);
} else if (!util.isArray(x[0])) { // 1次元配列だった場合
let r = [];
x.forEach((val, idx, arr) => {
r[idx] = math.calc.exp(val);
});
return r;
} else { // 2次元以上の配列だった場合
let r = [];
x.forEach((val, idx, arr) => {
r[idx] = this.exp(val);
});
return r;
}
},
/**
* # 自然対数 (底は e) を返す.
* @param x
* @returns {*}
*/
log(x) {
if (!util.isArray(x)) { // スカラー値の場合
return math.calc.log(x);
} else if (!util.isArray(x[0])) { // 1次元配列だった場合
let r = [];
x.forEach((val, idx, arr) => {
r[idx] = math.calc.log(val);
});
return r;
} else { // 2次元以上の配列だった場合
let r = [];
x.forEach((val, idx, arr) => {
r[idx] = this.log(val);
});
return r;
}
},
/**
* # 一番大きい数値を返す
* @param xArr
* @param axis
* @returns {number}
*/
max(xArr, axis) {
if (!util.isArray(xArr[0])) { // 1次元配列だった場合
return Math.max.apply(null, xArr);
} else if (!util.isArray(xArr[0][0])) { // 2次元配列だった場合
if (!util.truthy(axis)) {
return this.max(math.flatten(xArr));
} else if (axis === 0) {
xArr = math.matrix.util.colToRow(xArr);
let r = [];
xArr.forEach((val, idx, arr) => {
r[idx] = this.max(val);
});
return r;
} else if (axis === 1) {
let r = [];
xArr.forEach((val, idx, arr) => {
r[idx] = this.max(val);
});
return r;
}
} else { // 3次元以上の配列だった場合
if (!util.truthy(axis)) {
return this.max(math.flatten(xArr));
} else if (axis === 0) {
xArr = math.matrix.util.colToRow(xArr);
let r = [];
xArr.forEach((val, idx, arr) => {
r[idx] = this.max(val, 0);
});
return r;
} else if (axis === 1) {
let r = [];
xArr.forEach((val, idx, arr) => {
val = math.matrix.util.colToRow(val);
r[idx] = this.max(val, 1);
});
return r;
} else if (axis === 2) {
let r = [];
xArr.forEach((val, idx, arr) => {
r[idx] = this.max(val, 1);
});
return r;
}
}
},
/**
* # 大きい方の値を返す
* @param x
* @param y
* @returns {*}
*/
maximum(x, y) {
return math.util.caluclate(x, y, math.calc.max);
},
/**
* # 多次元配列を1次元配列にする
* @param x
* @returns {*}
*/
flatten(x) {
if (!util.isArray(x[0])) { // 1次元配列だった場合
return Array.prototype.concat.apply([], x);;
} else { // 2次元以上の配列だった場合
let r = [];
x.forEach((val, idx, arr) => {
r[idx] = this.flatten(val);
});
return Array.prototype.concat.apply([], r);
}
},
/**
* # 行列の内積を計算
* @param x
* @param y
*/
dot(x, y) {
function Init () {
let r = [];
return {
getRow(x, idx) {
return x[idx];
},
getRowElem(row, idx) {
return row[idx];
},
getCol(y, idx) {
return math.matrix.util.colToRow(y)[idx];
},
getColElem(col, idx) {
return col[idx];
},
setResultRowElem(r, rowIdx, colIdx, val) {
if (!util.truthy(r[rowIdx])) {
r[rowIdx] =[];
}
return r[rowIdx][colIdx] = val;
},
getResult() {
return r;
}
};
}
if (!util.isArray(x) && !util.isArray(y)) { // x, yがスカラ値なら
return math.multi(x, y);
}
if ((math.util.shape(x)[1] === 0) && (math.util.shape(y)[1] === 0)) { // x, yが1次元なら、
if (x.length === y.length) {
return math.sum(math.multi(x, y));
}
throw new Error('x, yそれぞれの配列の要素数が違うため計算できません.');
}
let array1dYflag = false;
let array2dYflag = false;
let array1dXflag = false;
let array2dXflag = false;
if (math.util.shape(x)[1] === 0) { // xが1次元ならブロードキャスト
let r = [];
y[0].forEach((val, idx, arr) => {
r[idx] = [];
y.forEach((_val, _idx, _arr) => {
r[idx][_idx] = x[_idx];
});
});
x = r;
array1dXflag = true;
}
if (math.util.shape(x)[0] === 1) { // xが2次元で要素が1つなら
let r = [];
y[0].forEach((val, idx, arr) => {
r[idx] = [];
y.forEach((_val, _idx, _arr) => {
r[idx][_idx] = x[0][_idx];
});
});
x = r;
array2dXflag = true;
}
if (math.util.shape(y)[1] === 0) { // yが1次元ならブロードキャスト
let r = [];
x[0].forEach((val, idx, arr) => {
r[idx] = [];
x.forEach((_val, _idx, _arr) => {
r[idx][_idx] = y[idx];
});
});
y = r;
array1dYflag = true;
}
if (math.util.shape(y)[1] === 1) { // yが2次元で1次元目の要素が1つなら
let r = [];
x[0].forEach((val, idx, arr) => {
r[idx] = [];
x.forEach((_val, _idx, _arr) => {
r[idx][_idx] = y[idx][0];
});
});
y = r;
array2dYflag = true;
}
// 内積の計算を実行
const calc = Init();
let xRow = [];
for (let i = 0, l = x.length; i < l; i += 1) {
xRow[i] = calc.getRow(x, i);
let yCol = [];
for (let j = 0, m = math.matrix.util.colToRow(y).length; j < m; j += 1) {
yCol[j] = calc.getCol(y, j);
calc.setResultRowElem(calc.getResult(), i, j, math.sum(math.multi(xRow[i], yCol[j])));
}
}
let r = calc.getResult();
// 結果を加工
if (util.truthy(array1dXflag)) { // xが1次元なら
r = r[0];
array1dXflag = false;
}
if (util.truthy(array2dXflag)) { // xが2次元で要素が1つなら
r = [r[0]];
array2dXflag = false;
}
if (util.truthy(array1dYflag)) { // yが1次元なら
r = math.matrix.util.colToRow(r)[0];
array1dYflag = false;
}
if (util.truthy(array2dYflag)) { // yが2次元で1次元目の要素が1つなら
r.forEach((val, idx, arr) => {
r[idx] = [val[0]];
});
array2dYflag = false;
}
return r;
}
};
return {
util: util,
math: math
};
};