diff --git a/algorithms/binary-exponentiation.typ b/algorithms/binary-exponentiation.typ new file mode 100644 index 0000000..08001df --- /dev/null +++ b/algorithms/binary-exponentiation.typ @@ -0,0 +1,126 @@ +#import "../lib/style.typ": * +#import "../lib/mapcode.typ": * + +== Binary Exponentiation +#set math.equation(numbering: none) + +Compute $"base"^"exp"$ for a non-negative integer exponent. + +Formal definition: +$ +"pow"("base", "exp") &= cases( + 1 & "if " exp = 0, + ("pow"("base", "exp"/2))^2 & "if " "exp" " is even", + "base" * ("pow"("base", "exp"/2))^2 & "if " "exp" " is odd" +) +$ + +Examples: +- $"pow"(2, 10) -> 1024$ +- $"pow"(3, 5) -> 243$ + +*As mapcode:* + +_primitives_: `mult`($*$) is strict. + +$ I = ("base", "exp"): NN times NN quad quad quad X_"exp" &= "Map"(NN -> NN_bot) quad quad quad A = NN\ +rho("base", "exp") & = {e -> bot | e " is a required subproblem for " "exp"}\ +F(x_"exp") & = cases( + 1 & "if " e = 0, + (x_(e/2))^2 & "if " e " is even", + "base" * (x_(e/2))^2 & "if " e " is odd" + )\ + pi(x) & = x_"exp" +$ + + +#let inst_base = 2; +#let inst_exp = 10; +#figure( + caption: [Binary Exponentiation computation using mapcode for $#inst_base^#inst_exp$], +$ +#{ + let get_val = (x, key) => { + let item = x.find(p => p.at(0) == key) + if item != none { + item.at(1) + } else { + none + } + } + + let rho = ((base, exp)) => { + let keys = () + let current_exp = exp + while true { + keys.push(current_exp) + if current_exp == 0 { + break + } + current_exp = calc.floor(current_exp / 2) + } + keys.sorted().map(k => (k, none)) + } + + let F_i = ((base, exp)) => (x) => (elem) => { + let e = elem.at(0) + let val = elem.at(1) + if val != none { + return (e, val) + } + + if e == 0 { + (e, 1) + } else if calc.rem(e, 2) == 0 { + let half_e = calc.floor(e/2); + let half_val = get_val(x, half_e) + if half_val != none { + (e, half_val * half_val) + } else { + (e, none) + } + } else { + let half_e = calc.floor(e/2); + let half_val = get_val(x, half_e) + if half_val != none { + (e, base * half_val * half_val) + } else { + (e, none) + } + } + } + + let F = ((base, exp)) => (x) => { + x.map(elem => F_i((base, exp))(x)(elem)) + } + + let pi_func = ((base, exp)) => (x) => get_val(x, exp) + + let X_h = (base) => (x, diff_mask: none) => { + let base_info = [$"base": #base$] + let cells = x.enumerate().map(((i, p)) => { + let key = p.at(0) + let val = p.at(1) + let val_str = if val != none {[$#val$]} else {[$bot$]} + let cell_content = [$#key -> #val_str$] + if diff_mask != none and diff_mask.at(i).at(1) { + rect(fill: yellow.transparentize(70%), inset: 2pt)[#cell_content] + } else { + rect(stroke: none, inset: 2pt)[#cell_content] + } + }) + stack(dir: ttb, spacing: 0.5em, base_info, $vec(delim: "{", ..cells)$) + } + + mapcode-viz( + rho, F((inst_base, inst_exp)), pi_func((inst_base, inst_exp)), + I_h: ((b,e)) => [$#b^#e$], + X_h: X_h(inst_base), + pi_name: [$pi((#inst_base, #inst_exp))$], + group-size: 2, + cell-size: 30mm, + scale-fig: 95% + )((inst_base, inst_exp)) +} +$ +) diff --git a/algorithms/gcd.typ b/algorithms/gcd.typ new file mode 100644 index 0000000..f6fd5c3 --- /dev/null +++ b/algorithms/gcd.typ @@ -0,0 +1,112 @@ +#import "../lib/style.typ": * +#import "../lib/mapcode.typ": * + +== Greatest Common Divisor (GCD) +#set math.equation(numbering: none) + +Computes the greatest common divisor of two non-negative integers using the Euclidean algorithm. + +Formal definition: +$ +"gcd"(a, b) &= cases( + a & "if " b = 0, + "gcd"(b, a mod b) & "otherwise" +) +$ + +Examples: +- $`gcd`(48, 18) -> 6$ +- $`gcd`(101, 103) -> 1$ + +*As mapcode:* + +_primitives_: `mod` (modulo operator) + +$ +I = ("a", "b"): NN times NN \ +X = "Map"(("a","b") -> NN_bot) \ +A = NN\ +\ +rho("a", "b") & = { ("x","y") -> bot | ("x","y") " is a pair in the GCD chain"}\ +F(x) & = cases( + "a" & "if " "b" = 0, + x_("b", "a" " mod " "b") & "otherwise" +)\ +pi(x) & = x_((a,b)) " where " b=0 +$ + +#let inst_a = 48; +#let inst_b = 18; + +#figure( + caption: [GCD computation using mapcode for $`gcd`(#inst_a, #inst_b)$], +$ +#{ + let get_pairs = (a_start, b_start) => { + let pairs = () + let a = a_start + let b = b_start + while true { + pairs.push((a, b)) + if b == 0 { + break + } + let temp = b + b = calc.rem(a, b) + a = temp + } + pairs + } + + let pairs = get_pairs(inst_a, inst_b) + let num_steps = pairs.len() + + let rho = (inst) => { + let x = () + for i in range(0, num_steps) { + x.push(none) + } + x + } + + let F_i = (x) => ((i,)) => { + if i == num_steps - 1 { + // Base case: gcd(a, 0) = a + let (a, b) = pairs.at(i) + a + } else if x.at(i + 1) != none { + x.at(i + 1) + } else { + none + } + } + let F = map_tensor(F_i, dim: 1) + + let pi_func = (x) => x.at(0) + + let X_h = (x, diff_mask: none) => { + let cells = x.enumerate().map(((i, x_i)) => { + let (a, b) = pairs.at(i) + let val = if x_i != none {[#x_i]} else {[$bot$]} + let cell_content = [$"gcd"(#a, #b) -> #val$] + if diff_mask != none and diff_mask.at(i) { + rect(fill: yellow.transparentize(70%), inset: 2pt)[#cell_content] + } else { + rect(stroke: none, inset: 2pt)[#cell_content] + } + }) + $vec(delim: "{", ..cells)$ + } + + mapcode-viz( + rho, F, pi_func, + I_h: ((a,b)) => [$"gcd"(#a, #b)$], + X_h: X_h, + pi_name: [$pi((#inst_a, #inst_b))$], + group-size: 2, + cell-size: 40mm, + scale-fig: 95% + )((inst_a, inst_b)) +} +$ +) diff --git a/main.typ b/main.typ index 9952d9b..ee6d2d1 100644 --- a/main.typ +++ b/main.typ @@ -52,3 +52,7 @@ All primitives are _strict_ meaning they do not allow for undefined values (i.e. #include "algorithms/LongestCommonSubsequence.typ" #pagebreak() #include "algorithms/leetcode/P2_add-two-numbers.typ" +#pagebreak() +#include "algorithms/binary-exponentiation.typ" +#pagebreak() +#include "algorithms/gcd.typ"