|
74 | 74 | for (let c = 0; c < M; c++) expected[makeId('multiplier', null, c)] = String(rD[c]); |
75 | 75 |
|
76 | 76 | const ppByCol = []; |
| 77 | + const carryByCol = []; |
77 | 78 |
|
78 | 79 | for (let k = 0; k < M; k++) { |
79 | 80 | const d = rD[k]; |
80 | | - let carry = 0; |
81 | 81 | const pp = new Array(cols).fill(null); |
82 | 82 | const carryRow = new Array(cols).fill(null); |
83 | 83 |
|
84 | 84 | for (let i = 0; i < N; i++) { |
85 | | - const prod = mD[i] * d + carry; |
86 | | - const digit = prod % 10; |
87 | | - const nextCarry = Math.floor(prod / 10); |
| 85 | + const prod = mD[i] * d; |
| 86 | + const onesDigit = prod % 10; |
| 87 | + const tensDigit = Math.floor(prod / 10); |
88 | 88 | const col = i + k; |
89 | | - pp[col] = digit; |
90 | | - if (i < N - 1) { |
91 | | - if (nextCarry > 0) carryRow[col + 1] = nextCarry; |
92 | | - } else if (nextCarry > 0) { |
93 | | - pp[col + 1] = nextCarry; |
| 89 | + pp[col] = onesDigit; |
| 90 | + if (tensDigit > 0) { |
| 91 | + if (i < N - 1) { |
| 92 | + carryRow[col + 1] = tensDigit; |
| 93 | + } else { |
| 94 | + pp[col + 1] = tensDigit; |
| 95 | + } |
94 | 96 | } |
95 | | - carry = nextCarry; |
96 | 97 | } |
97 | 98 |
|
98 | 99 | ppByCol.push(pp); |
| 100 | + carryByCol.push(carryRow); |
99 | 101 |
|
100 | 102 | for (let c = k; c <= N + k; c++) { |
101 | 103 | const key = makeId('pp', k, c); |
|
112 | 114 | for (let c = 0; c < cols; c++) { |
113 | 115 | let columnSum = addCarry; |
114 | 116 | for (let k = 0; k < M; k++) { |
115 | | - const v = ppByCol[k][c]; |
116 | | - if (v !== null) columnSum += v; |
| 117 | + const pv = ppByCol[k][c]; |
| 118 | + if (pv !== null) columnSum += pv; |
| 119 | + const cv = carryByCol[k][c]; |
| 120 | + if (cv !== null) columnSum += cv; |
117 | 121 | } |
118 | 122 | const digit = columnSum % 10; |
119 | 123 | const nextAddCarry = Math.floor(columnSum / 10); |
|
413 | 417 | const rD = digitsOf(state.multiplier, state.M); |
414 | 418 | const a = mD[pair.i]; |
415 | 419 | const b = rD[pair.k]; |
416 | | - let prevCarry = 0; |
417 | | - for (let j = 0; j < pair.i; j++) { |
418 | | - prevCarry = Math.floor((mD[j] * b + prevCarry) / 10); |
419 | | - } |
420 | | - const product = a * b + prevCarry; |
| 420 | + const product = a * b; |
421 | 421 | const correctOnes = product % 10; |
422 | 422 | const correctTens = Math.floor(product / 10); |
423 | 423 |
|
|
428 | 428 | return `Looks like the two digits got swapped — try flipping them.`; |
429 | 429 | } |
430 | 430 |
|
431 | | - const sumValue = a + b + prevCarry; |
| 431 | + const sumValue = a + b; |
432 | 432 | const addOnes = sumValue % 10; |
433 | 433 | const addTens = Math.floor(sumValue / 10); |
434 | 434 | if (typedOnes === addOnes && typedTens === addTens && |
|
450 | 450 | if (!Number.isInteger(typedOnes) || !Number.isInteger(typedTens)) return null; |
451 | 451 |
|
452 | 452 | const c = pair.c; |
453 | | - const ppVals = []; |
| 453 | + const addends = []; |
454 | 454 | for (let k = 0; k < state.M; k++) { |
455 | | - const v = state.expected[makeId('pp', k, c)]; |
456 | | - if (v !== '' && v !== undefined) { |
457 | | - const n = parseInt(v, 10); |
458 | | - if (Number.isInteger(n)) ppVals.push(n); |
| 455 | + const pv = state.expected[makeId('pp', k, c)]; |
| 456 | + if (pv !== '' && pv !== undefined) { |
| 457 | + const n = parseInt(pv, 10); |
| 458 | + if (Number.isInteger(n)) addends.push(n); |
| 459 | + } |
| 460 | + const cv = state.expected[makeId('carry', k, c)]; |
| 461 | + if (cv !== '' && cv !== undefined) { |
| 462 | + const n = parseInt(cv, 10); |
| 463 | + if (Number.isInteger(n)) addends.push(n); |
459 | 464 | } |
460 | 465 | } |
461 | 466 | const addCarryVal = parseInt(state.expected[makeId('addcarry', null, c)] || '0', 10) || 0; |
462 | | - const total = ppVals.reduce((acc, v) => acc + v, 0) + addCarryVal; |
| 467 | + const total = addends.reduce((acc, v) => acc + v, 0) + addCarryVal; |
463 | 468 | const correctOnes = total % 10; |
464 | 469 | const correctTens = Math.floor(total / 10); |
465 | 470 |
|
|
470 | 475 | return `Looks like the two digits got swapped — try flipping them.`; |
471 | 476 | } |
472 | 477 |
|
473 | | - if (ppVals.length >= 2) { |
474 | | - const product = ppVals.reduce((acc, v) => acc * v, 1); |
| 478 | + if (addends.length >= 2) { |
| 479 | + const product = addends.reduce((acc, v) => acc * v, 1); |
475 | 480 | const mulOnes = product % 10; |
476 | 481 | const mulTens = Math.floor(product / 10); |
477 | 482 | if (typedOnes === mulOnes && typedTens === mulTens && |
|
647 | 652 | const mD = digitsOf(state.multiplicand, state.N); |
648 | 653 | const rD = digitsOf(state.multiplier, state.M); |
649 | 654 |
|
650 | | - const carryBefore = (k, i) => { |
651 | | - let c = 0; |
652 | | - for (let j = 0; j < i; j++) { |
653 | | - c = Math.floor((mD[j] * rD[k] + c) / 10); |
654 | | - } |
655 | | - return c; |
656 | | - }; |
657 | | - |
658 | 655 | if (p.type === 'pp') { |
659 | 656 | const k = p.k; |
660 | 657 | const i = p.col - k; |
661 | 658 | if (i < state.N) { |
662 | 659 | const a = mD[i]; |
663 | 660 | const b = rD[k]; |
664 | | - const carry = carryBefore(k, i); |
665 | 661 | const raw = a * b; |
666 | | - const total = raw + carry; |
667 | | - const write = total % 10; |
668 | | - const out = Math.floor(total / 10); |
669 | | - let msg = `${a} × ${b} = ${raw}`; |
670 | | - if (carry > 0) msg += `, plus the carry ${carry} = ${total}`; |
671 | | - msg += `. Write ${write}`; |
| 662 | + const write = raw % 10; |
| 663 | + const out = Math.floor(raw / 10); |
| 664 | + let msg = `${a} × ${b} = ${raw}. Write ${write}`; |
672 | 665 | if (out > 0) { |
673 | 666 | msg += i < state.N - 1 |
674 | | - ? `, and carry ${out} into the next column.` |
675 | | - : `, and put the ${out} in the next column on the left.`; |
| 667 | + ? `, and write ${out} as the carry above the next column.` |
| 668 | + : `, and write ${out} in the next box to the left.`; |
676 | 669 | } else { |
677 | 670 | msg += '.'; |
678 | 671 | } |
679 | 672 | return msg; |
680 | 673 | } else { |
681 | | - const finalCarry = carryBefore(k, state.N); |
682 | | - return `Bring down the carry ${finalCarry} as the next digit.`; |
| 674 | + const lastA = mD[state.N - 1]; |
| 675 | + const lastB = rD[k]; |
| 676 | + const lastProd = lastA * lastB; |
| 677 | + const lastTens = Math.floor(lastProd / 10); |
| 678 | + return `From the last step: ${lastA} × ${lastB} = ${lastProd}. The ${lastTens} (tens digit) goes here.`; |
683 | 679 | } |
684 | 680 | } |
685 | 681 |
|
686 | 682 | if (p.type === 'carry') { |
687 | | - return `That's the carry from the last multiplication. Write it above the next column.`; |
| 683 | + return `That's the tens digit of the last multiplication. Write it above the next column.`; |
688 | 684 | } |
689 | 685 |
|
690 | 686 | if (p.type === 'sum') { |
|
697 | 693 | for (let k = 0; k < state.M; k++) { |
698 | 694 | const v = state.expected[makeId('pp', k, c)]; |
699 | 695 | if (v) { parts.push(v); total += parseInt(v, 10); } |
| 696 | + const cv = state.expected[makeId('carry', k, c)]; |
| 697 | + if (cv) { parts.push(cv); total += parseInt(cv, 10); } |
700 | 698 | } |
701 | 699 | if (parts.length === 0) return 'This column has nothing to add — it stays blank.'; |
702 | 700 | const digit = total % 10; |
|
0 commit comments