All files / web/src/app/create/worksheets/typstHelpers/subtraction problemStack.ts

100% Statements 129/129
100% Branches 1/1
100% Functions 1/1
100% Lines 129/129

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 1301x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x  
// Main subtraction problem stack function
// Composes all row components into the complete problem rendering
 
import { getPlaceValueColorNames } from '../shared/colors'
import type { CellDimensions } from '../shared/types'
import { generateAnswerBoxesRow, generateLineRow, generateTenFramesRow } from './answerRow'
import { generateBorrowBoxesRow } from './borrowBoxes'
import { generateMinuendRow } from './minuendRow'
import { generateOperatorOverlay } from './operatorOverlay'
import { generateSubtrahendRow } from './subtrahendRow'
 
/**
 * Generate the main subtraction problem stack function for Typst
 *
 * This function composes all the extracted row components into the complete
 * subtraction problem rendering logic. It handles:
 * - Borrow boxes (with optional hints/arrows)
 * - Minuend row (with optional scratch work boxes)
 * - Subtrahend row (with − sign)
 * - Line separator
 * - Ten-frames (optional borrowing visualization)
 * - Answer boxes
 *
 * @param cellSize - Size of each digit cell in inches
 * @param maxDigits - Maximum number of digits supported (default: 3)
 * @returns Typst function definition as string
 */
export function generateSubtractionProblemStackFunction(
  cellSize: number,
  maxDigits: number = 3
): string {
  const cellSizeIn = `${cellSize}in`
  const cellSizePt = cellSize * 72
 
  const cellDimensions: CellDimensions = {
    cellSize,
    cellSizeIn,
    cellSizePt,
  }
 
  const placeColors = getPlaceValueColorNames()
 
  return String.raw`
// Subtraction problem rendering function (supports 1-${maxDigits} digit problems)
// Returns the stack/grid structure for rendering a single subtraction problem
// Per-problem display flags: show-borrows, show-answers, show-colors, show-ten-frames, show-numbers, show-borrow-notation, show-borrowing-hints
#let subtraction-problem-stack(minuend, subtrahend, index-or-none, show-borrows, show-answers, show-colors, show-ten-frames, show-numbers, show-borrow-notation, show-borrowing-hints) = {
  // Place value colors array for dynamic lookup
  let place-colors = (${placeColors.join(', ')})
 
  // Extract digits dynamically based on problem size
  let max-digits = ${maxDigits}
  // Allow one extra digit for potential carry in difference check
  let max-extraction = max-digits + 1
  let m-digits = ()
  for i in range(0, max-extraction) {
    m-digits.push(calc.rem(calc.floor(minuend / calc.pow(10, i)), 10))
  }
  let s-digits = ()
  for i in range(0, max-extraction) {
    s-digits.push(calc.rem(calc.floor(subtrahend / calc.pow(10, i)), 10))
  }
 
  // Find highest non-zero digit position for each number
  let m-highest = 0
  for i in range(0, max-extraction).rev() {
    if m-digits.at(i) != 0 {
      m-highest = i
      break
    }
  }
  let s-highest = 0
  for i in range(0, max-extraction).rev() {
    if s-digits.at(i) != 0 {
      s-highest = i
      break
    }
  }
 
  // Calculate difference to determine answer digit count
  let diff = minuend - subtrahend
  let diff-digits = ()
  for i in range(0, max-extraction) {
    diff-digits.push(calc.rem(calc.floor(diff / calc.pow(10, i)), 10))
  }
  let diff-highest = 0
  for i in range(0, max-extraction).rev() {
    if diff-digits.at(i) != 0 {
      diff-highest = i
      break
    }
  }
 
  // Grid is sized for minuend/subtrahend, not difference
  let grid-digits = calc.max(m-highest, s-highest) + 1
 
  // Answer boxes only show up to difference digits
  let answer-digits = diff-highest + 1
 
  // Generate column list dynamically based on grid digits
  let column-list = (0.5em,)
  for i in range(0, grid-digits) {
    column-list.push(${cellSizeIn})
  }
 
  // Wrap grid in a box to enable place() overlay for operator
  // Note: Problem numbers are now rendered at the problem-box level, not here
  box[
    #grid(
        columns: column-list,
        gutter: 0pt,
 
${generateBorrowBoxesRow(cellDimensions)}
 
${generateMinuendRow(cellDimensions)}
 
${generateSubtrahendRow(cellDimensions)}
 
${generateLineRow(cellDimensions)}
 
${generateTenFramesRow(cellDimensions)}
 
${generateAnswerBoxesRow(cellDimensions)}
      )
${generateOperatorOverlay(cellDimensions)}
    ]
}
`
}