ARBS / testing /bigcalc.html
CLIWorks's picture
Upload folder using huggingface_hub
d8bc908 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Big Number Calculator</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; display: flex; justify-content: center; padding: 20px; }
.calc { background: #fff; border-radius: 12px; box-shadow: 0 2px 20px rgba(0,0,0,.1); padding: 30px; max-width: 720px; width: 100%; }
h1 { font-size: 24px; margin-bottom: 8px; color: #222; }
p.sub { color: #666; font-size: 14px; margin-bottom: 20px; }
.field { margin-bottom: 16px; }
.field label { display: block; font-weight: 600; font-size: 13px; color: #444; margin-bottom: 4px; }
.field textarea { width: 100%; padding: 10px 12px; font-size: 15px; font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; border: 1px solid #ccc; border-radius: 6px; resize: vertical; min-height: 40px; }
.field textarea:focus { outline: none; border-color: #4a90d9; box-shadow: 0 0 0 2px rgba(74,144,217,.2); }
.row { display: flex; gap: 12px; align-items: center; }
.row .field { flex: 1; margin-bottom: 12px; }
.precision { width: 80px; padding: 8px 10px; font-size: 14px; border: 1px solid #ccc; border-radius: 6px; text-align: center; }
.precision:focus { outline: none; border-color: #4a90d9; }
.ops { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin: 16px 0; }
.ops button { padding: 12px; font-size: 15px; font-weight: 600; border: 1px solid #ddd; border-radius: 6px; background: #fafafa; cursor: pointer; transition: all .15s; }
.ops button:hover { background: #e8f0fe; border-color: #4a90d9; }
.ops button:active { background: #d0e2fc; }
.ops button.op-add { background: #d4edda; border-color: #b7dfb9; }
.ops button.op-sub { background: #f8d7da; border-color: #f5c6cb; }
.ops button.op-mul { background: #d1ecf1; border-color: #bee5eb; }
.ops button.op-div { background: #fff3cd; border-color: #ffeaa7; }
.ops button.op-pow { background: #e2d1f9; border-color: #d0b8f0; }
.ops button.op-root { background: #fce4d6; border-color: #f8cbad; }
.ops button.op-fact { background: #ffd6e0; border-color: #ffb3c6; }
.result-label { font-weight: 600; font-size: 13px; color: #444; margin-bottom: 4px; }
.result { background: #f9f9fb; border: 1px solid #e0e0e0; border-radius: 6px; padding: 14px 16px; font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; font-size: 14px; line-height: 1.5; word-break: break-all; white-space: pre-wrap; min-height: 50px; max-height: 300px; overflow-y: auto; }
.result .error { color: #d32f2f; }
.result .answer { color: #1a1a1a; }
.result .meta { color: #888; font-size: 12px; margin-top: 4px; }
@media (max-width: 600px) { .ops { grid-template-columns: repeat(3, 1fr); } .calc { padding: 16px; } }
</style>
</head>
<body>
<div class="calc">
<h1>Big Number Calculator</h1>
<p class="sub">Integers, decimals, and scientific notation (e.g. 3.5e19). Arbitrary precision.</p>
<div class="row">
<div class="field">
<label for="x">X</label>
<textarea id="x" rows="2" placeholder="Enter first number..."></textarea>
</div>
<div class="field" id="y-field">
<label for="y">Y</label>
<textarea id="y" rows="2" placeholder="Enter second number..."></textarea>
</div>
</div>
<div class="row" style="margin-bottom: 8px;">
<label style="font-weight:600;font-size:13px;color:#444;margin-right:8px;">Precision:</label>
<input id="precision" class="precision" type="number" value="20" min="0" max="1000">
<span style="font-size:13px;color:#666;">digits after decimal</span>
</div>
<div class="ops">
<button class="op-add" onclick="calc('add')">X + Y</button>
<button class="op-sub" onclick="calc('sub')">X − Y</button>
<button class="op-mul" onclick="calc('mul')">X × Y</button>
<button class="op-div" onclick="calc('div')">X / Y</button>
<button class="op-pow" onclick="calc('pow')">X<sup>Y</sup></button>
<button class="op-root" onclick="calc('sqrt')">√X</button>
<button onclick="calc('square')"></button>
<button class="op-fact" onclick="calc('fact')">X!</button>
<button onclick="calc('mod')">X MOD Y</button>
<button onclick="calc('gcd')">GCD</button>
<button onclick="calc('lcm')">LCM</button>
</div>
<div class="result-label">Result</div>
<div id="result" class="result">Enter numbers and click an operation.</div>
</div>
<script>
// ======== Big Number Calculator ========
// Uses BigInt for integer precision. Tracks decimal places for decimal arithmetic.
// Supports: integers, decimals, scientific notation (e.g. 1.5e3, 2E18)
function parseNumber(str) {
str = (str || '').trim().replace(/\s/g, '');
if (!str) return null;
let sign = 1;
if (str[0] === '-') { sign = -1; str = str.slice(1); }
else if (str[0] === '+') { str = str.slice(1); }
if (!str || str === '.') return null;
// Scientific notation
let exp = 0;
const ei = str.search(/[eE]/);
if (ei >= 0) {
const eStr = str.slice(ei + 1);
if (!eStr || (eStr[0] !== '+' && eStr[0] !== '-' && !/^\d/.test(eStr))) return null;
exp = parseInt(eStr, 10);
if (isNaN(exp)) return null;
str = str.slice(0, ei);
}
if (!str || str === '.') return null;
let intPart = '0', fracPart = '';
const di = str.indexOf('.');
if (di >= 0) {
intPart = str.slice(0, di).replace(/^0+/, '') || '0';
fracPart = str.slice(di + 1).replace(/0+$/, '');
} else {
intPart = str.replace(/^0+/, '') || '0';
}
if (!/^\d+$/.test(intPart) || !/^\d*$/.test(fracPart)) return null;
// Apply exponent: shift decimal point
if (exp > 0) {
const move = Math.min(exp, fracPart.length);
intPart += fracPart.slice(0, move);
fracPart = fracPart.slice(move);
exp -= move;
if (exp > 0) {
intPart += '0'.repeat(exp);
exp = 0;
}
} else if (exp < 0) {
exp = -exp;
const move = Math.min(exp, intPart.length - 1);
fracPart = intPart.slice(-move) + fracPart;
intPart = intPart.slice(0, -move) || '0';
exp -= move;
if (exp > 0) {
fracPart = '0'.repeat(exp) + fracPart;
exp = 0;
}
}
intPart = intPart.replace(/^0+/, '') || '0';
fracPart = fracPart.replace(/0+$/, '');
return { sign, intPart, fracPart };
}
function formatNumber(n) {
if (!n) return '0';
let s = n.sign === -1 ? '-' : '';
s += n.intPart;
if (n.fracPart) s += '.' + n.fracPart;
return s;
}
function toBigInt(n, targetDec) {
// Convert parsed number to BigInt scaled by 10^targetDec
let s = n.intPart + (n.fracPart || '').padEnd(targetDec, '0');
s = s.replace(/^0+/, '') || '0';
return BigInt(s);
}
function fromBigInt(val, decPlaces, precision, sign) {
// Convert BigInt with decimal tracking back to formatted string
if (val === 0n) return '0';
let s = val < 0n ? (-val).toString() : val.toString();
const negative = (val < 0n) || (sign === -1);
if (decPlaces <= 0) {
// Integer result
if (decPlaces < 0) s += '0'.repeat(-decPlaces);
return (!negative ? '' : '-') + s;
}
// Pad with leading zeros if needed
while (s.length <= decPlaces) s = '0' + s;
const ins = s.length - decPlaces;
let intPart = s.slice(0, ins) || '0';
let fracPart = s.slice(ins);
// Round to precision
if (fracPart.length > precision) {
const roundDigit = +fracPart[precision];
fracPart = fracPart.slice(0, precision);
if (roundDigit >= 5) {
// Round up
let bi = BigInt(intPart + fracPart) + 1n;
let newS = bi.toString();
if (newS.length > intPart.length + fracPart.length) {
// Carried over
intPart = newS.slice(0, newS.length - fracPart.length);
fracPart = newS.slice(newS.length - fracPart.length);
} else {
intPart = newS.slice(0, newS.length - fracPart.length) || '0';
fracPart = newS.slice(newS.length - fracPart.length);
}
}
}
fracPart = fracPart.replace(/0+$/, '');
let result = intPart;
if (fracPart) result += '.' + fracPart;
return (!negative ? '' : '-') + result;
}
// Compute effective decimal places from both operands
function getDec(a, b) {
return Math.max((a.fracPart || '').length, (b ? (b.fracPart || '').length : 0));
}
// ======== String-based arithmetic helpers for very large numbers ========
// Used when BigInt can't directly handle the scale
function strCompare(a, b) {
// Compare two digit strings (positive, no leading zeros)
if (a.length !== b.length) return a.length > b.length ? 1 : -1;
return a === b ? 0 : (a > b ? 1 : -1);
}
function strAdd(a, b) {
let carry = 0, r = '';
let i = a.length - 1, j = b.length - 1;
while (i >= 0 || j >= 0 || carry) {
const da = i >= 0 ? +a[i--] : 0;
const db = j >= 0 ? +b[j--] : 0;
const s = da + db + carry;
r = (s % 10) + r;
carry = s / 10 | 0;
}
return r;
}
function strSub(a, b) {
// Requires |a| >= |b|
let borrow = 0, r = '';
let i = a.length - 1, j = b.length - 1;
while (i >= 0) {
const da = +a[i--];
const db = j >= 0 ? +b[j--] : 0;
let d = da - db - borrow;
if (d < 0) { d += 10; borrow = 1; } else borrow = 0;
r = d + r;
}
return r.replace(/^0+/, '') || '0';
}
function strMul(a, b) {
if (a === '0' || b === '0') return '0';
const res = Array(a.length + b.length).fill(0);
for (let i = a.length - 1; i >= 0; i--) {
for (let j = b.length - 1; j >= 0; j--) {
const p = +a[i] * +b[j] + res[i + j + 1];
res[i + j + 1] = p % 10;
res[i + j] += p / 10 | 0;
}
}
return res.join('').replace(/^0+/, '') || '0';
}
function strDivMod(dividend, divisor) {
// Returns { quot, rem }
if (divisor === '0') return null;
let quot = '', rem = '';
for (let i = 0; i < dividend.length; i++) {
rem += dividend[i];
rem = rem.replace(/^0+/, '') || '0';
let count = 0;
while (strCompare(rem, divisor) >= 0) {
rem = strSub(rem, divisor);
count++;
}
quot += count;
}
return { quot: quot.replace(/^0+/, '') || '0', rem: rem || '0' };
}
function gcdStr(a, b) {
while (b !== '0') {
const result = strDivMod(a, b);
if (!result) return null;
a = b;
b = result.rem;
}
return a;
}
// ======== Core operations ========
function opAdd(x, y, prec) {
const dec = Math.max((x.fracPart || '').length, (y.fracPart || '').length);
const a = toBigInt(x, dec);
const b = toBigInt(y, dec);
const r = (x.sign === -1 ? -a : a) + (y.sign === -1 ? -b : b);
return fromBigInt(r, dec, prec);
}
function opSub(x, y, prec) {
const dec = Math.max((x.fracPart || '').length, (y.fracPart || '').length);
const a = toBigInt(x, dec);
const b = toBigInt(y, dec);
const r = (x.sign === -1 ? -a : a) - (y.sign === -1 ? -b : b);
return fromBigInt(r, dec, prec);
}
function opMul(x, y, prec) {
const decX = (x.fracPart || '').length;
const decY = (y.fracPart || '').length;
const a = toBigInt(x, 0);
const b = toBigInt(y, 0);
const r = (x.sign === -1 ? -a : a) * (y.sign === -1 ? -b : b);
return fromBigInt(r, decX + decY, prec);
}
function opDiv(x, y, prec) {
if (y.intPart === '0' && !y.fracPart) return null;
const decX = (x.fracPart || '').length;
const decY = (y.fracPart || '').length;
const scale = decY + prec;
const a = toBigInt(x, scale);
const b = toBigInt(y, 0);
const r = (x.sign === -1 ? -a : a) / b;
const finalDec = scale - decX;
return fromBigInt(r, finalDec, prec, x.sign * y.sign);
}
function opPow(x, y, prec) {
// Y must be a non-negative integer for arbitrary precision
// For decimal Y: use BigInt exponent (large exponents produce astronomical results)
if (y.sign === -1) return '0';
if (y.fracPart) {
// Decimal exponent: use exp(Y * ln(X)) via BigFloat approximation
return opPowDecimal(x, y, prec);
}
// Integer exponent
const expStr = y.intPart;
if (expStr === '0') return '1';
// For large exponents, result can be astronomical. Use string-based pow.
const xStr = formatNumber(x);
const xSign = x.sign;
const xDec = (x.fracPart || '').length;
// Exponentiation by squaring on string-based multiplication
let result = '1';
let base = xStr.replace(/^-/, '');
let e = expStr;
if (strCompare(e, '100000') > 0) {
return 'Result too large (exponent > 100000)';
}
while (e !== '0') {
// Check if e is odd
if (+e[e.length - 1] % 2 === 1) {
// result = result * base (with decimal tracking)
const decB = (result.indexOf('.') >= 0 ? result.split('.')[1].length : 0);
const rClean = result.replace('.', '');
const bDec = (base.indexOf('.') >= 0 ? base.split('.')[1].length : 0);
const bClean = base.replace('.', '');
let prod = strMul(rClean, bClean);
let totalDec = decB + bDec;
while (prod.length <= totalDec) prod = '0' + prod;
const ins = prod.length - totalDec;
result = prod.slice(0, ins) + '.' + prod.slice(ins);
result = result.replace(/0+$/, '');
if (result[result.length - 1] === '.') result = result.slice(0, -1);
result = result.replace(/^0+/, '') || '0';
}
// base = base * base
{
const bDec = (base.indexOf('.') >= 0 ? base.split('.')[1].length : 0);
const bClean = base.replace('.', '');
let sq = strMul(bClean, bClean);
let totalDec = bDec * 2;
while (sq.length <= totalDec) sq = '0' + sq;
const ins = sq.length - totalDec;
base = sq.slice(0, ins) + '.' + sq.slice(ins);
base = base.replace(/0+$/, '');
if (base[base.length - 1] === '.') base = base.slice(0, -1);
base = base.replace(/^0+/, '') || '0';
}
e = strDivMod(e, '2').quot;
}
if (xSign === -1 && expStr !== '0' && expStr[expStr.length - 1] === '1') {
result = '-' + result;
}
// Format result with precision
if (result[0] === '-') {
const r = result.slice(1);
if (r.indexOf('.') >= 0) {
const [ip, fp] = r.split('.');
let out = '-' + ip + '.' + fp.slice(0, prec);
out = out.replace(/0+$/, '');
if (out[out.length - 1] === '.') out = out.slice(0, -1);
return out;
}
return '-' + r;
} else {
if (result.indexOf('.') >= 0) {
const [ip, fp] = result.split('.');
let out = ip + '.' + fp.slice(0, prec);
out = out.replace(/0+$/, '');
if (out[out.length - 1] === '.') out = out.slice(0, -1);
return out;
}
return result;
}
}
function opPowDecimal(x, y, prec) {
// Approximate using ln and exp (not arbitrary precision — falls back to JS number for exponent)
// This is a limiation: decimal exponents use JS floating point
const xVal = parseFloat(formatNumber(x));
const yVal = parseFloat(formatNumber(y));
if (xVal <= 0) return 'Requires X > 0 for decimal exponent';
const result = Math.pow(xVal, yVal);
if (!isFinite(result)) return 'Overflow';
return result.toPrecision(prec + 6).replace(/0+$/, '').replace(/\.$/, '').slice(0, prec + 10);
}
function opSqrt(x, prec) {
if (x.sign === -1) return 'Negative input';
const xDec = (x.fracPart || '').length;
const scale = prec * 2;
const a = toBigInt(x, scale);
const r = bigIntSqrt(a);
return fromBigInt(r, prec, prec, 1);
}
function bigIntSqrt(n) {
if (n < 0n) return null;
if (n === 0n) return 0n;
let x = n;
let y = (x + 1n) >> 1n;
while (y < x) {
x = y;
y = (y + n / y) >> 1n;
}
return x;
}
function opSquare(x, prec) {
return opMul(x, x, prec);
}
function opFact(x, prec) {
if (x.sign === -1) return 'Negative input';
if (x.fracPart) return 'Integer required';
const n = x.intPart;
if (n === '0' || n === '1') return '1';
// Estimate result size — limit to prevent browser hang
const nNum = parseInt(n, 10);
if (nNum > 100000) return 'n > 100000 (result too large)';
let result = '1';
for (let i = 2n; i <= BigInt(n); i++) {
result = strMul(result, i.toString());
// Yield occasionally to keep UI responsive
if (i % 10000n === 0n) {
// Check for cancellation
}
}
return result;
}
function opMod(x, y, prec) {
if (y.intPart === '0' && !y.fracPart) return 'Division by zero';
// MOD is integer operation
const a = BigInt(x.sign === -1 ? '-' + x.intPart : x.intPart);
const b = BigInt(y.sign === -1 ? '-' + y.intPart : y.intPart);
if (b === 0n) return 'Division by zero';
return (a % b).toString();
}
function opGcd(x, y) {
// GCD only on integer parts
const a = x.intPart.replace(/^0+/, '') || '0';
const b = y.intPart.replace(/^0+/, '') || '0';
if (a === '0' && b === '0') return '0';
if (a === '0') return b;
if (b === '0') return a;
return gcdStr(a, b);
}
function opLcm(x, y, prec) {
const a = x.intPart.replace(/^0+/, '') || '0';
const b = y.intPart.replace(/^0+/, '') || '0';
if (a === '0' || b === '0') return '0';
const g = gcdStr(a, b);
if (!g) return 'Error';
// LCM = |a * b| / g
const prod = strMul(a, b);
return strDivMod(prod, g).quot;
}
// ======== UI ========
function showY(show) {
document.getElementById('y-field').style.display = show ? 'block' : 'none';
document.getElementById('y').disabled = !show;
}
let lastOp = '';
function calc(op) {
const resultDiv = document.getElementById('result');
const xStr = document.getElementById('x').value;
const yStr = document.getElementById('y').value;
const prec = parseInt(document.getElementById('precision').value, 10) || 20;
// Single-operand ops
const singleOps = ['sqrt', 'square', 'fact'];
const hideY = singleOps.includes(op);
showY(!hideY);
lastOp = op;
const x = parseNumber(xStr);
if (hideY) {
if (!x) {
resultDiv.innerHTML = '<span class="error">Invalid number X</span>';
return;
}
let result;
switch (op) {
case 'sqrt': result = opSqrt(x, prec); break;
case 'square': result = opSquare(x, prec); break;
case 'fact': result = opFact(x, prec); break;
}
displayResult(result, op, xStr, '', prec);
return;
}
// Two-operand ops
const y = parseNumber(yStr);
if (!x || !y) {
resultDiv.innerHTML = '<span class="error">Invalid number</span>';
return;
}
let result;
switch (op) {
case 'add': result = opAdd(x, y, prec); break;
case 'sub': result = opSub(x, y, prec); break;
case 'mul': result = opMul(x, y, prec); break;
case 'div': result = opDiv(x, y, prec); break;
case 'pow': result = opPow(x, y, prec); break;
case 'mod': result = opMod(x, y, prec); break;
case 'gcd': result = opGcd(x, y); break;
case 'lcm': result = opLcm(x, y, prec); break;
default: result = 'Unknown operation';
}
displayResult(result, op, xStr, yStr, prec);
}
function displayResult(result, op, xStr, yStr, prec) {
const div = document.getElementById('result');
if (result === null) {
div.innerHTML = '<span class="error">Error: division by zero</span>';
return;
}
const opLabels = {
add: 'X + Y', sub: 'X − Y', mul: 'X × Y', div: 'X / Y',
pow: 'X^Y', sqrt: '√X', square: 'X²', fact: 'X!',
mod: 'X MOD Y', gcd: 'GCD', lcm: 'LCM'
};
const digitCount = result.replace(/^-/, '').replace(/\./g, '').length;
const output = String(result).length > 500
? '<span class="answer">' + result.slice(0, 200) + '…' + result.slice(-50) + '</span>'
: '<span class="answer">' + result + '</span>';
div.innerHTML = output +
'<div class="meta">' + opLabels[op] + ' &middot; ' + digitCount + ' digits' +
(String(result).length > 500 ? ' (truncated, ' + String(result).length + ' chars)' : '') + '</div>';
}
// Keyboard shortcut: Enter to recalc last op
document.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
if (lastOp) calc(lastOp);
}
});
</script>
</body>
</html>