'use strict'; /* ────────────────────────────────────────────────────────────────── Answer normalization + correctness check. Same algorithm runs on user input and stored canonical answer (from /tasks endpoint), then compared. Canonical answer forms (produced by backend/scripts/import-exam-tasks.js): MC: single Cyrillic letter: "а" | "б" | "в" | "г" | "д" open NUM: decimal: "-2" "7500" "1.5" "0.25" open FRAC: fraction: "9/4" "-104/9" (sign on numerator) open PAIR: two values, ";" sep: "-2;4" The user can type the answer in many equivalent forms — the normalizer accepts any form and decides equivalence numerically. ────────────────────────────────────────────────────────────────── */ (function () { const EPS = 1e-6; // Try to coerce a string to a number; returns NaN on failure. function toNumber(s) { if (s == null) return NaN; let t = String(s).trim().replace(/\$/g, '').replace(/\s+/g, '').replace(',', '.'); // Fraction "a/b" → a/b const f = t.match(/^(-?\d+(?:\.\d+)?)\s*\/\s*(-?\d+(?:\.\d+)?)$/); if (f) { const num = Number(f[1]); const den = Number(f[2]); if (!Number.isFinite(num) || !Number.isFinite(den) || den === 0) return NaN; return num / den; } const n = Number(t); return Number.isFinite(n) ? n : NaN; } // Parse a "pair" answer "A;B" — accepts ";" or "," or " и " or whitespace function toPair(s) { if (s == null) return null; const t = String(s).trim().replace(/\$/g, '').replace(/\s+и\s+/g, ';'); const parts = t.split(/[;,]/).map(p => p.trim()).filter(Boolean); if (parts.length !== 2) return null; const a = toNumber(parts[0]); const b = toNumber(parts[1]); if (Number.isNaN(a) || Number.isNaN(b)) return null; // Order-insensitive comparison: return sorted pair return a <= b ? [a, b] : [b, a]; } /* Detect form of the canonical answer. Returns: 'mc' | 'pair' | 'numeric' */ function detectForm(canonical) { if (canonical == null) return 'numeric'; const t = String(canonical).trim(); if (/^[а-д]$/.test(t)) return 'mc'; if (/^[^;]+;[^;]+$/.test(t)) return 'pair'; return 'numeric'; } /* Main check. Returns true iff user input matches canonical answer. False if either side can't be normalized. Both inputs are strings as returned by the form or stored in DB. */ function check(userInput, canonical) { if (userInput == null || canonical == null) return false; const form = detectForm(canonical); if (form === 'mc') { const u = String(userInput).trim().toLowerCase(); return u === String(canonical).trim().toLowerCase(); } if (form === 'pair') { const cp = toPair(canonical); const up = toPair(userInput); if (!cp || !up) return false; return Math.abs(cp[0] - up[0]) < EPS && Math.abs(cp[1] - up[1]) < EPS; } // Numeric (integer / decimal / fraction) const c = toNumber(canonical); const u = toNumber(userInput); if (Number.isNaN(c) || Number.isNaN(u)) return false; return Math.abs(c - u) < EPS; } window.EP = window.EP || {}; window.EP.answer = { check, detectForm, toNumber, toPair }; })();