From 333d3311a5a3170d1b0f3f8163b333510404ac2e Mon Sep 17 00:00:00 2001 From: Hardik Chadha <143736130+Hardik1106@users.noreply.github.com> Date: Fri, 14 Nov 2025 10:45:40 +0530 Subject: [PATCH 1/5] Add Catalan Numbers and Edit Distance Algorithms --- algorithms/catalannumbers.typ | 107 ++++++++++++++++++++++++++ algorithms/editdistance.typ | 136 ++++++++++++++++++++++++++++++++++ main.typ | 8 ++ 3 files changed, 251 insertions(+) create mode 100644 algorithms/catalannumbers.typ create mode 100644 algorithms/editdistance.typ diff --git a/algorithms/catalannumbers.typ b/algorithms/catalannumbers.typ new file mode 100644 index 0000000..fe938be --- /dev/null +++ b/algorithms/catalannumbers.typ @@ -0,0 +1,107 @@ +#import "../lib/style.typ": * +#import "../lib/mapcode.typ": * + +== Catalan Numbers + +Compute the $n$-th Catalan number, which appears in many combinatorial problems such as counting the number of valid parentheses expressions, binary search trees, paths in a grid, etc. + +Formal definition: +$ +"Cat"(n) = frac(1, n+1) binom(2n, n) = frac((2n)!, (n+1)!n!) +$ + +equivalently, as recursive definition: +$ +"Cat"(n) = cases( + 1 & "if " n = 0, + sum_(i=0)^(n-1) "Cat"(i) dot "Cat"(n-1-i) & "otherwise" +) +$ + +Examples: +- $"Cat"(0) = 1$ +- $"Cat"(1) = 1$ +- $"Cat"(2) = 2$ +- $"Cat"(3) = 5$ +- $"Cat"(4) = 14$ +- $"Cat"(5) = 42$ + +*As mapcode:* + +_primitives_: `sum`($+$), `mul`($dot$) + +$ +I = n:NN quad quad quad +X_n & = [0..n] -> NN_bot quad quad quad +A = NN\ +rho(n) & = { i -> bot | i in {0 dots n}} \ +F(x_i) & = cases( + 1 & "if " i = 0, + sum_(k=0)^(i-1) x_k dot x_(i-1-k) & "otherwise" +)\ +pi_n (x) & = x_n +$ + +#let inst = 5; +#figure( + caption: [Catalan Numbers computation using mapcode for $n = #inst$], +$#{ + let rho = (inst) => { + let x = () + for i in range(0, inst + 1) { + x.push(none) + } + x + } + + let F_i = (x) => ((i,)) => { + if i == 0 { + 1 + } else { + // Check if all dependencies are ready + let all_ready = true + for k in range(0, i) { + if x.at(k) == none { + all_ready = false + break + } + } + + if all_ready { + let total = 0 + for k in range(0, i) { + total += x.at(k) * x.at(i - 1 - k) + } + total + } else { + none + } + } + } + let F = map_tensor(F_i, dim: 1) + + let pi = (n) => (x) => x.at(n) + + let X_h = (x, diff_mask: none) => { + set text(weight: "bold") + let cells = x.enumerate().map(((i, x_i)) => { + let val = if x_i != none {[$#x_i$]} else {[$bot$]} + if diff_mask != none and diff_mask.at(i) { + // changed element: highlight + rect(fill: yellow.transparentize(70%), inset: 2pt)[$#val$] + } else { + rect(stroke: none, inset: 2pt)[$#val$] + } + }) + $vec(delim: "[", ..cells)$ + } + + mapcode-viz( + rho, F, pi(inst), + X_h: X_h, + pi_name: [$pi_#inst$], + group-size: calc.min(6, inst), + cell-size: 15mm, scale-fig: 95% + )(inst) +}$ +) diff --git a/algorithms/editdistance.typ b/algorithms/editdistance.typ new file mode 100644 index 0000000..9e9ac3d --- /dev/null +++ b/algorithms/editdistance.typ @@ -0,0 +1,136 @@ +#import "../lib/style.typ": * +#import "../lib/mapcode.typ": * + +== Edit Distance (Levenshtein Distance) + +Compute the edit distance (also known as Levenshtein distance) between two strings $S$ and $T$. The edit distance is the minimum number of single-character edits (insertions, deletions, or substitutions) required to change one string into the other. + +Formal definition: +$ +"ED"(i, j) = cases( + i & "if " j = 0, + j & "if " i = 0, + "ED"(i-1, j-1) & "if " S_i = T_j, + 1 + min("ED"(i-1, j), "ED"(i, j-1), "ED"(i-1, j-1)) & "otherwise" +) +$ + +where: +- $"ED"(i-1, j)$ represents deletion from $S$ +- $"ED"(i, j-1)$ represents insertion into $S$ +- $"ED"(i-1, j-1)$ represents substitution + +Examples: +- $"ED"("kitten", "sitting") = 3$ (substitute k→s, substitute e→i, insert g) +- $"ED"("horse", "ros") = 3$ (delete h, delete r, substitute s→r) +- $"ED"("intention", "execution") = 5$ + +*As mapcode:* + +_primitives_: `sum`($+$), `min`(min) + +$ +I = S times T quad "where" S, T in Sigma^* quad quad quad +X_(S,T) & = [0..|S|] times [0..|T|] -> NN_bot quad quad quad +A = NN\ +rho(S,T) & = { (i,j) -> bot | i in {0 dots |S|}, j in {0 dots |T|}} \ +F_(S, T)(x_(i,j)) & = cases( + i & "if " j = 0, + j & "if " i = 0, + x_(i-1,j-1) & "if " S_i = T_j, + 1 + min(x_(i-1,j), x_(i,j-1), x_(i-1,j-1)) & "otherwise" +)\ +pi_(S,T) (x) & = x_(|S|,|T|) +$ + +#let inst_S = "horse"; +#let inst_T = "ros"; +#let inst_m = inst_S.len(); +#let inst_n = inst_T.len(); + +#figure( + caption: [Edit Distance computation using mapcode for $S = "#inst_S"$ and $T = "#inst_T"$; dynamic-programming table visualization.], +$#{ + let rho = ((inst_m, inst_n)) => { + let x = () + for i in range(0, inst_m + 1) { + let row = () + for j in range(0, inst_n + 1) { + row.push(none) + } + x.push(row) + } + x + } + + let F_i = ((S, T)) => (x) => ((i,j)) => { + if j == 0 { + i + } else if i == 0 { + j + } else if S.at(i - 1) == T.at(j - 1) { + if x.at(i - 1).at(j - 1) != none { + x.at(i - 1).at(j - 1) + } else { + none + } + } else { + if x.at(i - 1).at(j) != none and x.at(i).at(j - 1) != none and x.at(i - 1).at(j - 1) != none { + 1 + calc.min(x.at(i - 1).at(j), x.at(i).at(j - 1), x.at(i - 1).at(j - 1)) + } else { + none + } + } + } + let F = ((S, T)) => map_tensor(F_i((S, T)), dim: 2) + + let pi = ((S, T)) => (x) => { + let m = S.len() + let n = T.len() + x.at(m).at(n) + } + + // draw DP table with sequence labels + let x_h(x, diff_mask:none) = { + set text(weight: "bold") + let rows = () + + // header row: show T characters (with an initial empty corner) + let header_cells = () + header_cells.push(rect(stroke: none, inset: 4pt)[$bot$]) + header_cells.push(rect(fill: orange.transparentize(70%), inset: 4pt)[$emptyset$]) + for j in range(0, inst_n) { + header_cells.push(rect(fill: orange.transparentize(70%), inset: 4pt)[$#inst_T.at(j)$]) + } + rows.push(grid(columns: header_cells.len() * (14pt,), rows: 14pt, align: center + horizon, ..header_cells)) + + for i in range(0, x.len()) { + let row = () + // left label: S character for i>0, empty for i=0 + if i == 0 { + row.push(rect(fill: green.transparentize(70%), inset: 4pt)[$emptyset$]) + } else { + row.push(rect(fill: green.transparentize(70%), inset: 4pt)[$#inst_S.at(i - 1)$]) + } + + for j in range(0, x.at(i).len()) { + let val = if x.at(i).at(j) != none {[$#x.at(i).at(j)$]} else {[$bot$]} + if diff_mask != none and diff_mask.at(i).at(j) { + row.push(rect(stroke: gray, fill: yellow.transparentize(70%), inset: 4pt)[$#val$]) + } else { + row.push(rect(stroke: gray, inset: 4pt)[$#val$]) + } + } + rows.push(grid(columns: row.len() * (14pt,), rows: 14pt, align: center + horizon, ..row)) + } + grid(align: center, ..rows) + } + + mapcode-viz( + rho,F((inst_S, inst_T)), pi((inst_S, inst_T)), + X_h: x_h, + pi_name: [$mpi ((#inst_m, #inst_n))$], + group-size: calc.min(3, inst_m), + cell-size: 60mm, scale-fig: 75% + )((inst_m, inst_n)) +}$) \ No newline at end of file diff --git a/main.typ b/main.typ index 9952d9b..1694379 100644 --- a/main.typ +++ b/main.typ @@ -52,3 +52,11 @@ 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/editdistance.typ" +#pagebreak() +#include "algorithms/catalannumbers.typ" +// #pagebreak() +// #include "algorithms/mergesort.typ" +#pagebreak() +#include "algorithms/evenodd.typ" From 0a0392064afff4dd3426e33d0cc7dcee949be6de Mon Sep 17 00:00:00 2001 From: Hardik Chadha <143736130+Hardik1106@users.noreply.github.com> Date: Fri, 14 Nov 2025 11:07:55 +0530 Subject: [PATCH 2/5] Minor Fixes --- algorithms/editdistance.typ | 11 ++++++++--- main.typ | 4 ---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/algorithms/editdistance.typ b/algorithms/editdistance.typ index 9e9ac3d..1e662d5 100644 --- a/algorithms/editdistance.typ +++ b/algorithms/editdistance.typ @@ -30,7 +30,10 @@ Examples: _primitives_: `sum`($+$), `min`(min) $ -I = S times T quad "where" S, T in Sigma^* quad quad quad +I = S times T quad "where" S, T in Sigma^* quad quad "(strings over an alphabet Sigma)" +$ + +$ X_(S,T) & = [0..|S|] times [0..|T|] -> NN_bot quad quad quad A = NN\ rho(S,T) & = { (i,j) -> bot | i in {0 dots |S|}, j in {0 dots |T|}} \ @@ -49,7 +52,7 @@ $ #let inst_n = inst_T.len(); #figure( - caption: [Edit Distance computation using mapcode for $S = "#inst_S"$ and $T = "#inst_T"$; dynamic-programming table visualization.], + caption: [Edit Distance computation using mapcode for $S = "horse"$ and $T = "ros"$; dynamic-programming table visualization.], $#{ let rho = ((inst_m, inst_n)) => { let x = () @@ -128,8 +131,10 @@ $#{ mapcode-viz( rho,F((inst_S, inst_T)), pi((inst_S, inst_T)), + I_h: (i) => [$S=#inst_S$, $T=#inst_T $], X_h: x_h, - pi_name: [$mpi ((#inst_m, #inst_n))$], + pi_name: [$pi_(S,T) (x) & = x_(|S|,|T|) +$], group-size: calc.min(3, inst_m), cell-size: 60mm, scale-fig: 75% )((inst_m, inst_n)) diff --git a/main.typ b/main.typ index 1694379..0dd3eff 100644 --- a/main.typ +++ b/main.typ @@ -56,7 +56,3 @@ All primitives are _strict_ meaning they do not allow for undefined values (i.e. #include "algorithms/editdistance.typ" #pagebreak() #include "algorithms/catalannumbers.typ" -// #pagebreak() -// #include "algorithms/mergesort.typ" -#pagebreak() -#include "algorithms/evenodd.typ" From c04f0a5f37a476abcd9a288e051c90c9e569b51f Mon Sep 17 00:00:00 2001 From: Hardik Chadha <143736130+Hardik1106@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:24:31 +0530 Subject: [PATCH 3/5] Removed Catalan Numbers --- algorithms/catalannumbers.typ | 107 ---------------------------------- main.typ | 2 - 2 files changed, 109 deletions(-) delete mode 100644 algorithms/catalannumbers.typ diff --git a/algorithms/catalannumbers.typ b/algorithms/catalannumbers.typ deleted file mode 100644 index fe938be..0000000 --- a/algorithms/catalannumbers.typ +++ /dev/null @@ -1,107 +0,0 @@ -#import "../lib/style.typ": * -#import "../lib/mapcode.typ": * - -== Catalan Numbers - -Compute the $n$-th Catalan number, which appears in many combinatorial problems such as counting the number of valid parentheses expressions, binary search trees, paths in a grid, etc. - -Formal definition: -$ -"Cat"(n) = frac(1, n+1) binom(2n, n) = frac((2n)!, (n+1)!n!) -$ - -equivalently, as recursive definition: -$ -"Cat"(n) = cases( - 1 & "if " n = 0, - sum_(i=0)^(n-1) "Cat"(i) dot "Cat"(n-1-i) & "otherwise" -) -$ - -Examples: -- $"Cat"(0) = 1$ -- $"Cat"(1) = 1$ -- $"Cat"(2) = 2$ -- $"Cat"(3) = 5$ -- $"Cat"(4) = 14$ -- $"Cat"(5) = 42$ - -*As mapcode:* - -_primitives_: `sum`($+$), `mul`($dot$) - -$ -I = n:NN quad quad quad -X_n & = [0..n] -> NN_bot quad quad quad -A = NN\ -rho(n) & = { i -> bot | i in {0 dots n}} \ -F(x_i) & = cases( - 1 & "if " i = 0, - sum_(k=0)^(i-1) x_k dot x_(i-1-k) & "otherwise" -)\ -pi_n (x) & = x_n -$ - -#let inst = 5; -#figure( - caption: [Catalan Numbers computation using mapcode for $n = #inst$], -$#{ - let rho = (inst) => { - let x = () - for i in range(0, inst + 1) { - x.push(none) - } - x - } - - let F_i = (x) => ((i,)) => { - if i == 0 { - 1 - } else { - // Check if all dependencies are ready - let all_ready = true - for k in range(0, i) { - if x.at(k) == none { - all_ready = false - break - } - } - - if all_ready { - let total = 0 - for k in range(0, i) { - total += x.at(k) * x.at(i - 1 - k) - } - total - } else { - none - } - } - } - let F = map_tensor(F_i, dim: 1) - - let pi = (n) => (x) => x.at(n) - - let X_h = (x, diff_mask: none) => { - set text(weight: "bold") - let cells = x.enumerate().map(((i, x_i)) => { - let val = if x_i != none {[$#x_i$]} else {[$bot$]} - if diff_mask != none and diff_mask.at(i) { - // changed element: highlight - rect(fill: yellow.transparentize(70%), inset: 2pt)[$#val$] - } else { - rect(stroke: none, inset: 2pt)[$#val$] - } - }) - $vec(delim: "[", ..cells)$ - } - - mapcode-viz( - rho, F, pi(inst), - X_h: X_h, - pi_name: [$pi_#inst$], - group-size: calc.min(6, inst), - cell-size: 15mm, scale-fig: 95% - )(inst) -}$ -) diff --git a/main.typ b/main.typ index 0dd3eff..902e7e2 100644 --- a/main.typ +++ b/main.typ @@ -54,5 +54,3 @@ All primitives are _strict_ meaning they do not allow for undefined values (i.e. #include "algorithms/leetcode/P2_add-two-numbers.typ" #pagebreak() #include "algorithms/editdistance.typ" -#pagebreak() -#include "algorithms/catalannumbers.typ" From 498cc1704cc48eed373313741a7d912f2a5ca051 Mon Sep 17 00:00:00 2001 From: Hardik Chadha <143736130+Hardik1106@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:29:34 +0530 Subject: [PATCH 4/5] Added the Catalan Numbers --- algorithms/catalannumbers.typ | 107 ++++++++++++++++++++++++++++++++++ main.typ | 2 + 2 files changed, 109 insertions(+) create mode 100644 algorithms/catalannumbers.typ diff --git a/algorithms/catalannumbers.typ b/algorithms/catalannumbers.typ new file mode 100644 index 0000000..fe938be --- /dev/null +++ b/algorithms/catalannumbers.typ @@ -0,0 +1,107 @@ +#import "../lib/style.typ": * +#import "../lib/mapcode.typ": * + +== Catalan Numbers + +Compute the $n$-th Catalan number, which appears in many combinatorial problems such as counting the number of valid parentheses expressions, binary search trees, paths in a grid, etc. + +Formal definition: +$ +"Cat"(n) = frac(1, n+1) binom(2n, n) = frac((2n)!, (n+1)!n!) +$ + +equivalently, as recursive definition: +$ +"Cat"(n) = cases( + 1 & "if " n = 0, + sum_(i=0)^(n-1) "Cat"(i) dot "Cat"(n-1-i) & "otherwise" +) +$ + +Examples: +- $"Cat"(0) = 1$ +- $"Cat"(1) = 1$ +- $"Cat"(2) = 2$ +- $"Cat"(3) = 5$ +- $"Cat"(4) = 14$ +- $"Cat"(5) = 42$ + +*As mapcode:* + +_primitives_: `sum`($+$), `mul`($dot$) + +$ +I = n:NN quad quad quad +X_n & = [0..n] -> NN_bot quad quad quad +A = NN\ +rho(n) & = { i -> bot | i in {0 dots n}} \ +F(x_i) & = cases( + 1 & "if " i = 0, + sum_(k=0)^(i-1) x_k dot x_(i-1-k) & "otherwise" +)\ +pi_n (x) & = x_n +$ + +#let inst = 5; +#figure( + caption: [Catalan Numbers computation using mapcode for $n = #inst$], +$#{ + let rho = (inst) => { + let x = () + for i in range(0, inst + 1) { + x.push(none) + } + x + } + + let F_i = (x) => ((i,)) => { + if i == 0 { + 1 + } else { + // Check if all dependencies are ready + let all_ready = true + for k in range(0, i) { + if x.at(k) == none { + all_ready = false + break + } + } + + if all_ready { + let total = 0 + for k in range(0, i) { + total += x.at(k) * x.at(i - 1 - k) + } + total + } else { + none + } + } + } + let F = map_tensor(F_i, dim: 1) + + let pi = (n) => (x) => x.at(n) + + let X_h = (x, diff_mask: none) => { + set text(weight: "bold") + let cells = x.enumerate().map(((i, x_i)) => { + let val = if x_i != none {[$#x_i$]} else {[$bot$]} + if diff_mask != none and diff_mask.at(i) { + // changed element: highlight + rect(fill: yellow.transparentize(70%), inset: 2pt)[$#val$] + } else { + rect(stroke: none, inset: 2pt)[$#val$] + } + }) + $vec(delim: "[", ..cells)$ + } + + mapcode-viz( + rho, F, pi(inst), + X_h: X_h, + pi_name: [$pi_#inst$], + group-size: calc.min(6, inst), + cell-size: 15mm, scale-fig: 95% + )(inst) +}$ +) diff --git a/main.typ b/main.typ index 902e7e2..b053743 100644 --- a/main.typ +++ b/main.typ @@ -54,3 +54,5 @@ All primitives are _strict_ meaning they do not allow for undefined values (i.e. #include "algorithms/leetcode/P2_add-two-numbers.typ" #pagebreak() #include "algorithms/editdistance.typ" +#pagebreak() +#include "algorithms/catalannumbers.typ" \ No newline at end of file From aeed6ba076be837156184584594ec1cf2f7636c5 Mon Sep 17 00:00:00 2001 From: Hardik Chadha <143736130+Hardik1106@users.noreply.github.com> Date: Fri, 14 Nov 2025 16:27:42 +0530 Subject: [PATCH 5/5] Minor Fixes --- algorithms/catalannumbers.typ | 2 +- algorithms/editdistance.typ | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/algorithms/catalannumbers.typ b/algorithms/catalannumbers.typ index fe938be..1848663 100644 --- a/algorithms/catalannumbers.typ +++ b/algorithms/catalannumbers.typ @@ -99,7 +99,7 @@ $#{ mapcode-viz( rho, F, pi(inst), X_h: X_h, - pi_name: [$pi_#inst$], + pi_name: [$mpi_#inst$], group-size: calc.min(6, inst), cell-size: 15mm, scale-fig: 95% )(inst) diff --git a/algorithms/editdistance.typ b/algorithms/editdistance.typ index 1e662d5..d8aefa7 100644 --- a/algorithms/editdistance.typ +++ b/algorithms/editdistance.typ @@ -133,9 +133,9 @@ $#{ rho,F((inst_S, inst_T)), pi((inst_S, inst_T)), I_h: (i) => [$S=#inst_S$, $T=#inst_T $], X_h: x_h, - pi_name: [$pi_(S,T) (x) & = x_(|S|,|T|) -$], + F_name: [$F_(S,T)$], + pi_name: [$mpi_(S,T)$], group-size: calc.min(3, inst_m), - cell-size: 60mm, scale-fig: 75% + cell-size: 50mm, scale-fig: 85% )((inst_m, inst_n)) }$) \ No newline at end of file