import isarr from './isarr.mjs'
import isobj from './isobj.mjs'
import isenum from './isenum.mjs'
import optVal from './_optVal.mjs'
//本檔為內部使用, 不由index匯出
/**
* 廣義數字陣列之計算(arrAdd、arrMulti、arrAccum、arrProduct、arrNorm)共用之opt.base與「每個陣列至少1個有效數字」(單一擁有者)
*
* 規格: 主控dspec〈輸入參數之檢測機制〉§11 第221項待決第15項(使用者2026-10-31)
* 基於廣義數字之陣列, 於計算處理時, 僅針對[有效數字類]處理, 非有效數字一律以該函數之基準值opt.base代入(arrAdd 0、arrMulti 1、arrAccum 0、arrProduct 0、arrNorm 0);
* 每個輸入陣列各自至少要有1個有效數字(含空陣列, 皆視為0個), 否則throw Error(`invalid <名稱>: must contain a valid number`), 多陣列帶索引如invalid arrs[1]
* opt.base以isenum判定, 非預期(K3)視為未給、改用預設, opt.debug為true時警告
*
* 規格: 主控dspec〈輸入參數之檢測機制〉§11 第221項第6項(使用者2026-11-03), 空陣列為合法資料, 不因其為空而報錯, 但由陣列算出單一數值者, 空陣列算不出而報錯
* 逐項處理而回陣列者(arrAdd、arrMulti、arrAccum): 空進空出, 空陣列回[](reduceArrs於n=0時回[]; arrAccum自行回[]); 有元素但無有效數字者仍throw
* 由陣列算出單一數值者(arrMax、arrMin、arrNorm、arrProduct): 空陣列throw(arrNorm經chkValid視空陣列為0個有效數字; arrProduct另以chkNotEmpty使任一邊為空即throw; arrMax與arrMin不使用本模組, 各自檢查)
*/
//分離不定參數中的陣列與opt: 最後一個參數為一般物件(非陣列)即為opt; 最後一個參數為undefined或null視為未給opt(呼叫端寫arrAdd(a, b, opt)而opt可能未定義時, 不當成第3個陣列)
function splitArrs(args) {
let arrs = Array.prototype.slice.call(args)
let opt = null
if (arrs.length > 0) {
let last = arrs[arrs.length - 1]
if (last === undefined || last === null) {
arrs.pop()
}
else {
let b = false
try {
b = isobj(last)
}
catch (err) {
b = false
}
if (b) {
opt = arrs.pop()
}
}
}
return { arrs, opt }
}
//取opt.base: 讀取一次(快照), 讀取拋錯視為未給; 非預期改用預設def
function getBase(opt, def, fn) {
let isObjOpt = false
try {
isObjOpt = typeof opt === 'object' && opt !== null && !Array.isArray(opt) //Array.isArray對已撤銷之Proxy會拋錯
}
catch (err) {
isObjOpt = false
}
if (!isObjOpt) {
return def //opt非物件(含未給、讀不到)視為未給, 無從帶debug
}
let v
try {
v = opt.base
}
catch (err) {
v = undefined
}
return optVal(v, def, { fn, name: 'opt.base', kind: 'enum' }, opt)
}
//陣列內是否至少有1個有效數字
function hasValid(arr) {
for (let i = 0; i < arr.length; i++) {
if (isenum(arr[i])) {
return true
}
}
return false
}
//無有效數字之錯誤(訊息不帶輸入值); name為陣列參數名(如'arrs'、'vec'), multi為true時帶索引
function errNoValid(name, i, multi) {
return new Error(`invalid ${name}${multi ? `[${i}]` : ''}: must contain a valid number`)
}
//檢查每個陣列各自至少1個有效數字, 否則throw(空陣列視為0個有效數字, 故亦throw); name為陣列參數名(如'arrs'、'vec'), multi為true時帶索引
function chkValid(arrs, name, multi) {
for (let i = 0; i < arrs.length; i++) {
if (!hasValid(arrs[i])) {
throw errNoValid(name, i, multi)
}
}
}
//由多個陣列算出單一數值者(如arrProduct), 空陣列算不出數值, 任一陣列為空陣列即throw(索引為第一個空陣列); 含非陣列者(容器無效)不處理, 由呼叫端依容器之處置回傳
function chkNotEmpty(arrs, name, multi) {
for (let a of arrs) {
if (!isarr(a)) {
return
}
}
for (let i = 0; i < arrs.length; i++) {
if (arrs[i].length === 0) {
throw errNoValid(name, i, multi)
}
}
}
//n個同長度陣列逐位置運算: 無法運算之容器回傳[](參數少於2個、非陣列、長度不同, 處置不變); 皆為空陣列(長度0)則空進空出回傳[], 先於有效數字之檢查; 有效數字取值為去頭尾空白後Number, 非有效數字以base代入; fun為(累計值, 元素值)=>新累計值, init為累計初始值
function reduceArrs(arrs, opt, fn, base, init, fun) {
//check
if (arrs.length < 2) {
return []
}
for (let a of arrs) {
if (!isarr(a)) {
return []
}
}
let n = arrs[0].length
for (let a of arrs) {
if (a.length !== n) {
return []
}
}
//空進空出, 空陣列為合法資料, 不因其為空而報錯
if (n === 0) {
return []
}
//每個陣列各自至少1個有效數字
chkValid(arrs, 'arrs', true)
//base
let b = getBase(opt, base, fn)
let r = []
for (let k = 0; k < n; k++) {
let t = init
for (let a of arrs) {
let v = a[k]
t = fun(t, isenum(v) ? Number(v) : b)
}
r.push(t)
}
return r
}
export { splitArrs, getBase, chkValid, chkNotEmpty, reduceArrs }