// ============================================================
// CONFIGURACIÓN
// ============================================================
const EXTENDED_MODE = true; // true: Múltiplos + Combinaciones (sumas/restas)
const MAX_N = 999_999_999n;
// Precisión interna (30 decimales)
const PI_STR = "3141592653589793238462643383279";
const PI_VAL = BigInt(PI_STR);
const PI_FACTOR = 10n ** 30n;
const OUTPUT_SCALE = 10n ** 16n;
// ============================================================
// LÓGICA MATEMÁTICA
// ============================================================
function getSinEst(p, q) {
let error = (p * PI_FACTOR) - (q * PI_VAL);
// sin(p) ≈ (-1)^q * (p - q*pi)
return (q % 2n === 0n) ? error : -error;
}
function getContinuedFraction(termsCount) {
let terms = [];
let p = PI_VAL, q = PI_FACTOR;
for (let i = 0; i < termsCount; i++) {
let a = p / q;
terms.push(a);
let next_q = p % q;
p = q; q = next_q;
if (q === 0n) break;
}
return terms;
}
function buildCandidates(limitN) {
let cf = getContinuedFraction(50);
let rawData = new Map();
// 1. CONVERGENTES
let p_m2 = 0n, p_m1 = 1n, q_m2 = 1n, q_m1 = 0n;
let convs = [];
for (let a of cf) {
let p = a * p_m1 + p_m2, q = a * q_m1 + q_m2;
convs.push({ p, q, a });
if (p <= limitN) rawData.set(p, { q, source: "convergente" });
if (p > limitN) break;
p_m2 = p_m1; p_m1 = p; q_m2 = q_m1; q_m1 = q;
}
// 2. SEMICONVERGENTES MATEMÁTICOS
for (let i = 1; i < convs.length; i++) {
let pk = convs[i - 1].p, qk = convs[i - 1].q, aNext = convs[i].a;
let pPrev = (i === 1) ? 1n : convs[i - 2].p;
let qPrev = (i === 1) ? 0n : convs[i - 2].q;
for (let t = 1n; t < aNext; t++) {
let p = t * pk + pPrev, q = t * qk + qPrev;
if (p > limitN) break;
if (!rawData.has(p)) rawData.set(p, { q, source: "semiconvergente" });
}
}
// 3. MODO EXTENDIDO (Múltiplos + Combinaciones)
if (EXTENDED_MODE) {
let baseItems = Array.from(rawData.entries()); // [p, {q, source}]
// A. Múltiplos (del 2 al 10)
baseItems.forEach(([p, data]) => {
for (let k = 2n; k <= 10n; k++) {
let np = p * k, nq = data.q * k;
if (np <= limitN && !rawData.has(np)) {
rawData.set(np, { q: nq, source: "múltiplo" });
}
}
});
// B. Combinaciones (Sumas y Restas) de los mejores
// Seleccionamos los 10 candidatos con el error más bajo para combinar
let sortedByError = Array.from(rawData.entries())
.map(([p, d]) => ({ p, q: d.q, err: getSinEst(p, d.q) }))
.sort((a, b) => {
let absA = a.err < 0n ? -a.err : a.err;
let absB = b.err < 0n ? -b.err : b.err;
return absA < absB ? -1 : 1;
})
.slice(0, 10);
for (let i = 0; i < sortedByError.length; i++) {
for (let j = i + 1; j < sortedByError.length; j++) {
let c1 = sortedByError[i];
let c2 = sortedByError[j];
// Probar Suma
let sp = c1.p + c2.p;
let sq = c1.q + c2.q;
if (sp <= limitN && !rawData.has(sp)) {
rawData.set(sp, { q: sq, source: "combinación" });
}
// Probar Resta (valor absoluto)
let rp = c1.p > c2.p ? c1.p - c2.p : c2.p - c1.p;
let rq = c1.q > c2.q ? c1.q - c2.q : c2.q - c1.q;
if (rp > 1n && !rawData.has(rp)) {
rawData.set(rp, { q: rq, source: "combinación" });
}
}
}
}
return Array.from(rawData.entries()).map(([p, d]) => ({
n: p, q: d.q, sinEst: getSinEst(p, d.q), source: d.source
})).sort((a, b) => a.n < b.n ? -1 : 1);
}
// ============================================================
// EJECUCIÓN
// ============================================================
function run() {
const MODE_TEXT = EXTENDED_MODE ? "EXTENDIDO (Total)" : "PURO";
console.log(`\n--- MODO ${MODE_TEXT} ---`);
let candidates = buildCandidates(MAX_N);
let validPairs = [];
let pos = candidates.filter(c => c.sinEst > 0n);
let neg = candidates.filter(c => c.sinEst < 0n);
[pos, neg].forEach(group => {
for (let i = 0; i < group.length; i++) {
for (let j = i + 1; j < group.length; j++) {
let c1 = group[i], c2 = group[j];
let prod = (c1.sinEst * c2.sinEst * OUTPUT_SCALE) / (PI_FACTOR * PI_FACTOR);
if (prod === 0n) {
let valNum = Number(c1.sinEst * c2.sinEst) * 1e16 / 1e60;
validPairs.push({
x1: c1.n.toString(),
x2: c2.n.toString(),
"1e16*sin*sin": valNum.toExponential(10),
floor: 0,
tipo: `${c1.source}, ${c2.source}`
});
}
}
}
});
console.log(`Candidatos analizados: ${candidates.length}`);
console.log(`Pares válidos encontrados: ${validPairs.length}`);
if (validPairs.length > 0) {
console.table(validPairs.sort((a,b) => Number(a.x1) - Number(b.x1)));
}
}
run();