81 lines
3.0 KiB
JavaScript
81 lines
3.0 KiB
JavaScript
// REA: 932984 (10^4 정수), 93281c (실수), 4037e8 (FPU Round).
|
||
const EstMath = (() => {
|
||
const components = ["NC", "GC", "JC", "SC"];
|
||
const units = ["NU", "GU", "JU", "SU"];
|
||
const num = (value) => (value == null || value === "" ? 0 : Number(value));
|
||
const sum = (row) => components.reduce((n, key) => n + num(row[key]), 0);
|
||
function even(value) {
|
||
const sign = Math.sign(value),
|
||
a = Math.abs(value),
|
||
i = Math.floor(a),
|
||
f = a - i;
|
||
return sign * (f > 0.5 ? i + 1 : f < 0.5 ? i : i % 2 ? i + 1 : i);
|
||
}
|
||
function half(value) {
|
||
return Math.trunc(value) + Math.trunc((value - Math.trunc(value)) * 2);
|
||
}
|
||
function mode(value, method) {
|
||
if (method === 1) return half(value);
|
||
if (method === 3) return half(value + 0.4);
|
||
return Math.trunc(value);
|
||
}
|
||
function fixed(value, digits = 0, method = 2) {
|
||
const v = even(value * 10000),
|
||
k = 10 ** digits;
|
||
return mode((v * k) / 10000, method) / k;
|
||
}
|
||
function floating(value, digits = 0, method = 2) {
|
||
const k = 10 ** digits;
|
||
// The original keeps x87 extended intermediates. Account for one binary64
|
||
// rounding unit before truncation so exact decimal boundaries survive JS.
|
||
const scaled = value * k;
|
||
return (
|
||
mode(scaled + Math.sign(value) * (2e-10 * k + Number.EPSILON * Math.abs(scaled)), method) / k
|
||
);
|
||
}
|
||
function precision(row, fallback = 0) {
|
||
return num(row.DigetIndex)
|
||
? { digits: Math.max(0, num(row.Diget) - 1), method: num(row.DigetIndex) }
|
||
: { digits: fallback, method: 2 };
|
||
}
|
||
function amounts(row, digits = 0, totalFit = false) {
|
||
if ([1, 3].includes(num(row.CalcIndex))) return row;
|
||
const q = num(row.GN) / (["%", "%"].includes(row.Unite) ? 100 : 1);
|
||
const p = precision(row, digits),
|
||
raw = units.map((key) => num(row[key]) * q);
|
||
components.forEach((key, i) => (row[key] = fixed(raw[i], p.digits, p.method)));
|
||
row.TU = units.reduce((n, key) => n + num(row[key]), 0);
|
||
if (totalFit) {
|
||
const step = 10 ** -p.digits,
|
||
target = fixed(
|
||
raw.reduce((a, b) => a + b, 0),
|
||
p.digits,
|
||
p.method,
|
||
);
|
||
let difference = Math.round((target - sum(row)) / step);
|
||
const order = components
|
||
.map((key, i) => ({ key, remainder: raw[i] - row[key] }))
|
||
.sort((a, b) => (difference >= 0 ? b.remainder - a.remainder : a.remainder - b.remainder));
|
||
for (let i = 0; difference && i < 100; i++) {
|
||
row[order[i % 4].key] += Math.sign(difference) * step;
|
||
difference -= Math.sign(difference);
|
||
}
|
||
}
|
||
row.TC = sum(row);
|
||
return row;
|
||
}
|
||
function add(rows, predicate = (row) => num(row.CalcIndex) < 2) {
|
||
const result = Object.fromEntries(
|
||
components.map((key) => [
|
||
key,
|
||
floating(
|
||
rows.filter(predicate).reduce((n, row) => n + even(num(row[key]) * 10000), 0) / 10000,
|
||
),
|
||
]),
|
||
);
|
||
result.TC = sum(result);
|
||
return result;
|
||
}
|
||
return { components, units, num, sum, even, half, fixed, floating, precision, amounts, add };
|
||
})();
|