Everything on the site.

Big-O notationAlgorithms · Count the steps in real loops, then compare n, n log n, n² and 2ⁿ on a live chart.RecursionAlgorithms · Watch frames push and pop on the call stack for factorial and Fibonacci.BFS vs DFSAlgorithms · A queue against a stack: draw a graph and see which order each search visits it in.Sorting algorithmsAlgorithms · Seven sorts on the same input, with comparison, swap and write counts side by side.Binary searchAlgorithms · Halve the range, probe the middle, repeat: with lo, mid and hi drawn on the array.Sets, relations and functionsDiscrete maths · Shade any set expression on a Venn diagram and test a relation for its properties.Proof by inductionDiscrete maths · Four worked proofs revealed one step at a time, and a broken one to diagnose.CombinatoricsDiscrete maths · An exact nPr and nCr calculator, and a Pascal’s triangle that shows its own addition rule.Boolean algebraDiscrete maths · The laws, De Morgan, and normal forms, with a truth-table generator built in.Finite-state machinesTheory · Build a DFA in a table, see it drawn, and run strings through it one symbol at a time.Domain theoryTheory · Why recursion needs least fixed points, and how CPOs, Scott continuity and powerdomains supply them.Truth table generatorTools · Expression in, truth table out: with DNF, CNF, a minimal form and LaTeX.Graph traversal visualiserTools · Draw a graph, then animate BFS, DFS and Dijkstra with the frontier shown.Recursion tree visualiserTools · Paste a recursive function or a recurrence and see its call tree and call count.Gradient descentML maths · Drag the starting point, set the learning rate, and watch it converge or blow up.Vectors and embeddingsML maths · Bend the plane with a 2×2 matrix and measure how close toy word vectors are.Softmax and cross-entropyML maths · Edit the logits and watch the probabilities, the loss and the gradient update live.AttentionML maths · Q, K and V for three tokens, a live score heatmap, and the weighted sum of values.Formatting a CS paper in LaTeXLaTeX · Page limits, \documentclass, front matter, theorem environments and the bibliography.Camera-ready checklistLaTeX · Fonts embedded, PDF/A, copyright form, source bundle, and why final versions get rejected.LaTeX templatesLaTeX · Where the official classes live, plus a minimal article template you can copy.AlgorithmsSection · Step-through visualisations of sorting, searching, recursion, graph traversal and Big-O growth, each with pseudocode that highlights the line being run.Discrete mathsSection · Sets, relations and functions, proof by induction, counting, and Boolean algebra, explained with worked examples and small interactive checkers.TheorySection · Finite-state machines, automata and the mathematical semantics of programming languages, explained from first principles.ML mathsSection · Gradient descent, vectors and embeddings, softmax and cross-entropy, and attention, each with a formula, a worked numeric example and a live version.ToolsSection · A truth-table generator with CNF, DNF and LaTeX output, a graph-traversal visualiser for BFS, DFS and Dijkstra, and a recursion-tree visualiser.LaTeXSection · How to format a CS conference or workshop paper in LaTeX, a camera-ready checklist, and a minimal template you can adapt.
Algorithms

Recursion and the call stack, step by step

A recursive function solves a problem by calling itself on a smaller version of the same problem. This page shows what actually happens in the machine when it does that: frames are pushed onto the call stack, wait, and are popped as answers come back.

Interactive: Call-stack stepper for factorial, Fibonacci and memoised FibonacciUpdated

Recursion can feel like a trick the first time you see it: how can a function use itself before it is finished? The answer is that each call is a separate piece of work with its own variables, and the computer keeps track of the unfinished ones on a stack. Once you can picture that stack, recursion stops being mysterious.

Two ingredients: a base case and a smaller step

The factorial of a positive integer is the product of all the integers from 1 up to it: 4!=4×3×2×1=244! = 4 \times 3 \times 2 \times 1 = 24. It has a natural recursive definition:

