Code viewer for World: Machine Translation Accura...
const style = document.createElement("style");
style.innerHTML = `

  body {
    background: radial-gradient(circle at top, #1c1c1c, #0b0b0b 70%);
    font-family: 'Inter', sans-serif;
    color: #eaeaea;
    padding: 26px;
    margin: 0;
    overflow-x: hidden;
    animation: bgFloat 10s ease-in-out infinite alternate;
  }

  @keyframes bgFloat {
    0% { background-position: 0px 0px; }
    100% { background-position: 20px 20px; }
  }

  h2, h3 {
    font-weight: 700;
    margin-bottom: 12px;
    background: linear-gradient(90deg, #c6cbff, #727bff, #6f9dff);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    text-shadow: 0 0 14px rgba(180,180,255,0.25);
    animation: headerGlow 3s ease-in-out infinite alternate;
  }

  @keyframes headerGlow {
    from { filter: brightness(1); }
    to { filter: brightness(1.4); }
  }

  .card {
    background: rgba(255,255,255,0.06);
    padding: 22px;
    border-radius: 18px;
    border: 1px solid rgba(255,255,255,0.12);
    backdrop-filter: blur(16px);
    box-shadow: 0 10px 28px rgba(0,0,0,0.45);
    margin-bottom: 28px;
    animation: slideFade 0.6s ease forwards;
    opacity: 0;
  }

  @keyframes slideFade {
    from { opacity: 0; transform: translateY(20px) scale(0.98); }
    to { opacity: 1; transform: translateY(0) scale(1); }
  }

  p {
    background: rgba(0,0,0,0.32);
    padding: 14px;
    border-radius: 12px;
    border: 1px solid rgba(255,255,255,0.14);
    font-size: 1.05rem;
    animation: floatText 6s ease-in-out infinite alternate;
  }

  @keyframes floatText {
    from { transform: translateY(0px); }
    to { transform: translateY(-4px); }
  }

  button {
    background: linear-gradient(135deg, #5050ff, #6868ff, #9d78ff);
    padding: 12px 20px;
    color: white;
    border-radius: 12px;
    border: none;
    margin: 8px 6px 0 0;
    font-weight: 600;
    cursor: pointer;
    transition: transform 0.25s ease, box-shadow 0.25s ease;
    box-shadow: 0 0 16px rgba(120,120,255,0.45);
    position: relative;
    overflow: hidden;
  }

  button:hover {
    transform: translateY(-4px);
    box-shadow: 0 0 28px rgba(160,160,255,0.9);
  }

  button:active::after {
    content: "";
    position: absolute;
    left: 50%; top: 50%;
    width: 0; height: 0;
    background: rgba(255,255,255,0.5);
    border-radius: 50%;
    transform: translate(-50%, -50%);
    animation: ripple 0.5s ease-out;
  }

  @keyframes ripple {
    to { width: 180px; height: 180px; opacity: 0; }
  }

  #results {
    background: rgba(255,255,255,0.05);
    padding: 20px;
    border-radius: 16px;
    border: 1px solid rgba(255,255,255,0.2);
    margin-bottom: 28px;
    animation: popIn 0.5s ease forwards;
    opacity: 0;
  }

  @keyframes popIn {
    from { transform: scale(0.9); opacity: 0; }
    to { transform: scale(1); opacity: 1; }
  }

  .highlight {
    background: #ffe457 !important;
    color: black !important;
    box-shadow: 0 0 16px rgba(255,220,80,0.85);
    animation: pulse 1.2s infinite ease-in-out;
  }

  .wrong {
    background: #ff5c5c !important;
    color: white !important;
    box-shadow: 0 0 18px rgba(255,90,90,0.9);
    animation: pulseWrong 1.1s infinite ease-in-out;
  }

  @keyframes pulse {
    0% { transform: scale(1); }
    50% { transform: scale(1.015); }
    100% { transform: scale(1); }
  }
  @keyframes pulseWrong {
    0% { transform: translateY(0); }
    50% { transform: translateY(-2px); }
    100% { transform: translateY(0); }
  }

  #historyTableWrapper {
    max-height: 260px;
    overflow-y: auto;
    background: rgba(0,0,0,0.4);
    border-radius: 12px;
    border: 1px solid rgba(255,255,255,0.06);
  }

  table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.8rem;
  }

  th, td {
    padding: 8px 10px;
    border-bottom: 1px solid rgba(255,255,255,0.06);
    vertical-align: top;
    text-align: left;
  }

  th {
    position: sticky;
    top: 0;
    background: rgba(15,15,15,0.95);
    z-index: 1;
  }

  .tag-ok {
    color: #7cffb2;
    font-weight: 600;
  }

  .tag-bad {
    color: #ff8686;
    font-weight: 600;
  }

  #accuracyChart {
    width: 100%;
    max-width: 100%;
  }

  /* Layout */
  .layout {
    display: flex;
    flex-direction: column;
    gap: 24px;
  }

  .left-column,
  .right-column {
    display: flex;
    flex-direction: column;
    gap: 24px;
  }

  @media (min-width: 960px) {
    .layout {
      display: grid;
      grid-template-columns: minmax(0, 2.1fr) minmax(280px, 1.1fr);
      align-items: flex-start;
      gap: 28px;
    }

    #results {
      margin-bottom: 0;
    }
  }

  .button-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
  }

  .button-row-note {
    font-size: 0.9rem;
    opacity: 0.8;
    margin-top: 6px;
    background: none;
    border: none;
    padding: 0;
    animation: none;
  }

`;
document.head.appendChild(style);


