Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions algorithms/DigitReversal.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#import "../lib/style.typ": *
#import "../lib/mapcode.typ": *

== Digit Reversal

Compute the digit reversal of a non-negative integer $n$.
i.e $n in NN_0, n >= 0$

Formal definition:
$
f(n) = h(n, 0)
$
$
h(n, a) = cases(
a & "if " n = 0,
h(n " div " 10, a * 10 + n " mod " 10) & "otherwise"
)
$

Examples:
- $"rev"(123) -> 321$
- $"rev"(90) -> 9$

*As mapcode:*

_primitives_: `div`, `mod`, `+`, `*`

// Put all definitions in one math block.
// Use '&=' to align and '\' for newlines.
$
I & = (n: NN_0, d: NN) \
X_d & = [0..d] -> (NN_0 times NN_0)_bot \
A & = NN_0 \
rho(n, d) & = { i -> bot | i in [0..d] }
$

// Define F(x_i) in its own math block
$
F(x_i) = cases(
(n, 0) & "if " i = 0,

// Use line breaks (\\) to wrap long conditions
(n_p " div " 10, a_p * 10 + n_p " mod " 10) & "if " i > 0 and n_p > 0 \\
& and x_(i-1) != bot,

(0, a_p) & "if " i > 0 and n_p = 0 \\
& and x_(i-1) != bot,

bot & "otherwise"
)
$
(where $(n_p, a_p) = x_(i-1)$)
$
pi(x) = x_d.2 quad // Accumulator of the last state
$

// --- Implementation ---

// The number to reverse
#let inst_n = 123;
// The number of steps (digits + 1 for the '0' state)
#let num_steps = 4;
// The instance (n, d), where d is the max index
#let inst = (inst_n, num_steps - 1);

#figure(
caption: [Digit Reversal computation using mapcode for $n = #inst_n$],
$
#{
// inst = (n, d)
let (n_val, d_val) = inst;

let rho = (inst) => {
let (n, d) = inst;
let x = ()
for i in range(0, d + 1) {
x.push(none) // `none` is bot
}
x
}

// F_i defines the logic for one element x[i]
let F_i = (x) => ((i,)) => {
if i == 0 {
// Base case: (input_n, 0_accumulator)
(n_val, 0)
} else {
let prev_state = x.at(i - 1);
if prev_state == none {
none // Dependency not met
} else {
let (prev_n, prev_acc) = prev_state;
if prev_n == 0 {
// Fixed point reached, propagate this state
(0, prev_acc)
} else {
// Compute the next state
let next_n = calc.floor(prev_n / 10);
let next_acc = prev_acc * 10 + calc.rem(prev_n, 10);
(next_n, next_acc)
}
}
}
}

// Create the tensor-wide operator
let F = map_tensor(F_i, dim: 1)

// pi extracts the final answer
let pi = (i) => (x) => {
let (n, acc) = x.at(i);
acc // Return the accumulator part of the last state
}

// X_h visualizes the state (an array of tuples)
// --- THIS BLOCK IS NOW FIXED ---
let X_h = (x, diff_mask: none) => {
let cells = x.enumerate().map(((i, x_i)) => {
let val = if x_i != none {
let (n, acc) = x_i;
// Format the (n, acc) tuple
[$n: #n, acc: #acc$]
} else {
[$bot$]
}

if diff_mask != none and diff_mask.at(i) {
// changed element: highlight (style from factorial.typ)
rect(fill: yellow.transparentize(70%), inset: 2pt)[$#val$]
} else {
// (style from factorial.typ)
rect(stroke: none, inset: 2pt)[$#val$]
}
})

$vec(delim: "[", ..cells)$
}
// --- END OF FIX ---

mapcode-viz(
rho, F, pi(d_val),
X_h: X_h,
pi_name: [$mpi(#repr(inst_n))$],
group-size: calc.min(7, d_val + 1),
cell-size: 30mm, // Wider to fit the tuple
scale-fig: 85%
)(inst)
}
$
)
110 changes: 110 additions & 0 deletions algorithms/LIS.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#import "../lib/style.typ": *
#import "../lib/mapcode.typ": *

== Longest Increasing Subsequence (LIS)
#set math.equation(numbering: none)

Compute the length of the longest increasing subsequence (LIS) of a vector of integers.

Formal definition:
$
I = "nums" in "vec"[ZZ] \
X_n = [0..n-1] -> NN_bot " where " n = |"nums"| \
A = NN
$

$
rho("nums") = { i -> bot | i in [0..n-1] } \
\
F(x)_i = 1 + max( {0} union { x_j | j in [0..i-1] " and " "nums"_i > "nums"_j } ) \
\
pi(x) = max( {0} union { x_i | i in [0..n-1] } )
$
(The $F$ function is strict: if any $x_j$ it depends on is $bot$, $F(x)_i$ remains $bot$.)

*As mapcode:*

#let inst = (10, 9, 2, 5, 3, 7, 101, 18);
#figure(
caption: [LIS computation using mapcode for $n = #inst$],
$
#{
let rho = (inst) => {
// x_0 = [bot, bot, ..., bot]
let x = ()
for i in range(0, inst.len()) {
x.push(none)
}
x
}

// F_i_gen creates the function for a single element F(x)_i
// It needs the `inst` (nums) to compare values.
let F_i_gen = (inst) => (x) => ((i,)) => {
let max_len = 0
let all_deps_met = true

// Loop j from 0 to i-1
for j in range(0, i) {
if inst.at(i) > inst.at(j) {
// This is a potential predecessor
if x.at(j) == none {
// Dependency not met, this cell remains bot
all_deps_met = false
break
} else if x.at(j) > max_len {
max_len = x.at(j)
}
}
}

if all_deps_met {
1 + max_len
} else {
none
}
}

// F_gen creates the parallel map F(x)
let F = (inst) => map_tensor(F_i_gen(inst), dim: 1)

// pi finds the maximum value in the final dp array
let pi = (inst) => (x) => {
if x.len() == 0 {
0
} else {
let max_val = 0
for i in range(0, x.len()) {
if x.at(i) != none and x.at(i) > max_val {
max_val = x.at(i)
}
}
max_val
}
}

// X_h is the helper to visualize the state vector x
let X_h = (x, diff_mask: none) => {
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(inst), pi(inst),
X_h: X_h,
pi_name: [$mpi$],
group-size: calc.min(8, inst.len()),
cell-size: 8mm,
scale-fig: 75%
)(inst)
}
$
)