n!={1if n≤1n×(n−1)!otherwisen! = \begin{cases} 1 & \text{if } n \le 1 \\ n \times (n-1)! & \text{otherwise} \end{cases}

Every correct recursive definition has the same two parts.

  • A base case that is answered directly, without recursion. Here it is n≤1n \le 1.
  • A recursive case that reduces the problem to a smaller instance and builds the answer from it. Here n!n! is built from (n−1)!(n-1)!.

“Smaller” matters. Each call must move towards a base case, or the recursion never stops. In code, factorial is three lines:

function fact(n) {
  if (n <= 1) return 1;
  return n * fact(n - 1);
}

The call stack, step by step

When fact(4) runs, it cannot finish 4 * fact(3) until fact(3) has returned. So the computer pauses fact(4), remembers where it was, and starts fact(3) as a brand new call with its own n = 3. The bookkeeping record for each unfinished call is a stack frame: it holds the call’s parameters, its local variables and the place to resume. Frames live on the call stack, last in, first out.

Step through it below. The tree shows every call made so far; the panel on the right shows the frames on the stack right now, with the running call on top.

Call-stack stepperBlue is running, amber is waiting for a call to return, green has returned.

Pseudocode

    Call stack (top is running)

    0Calls
    0Stack depth

    For fact(4) the stack grows to four frames, fact(4), fact(3), fact(2), fact(1), then unwinds as the answers 1, 2, 6 and 24 come back. The multiplications happen on the way back up, after each inner call returns.

    Worked trace of fact(4)

    StepWhat happensStack (top last)
    1call fact(4); 4 > 1 so it needs fact(3)fact(4)
    2call fact(3); needs fact(2)fact(4), fact(3)
    3call fact(2); needs fact(1)fact(4), fact(3), fact(2)
    4call fact(1); base case, returns 1fact(4), fact(3), fact(2), fact(1)
    5fact(2) computes 2 × 1 = 2 and returnsfact(4), fact(3), fact(2)
    6fact(3) computes 3 × 2 = 6 and returnsfact(4), fact(3)
    7fact(4) computes 4 × 6 = 24 and returnsfact(4)

    When recursion branches: Fibonacci

    The Fibonacci numbers are 0, 1, 1, 2, 3, 5, 8, 13, …, each the sum of the previous two:

    F(0)=0,F(1)=1,F(n)=F(n−1)+F(n−2) for n≥2.F(0) = 0,\quad F(1) = 1,\quad F(n) = F(n-1) + F(n-2) \text{ for } n \ge 2.

    The direct translation makes two recursive calls, so the calls form a tree rather than a chain. Choose “Fibonacci (naive)” in the stepper and run n=5n = 5. The same sub-problems are solved again and again: fib(2) is computed three times and fib(1) five times.

    It gets worse quickly. The number of calls made by naive fib(n) is 2F(n+1)−12F(n+1) - 1, which grows like φn\varphi^n where φ≈1.618\varphi \approx 1.618 is the golden ratio:

    nCalls made by naive fib(n)
    515
    10177
    2021,891
    302,692,537
    40331,160,281

    The stack never gets deeper than nn frames, though. Depth and total work are different things: depth is the longest path from the root of the call tree to a leaf; total work is the number of nodes in the tree.

    Memoisation: remember answers you have computed

    The fix is to store each result the first time it is computed and look it up afterwards. That is memoisation. Select “Fibonacci (memoised)”: every value from fib(0) to fib(n) is computed once, the second branch of each call becomes an instant lookup, and the call count drops from exponential to 2n−12n - 1.

    You can also turn the recursion inside out and fill a table from the bottom up: start from F(0)F(0) and F(1)F(1) and loop upwards. This is dynamic programming. Memoisation is the same idea organised top-down, so it only computes the sub-problems the answer actually needs.

    Try it with your own function The recursion tree visualiser traces any small recursive JavaScript function you paste in, counts the calls and shows which arguments repeat. Turn on memoisation there to see the tree collapse.

    How to write a recursive function

    1. Find the smaller problem. Ask: if someone handed me the answer for a slightly smaller input, could I finish the job? For a list, “smaller” is usually the list without its first element; for a range, half of it; for a tree, a subtree.
    2. Handle the smallest inputs directly. Zero, one, the empty list, a leaf. Check that every chain of recursive calls reaches one of them.
    3. Trust the recursive call. While writing the recursive case, assume the call on the smaller input returns the right answer. This is exactly the inductive hypothesis in a proof by induction, and induction is how you prove a recursive function correct.

    Recursion versus loops

    Any recursive function can be rewritten with a loop and an explicit stack, and any loop can be written recursively. The choice is about clarity and resources.

    • Natural fit. Recursion matches recursively shaped data: trees, nested lists, expressions, the file system, divide-and-conquer algorithms such as merge sort and quicksort.
    • Stack depth. Each pending call uses stack memory, and the stack is limited. Recursing a million levels deep in JavaScript or Python fails with a “maximum call stack size exceeded” or “recursion depth” error. Algorithms whose depth grows linearly with the input, such as a naive recursive list traversal or depth-first search on a long path, may need an explicit stack.
    • Tail calls. A call is in tail position when its result is returned directly, with nothing left to do afterwards. A language that eliminates tail calls can reuse the frame, so tail recursion runs in constant stack space. Scheme guarantees this. ECMAScript 2015 specifies it too, but most JavaScript engines do not implement it, so don’t rely on it in browser code.

    Recursion and running time

    The cost of a recursive algorithm is described by a recurrence. Factorial does constant work plus one call on n−1n-1: T(n)=T(n−1)+O(1)T(n) = T(n-1) + O(1), which is O(n)O(n). Naive Fibonacci does T(n)=T(n−1)+T(n−2)+O(1)T(n) = T(n-1) + T(n-2) + O(1), which is exponential. Merge sort does T(n)=2T(n/2)+O(n)T(n) = 2T(n/2) + O(n), which is O(nlog⁡n)O(n \log n). Drawing the call tree and adding up the work level by level is the most reliable way to solve these; see Big-O notation for the vocabulary.

    References

    1. H. Abelson and G. J. Sussman, Structure and Interpretation of Computer Programs, 2nd ed., MIT Press, 1996. Section 1.2 contrasts recursive and iterative processes.
    2. R. L. Graham, D. E. Knuth and O. Patashnik, Concrete Mathematics, 2nd ed., Addison-Wesley, 1994. Chapter 1 is about recurrences.
    3. “Recursion (computer science)”, Wikipedia.