/* API KEYS */
let groq_api_key = "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";
let google_api_key ="AIzaSyBK4kUVcCvrWp7iTqh7oO2YCLIf3Iup0Rs";


/* GLOBAL STATS & HISTORY */
let totalProcessed = 0;
let groqCorrectCount = 0;
let googleCorrectCount = 0;

let historyEntries = []; // per-query history
let accuracyLabels = []; // e.g., ["#1", "#2", ...]
let groqAccuracyHistory = [];
let googleAccuracyHistory = [];

let accuracyChart = null;
let chartJsLoaded = false;


/* DATASET */
const dataset = [
  { english: "Hello, how are you?", french: "Bonjour, comment ça va ?" },
  { english: "What is your name?", french: "Comment vous appelez-vous ?" },
  { english: "I like to eat apples.", french: "J'aime manger des pommes." },
  { english: "She is reading a book.", french: "Elle lit un livre." },
  { english: "We are going to the park.", french: "Nous allons au parc." },
  { english: "He is my brother.", french: "C'est mon frère." },
  { english: "I have a cat.", french: "J'ai un chat." },
  { english: "Can you help me?", french: "Pouvez-vous m'aider ?" },
  { english: "It is raining today.", french: "Il pleut aujourd'hui." },
  {
    english: "Where are you going?",
    french: "Où allez-vous ?",
    accepted: ["Où allez-vous ?", "Où vas-tu ?", "Où vas tu ?", "Où vas-tu ?"]
  },
 
];


/* TRANSLATION FUNCTIONS (Groq + Gemini) */

