diff --git a/frontend/textbooks/algebra_9_ch1.html b/frontend/textbooks/algebra_9_ch1.html
index b1531c6..1b12907 100644
--- a/frontend/textbooks/algebra_9_ch1.html
+++ b/frontend/textbooks/algebra_9_ch1.html
@@ -993,38 +993,512 @@ function buildP2(){
}
function buildP3(){
- const root = document.getElementById('p3-body');
- root.innerHTML = `
-
-
-
-
Содержание параграфа «Сложение и вычитание» будет добавлено в следующих обновлениях.
-
Раздел Phase 1.
-
-
` + secNav('p2', 'p4') + readButton('p3');
- renderMath(root);
+ const box = document.getElementById('p3-body');
+ let html = '';
+
+ html += makeCard('theory', 'С одинаковыми знаменателями', '3.1', `
+ Чтобы сложить (или вычесть) рациональные дроби с одинаковыми знаменателями , надо сложить (вычесть) их числители, а знаменатель оставить прежним:
+ \\[\\dfrac{A}{C} + \\dfrac{B}{C} = \\dfrac{A+B}{C}, \\qquad \\dfrac{A}{C} - \\dfrac{B}{C} = \\dfrac{A-B}{C}, \\quad C \\ne 0.\\]
+ Примеры:
+
+ $\\dfrac{x}{5} + \\dfrac{3}{5} = \\dfrac{x+3}{5}$
+ $\\dfrac{2a}{a-1} - \\dfrac{a+5}{a-1} = \\dfrac{2a - (a+5)}{a-1} = \\dfrac{a-5}{a-1}$ — не забывай менять знаки при вычитании!
+
+ Часто допускаемая ошибка
+ При вычитании всегда заключай числитель вычитаемой дроби в скобки : $\\dfrac{A}{C} - \\dfrac{B}{C} = \\dfrac{A - (B)}{C}$, иначе можно «потерять» минус и получить $A - B$ вместо $A - B_1 + B_2$.
+
`);
+
+ html += makeCard('rule', 'С разными знаменателями · Алгоритм', '3.2', `
+ Если знаменатели разные — приводим дроби к общему знаменателю.
+
+ Разложить каждый знаменатель на множители.
+ Найти НОЗ — наименьший общий знаменатель: произведение всех различных множителей, взятых в наивысших степенях.
+ Привести каждую дробь к НОЗ: умножить числитель и знаменатель на нужный дополнительный множитель .
+ Сложить/вычесть числители (знаменатель — НОЗ).
+ Сократить результат, если возможно.
+
+ Пример НОЗ:
+
+ $\\dfrac{1}{2x}$ и $\\dfrac{1}{3x}$ → НОЗ $= 6x$ (НОК(2,3) $= 6$, $x$ один раз).
+ $\\dfrac{1}{x-2}$ и $\\dfrac{1}{x^2-4}$ → $x^2-4 = (x-2)(x+2)$, НОЗ $= (x-2)(x+2)$.
+ `);
+
+ html += makeCard('example', 'Примеры пошагово', '3.3', `
+ Пример 1. $\\dfrac{1}{x-2} + \\dfrac{1}{x+2}$. НОЗ $= (x-2)(x+2) = x^2 - 4$.
+ \\[\\dfrac{1}{x-2} + \\dfrac{1}{x+2} = \\dfrac{(x+2) + (x-2)}{(x-2)(x+2)} = \\dfrac{2x}{x^2-4}.\\]
+ Пример 2. $\\dfrac{2}{a} - \\dfrac{3}{a^2}$. НОЗ $= a^2$. Дополнительный множитель для $\\dfrac{2}{a}$ равен $a$:
+ \\[\\dfrac{2}{a} - \\dfrac{3}{a^2} = \\dfrac{2a}{a^2} - \\dfrac{3}{a^2} = \\dfrac{2a - 3}{a^2}.\\]
+ Пример 3 (с сокращением). $\\dfrac{1}{x-1} - \\dfrac{1}{x+1} = \\dfrac{(x+1) - (x-1)}{(x-1)(x+1)} = \\dfrac{2}{x^2-1}.$
`);
+
+ /* INTERACTIVE 1 — Конструктор НОЗ */
+ html += `
+
+
Выбери пару знаменателей — увидишь разложение каждого, НОЗ и дополнительные множители.
+
+ Пара №1 / 5
+
+
+
+ Найти НОЗ
+ Скрыть
+
+
+
`;
+
+ /* INTERACTIVE 2 — Калькулятор сложения */
+ html += `
+
+
Введи целые числители $A$ и $B$, выбери знак и знаменатель $C$ — получишь результат.
+
+ $A =$
+
+
+ +
+ −
+
+ $B =$
+
+ над $C =$
+
+ x
+ x+1
+ x-2
+ 2
+ 5
+
+ Вычислить
+
+
+
+
`;
+
+ /* INTERACTIVE 3 — DnD-сортер: какой НОЗ? */
+ html += `
+
+
Перетащи каждую пару знаменателей в нужный ящик: тип НОЗ.
+
+
+
+
Произведение знаменателей
+
+
+
+
Проверить Сначала
+
+
`;
+
+ /* INTERACTIVE 4 — Тренажёр сложения/вычитания */
+ html += `
+
+
Реши пример и введи число, которое спрашивают в подсказке.
+
Задача 1 / 6 Очки: 0 / 6
+
+
+ ответ =
+
+ Проверить
+ Заново
+
+
+
`;
+
+ html += secNav('p2', 'p4');
+ html += readButton('p3');
+
+ box.innerHTML = html;
+ renderMath(box);
+
+ /* IV1 — Конструктор НОЗ */
+ (function(){
+ const PAIRS = [
+ { d1:'x', d2:'3x', fact1:'x', fact2:'3 \\cdot x', lcm:'3x', add1:'3', add2:'1' },
+ { d1:'x-1', d2:'x+1', fact1:'(x-1)', fact2:'(x+1)', lcm:'(x-1)(x+1) = x^2 - 1', add1:'(x+1)', add2:'(x-1)' },
+ { d1:'x-2', d2:'x^2-4', fact1:'(x-2)', fact2:'(x-2)(x+2)', lcm:'(x-2)(x+2)', add1:'(x+2)', add2:'1' },
+ { d1:'2x', d2:'3x', fact1:'2 \\cdot x', fact2:'3 \\cdot x', lcm:'6x', add1:'3', add2:'2' },
+ { d1:'x', d2:'x^2', fact1:'x', fact2:'x \\cdot x', lcm:'x^2', add1:'x', add2:'1' },
+ ];
+ const sl = document.getElementById('p3-iv1-sl');
+ const idx = document.getElementById('p3-iv1-idx');
+ const pEl = document.getElementById('p3-iv1-pair');
+ const out = document.getElementById('p3-iv1-out');
+ const go = document.getElementById('p3-iv1-go');
+ const hide= document.getElementById('p3-iv1-hide');
+ const seen = new Set();
+ function show(){
+ const k = +sl.value; idx.textContent = k;
+ const p = PAIRS[k-1];
+ pEl.innerHTML = 'Знаменатели: $'+p.d1+'$ и $'+p.d2+'$';
+ out.innerHTML = 'Разложение: $'+p.d1+' = '+p.fact1+'$, $'+p.d2+' = '+p.fact2+'$
'
+ + 'НОЗ $= '+p.lcm+'$
'
+ + 'Доп. множители: к $\\dfrac{?}{'+p.d1+'}$ → $'+p.add1+'$; к $\\dfrac{?}{'+p.d2+'}$ → $'+p.add2+'$
';
+ out.style.display = 'none';
+ renderMath(pEl);
+ seen.add(k);
+ if(seen.size === PAIRS.length && !seen.has('done')){ addXp(10,'p3-iv1'); bumpProgress('p3', 15); seen.add('done'); }
+ }
+ sl.addEventListener('input', show);
+ go.addEventListener('click', ()=>{ out.style.display = 'block'; renderMath(out); });
+ hide.addEventListener('click', ()=>{ out.style.display = 'none'; });
+ show();
+ })();
+
+ /* IV2 — Калькулятор A/C ± B/C */
+ (function(){
+ const aI = document.getElementById('p3-iv2-a');
+ const bI = document.getElementById('p3-iv2-b');
+ const opI= document.getElementById('p3-iv2-op');
+ const cI = document.getElementById('p3-iv2-c');
+ const out= document.getElementById('p3-iv2-out');
+ const fb = document.getElementById('p3-iv2-fb');
+ const go = document.getElementById('p3-iv2-go');
+ let solved = 0;
+ function calc(){
+ const a = parseInt(aI.value, 10), b = parseInt(bI.value, 10);
+ const op= opI.value;
+ const c = cI.value;
+ if(isNaN(a) || isNaN(b)){ feedback(fb, false, '✗ Введи целые числа $A$ и $B$.'); return; }
+ const sum = (op === '+') ? a + b : a - b;
+ const numericC = /^\d+$/.test(c);
+ let resHtml;
+ if(sum === 0){
+ resHtml = '$\\dfrac{'+a+'}{'+c+'} '+op+' \\dfrac{'+b+'}{'+c+'} = \\dfrac{0}{'+c+'} = 0$';
+ } else if(numericC){
+ const C = parseInt(c, 10);
+ const g = gcd(Math.abs(sum), C);
+ const n2 = sum/g, d2 = C/g;
+ if(d2 === 1) resHtml = '$\\dfrac{'+a+'}{'+c+'} '+op+' \\dfrac{'+b+'}{'+c+'} = \\dfrac{'+sum+'}{'+c+'} = '+n2+'$';
+ else if(g === 1) resHtml = '$\\dfrac{'+a+'}{'+c+'} '+op+' \\dfrac{'+b+'}{'+c+'} = \\dfrac{'+sum+'}{'+c+'}$';
+ else resHtml = '$\\dfrac{'+a+'}{'+c+'} '+op+' \\dfrac{'+b+'}{'+c+'} = \\dfrac{'+sum+'}{'+c+'} = \\dfrac{'+n2+'}{'+d2+'}$';
+ } else {
+ resHtml = '$\\dfrac{'+a+'}{'+c+'} '+op+' \\dfrac{'+b+'}{'+c+'} = \\dfrac{'+sum+'}{'+c+'}$';
+ }
+ out.innerHTML = resHtml;
+ renderMath(out);
+ feedback(fb, true, '✓ Готово! +10 XP');
+ solved++;
+ if(solved === 1){ addXp(10,'p3-iv2'); bumpProgress('p3', 15); }
+ }
+ go.addEventListener('click', calc);
+ calc();
+ })();
+
+ /* IV3 — DnD сортер НОЗ */
+ (function(){
+ const items = [
+ { id:'s1', cat:'prod', html:'$x$ и $x+1$' },
+ { id:'s2', cat:'one', html:'$x-3$ и $2(x-3)$' },
+ { id:'s3', cat:'prod', html:'$x-1$ и $x+1$' },
+ { id:'s4', cat:'num', html:'$4$ и $6$' },
+ { id:'s5', cat:'one', html:'$x^2$ и $x^3$' },
+ { id:'s6', cat:'one', html:'$x$ и $x^2-x$' },
+ ];
+ const sorter = setupSorter({
+ poolId:'p3-iv3-pool',
+ scopeSelector:'#p3-iv3',
+ items: items,
+ cats:['prod','one','num'],
+ columnLayout:true,
+ });
+ document.getElementById('p3-iv3-check').addEventListener('click', ()=>{
+ const fb = document.getElementById('p3-iv3-fb');
+ const placedCount = items.filter(it => sorter.placed[it.id]).length;
+ const correct = items.filter(it => sorter.placed[it.id] === it.cat).length;
+ if(placedCount < items.length){ feedback(fb, false, '✗ Размести все 6 пар.'); return; }
+ if(correct === items.length){ feedback(fb, true, '✓ Все 6 на месте! +15 XP'); addXp(15,'p3-iv3'); bumpProgress('p3', 25); }
+ else feedback(fb, false, '✗ Правильно ' + correct + ' из 6. Попробуй ещё.');
+ });
+ document.getElementById('p3-iv3-reset').addEventListener('click', ()=>{ sorter.reset(); document.getElementById('p3-iv3-fb').style.display = 'none'; });
+ })();
+
+ /* IV4 — Тренажёр */
+ (function(){
+ const Q = [
+ { q:'$\\dfrac{3}{x} + \\dfrac{5}{x}$', ans:8, prompt:'числитель =', hint:'сложили $3 + 5 = 8$, знаменатель тот же', res:'\\dfrac{8}{x}' },
+ { q:'$\\dfrac{7}{a} - \\dfrac{4}{a}$', ans:3, prompt:'числитель =', hint:'$7 - 4 = 3$', res:'\\dfrac{3}{a}' },
+ { q:'$\\dfrac{1}{x-1} + \\dfrac{1}{x-1}$', ans:2, prompt:'числитель =', hint:'$1 + 1 = 2$', res:'\\dfrac{2}{x-1}' },
+ { q:'$\\dfrac{x}{2} + \\dfrac{x}{3}$', ans:5, prompt:'коэф. при x =', hint:'НОЗ $= 6$: $\\dfrac{3x + 2x}{6} = \\dfrac{5x}{6}$', res:'\\dfrac{5x}{6}' },
+ { q:'$\\dfrac{1}{x} - \\dfrac{1}{x^2}$', ans:2, prompt:'показатель x в знаменателе =', hint:'НОЗ $= x^2$, числитель $= x - 1$', res:'\\dfrac{x-1}{x^2}' },
+ { q:'$\\dfrac{2}{x+1} + \\dfrac{3}{x+1}$', ans:5, prompt:'числитель =', hint:'$2 + 3 = 5$', res:'\\dfrac{5}{x+1}' },
+ ];
+ let i = 0, score = 0;
+ function show(){
+ if(i >= Q.length){
+ document.getElementById('p3-iv4-q').innerHTML = 'Готово! Результат: ' + score + ' / ' + Q.length;
+ document.getElementById('p3-iv4-prompt').textContent = 'ответ =';
+ if(score === Q.length){ addXp(15,'p3-iv4'); bumpProgress('p3', 25); }
+ else if(score >= 4){ addXp(8,'p3-iv4'); bumpProgress('p3', 15); }
+ return;
+ }
+ document.getElementById('p3-iv4-i').textContent = (i+1);
+ document.getElementById('p3-iv4-s').textContent = score;
+ document.getElementById('p3-iv4-q').innerHTML = 'Найди: ' + Q[i].q;
+ document.getElementById('p3-iv4-prompt').textContent = Q[i].prompt;
+ document.getElementById('p3-iv4-ans').value = '';
+ renderMath(document.getElementById('p3-iv4-q'));
+ document.getElementById('p3-iv4-fb').style.display = 'none';
+ }
+ function go(){
+ if(i >= Q.length) return;
+ const fb = document.getElementById('p3-iv4-fb');
+ const ans = parseInt(document.getElementById('p3-iv4-ans').value, 10);
+ if(isNaN(ans)){ feedback(fb, false, '✗ Введи целое число.'); return; }
+ if(ans === Q[i].ans){ score++; feedback(fb, true, '✓ Верно! Ответ: $' + Q[i].res + '$ ('+Q[i].hint+'). Дальше ▶'); }
+ else feedback(fb, false, '✗ Неверно. Должно быть $' + Q[i].ans + '$. Ответ: $' + Q[i].res + '$ ('+Q[i].hint+'). Дальше ▶');
+ document.getElementById('p3-iv4-s').textContent = score;
+ i++;
+ setTimeout(show, 1400);
+ }
+ document.getElementById('p3-iv4-go').addEventListener('click', go);
+ document.getElementById('p3-iv4-ans').addEventListener('keydown', e=>{ if(e.key === 'Enter') go(); });
+ document.getElementById('p3-iv4-start').addEventListener('click', ()=>{ i=0; score=0; show(); });
+ show();
+ })();
+
wireReadBtn('p3');
}
function buildP4(){
- const root = document.getElementById('p4-body');
- root.innerHTML = `
-
-
-
-
Содержание параграфа «Умножение и деление» будет добавлено в следующих обновлениях.
-
Раздел Phase 1.
-
-
` + secNav('p3', 'p5') + readButton('p4');
- renderMath(root);
+ const box = document.getElementById('p4-body');
+ let html = '';
+
+ html += makeCard('theory', 'Умножение дробей', '4.1', `
+ Чтобы умножить две рациональные дроби, надо перемножить их числители и перемножить знаменатели:
+ \\[\\dfrac{A}{B} \\cdot \\dfrac{C}{D} = \\dfrac{A \\cdot C}{B \\cdot D}, \\quad B \\ne 0,\\ D \\ne 0.\\]
+ Чаще удобно сократить общие множители до умножения — это упрощает вычисления.
+ Пример: $\\dfrac{2x}{3} \\cdot \\dfrac{5}{x^2} = \\dfrac{2x \\cdot 5}{3 \\cdot x^2} = \\dfrac{10x}{3x^2} = \\dfrac{10}{3x}$ — сократили $x$.
+ Можно ли «крест-накрест»?
+ При умножении сокращать можно любой множитель числителя с любым множителем знаменателя (даже «крест-накрест»): $\\dfrac{a}{b} \\cdot \\dfrac{b}{c} = \\dfrac{a}{c}$, $b$ ушло.
+
`);
+
+ html += makeCard('rule', 'Деление дробей · Правило', '4.2', `
+ Чтобы разделить одну рациональную дробь на другую, надо первую дробь умножить на обратную ко второй:
+ \\[\\dfrac{A}{B} : \\dfrac{C}{D} = \\dfrac{A}{B} \\cdot \\dfrac{D}{C} = \\dfrac{A \\cdot D}{B \\cdot C}, \\quad B \\ne 0,\\ C \\ne 0,\\ D \\ne 0.\\]
+ Обратная дробь к $\\dfrac{C}{D}$ — это $\\dfrac{D}{C}$ (числитель и знаменатель меняются местами).
+ Условие $C \\ne 0$ возникает потому, что мы делим на дробь $\\dfrac{C}{D}$, а на ноль делить нельзя.
+ Запомни: деление = умножение на перевёрнутую дробь. После этого работаем как с обычным умножением.
`);
+
+ html += makeCard('example', 'Возведение дроби в степень', '4.3', `
+ Чтобы возвести дробь в натуральную степень $n$, надо возвести в эту степень и числитель, и знаменатель:
+ \\[\\left(\\dfrac{A}{B}\\right)^n = \\dfrac{A^n}{B^n}, \\quad B \\ne 0.\\]
+ Примеры:
+
+ $\\left(\\dfrac{2}{x}\\right)^3 = \\dfrac{2^3}{x^3} = \\dfrac{8}{x^3}$
+ $\\left(\\dfrac{x-1}{x+1}\\right)^2 = \\dfrac{(x-1)^2}{(x+1)^2}$ — скобки обязательны!
+ $\\left(\\dfrac{3a}{b^2}\\right)^2 = \\dfrac{9a^2}{b^4}$
+ `);
+
+ /* INTERACTIVE 1 — Умножение с подсветкой */
+ html += `
+
+
Выбери задачу — увидишь произведение дробей. Нажми «Показать шаги», чтобы увидеть сокращение.
+
+ Задача №1 / 5
+
+
+
+ Показать шаги
+ Скрыть
+
+
+
`;
+
+ /* INTERACTIVE 2 — Калькулятор деления a/b : c/d */
+ html += ``;
+
+ /* INTERACTIVE 3 — Найди ошибку */
+ html += `
+
+
Реши каждое преобразование: верно оно или нет. 6 заданий.
+
Задача 1 / 6 Очки: 0 / 6
+
+
+ Верно
+ Ошибка
+
+
+
`;
+
+ /* INTERACTIVE 4 — Тренажёр умножения и деления */
+ html += `
+
+
Вычисли и введи число, которое спрашивают в подсказке.
+
Задача 1 / 6 Очки: 0 / 6
+
+
+ ответ =
+
+ Проверить
+ Заново
+
+
+
`;
+
+ html += secNav('p3', 'p5');
+ html += readButton('p4');
+
+ box.innerHTML = html;
+ renderMath(box);
+
+ /* IV1 — Умножение со скрытием шагов */
+ (function(){
+ const T = [
+ { before:'\\dfrac{3}{x} \\cdot \\dfrac{x}{5}', steps:'\\dfrac{3 \\cdot x}{x \\cdot 5} = \\dfrac{3x}{5x}', after:'\\dfrac{3}{5}', note:'сократили $x$' },
+ { before:'\\dfrac{2a}{b^2} \\cdot \\dfrac{b}{4}', steps:'\\dfrac{2a \\cdot b}{b^2 \\cdot 4} = \\dfrac{2ab}{4b^2}', after:'\\dfrac{a}{2b}', note:'сократили $2b$' },
+ { before:'\\dfrac{x+1}{x-1} \\cdot \\dfrac{x-1}{x+2}', steps:'\\dfrac{(x+1)(x-1)}{(x-1)(x+2)}', after:'\\dfrac{x+1}{x+2}', note:'сократили $(x-1)$' },
+ { before:'\\dfrac{6}{x^2} \\cdot \\dfrac{x}{2}', steps:'\\dfrac{6 \\cdot x}{x^2 \\cdot 2} = \\dfrac{6x}{2x^2}', after:'\\dfrac{3}{x}', note:'сократили $2x$' },
+ { before:'\\dfrac{x-3}{4} \\cdot \\dfrac{8}{(x-3)^2}', steps:'\\dfrac{(x-3) \\cdot 8}{4 \\cdot (x-3)^2} = \\dfrac{8(x-3)}{4(x-3)^2}', after:'\\dfrac{2}{x-3}', note:'сократили $4(x-3)$' },
+ ];
+ const sl = document.getElementById('p4-iv1-sl');
+ const idx = document.getElementById('p4-iv1-idx');
+ const bEl = document.getElementById('p4-iv1-before');
+ const aEl = document.getElementById('p4-iv1-after');
+ const go = document.getElementById('p4-iv1-go');
+ const hide = document.getElementById('p4-iv1-hide');
+ const seen = new Set();
+ function show(){
+ const k = +sl.value; idx.textContent = k;
+ const t = T[k-1];
+ bEl.innerHTML = '$' + t.before + '$';
+ aEl.innerHTML = ''+t.note+'
$' + t.before + ' \\;=\\; ' + t.steps + ' \\;=\\; ' + t.after + '$';
+ aEl.style.display = 'none';
+ renderMath(bEl);
+ seen.add(k);
+ if(seen.size === T.length && !seen.has('done')){ addXp(10,'p4-iv1'); bumpProgress('p4', 15); seen.add('done'); }
+ }
+ sl.addEventListener('input', show);
+ go.addEventListener('click', ()=>{ aEl.style.display = 'block'; renderMath(aEl); });
+ hide.addEventListener('click', ()=>{ aEl.style.display = 'none'; });
+ show();
+ })();
+
+ /* IV2 — Деление калькулятор */
+ (function(){
+ const aI = document.getElementById('p4-iv2-a');
+ const bI = document.getElementById('p4-iv2-b');
+ const cI = document.getElementById('p4-iv2-c');
+ const dI = document.getElementById('p4-iv2-d');
+ const out= document.getElementById('p4-iv2-out');
+ const fb = document.getElementById('p4-iv2-fb');
+ const go = document.getElementById('p4-iv2-go');
+ let solved = 0;
+ function calc(){
+ const a = parseInt(aI.value,10), b = parseInt(bI.value,10),
+ c = parseInt(cI.value,10), d = parseInt(dI.value,10);
+ if([a,b,c,d].some(v => isNaN(v))){ feedback(fb, false, '✗ Введи 4 целых числа.'); return; }
+ if(b === 0 || c === 0 || d === 0){ feedback(fb, false, '✗ $b$, $c$, $d$ не должны быть равны $0$.'); return; }
+ const num = a * d;
+ const den = b * c;
+ const g = gcd(Math.abs(num), Math.abs(den));
+ let n2 = num/g, d2 = den/g;
+ if(d2 < 0){ n2 = -n2; d2 = -d2; }
+ let resHtml;
+ if(d2 === 1) resHtml = '$\\dfrac{'+a+'}{'+b+'} : \\dfrac{'+c+'}{'+d+'} = \\dfrac{'+a+'}{'+b+'} \\cdot \\dfrac{'+d+'}{'+c+'} = \\dfrac{'+num+'}{'+den+'} = '+n2+'$';
+ else if(g === 1) resHtml = '$\\dfrac{'+a+'}{'+b+'} : \\dfrac{'+c+'}{'+d+'} = \\dfrac{'+a+'}{'+b+'} \\cdot \\dfrac{'+d+'}{'+c+'} = \\dfrac{'+num+'}{'+den+'}$ (несократимо)';
+ else resHtml = '$\\dfrac{'+a+'}{'+b+'} : \\dfrac{'+c+'}{'+d+'} = \\dfrac{'+a+'}{'+b+'} \\cdot \\dfrac{'+d+'}{'+c+'} = \\dfrac{'+num+'}{'+den+'} = \\dfrac{'+n2+'}{'+d2+'}$';
+ out.innerHTML = resHtml;
+ renderMath(out);
+ feedback(fb, true, '✓ Готово! +10 XP');
+ solved++;
+ if(solved === 1){ addXp(10,'p4-iv2'); bumpProgress('p4', 15); }
+ }
+ go.addEventListener('click', calc);
+ calc();
+ })();
+
+ /* IV3 — Верно/Ошибка */
+ (function(){
+ const Q = [
+ { expr:'$\\dfrac{x}{2} \\cdot \\dfrac{3}{y} = \\dfrac{3x}{2y}$', ok:true, why:'умножили числители и знаменатели' },
+ { expr:'$\\dfrac{a}{b} : \\dfrac{c}{d} = \\dfrac{ad}{bc}$', ok:true, why:'это правило деления (умножение на обратную)' },
+ { expr:'$\\dfrac{a}{b} \\cdot \\dfrac{c}{d} = \\dfrac{a+c}{b+d}$', ok:false, why:'при умножении числители перемножаются , а не складываются' },
+ { expr:'$\\dfrac{2}{x} \\cdot \\dfrac{x}{2} = 1$', ok:true, why:'сократили $2x$ в числителе и $2x$ в знаменателе' },
+ { expr:'$\\dfrac{a}{b} : \\dfrac{c}{d} = \\dfrac{a}{b} \\cdot \\dfrac{c}{d}$', ok:false, why:'нужно умножать на обратную дробь: $\\dfrac{d}{c}$, а не $\\dfrac{c}{d}$' },
+ { expr:'$\\left(\\dfrac{2}{x}\\right)^2 = \\dfrac{2}{x^2}$', ok:false, why:'числитель тоже возводится в степень: правильно $\\dfrac{4}{x^2}$' },
+ ];
+ let i = 0, score = 0;
+ function show(){
+ if(i >= Q.length){
+ document.getElementById('p4-iv3-q').innerHTML = 'Готово! Результат: ' + score + ' / ' + Q.length;
+ if(score === Q.length){ addXp(15,'p4-iv3'); bumpProgress('p4', 25); }
+ else if(score >= Q.length - 2){ addXp(8,'p4-iv3'); bumpProgress('p4', 15); }
+ return;
+ }
+ document.getElementById('p4-iv3-i').textContent = (i+1);
+ document.getElementById('p4-iv3-s').textContent = score;
+ document.getElementById('p4-iv3-q').innerHTML = Q[i].expr;
+ renderMath(document.getElementById('p4-iv3-q'));
+ document.getElementById('p4-iv3-fb').style.display = 'none';
+ }
+ function answer(isOk){
+ if(i >= Q.length) return;
+ const fb = document.getElementById('p4-iv3-fb');
+ if(isOk === Q[i].ok){ score++; feedback(fb, true, '✓ Верно! '+Q[i].why+'. Дальше ▶'); }
+ else feedback(fb, false, '✗ Неверно. '+Q[i].why+'. Дальше ▶');
+ document.getElementById('p4-iv3-s').textContent = score;
+ i++;
+ setTimeout(show, 1300);
+ }
+ document.getElementById('p4-iv3-ok').addEventListener('click', ()=>answer(true));
+ document.getElementById('p4-iv3-err').addEventListener('click', ()=>answer(false));
+ show();
+ })();
+
+ /* IV4 — Тренажёр умножения/деления */
+ (function(){
+ const Q = [
+ { q:'$\\dfrac{3}{4} \\cdot \\dfrac{8}{9}$', ans:2, prompt:'числитель =', hint:'$=\\dfrac{24}{36} = \\dfrac{2}{3}$', res:'\\dfrac{2}{3}' },
+ { q:'$\\dfrac{x}{2} \\cdot \\dfrac{4}{x^2}$', ans:1, prompt:'показатель x в знам. =', hint:'$=\\dfrac{4x}{2x^2} = \\dfrac{2}{x}$', res:'\\dfrac{2}{x}' },
+ { q:'$\\dfrac{6}{a} : \\dfrac{2}{a}$', ans:3, prompt:'число =', hint:'$=\\dfrac{6}{a} \\cdot \\dfrac{a}{2} = \\dfrac{6a}{2a} = 3$', res:'3' },
+ { q:'$\\dfrac{5}{x+1} \\cdot \\dfrac{x+1}{10}$', ans:2, prompt:'знаменатель =', hint:'сократили $(x+1)$ и $5$: $\\dfrac{1}{2}$', res:'\\dfrac{1}{2}' },
+ { q:'$\\left(\\dfrac{3}{x}\\right)^2$', ans:9, prompt:'числитель =', hint:'$=\\dfrac{3^2}{x^2} = \\dfrac{9}{x^2}$', res:'\\dfrac{9}{x^2}' },
+ { q:'$\\dfrac{a^2}{b} : \\dfrac{a}{b^2}$', ans:1, prompt:'показатель a в ответе =', hint:'$=\\dfrac{a^2}{b} \\cdot \\dfrac{b^2}{a} = \\dfrac{a^2 b^2}{ab} = ab$ — у $a$ степень $1$', res:'ab' },
+ ];
+ let i = 0, score = 0;
+ function show(){
+ if(i >= Q.length){
+ document.getElementById('p4-iv4-q').innerHTML = 'Готово! Результат: ' + score + ' / ' + Q.length;
+ document.getElementById('p4-iv4-prompt').textContent = 'ответ =';
+ if(score === Q.length){ addXp(15,'p4-iv4'); bumpProgress('p4', 25); }
+ else if(score >= 4){ addXp(8,'p4-iv4'); bumpProgress('p4', 15); }
+ return;
+ }
+ document.getElementById('p4-iv4-i').textContent = (i+1);
+ document.getElementById('p4-iv4-s').textContent = score;
+ document.getElementById('p4-iv4-q').innerHTML = 'Вычисли: ' + Q[i].q;
+ document.getElementById('p4-iv4-prompt').textContent = Q[i].prompt;
+ document.getElementById('p4-iv4-ans').value = '';
+ renderMath(document.getElementById('p4-iv4-q'));
+ document.getElementById('p4-iv4-fb').style.display = 'none';
+ }
+ function go(){
+ if(i >= Q.length) return;
+ const fb = document.getElementById('p4-iv4-fb');
+ const ans = parseInt(document.getElementById('p4-iv4-ans').value, 10);
+ if(isNaN(ans)){ feedback(fb, false, '✗ Введи целое число.'); return; }
+ if(ans === Q[i].ans){ score++; feedback(fb, true, '✓ Верно! Ответ: $' + Q[i].res + '$ ('+Q[i].hint+'). Дальше ▶'); }
+ else feedback(fb, false, '✗ Неверно. Должно быть $' + Q[i].ans + '$. Ответ: $' + Q[i].res + '$ ('+Q[i].hint+'). Дальше ▶');
+ document.getElementById('p4-iv4-s').textContent = score;
+ i++;
+ setTimeout(show, 1400);
+ }
+ document.getElementById('p4-iv4-go').addEventListener('click', go);
+ document.getElementById('p4-iv4-ans').addEventListener('keydown', e=>{ if(e.key === 'Enter') go(); });
+ document.getElementById('p4-iv4-start').addEventListener('click', ()=>{ i=0; score=0; show(); });
+ show();
+ })();
+
wireReadBtn('p4');
}