const fs = require('fs'); const d = 'storage/app/private/investmoz-v2/'; const read = (f) => JSON.parse(fs.readFileSync(d + f, 'utf8').replace(/^\uFEFF/, '')); const norm = (s) => s .replace(/[\u200b\ufeff]/g, '') .replace(/\s+/g, ' ') .trim() .replace( '2. Visão, objectivos e princípios', '2. Visão, objetivos e princípios', ); const clean = (a) => a .filter((p) => !/(ndice|toc)/i.test(p.style) && norm(p.text)) .map((p) => ({ ...p, text: norm(p.text) })); const a = clean(read('master-paragraphs.json')), b = clean(read('master-paragraphs-reopened.json')); const pairs = []; function row(x0, x1, y0, y1, reverse) { const size = y1 - y0; let prev = new Uint32Array(size + 1), curr = new Uint32Array(size + 1); for (let i = 0; i < x1 - x0; i++) { curr[0] = 0; const text = a[reverse ? x1 - 1 - i : x0 + i].text; for (let j = 1; j <= size; j++) curr[j] = text === b[reverse ? y1 - j : y0 + j - 1].text ? prev[j - 1] + 1 : Math.max(prev[j], curr[j - 1]); [prev, curr] = [curr, prev]; } return prev; } function align(x0, x1, y0, y1) { while (x0 < x1 && y0 < y1 && a[x0].text === b[y0].text) pairs.push([x0++, y0++]); const tail = []; while (x0 < x1 && y0 < y1 && a[x1 - 1].text === b[y1 - 1].text) tail.push([--x1, --y1]); if (x0 < x1 && y0 < y1) { if (x1 - x0 === 1) { for (let j = y0; j < y1; j++) if (a[x0].text === b[j].text) { pairs.push([x0, j]); break; } } else { let mid = (x0 + x1) >> 1; let f = row(x0, mid, y0, y1, false), r = row(mid, x1, y0, y1, true), split = 0, best = -1; for (let j = 0; j <= y1 - y0; j++) if (f[j] + r[y1 - y0 - j] > best) { best = f[j] + r[y1 - y0 - j]; split = j; } align(x0, mid, y0, y0 + split); align(mid, x1, y0 + split, y1); } } pairs.push(...tail.reverse()); } align(0, a.length, 0, b.length); const ma = new Set(pairs.map((p) => p[0])), mb = new Set(pairs.map((p) => p[1])); const removed = a.filter((p, i) => !ma.has(i)), added = b.filter((p, i) => !mb.has(i)); const result = { method: 'LCS paragraph alignment; exclude TOC and empty paragraphs; normalize heading orthography only', matched: pairs.length, removed, added, mapping: pairs.map(([i, j]) => ({ old: a[i].p, current: b[j].p })), }; fs.writeFileSync( d + 'master-aligned-changes.json', JSON.stringify(result, null, 2), ); console.log( JSON.stringify({ matched: pairs.length, removed: removed.length, added: added.length, addedTexts: added.slice(0, 8), removedFirst: removed.slice(0, 4), }), );