/*
 * Copyright (C) 2026 filifa
 *
 * License: 0BSD (https://spdx.org/licenses/0BSD.html)
 */
let exp = null;
let s = null;

function stirling2(p) {
	if (p === 0) {
		return [1n];
	}

	let s = [0n];
	for (let i = 1; i < p + 1; i++) {
		s.push(1n);
		for (let j = i - 1; j > 0; j--) {
			s[j] = BigInt(j) * s[j] + s[j-1];
		}
	}

	return s;
}

function faulhaber(n, s) {
	const limit = BigInt(s.length);
	if (limit === 1n) {
		return n;
	}

	let q = n + 1n;
	let total = 0n;
	for (let k = 1n; k < limit; k++) {
		q *= n + 1n - k;
		total += s[k] * q / (k + 1n);
	}

	return total;
}

function calculate() {
	const limit = document.querySelector("#limit");
	const exponent = document.querySelector("#exponent");

	const e = parseInt(exponent.value)
	if (exp === null || exp !== e) {
		exp = e;
		s = stirling2(exp);
	}

	const n = BigInt(limit.value);
	const r = faulhaber(n, s);
	const result = document.querySelector("#result");
	result.value = r;
}

function calculateStirling() {
	const limit = document.querySelector("#stirling-limit");
	const p = parseInt(limit.value)
	s = stirling2(p);

	const table = buildTable(s);
	const result = document.querySelector("#stirling-result");
	while (result.firstChild) {
		result.removeChild(result.firstChild);
	}
	result.appendChild(table);
}

function buildTable(values) {
	const table = document.createElement("table");
	const caption = document.createElement("caption");
	caption.textContent = "Stirling numbers S(p,k) from k=0 to k=p";
	table.appendChild(caption);

	const tbody = document.createElement("tbody");
	table.appendChild(tbody);

	const n = values.length;
	const ks = Array(n);
	for (let i = 0; i < n; i++) {
		ks[i] = i;
	}

	const nrow = buildRow("k", ks);
	tbody.appendChild(nrow);

	const vrow = buildRow("S(p,k)", values);
	tbody.appendChild(vrow);

	return table;
}

function buildRow(htext, values) {
	const row = document.createElement("tr");
	const head = document.createElement("th");
	head.textContent = htext;
	row.appendChild(head);

	const n = values.length;
	for (let i = 0; i < n; i++) {
		const elem = document.createElement("td");
		elem.textContent = values[i];
		row.appendChild(elem);
	}

	return row;
}

document.querySelector("#enter").addEventListener("click", () => calculate())
document.querySelector("#stirling-enter").addEventListener("click", () => calculateStirling())
