/*
 * Copyright (C) 2026 filifa
 *
 * License: 0BSD (https://spdx.org/licenses/0BSD.html)
 */
function sievePrimes(n) {
	const isPrime = Array(n);

	isPrime[0] = false;
	isPrime[1] = false;
	for (let i = 2; i < n; i++) {
		isPrime[i] = true;
	}

	for (let i = 2; i < Math.sqrt(n); i++) {
		if (!isPrime[i]) {
			continue;
		}

		for (let j = i * i; j < n; j += i) {
			isPrime[j] = false;
		}
	}

	return isPrime;
}

function numDivisors(p, k) {
	return k + 1;
}

function sumDivisors(p, k) {
	return (p**(k+1) - 1) / (p - 1)
}

function totient(p, k) {
	return p**(k-1) * (p - 1);
}

function radical(p, k) {
	return p;
}

function littleOmega(p, k) {
	return 1;
}

function bigOmega(p, k) {
	return k;
}

function mobius(p, k) {
	if (k === 1) {
		return -1;
	}

	return 0;
}

function initArrays(l, r, additive) {
	const sieve = Array(r - l);
	const quo = Array(r - l);
	for (let i = 0; i < r-l; i++) {
		quo[i] = l + i;
		if (additive) {
			sieve[i] = 0;
		} else {
			sieve[i] = 1;
		}
	}

	return [sieve, quo];
}

function sieveSegment(l, r, f, primes, additive) {
	const [sieve, quo] = initArrays(l, r, additive);

	for (let i = 0; i < primes.length; i++) {
		if (i*i > r) {
			break;
		}

		if (!primes[i]) {
			continue;
		}

		sievePowers(i, l, r, f, sieve, quo, additive);
	}

	unusuals(f, sieve, quo, additive);

	return sieve;
}

function unusuals(f, sieve, quo, additive) {
	for (let i = 0; i < quo.length; i++) {
		const q = quo[i];
		if (q === 1) {
			continue;
		}

		if (additive) {
			sieve[i] += f(q, 1);
		} else {
			sieve[i] *= f(q, 1);
		}
	}
}

function sievePowers(p, l, r, f, sieve, quo, additive) {
	let i = 1;
	for (let q = p; q < r; q *= p) {
		const y = f(p, i);
		sieveMultiples(p, q, l, r, y, sieve, quo, additive);
		i++;
	}
}

function sieveMultiples(p, q, l, r, y, sieve, quo, additive) {
	const low = Math.ceil(l / q);
	for (let i = low * q; i < r; i += q) {
		if (i % (p*q) === 0) {
			continue;
		}

		if (additive) {
			sieve[i - l] += y;
		} else {
			sieve[i - l] *= y;
		}

		quo[i - l] /= q
	}
}

function getFunc(func) {
	switch (func.value) {
		case "num-divisors":
			return [numDivisors, false];
		case "sum-divisors":
			return [sumDivisors, false];
		case "totient":
			return [totient, false];
		case "mobius":
			return [mobius, false];
		case "radical":
			return [radical, false];
		case "little-omega":
			return [littleOmega, true];
		case "big-omega":
			return [bigOmega, true];
		default:
			throw new Error("function not implemented!");
	}
}

function buildTable(l, r, values) {
	const table = document.createElement("table");
	const caption = document.createElement("caption");
	caption.id = "result-caption";
	caption.textContent = "Results";
	table.appendChild(caption);

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

	const ns = Array(r-l);
	for (let i = l; i < r; i++) {
		ns[i - l] = i;
	}

	const nrow = buildRow("n", l, r, ns);
	tbody.appendChild(nrow);

	const vrow = buildRow("f(n)", l, r, values);
	tbody.appendChild(vrow);

	return table;
}

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

	for (let i = l; i < r; i++) {
		if (i === 0) {
			continue;
		}

		const elem = document.createElement("td");
		elem.textContent = values[i - l];
		row.appendChild(elem);
	}

	return row;
}

function calculate() {
	const lower = document.querySelector("#lower");
	const upper = document.querySelector("#upper");
	const func = document.querySelector("#function");

	const [f, additive] = getFunc(func);

	const l = parseInt(lower.value);
	const r = parseInt(upper.value);
	const s = Math.floor(Math.sqrt(r));
	const primes = sievePrimes(s + 1);

	const sieve = sieveSegment(l, r, f, primes, additive);

	const table = buildTable(l, r, sieve);
	const result = document.querySelector("#result");
	while (result.firstChild) {
		result.removeChild(result.firstChild);
	}
	result.appendChild(table);
}

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