async function translateWithGroq(text) {
  try {
    const response = await fetch("https://api.groq.com/openai/v1/chat/completions", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${groq_api_key}`,
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        model: "openai/gpt-oss-20b",
        messages: [
          { role: "system", content: "Translate all text to French only." },
          { role: "user", content: text }
        ]
      })
    });
    const data = await response.json();
    return (data.choices?.[0]?.message?.content || "").toString().trim();
  } catch (e) {
    console.warn("Groq call failed", e);
    return "(error calling Groq)";
  }
}

async function translateWithGoogle(text) {
  try {
    const response = await fetch(
      `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:generateContent?key=${google_api_key}`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          contents: [{ parts: [{ text: `Translate this to French only:\n"${text}"` }] }],
          generationConfig: { temperature: 0.0, responseMimeType: "text/plain" }
        })
      }
    );

    const data = await response.json();

    const candidates = data?.candidates || [];
    if (!candidates.length) return "(no response from Gemini)";

    const first = candidates[0];
    const parts = first?.content?.parts || [];
    const textParts = parts.map(p => (p?.text || "").toString().trim()).filter(Boolean);
    const joined = textParts.join(" ").trim();

    const fallback = data?.output?.[0]?.content?.[0]?.text || data?.output?.[0]?.text || "";

    const final = joined || fallback || (candidates[0]?.content?.parts?.[0]?.text || "");
    return final.toString().trim();
  } catch (e) {
    console.warn("Gemini call failed", e);
    return "(error calling Gemini)";
  }
}


/* NORMALIZATION & FUZZY MATCHING */

const SIMILARITY_THRESHOLD = 0.55;

function stripDiacritics(s) {
  return s.normalize("NFD").replace(/\p{Diacritic}/gu, "");
}

function cleanForMatching(s) {
  if (!s && s !== "") return "";
  let out = String(s);

  out = out.replace(/^["'`]+|["'`]+$/g, "");

  // Normalizing unicode & remove diacritics
  out = stripDiacritics(out);

  out = out.toLowerCase();

  // replace smart apostrophes with ascii
  out = out.replace(/\u2019/g, "'");

  out = out.replace(/[^0-9a-z%.'\s\-]/g, " ");

  // collapse runs of whitespace
  out = out.replace(/\s+/g, " ").trim();

  return out;
}

function tokenize(s) {
  const c = cleanForMatching(s);
  if (!c) return [];
  return c.split(" ").map(t => t.trim()).filter(Boolean);
}

function tokenSimilarity(a, b) {
  const ta = Array.from(new Set(tokenize(a)));
  const tb = Array.from(new Set(tokenize(b)));
  if (!ta.length || !tb.length) return 0;

  let intersection = 0;
  const bSet = new Set(tb);
  for (const t of ta) if (bSet.has(t)) intersection++;

  const unionSize = new Set([...ta, ...tb]).size;
  return unionSize ? (intersection / unionSize) : 0;
}

function numericToleranceMatch(generated, reference, tol = 0.12) {
  const numRx = /-?\d+(\.\d+)?/g;
  const genNums = (generated.match(numRx) || []).map(Number);
  const refNums = (reference.match(numRx) || []).map(Number);

  if (!genNums.length || !refNums.length) return null;

  // simple heuristic: if any pair matches within tol fraction, we consider numeric match
  for (const gn of genNums) {
    for (const rn of refNums) {
      if (isFinite(gn) && isFinite(rn)) {
        const diff = Math.abs(gn - rn);
        if (diff <= Math.abs(rn) * tol) return true;
      }
    }
  }
  return false;
}

function isMatch(generated, acceptedArrayOrSingle) {
  const gen = (generated || "").toString();
  const acceptedArray = Array.isArray(acceptedArrayOrSingle)
    ? acceptedArrayOrSingle
    : [acceptedArrayOrSingle];

  const genClean = cleanForMatching(gen);
  for (const a of acceptedArray) {
    const aClean = cleanForMatching(a);
    if (!aClean && !genClean) return true;
    if (aClean === genClean) return true;
  }

  for (const a of acceptedArray) {
    const numericResult = numericToleranceMatch(gen, a);
    if (numericResult === true) return true;
  }

  for (const a of acceptedArray) {
    const sim = tokenSimilarity(gen, a);
    if (sim >= SIMILARITY_THRESHOLD) return true;
  }

  return false;
}


/* TTS + HIGHLIGHT SYSTEM */
let loadedVoices = [];

function loadVoices() {
  return new Promise(res => {
    let v = speechSynthesis.getVoices();
    if (v.length) {
      loadedVoices = v;
      res();
    } else {
      speechSynthesis.onvoiceschanged = () => {
        loadedVoices = speechSynthesis.getVoices();
        res();
      };
    }
  });
}

function getVoice(lang) {
  if (lang === "en-US")
    return loadedVoices.find(v => v.name.includes("Aria")) ||
           loadedVoices.find(v => v.name.includes("Jenny")) ||
           loadedVoices.find(v => v.lang && v.lang.startsWith("en")) ||
           loadedVoices[0];

  if (lang === "fr-FR")
    return loadedVoices.find(v => v.name.includes("Denise")) ||
           loadedVoices.find(v => v.name.includes("Heloise")) ||
           loadedVoices.find(v => v.lang && v.lang.startsWith("fr")) ||
           loadedVoices[0];
}

function normalizeText(s) {
  if (!s) return "";
  return s.normalize("NFC")
          .replace(/\u2019/g,"'")
          .replace(/\s+/g," ")
          .replace(/\s+([?!.,;:])/g,"$1")
          .trim()
          .toLowerCase();
}

async function speak(text, lang, id, isWrong = false) {
  await loadVoices();

  const el = document.getElementById(id);
  if (el) {
    if (isWrong) el.classList.add("wrong");
    else el.classList.add("highlight");
  }

  const utter = new SpeechSynthesisUtterance(text);
  utter.lang = lang;
  utter.voice = getVoice(lang);

  return new Promise(res => {
    utter.onend = async () => {
      if (el) {
        if (isWrong) el.classList.remove("wrong");
        else el.classList.remove("highlight");
      }
      await new Promise(r => setTimeout(r, 700));
      res();
    };
    speechSynthesis.speak(utter);
  });
}

async function autoPlayAll(groqText, googleText, groqCorrect = true, googleCorrect = true) {
  await speak(current.english, "en-US", "engText");
  await speak(current.french, "fr-FR", "frText");
  await speak(groqText, "fr-FR", "groqText", !groqCorrect);
  await speak(googleText, "fr-FR", "googleText", !googleCorrect);
}


/* CHART.JS LOADER & CHART */
function loadChartJs() {
  return new Promise(resolve => {
    if (window.Chart) {
      chartJsLoaded = true;
      return resolve();
    }
    if (chartJsLoaded) return resolve();

    const script = document.createElement("script");
    script.src = "https://cdn.jsdelivr.net/npm/chart.js";
    script.onload = () => {
      chartJsLoaded = true;
      resolve();
    };
    document.head.appendChild(script);
  });
}

function updateChart() {
  if (!chartJsLoaded || !window.Chart) return;
  const canvas = document.getElementById("accuracyChart");
  if (!canvas) return;

  const ctx = canvas.getContext("2d");

  const groqData = groqAccuracyHistory;
  const googleData = googleAccuracyHistory;
  const gapData = groqData.map((v, i) => +(v - (googleData[i] ?? 0)).toFixed(1));

  if (!accuracyChart) {
    accuracyChart = new Chart(ctx, {
      type: "bar",
      data: {
        labels: accuracyLabels,
        datasets: [
          {
            label: "Groq Accuracy (%)",
            data: groqData,
            backgroundColor: "rgba(120, 120, 255, 0.6)",
            borderColor: "rgba(180, 180, 255, 1)",
            borderWidth: 1
          },
          {
            label: "Gemini Accuracy (%)",
            data: googleData,
            backgroundColor: "rgba(255, 130, 180, 0.6)",
            borderColor: "rgba(255, 190, 220, 1)",
            borderWidth: 1
          },
          {
            type: "line",
            label: "Gap (Groq - Gemini)",
            data: gapData,
            borderColor: "rgba(255, 240, 120, 1)",
            borderWidth: 2,
            fill: false,
            tension: 0.25,
            pointRadius: 3
          }
        ]
      },
      options: {
        responsive: true,
        plugins: {
          legend: {
            labels: {
              color: "#eaeaea"
            }
          },
          tooltip: {
            callbacks: {
              label: function(ctx) {
                return `${ctx.dataset.label}: ${ctx.parsed.y}%`;
              }
            }
          }
        },
        scales: {
          x: {
            ticks: { color: "#bbbbff" },
            grid: { color: "rgba(255,255,255,0.05)" }
          },
          y: {
            beginAtZero: true,
            max: 100,
            ticks: { color: "#bbbbff" },
            grid: { color: "rgba(255,255,255,0.06)" }
          }
        }
      }
    });
  } else {
    accuracyChart.data.labels = accuracyLabels;
    accuracyChart.data.datasets[0].data = groqData;
    accuracyChart.data.datasets[1].data = googleData;
    accuracyChart.data.datasets[2].data = gapData;
    accuracyChart.update();
  }
}


/* STATS, SUMMARY & HISTORY RENDER */
function updateStats() {
  const el = document.getElementById("statsText");
  if (!el) return;

  const groqAcc = totalProcessed ? ((groqCorrectCount / totalProcessed) * 100).toFixed(1) : 0;
  const googleAcc = totalProcessed ? ((googleCorrectCount / totalProcessed) * 100).toFixed(1) : 0;

  el.innerHTML = `
    Total processed: ${totalProcessed}<br>
    Groq correct: ${groqCorrectCount} (${groqAcc}%)<br>
    Google correct: ${googleCorrectCount} (${googleAcc}%)<br>
  `;
}

function updateSummary() {
  const el = document.getElementById("summaryText");
  if (!el) return;

  if (!totalProcessed) {
    el.innerHTML = "Run at least one test to see a detailed comparison between Groq and Gemini.";
    return;
  }

  const groqAcc = (groqCorrectCount / totalProcessed) * 100;
  const googleAcc = (googleCorrectCount / totalProcessed) * 100;

  let best;
  if (Math.abs(groqAcc - googleAcc) < 0.01) {
    best = "Both models are currently tied.";
  } else if (groqAcc > googleAcc) {
    best = "Groq is currently performing better overall.";
  } else {
    best = "Google Gemini is currently performing better overall.";
  }

  const overallPairAcc =
    ((groqCorrectCount + googleCorrectCount) / (2 * totalProcessed)) * 100;

  el.innerHTML = `
    <strong>Best model so far:</strong> ${
      groqAcc > googleAcc ? "Groq" : (googleAcc > groqAcc ? "Google Gemini" : "Tie")
    }<br>
    Groq accuracy: ${groqAcc.toFixed(1)}%<br>
    Gemini accuracy: ${googleAcc.toFixed(1)}%<br>
    Overall pair accuracy (both answers combined): ${overallPairAcc.toFixed(1)}%<br>
    ${best}
  `;
}

function truncateText(str, max = 60) {
  if (!str) return "";
  return str.length > max ? str.slice(0, max) + "…" : str;
}

function renderHistory() {
  const wrapper = document.getElementById("historyTableWrapper");
  if (!wrapper) return;

  if (!historyEntries.length) {
    wrapper.innerHTML = `<p style="background:none;border:none;padding:10px;font-size:0.9rem;">
      No queries tested yet. Press <strong>Test APIs</strong> or <strong>Run All</strong> to start logging results.
    </p>`;
    return;
  }

  const rows = historyEntries
    .map((h, i) => {
      return `
        <tr>
          <td>${i + 1}</td>
          <td>${truncateText(h.english, 50)}</td>
          <td>${truncateText(h.reference, 50)}</td>
          <td>${truncateText(h.groqResult, 50)}</td>
          <td>${truncateText(h.googleResult, 50)}</td>
          <td class="${h.groqCorrect ? "tag-ok" : "tag-bad"}">
            ${h.groqCorrect ? "✅" : "❌"}
          </td>
          <td class="${h.googleCorrect ? "tag-ok" : "tag-bad"}">
            ${h.googleCorrect ? "✅" : "❌"}
          </td>
        </tr>
      `;
    })
    .join("");

  wrapper.innerHTML = `
    <table>
      <thead>
        <tr>
          <th>#</th>
          <th>English</th>
          <th>Reference French</th>
          <th>Groq Output</th>
          <th>Gemini Output</th>
          <th>Groq</th>
          <th>Gemini</th>
        </tr>
      </thead>
      <tbody>
        ${rows}
      </tbody>
    </table>
  `;
}


/* UI RENDER */
let index = Math.floor(Math.random() * dataset.length);
let current = dataset[index];

function renderPage() {
  document.body.innerHTML = `
    <div class="layout">
      <!-- LEFT COLUMN: main interaction -->
      <div class="left-column">

        <div class="card">
          <h2>Select Sentence</h2>
          <select id="sentencePicker" style="padding:12px;border-radius:12px;width:100%;background:#111;color:#fff;border:1px solid #444;font-size:1rem;">
            <option value="-1">0. All sentences (use "Run All")</option>
            ${dataset
              .map((d, i) => `<option value="${i}">${i + 1}. ${d.english}</option>`)
              .join("")}
          </select>
        </div>

        <div class="card">
          <h2>English Sentence</h2>
          <p id="engText">${current.english}</p>
          <button id="playEng">🔊 English</button>
        </div>

        <div class="card">
          <h3>Correct French</h3>
          <p id="frText">${current.french}</p>
          <button id="playFr">🔊 French</button>
        </div>

        <div class="card">
          <h3>Groq Translation</h3>
          <p id="groqText">---</p>
          <button id="playGroq">🔊 Groq</button>
        </div>

        <div class="card">
          <h3>Google Gemini Translation</h3>
          <p id="googleText">---</p>
          <button id="playGoogle">🔊 Google</button>
        </div>

        <div class="card">
          <h3>Run Tests</h3>
          <div class="button-row">
            <button id="btnTest">Test APIs</button>
            <button id="btnRunAll">Run All (All Sentences)</button>
          </div>
        </div>

        <div id="results"></div>

        <div class="card">
          <h3>📜 History (per sentence)</h3>
          <div id="historyTableWrapper"></div>
        </div>

      </div>

      <!-- RIGHT COLUMN: stats & analytics -->
      <div class="right-column">
        <div class="card" id="statsCard">
          <h3>📊 Translation Statistics</h3>
          <p id="statsText">
            Total processed: 0 <br>
            Groq correct: 0 (0%)<br>
            Google correct: 0 (0%)<br>
          </p>
        </div>

        <div class="card">
          <h3>🧠 Advanced Model Comparison</h3>
          <p id="summaryText">
            Run at least one test to see a detailed comparison between Groq and Gemini.
          </p>
        </div>

        <div class="card">
          <h3>📈 Accuracy Trend (Groq vs Gemini)</h3>
          <canvas id="accuracyChart" height="210"></canvas>
        </div>
      </div>
    </div>
  `;

  document.querySelectorAll(".card").forEach((c, i) => {
    c.style.animationDelay = (i * 0.12) + "s";
  });

  // button bindings
  document.getElementById("btnTest").onclick = testAPIs;
  document.getElementById("btnRunAll").onclick = runAllTests;

  const picker = document.getElementById("sentencePicker");
  picker.value = index;  // ensure a valid sentence is selected

  picker.onchange = e => {
    const value = Number(e.target.value);
    if (value === -1) {
      e.target.value = index;
      return;
    }
    index = value;
    current = dataset[index];
    renderPage();
  };

  document.getElementById("playEng").onclick = () =>
    speak(current.english, "en-US", "engText");
  document.getElementById("playFr").onclick = () =>
    speak(current.french, "fr-FR", "frText");

  document.getElementById("playGroq").onclick = async () => {
    let t = await translateWithGroq(current.english);
    document.getElementById("groqText").innerText = t;
    const accepted = current.accepted || [current.french];
    const correct = isMatch(t, accepted);
    await speak(t, "fr-FR", "groqText", !correct);
  };

  document.getElementById("playGoogle").onclick = async () => {
    let t = await translateWithGoogle(current.english);
    document.getElementById("googleText").innerText = t;
    const accepted = current.accepted || [current.french];
    const correct = isMatch(t, accepted);
    await speak(t, "fr-FR", "googleText", !correct);
  };

  updateStats();
  updateSummary();
  renderHistory();
  loadChartJs().then(updateChart);
}


/* TEST APIs */
async function testAPIs() {
  const resultsDiv = document.getElementById("results");
  resultsDiv.style.opacity = "1";
  resultsDiv.innerHTML = "<p>Testing APIs... ⏳</p>";

  const groqResult = await translateWithGroq(current.english);
  const googleResult = await translateWithGoogle(current.english);

  document.getElementById("groqText").innerText = groqResult;
  document.getElementById("googleText").innerText = googleResult;

  const accepted = current.accepted || [current.french];

  const groqCorrect = isMatch(groqResult, accepted);
  const googleCorrect = isMatch(googleResult, accepted);

  totalProcessed++;
  if (groqCorrect) groqCorrectCount++;
  if (googleCorrect) googleCorrectCount++;

  const groqAcc = (groqCorrectCount / totalProcessed) * 100;
  const googleAcc = (googleCorrectCount / totalProcessed) * 100;

  accuracyLabels.push(`#${totalProcessed}`);
  groqAccuracyHistory.push(+groqAcc.toFixed(1));
  googleAccuracyHistory.push(+googleAcc.toFixed(1));

  historyEntries.push({
    index,
    english: current.english,
    reference: current.french,
    groqResult,
    googleResult,
    groqCorrect,
    googleCorrect
  });

  updateStats();
  updateSummary();
  renderHistory();
  loadChartJs().then(updateChart);

  resultsDiv.innerHTML = `
    <h3>Groq Translation</h3>
    <p>${groqResult}</p>
    <strong>${groqCorrect ? "✅ Correct" : "❌ Incorrect"}</strong>

    <h3>Google Gemini Translation</h3>
    <p>${googleResult}</p>
    <strong>${googleCorrect ? "✅ Correct" : "❌ Incorrect"}</strong>

    <h2>Total Correct: ${(groqCorrect + googleCorrect)} / 2</h2>
  `;

  autoPlayAll(groqResult, googleResult, groqCorrect, googleCorrect);
}


/* RUN ALL TESTS*/
async function runAllTests() {
  const resultsDiv = document.getElementById("results");
  resultsDiv.style.opacity = "1";
  resultsDiv.innerHTML = "<p>Running all sentences... ⏳</p>";

  totalProcessed = 0;
  groqCorrectCount = 0;
  googleCorrectCount = 0;
  historyEntries = [];
  accuracyLabels = [];
  groqAccuracyHistory = [];
  googleAccuracyHistory = [];

  if (accuracyChart) {
    accuracyChart.destroy();
    accuracyChart = null;
  }

  for (let i = 0; i < dataset.length; i++) {
    const item = dataset[i];
    const accepted = item.accepted || [item.french];

    let groqResult = "";
    let googleResult = "";

    try {
      groqResult = await translateWithGroq(item.english);
    } catch (e) {
      groqResult = "(error calling Groq)";
    }

    try {
      googleResult = await translateWithGoogle(item.english);
    } catch (e) {
      googleResult = "(error calling Gemini)";
    }

    const groqCorrect = isMatch(groqResult, accepted);
    const googleCorrect = isMatch(googleResult, accepted);

    totalProcessed++;
    if (groqCorrect) groqCorrectCount++;
    if (googleCorrect) googleCorrectCount++;

    const groqAcc = (groqCorrectCount / totalProcessed) * 100;
    const googleAcc = (googleCorrectCount / totalProcessed) * 100;

    accuracyLabels.push(`#${totalProcessed}`);
    groqAccuracyHistory.push(+groqAcc.toFixed(1));
    googleAccuracyHistory.push(+googleAcc.toFixed(1));

    historyEntries.push({
      index: i,
      english: item.english,
      reference: item.french,
      groqResult,
      googleResult,
      groqCorrect,
      googleCorrect
    });

    // live updates
    updateStats();
    updateSummary();
    renderHistory();
    await loadChartJs();
    updateChart();

    resultsDiv.innerHTML = `
      <p>Running all sentences... (${totalProcessed} / ${dataset.length})</p>
    `;
  }

  const finalGroqAcc = totalProcessed ? ((groqCorrectCount / totalProcessed) * 100).toFixed(1) : 0;
  const finalGoogleAcc = totalProcessed ? ((googleCorrectCount / totalProcessed) * 100).toFixed(1) : 0;

  resultsDiv.innerHTML = `
    <h3>Run All Complete ✅</h3>
    <p>Processed ${totalProcessed} sentences.</p>
    <p>Groq correct: ${groqCorrectCount} / ${totalProcessed} (${finalGroqAcc}%)</p>
    <p>Gemini correct: ${googleCorrectCount} / ${totalProcessed} (${finalGoogleAcc}%)</p>
  `;
}

renderPage();