#let title(cont, size: 18pt) = align(center)[ #text(size: size * 2, weight: "bold")[#underline[#cont]] ] // writes a butcher tableau using the minimum required amount of numbers. #let butcher(s, nums) = { let nums = nums.map(x => if type(x) == float or type(x) == int { $#x$ } else { x }) table( stroke: (x, y) => if x == 0 and y == s { (right: 0.7pt + black, top: 0.7pt + black) } else if x == 0 { (right: 0.7pt + black) } else if y == s { (top: 0.7pt + black) }, align: (x, y) => ( if x > 0 { center } else { left } ), columns: s + 1, $0$, ..range(s).map(x => none), // first row ..range(2, s + 1) .map(i => { let p(i) = calc.floor(i * i / 2 + i / 2 - 1) ( nums.slice(p(i - 1), p(i - 1) + i), range(i, s + 1).map(x => none), ).flatten() }) .flatten(), none, ..nums.rev().slice(0, s).rev() // last row ) } // automatically adds aligned "if" and "otherwise" strings to a case block. // // example: // ``` // $ccases(x, x > 0, -x, x <= 0)$ // // is the same as // $cases(x & quad "if" space x > 0, -x & quad "if" space x <= 0$ // // $ccases(x, x > 0, -x)$ // // is the same as // $cases(x & quad "if" space x > 0, -x & quad "otherwise"$ // ``` #let ccases(..args) = { let parsed = args.pos().map(x => if type(x) == content { x } else { $#x$ }) let result_array = parsed .windows(2) .enumerate() .filter(x => calc.rem-euclid(x.first(), 2) == 0) .map(array.last) .map(x => x.intersperse($& quad "if" space$).join()) if calc.rem-euclid(parsed.len(), 2) != 0 { result_array.push($#parsed.last() & quad "otherwise"$) } math.cases(..result_array) };