Monocurl Essays
Series Download

Geometric series, and why 0.999... equals 1

What it means to add infinitely many numbers, when a geometric series has a sum, the formula a/(1 - r) with proof, and why 0.999... is exactly 1.

22 min·High school·

AssumesCompound interest and where e comes fromWhat is a limit? The epsilon-delta definition, explained

A square of area 1 is filled by a piece of area $\frac12$, then $\frac14$, then $\frac18$, each piece half of what was left. The area covered after $n$ pieces is $1 - \frac{1}{2^n}$: it never equals 1, and it comes as close to 1 as you like.
Monocurl source
let SQ = 3.7
let SO = [-3.6, -1.85, 0]
# piece k of the halving, as [x, y, w, h] inside the unit square
let piece = |k| {
    var x = 0
    var y = 0
    var w = 1
    var h = 1
    for (j in range(1, k)) {
        if (mod(j, 2) == 1) {
            x = x + w / 2
            w = w / 2
        } else {
            y = y + h / 2
            h = h / 2
        }
    }
    if (mod(k, 2) == 1) {
        return [x, y, w / 2, h]
    }
    return [x, y, w, h / 2]
}
let Piece = |k, f| {
    let b = piece(k)
    var w = b[2]
    var h = b[3]
    var col = AZURE
    if (mod(k, 2) == 1) {
        w = w * f
    } else {
        h = h * f
        col = EMBER
    }
    return Bar(SO[0] + SQ * b[0], SO[1] + SQ * b[1], SQ * w, SQ * h, col)
}
let FRAC = ["\frac12", "\frac14", "\frac18", "\frac{1}{16}"]
let FSIZE = [1.3, 1.1, 0.85, 0.7]
let Fill = |m| {
    let done = to_int(floor(m + 0.001))
    var out = []
    for (k in range(1, 11)) {
        if (k <= done) {
            out .= Piece(k, 1)
        }
        if (k == done + 1 and m - done > 0.02) {
            out .= Piece(k, m - done)
        }
    }
    for (k in range(1, 5)) {
        if (k <= done) {
            let b = piece(k)
            out .= center{[SO[0] + SQ * (b[0] + b[2] / 2), SO[1] + SQ * (b[1] + b[3] / 2), 0]} Math(FRAC[k - 1], FSIZE[k - 1], INK)
        }
    }
    out .= center{[2.1, 1.3, 0]} scale{1.25} color{INK} Number(done, 0)
    out .= center{[2.1, 0.2, 0]} scale{1.25} color{AZURE} Number(1 - 0.5 ^ done, 4)
    out .= center{[2.1, -0.9, 0]} scale{1.25} color{ROSE} Number(0.5 ^ done, 4)
    return out
}
mesh frame = Outline([SO, [SO[0] + SQ, SO[1], 0], [SO[0] + SQ, SO[1] + SQ, 0], [SO[0], SO[1] + SQ, 0]], SLATE, 2.5)
mesh labels = [
    center{[2.1, 1.78, 0]} Note("pieces placed", 0.74),
    center{[2.1, 0.68, 0]} Note("area covered", 0.74),
    center{[2.1, -0.42, 0]} Note("area left", 0.74)
]
mesh fill = Fill(m: 0)
mesh payoff = []

slide "fill"
    play Wait(0.6)
    fill.m = 10
    play Lerp(7, [&fill], linear)
    payoff = center{[2.1, -1.7, 0]} Math("\frac12 + \frac14 + \frac18 + \cdots = 1", 1.05, LEAF)
    play Fade(0.5, [&payoff])
    play Wait(2)

Uses the shared definitions for this lesson's figures.

Stand one meter from a wall. Walk half of the way to it, then half of what is left, then half of what is left again, and keep going. Each step leaves some distance still to cover, so it seems you never arrive, however long you walk. And yet people reach walls all the time.

This is one of the paradoxes of Zeno of Elea, about 2500 years old, and behind it is a question with a precise answer: what should it mean to add up infinitely many numbers, here $\frac12 + \frac14 + \frac18 + \cdots$? Nobody can perform infinitely many additions, so the phrase needs a definition. Once it has one, we can prove which sums of this kind exist, find a formula for them, and settle an argument that every class has sooner or later: whether $0.999\ldots$ is equal to 1 or only very close to it.

Half, then half of the rest

Start with the numbers. After one step you have walked $\frac12$ of a meter. After two, $\frac12 + \frac14 = \frac34$. After three, $\frac78$.

  1. First walk half of the way: half a meter. Half a meter is left.
  2. Then half of what is left, a quarter of a meter. You have walked 3/4 of a meter and 1/4 is left.
  3. Then half of that, an eighth. Walked 7/8, with 1/8 left.
  4. Three more steps, of 1/16, 1/32 and 1/64. Walked 63/64, with 1/64 left. Every step halves what is left, and no step reaches the wall.
Walking toward a wall one meter away, each step covering half of the remaining distance. The blue part of the line is the distance walked and the red part is the distance left, which halves at every step.
Monocurl source
let WL = -3.3
let WU = 6.3
let WY = -0.45
let wx = |d| [WL + WU * d, WY, 0]
let Hop = |a, b| stroke{EMBER, 3.5} Polyline(map(sample(0, 1, 32), |t| [WL + WU * (a + (b - a) * t), WY + WU * (b - a) * 0.5 * sin(PI * t), 0]))
let HopName = |n| center{[WL + WU * (1 - 1.5 * 0.5 ^ n), WY + WU * 0.25 * 0.5 ^ (n - 1) + 0.38, 0]} Math(str_join(["\frac{1}{", to_string(2 ^ n), "}"]), 0.85, EMBER)
let State = |n| [
    Seg(wx(0), wx(1 - 0.5 ^ n), AZURE, 7),
    Seg(wx(1 - 0.5 ^ n), wx(1), ROSE, 7),
    Pt(wx(1 - 0.5 ^ n), EMBER, 0.12),
    center{[-1.7, -1.6, 0]} Math(str_join(["\text{walked } \frac{", to_string(2 ^ n - 1), "}{", to_string(2 ^ n), "}"]), 0.95, AZURE),
    center{[1.7, -1.6, 0]} Math(str_join(["\text{left } \frac{1}{", to_string(2 ^ n), "}"]), 0.95, ROSE)
]
mesh base = block {
    . Seg(wx(0), wx(1), SLATE, 3)
    . Seg([WL + WU, WY - 0.6, 0], [WL + WU, 1.9, 0], SLATE, 9)
    . center{[WL + WU + 0.42, 1.7, 0]} Note("wall", 0.74)
    for (j in range(0, 5)) {
        . Seg([WL + WU * j / 4, WY - 0.1, 0], [WL + WU * j / 4, WY + 0.1, 0], SLATE, 2.5)
    }
    . center{[WL, WY - 0.42, 0]} Math("0", 0.8, SLATE)
    . center{[WL + WU / 4, WY - 0.42, 0]} Math("0.25", 0.8, SLATE)
    . center{[WL + WU / 2, WY - 0.42, 0]} Math("0.5", 0.8, SLATE)
    . center{[WL + 3 * WU / 4, WY - 0.42, 0]} Math("0.75", 0.8, SLATE)
    . center{[WL + WU - 0.42, WY - 0.42, 0]} Math("1 \text{ m}", 0.8, SLATE)
}
mesh hops = []
mesh state = [Pt(wx(0), EMBER, 0.12)]

slide "First walk half of the way: half a meter. Half a meter is left."
    hops .= [Hop(0, 0.5), HopName(1)]
    state = State(1)
    play Fade(0.8)

slide "Then half of what is left, a quarter of a meter. You have walked 3/4 of a meter and 1/4 is left."
    state = []
    play Fade(0.3, [&state])
    hops .= [Hop(0.5, 0.75), HopName(2)]
    state = State(2)
    play Fade(0.8)

slide "Then half of that, an eighth. Walked 7/8, with 1/8 left."
    state = []
    play Fade(0.3, [&state])
    hops .= [Hop(0.75, 0.875), HopName(3)]
    state = State(3)
    play Fade(0.8)

slide "Three more steps, of 1/16, 1/32 and 1/64. Walked 63/64, with 1/64 left. Every step halves what is left, and no step reaches the wall."
    state = []
    play Fade(0.3, [&state])
    hops .= [Hop(0.875, 0.9375), Hop(0.9375, 0.96875), Hop(0.96875, 0.984375)]
    state = State(6)
    play Fade(0.8)

Uses the shared definitions for this lesson's figures.

The pattern is easier to see in what is left than in what has been walked. Each step covers half of the remaining distance, so the remaining distance is halved: $\frac12, \frac14, \frac18, \ldots$, and after $n$ steps it is $\frac{1}{2^n}$. The distance walked is the rest of the meter:

$$ \frac12 + \frac14 + \cdots + \frac{1}{2^n} = 1 - \frac{1}{2^n}. $$

The square at the top of the page shows the same fact as areas.

Pause and try

How many steps does it take to get within one millimeter of the wall? Within a thousandth of a millimeter?

Show answerHide answer

We need $\frac{1}{2^n} < 0.001$, that is $2^n > 1000$. Since $2^{10} = 1024$, ten steps are enough (and nine are not, because $2^9 = 512$). For a millionth of a meter we need $2^n > 1\,000\,000$, and $2^{20} = 1\,048\,576$, so twenty steps. Each further factor of a thousand costs only ten more steps.

So no finite number of steps reaches the wall, and yet the shortfall can be made smaller than any distance you care to name. Those two facts together are what the phrase “the sum is 1” will mean.

What an infinite sum means

We cannot define $\frac12 + \frac14 + \frac18 + \cdots$ as the result of adding all the terms, because that process has no last step and so no result. What we do have is a list of honest finite sums: $\frac12$, $\frac34$, $\frac78$, and so on. The definition is built on that list.

Read the clauses against the walk. The number $\varepsilon$ is a tolerance, such as one millimeter. The number $N$ is how many steps it takes to get within that tolerance, such as ten. “For all $n \ge N$” says that once inside the tolerance the partial sums stay inside. Nothing in the definition requires any partial sum to equal $S$. The sum of a series is a limit of sums, and is not itself the result of an addition.

  1. The partial sums 0.5, 0.75, 0.875, ... climb toward the dashed line at height 1 and never touch it.
  2. Choose a tolerance of 0.1. From the fourth partial sum on, every one lies in the band between 0.9 and 1.1.
  3. Shrink the tolerance to 0.02. Now it takes six terms, and again every later partial sum stays in the band.
The partial sums of $\frac12 + \frac14 + \frac18 + \cdots$ plotted against the number of terms. For each tolerance $\varepsilon$ there is a point $N$ after which every partial sum lies within $\varepsilon$ of 1.
Monocurl source
let fs = Frame(0, 10.6, 0.4, 1.1, [0.25, 0.3, 0], 6.0, 2.9)
let sn = |n| 1 - 0.5 ^ n
let Band = |eps| Region([at(fs, 0, 1 - eps), at(fs, 10.6, 1 - eps), at(fs, 10.6, 1 + eps), at(fs, 0, 1 + eps)], tint(LEAF, 0.25))
let Dots = |nn| block {
    for (n in range(1, 11)) {
        var col = AZURE
        if (n >= nn) {
            col = LEAF
        }
        . Pt(at(fs, n, sn(n)), col, 0.095)
    }
}
let Marks = |nn, eps| [
    Dash(at(fs, nn, 0.4), at(fs, nn, 1.1), SLATE, 2.5),
    center{at(fs, 8.3, 0.67)} Math(str_join(["\varepsilon = ", to_string(eps, 2)]), 0.95, LEAF),
    center{at(fs, 8.3, 0.52)} Math(str_join(["N = ", to_string(nn)]), 0.95, SLATE)
]
mesh band = []
mesh grid = GridLines(fs, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.1])
mesh axes = [
    Axes(fs),
    XTicks(fs, [2, 4, 6, 8, 10]),
    YTicks(fs, [0.5, 0.6, 0.7, 0.8, 0.9, 1], SLATE, 1),
    center{[fx(fs, 10.6) - 1.3, fy(fs, 0.4) - 0.7, 0]} Math("\text{number of terms } n", 0.78, SLATE),
    center{[fx(fs, 0) + 1.25, fy(fs, 1.1) + 0.3, 0]} Math("\text{partial sum } s_n", 0.78, SLATE)
]
mesh limit = Dash(at(fs, 0, 1), at(fs, 10.6, 1), LEAF, 3)
mesh dots = Dots(11)
mesh marks = []

slide "The partial sums 0.5, 0.75, 0.875, ... climb toward the dashed line at height 1 and never touch it."
    play Wait(0.5)

slide "Choose a tolerance of 0.1. From the fourth partial sum on, every one lies in the band between 0.9 and 1.1."
    band = Band(0.1)
    dots = Dots(4)
    marks = Marks(4, 0.1)
    play Fade(0.8)

slide "Shrink the tolerance to 0.02. Now it takes six terms, and again every later partial sum stays in the band."
    marks = []
    play Fade(0.3, [&marks])
    band = Band(0.02)
    dots = Dots(6)
    marks = Marks(6, 0.02)
    play Fade(0.8)

Uses the shared definitions for this lesson's figures.

Let us verify that our series converges to 1 by this definition. We have $|s_n - 1| = \frac{1}{2^n}$. One fact about powers is needed, and we will use it twice more below.

With $h = 1$ this says $2^n \ge 1 + n > n$. Now let $\varepsilon > 0$ and choose a whole number $N > \frac{1}{\varepsilon}$. For every $n \ge N$ we get $|s_n - 1| = \frac{1}{2^n} < \frac1n \le \frac1N < \varepsilon$. Hence

$$ \frac12 + \frac14 + \frac18 + \cdots = 1, $$

with the equals sign meaning exactly what the definition says.

Now back to Zeno. The walker’s trouble was the belief that infinitely many steps must take forever. Suppose you walk at one meter per second. The steps then take $\frac12$ s, $\frac14$ s, $\frac18$ s, and the time used after $n$ steps is $1 - \frac{1}{2^n}$ seconds. All of the infinitely many steps happen before the clock reads 1 second, and at that moment you are at the wall. Splitting one second into infinitely many pieces does not make it longer.

Any ratio: a shortcut and a warning

In our series each term is half of the one before. A series in which each term is a fixed multiple $r$ of the one before is called a geometric series, and $r$ is its ratio. The simplest one starts at 1:

$$ 1 + r + r^2 + r^3 + \cdots $$

There is a quick way to guess its sum. Call the sum $S$. Everything after the first term is $r$ times the whole series:

$$ S = 1 + r\,(1 + r + r^2 + \cdots) = 1 + rS, $$

and solving $S = 1 + rS$ gives $S = \frac{1}{1 - r}$. For $r = \frac12$ this predicts $1 + \frac12 + \frac14 + \cdots = 2$, which is right, since it is 1 more than the series we just summed. For $r = \frac13$ it predicts $\frac32$, and the partial sums $1,\ 1.333,\ 1.444,\ 1.481,\ 1.494$ agree.

Pause and try

Apply the shortcut to $r = 2$. What does it claim about $1 + 2 + 4 + 8 + \cdots$? Is the claim believable? If it is not, which step of the argument is at fault?

Show answerHide answer

The formula gives $S = \frac{1}{1 - 2} = -1$, so it claims $1 + 2 + 4 + 8 + \cdots = -1$. That is absurd: the partial sums $1, 3, 7, 15, \ldots$ are positive and grow without bound, so they do not approach $-1$ or anything else. Every algebraic step was valid for a number $S$. The fault is the first line, “call the sum $S$”, which assumes the series has a sum.

So the shortcut proves only this: if the series converges, its sum is $\frac{1}{1-r}$. It cannot tell us when that happens. To find out we go back to the definition and look at partial sums, which are finite sums and can be handled by ordinary algebra. The same idea, multiplying by $r$ and comparing, works for them without any assumption.

The sum of finitely many terms

Take five terms, $s_5 = 1 + r + r^2 + r^3 + r^4$. Multiplying by $r$ turns each term into the next one.

  1. The five terms of the partial sum, drawn as bars. Here r = 0.7, so each bar is 0.7 times as tall as the one before it.
  2. Multiply every bar by r. Each shrinks to the height of its right-hand neighbor, so r times the sum is the same row moved one place right, with a new last bar, r to the fifth.
  3. Subtract the second row from the first. The bars r, r squared, r cubed and r to the fourth are in both rows, and they cancel.
  4. Only the first bar of the top row and the last bar of the bottom row are left.
The partial sum $s_5 = 1 + r + r^2 + r^3 + r^4$ drawn as bars, for $r = 0.7$, and the same sum multiplied by $r$. Subtracting one row from the other cancels every bar except the first of the top row and the last of the bottom row.
Monocurl source
let R4 = 0.7
let HB = 1.65
let BW = 0.82
let colx = |k| -2.5 + 1.14 * k
let TOPY = 0.3
let BOTY = -1.62
let PW = ["1", "r", "r^2", "r^3", "r^4", "r^5"]
let Top = |f| block {
    for (k in range(0, 5)) {
        var col = AZURE
        if (k >= 1) {
            col = lerp(AZURE, MIST, f)
        }
        . Bar(colx(k) - BW / 2, TOPY, BW, HB * R4 ^ k, col, 0.3)
        . center{[colx(k), TOPY - 0.3, 0]} Math(PW[k], 0.95, col)
    }
}
let Bottom = |t, f| block {
    for (k in range(0, 5)) {
        var col = lerp(AZURE, EMBER, t)
        if (k <= 3) {
            col = lerp(col, MIST, f)
        }
        . Bar(lerp(colx(k), colx(k + 1), t) - BW / 2, lerp(TOPY, BOTY, t), BW, HB * R4 ^ k * lerp(1, R4, t), col, 0.3)
        if (t > 0.999) {
            . center{[colx(k + 1), BOTY - 0.3, 0]} Math(PW[k + 1], 0.95, col)
        }
    }
}
mesh top = Top(f: 0)
mesh bot = []
mesh names = [
    center{[-3.45, 1.1, 0]} Math("s_5", 1.15, AZURE),
    center{[colx(5), 0.85, 0]} Math("r = 0.7", 0.95, SLATE)
]
mesh eq = []

slide "The five terms of the partial sum, drawn as bars. Here r = 0.7, so each bar is 0.7 times as tall as the one before it."
    play Wait(0.5)

slide "Multiply every bar by r. Each shrinks to the height of its right-hand neighbor, so r times the sum is the same row moved one place right, with a new last bar, r to the fifth."
    bot = Bottom(t: 0, f: 0)
    play Set([&bot])
    bot.t = 1
    play Lerp(2, [&bot])
    names .= center{[colx(0), -1.05, 0]} Math("r\,s_5", 1.15, EMBER)
    play Fade(0.4, [&names])

slide "Subtract the second row from the first. The bars r, r squared, r cubed and r to the fourth are in both rows, and they cancel."
    top.f = 1
    bot.f = 1
    play Lerp(1.5, [&top, &bot])

slide "Only the first bar of the top row and the last bar of the bottom row are left."
    eq = center{[1.9, 1.7, 0]} Math("s_5 - r\,s_5 = 1 - r^5", 1.1, INK)
    play Fade(0.6, [&eq])

Uses the shared definitions for this lesson's figures.

In symbols, $r s_5 = r + r^2 + r^3 + r^4 + r^5$, and subtracting gives $s_5 - r s_5 = 1 - r^5$, that is $(1 - r)\,s_5 = 1 - r^5$. Nothing here depended on there being five terms.

The hypothesis $r \ne 1$ is needed only for the last division. When $r = 1$ every term is 1 and the sum of $n$ terms is $n$. Test the formula on cases we know: for $r = 2$ and $n = 3$ it gives $\frac{1 - 8}{1 - 2} = 7 = 1 + 2 + 4$, and for $r = \frac12$ it gives $s_n = 2\left(1 - \frac{1}{2^n}\right)$, which approaches 2.

When the series converges

The partial sum is $\frac{1 - r^n}{1 - r}$, and the only part that depends on $n$ is $r^n$. For $r = \frac12$ the powers shrink toward 0 and the partial sums settle at $\frac{1}{1-r}$. For $r = 2$ the powers explode. Everything turns on the behavior of $r^n$.

There is a picture of the formula $\frac{1}{1 - r}$. Take $0 < r < 1$ and stand squares of side $1, r, r^2, \ldots$ in a row. Their top-left corners lie on one straight line (the corner of the $k$th square is at distance $1 + r + \cdots + r^{k-1} = \frac{1 - r^k}{1 - r}$ along the base, at height $r^k$, and these points satisfy $y = 1 - (1 - r)x$). The line reaches the base at $x = \frac{1}{1-r}$, which is where the row of squares ends.

Squares of side $1, r, r^2, \ldots$ in a row, as $r$ grows from 0.5 to 0.7. The green line through their corners meets the base where the row ends. The small orange triangle with legs 1 and $1 - r$ has the same shape as the large triangle with legs $\frac{1}{1-r}$ and 1, which is the sum formula.
Monocurl source
let L5 = -3.45
let B5 = -1.2
let U5 = 2.0
let Stair = |r| {
    let S = 1 / (1 - r)
    var out = []
    var x = 0
    for (k in range(0, 12)) {
        let side = r ^ k
        out .= Bar(L5 + U5 * x, B5, U5 * side, U5 * side, AZURE, 0.16, 2)
        x = x + side
    }
    out .= Region([[L5, B5 + U5, 0], [L5 + U5, B5 + U5, 0], [L5 + U5, B5 + U5 * r, 0]], tint(EMBER, 0.4))
    out .= Seg([L5, B5 + U5, 0], [L5 + U5 * S, B5, 0], LEAF, 4)
    out .= center{[L5 - 0.24, B5 + U5 / 2, 0]} Math("1", 0.85, INK)
    out .= center{[L5 + U5 / 2, B5 + U5 + 0.27, 0]} Math("1", 0.85, EMBER)
    out .= center{[L5 + U5 + 0.6, B5 + U5 * (1 + r) / 2, 0]} Math("1 - r", 0.85, EMBER)
    out .= center{[L5 + U5 / 2, B5 - 0.28, 0]} Math("1", 0.8, AZURE)
    out .= center{[L5 + U5 * (1 + r / 2), B5 - 0.28, 0]} Math("r", 0.8, AZURE)
    out .= center{[L5 + U5 * (1 + r + r * r / 2), B5 - 0.28, 0]} Math("r^2", 0.8, AZURE)
    out .= Seg([L5, B5 - 0.72, 0], [L5 + U5 * S, B5 - 0.72, 0], LEAF, 3.5)
    out .= Seg([L5, B5 - 0.84, 0], [L5, B5 - 0.6, 0], LEAF, 3.5)
    out .= Seg([L5 + U5 * S, B5 - 0.84, 0], [L5 + U5 * S, B5 - 0.6, 0], LEAF, 3.5)
    out .= center{[2.3, 1.0, 0]} scale{1.2} color{INK} Number(r, 2)
    out .= center{[2.75, 0.3, 0]} scale{1.2} color{LEAF} Number(S, 2)
    return out
}
mesh labels = [
    center{[1.75, 1.7, 0]} Math("1 + r + r^2 + \cdots = \frac{1}{1 - r}", 0.95, LEAF),
    center{[1.55, 1.0, 0]} Math("r =", 0.9, INK),
    center{[1.45, 0.3, 0]} Math("\text{total width} =", 0.8, LEAF)
]
mesh stair = Stair(r: 0.5)

slide "grow"
    play Wait(0.8)
    stair.r = 0.7
    play Lerp(5, [&stair])
    play Wait(2)

Uses the shared definitions for this lesson's figures.

Pause and try

The series $1 - \frac12 + \frac14 - \frac18 + \cdots$ has alternating signs. Is it geometric? Before computing, decide whether its sum should be more or less than $\frac12$, and more or less than 1. Then find it.

Show answerHide answer

It is geometric with $a = 1$ and $r = -\frac12$, and $|r| < 1$, so it converges, to $\frac{1}{1 - (-\frac12)} = \frac{1}{3/2} = \frac23$. The partial sums are $1,\ \frac12,\ \frac34,\ \frac58,\ \frac{11}{16}, \ldots$ They jump back and forth across $\frac23$, each jump half as long as the last, so the sum lies between any two consecutive ones: between $\frac12$ and 1.

Where it fails

Here are the partial sums for four ratios, starting from $a = 1$.

Partial sums of 1 + r + r^2 + \cdots for four ratios. For r = \frac12 they settle at 2. For r = -1 they alternate between 1 and 0 forever. For r = 1 and r = 1.2 they grow without bound. Only the first series converges.
Partial sums of $1 + r + r^2 + \cdots$ for four ratios. For $r = \frac12$ they settle at 2. For $r = -1$ they alternate between 1 and 0 forever. For $r = 1$ and $r = 1.2$ they grow without bound. Only the first series converges.
Monocurl source
let fd = Frame(0, 10.6, -0.5, 6.5, [-0.85, 0.25, 0], 4.9, 3.1)
let psum = |r, n| {
    var total = 0
    for (k in range(0, n)) {
        total = total + r ^ k
    }
    return total
}
let Track = |r, col| {
    let pts = filter(map(range(1, 11), |n| [n, psum(r, n)]), |q| q[1] < 6.5)
    return [
        stroke{tint(col, 0.55), 2.5} Polyline(map(pts, |q| at(fd, q[0], q[1]))),
        map(pts, |q| Pt(at(fd, q[0], q[1]), col, 0.085))
    ]
}
mesh grid = GridLines(fd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6])
mesh axes = [
    Axes(fd),
    YTicks(fd, [1, 2, 3, 4, 5, 6]),
    center{[fx(fd, 10.6) - 1.2, fy(fd, -0.5) - 0.7, 0]} Math("\text{number of terms } n", 0.78, SLATE),
    center{[fx(fd, 0) + 1.25, fy(fd, 6.5) + 0.3, 0]} Math("\text{partial sum } s_n", 0.78, SLATE)
]
mesh xticks = block {
    for (n in [2, 4, 6, 8, 10]) {
        . center{[fx(fd, n), fy(fd, -0.5) - 0.28, 0]} Math(to_string(n), 0.72, SLATE)
    }
}
mesh two = Dash(at(fd, 0, 2), at(fd, 10.6, 2), LEAF, 2.5)
mesh tracks = [Track(1.2, ROSE), Track(1, GOLD), Track(-1, PLUM), Track(0.5, LEAF)]
mesh legend = [
    center{[2.85, 1.45, 0]} Math("r = 1.2", 0.85, ROSE),
    center{[2.85, 1.0, 0]} Cap("grows faster and faster", 0.7, ROSE),
    center{[2.85, 0.4, 0]} Math("r = 1", 0.85, GOLD),
    center{[2.85, -0.05, 0]} Cap("1, 2, 3, 4, ...", 0.7, GOLD),
    center{[2.85, -0.65, 0]} Math("r = \frac12", 0.85, LEAF),
    center{[2.85, -1.15, 0]} Cap("settles at 2", 0.7, LEAF),
    center{[2.85, -1.7, 0]} Math("r = -1", 0.85, PLUM),
    center{[2.85, -2.05, 0]} Cap("1, 0, 1, 0, ...", 0.7, PLUM)
]

Uses the shared definitions for this lesson's figures.

The case $r = -1$ is the series $1 - 1 + 1 - 1 + \cdots$. The shortcut $S = 1 + rS$ assigns it the value $\frac12$, and in the eighteenth century several mathematicians defended that answer. By our definition the partial sums are $1, 0, 1, 0, \ldots$, which approach nothing, so the series diverges and has no sum.

The proof used the fact that the terms of a convergent series have limit 0. The converse is false: terms that shrink to 0 do not guarantee convergence. The standard counterexample is the harmonic series $1 + \frac12 + \frac13 + \frac14 + \cdots$. Group its terms as

$$ 1 + \frac12 + \left(\frac13 + \frac14\right) + \left(\frac15 + \frac16 + \frac17 + \frac18\right) + \cdots $$

Each bracket is more than $\frac12$: the first has two terms that are each at least $\frac14$, the next has four terms that are each at least $\frac18$, and so on. So the partial sum with $2^k$ terms is at least $1 + \frac{k}{2}$, which grows without bound. The terms of a geometric series with $|r| < 1$ do more than shrink. They shrink by a fixed factor each time, and that is why the sum can be controlled.

The next figure shows the convergence and its failure for all ratios at once.

The partial sums s_2, s_4 and s_8 of 1 + r + r^2 + \cdots as functions of the ratio r, with the curve \frac{1}{1-r} in green. Inside the shaded strip -1 < r < 1 the partial sums close in on the green curve as terms are added. Outside it they move away.
The partial sums $s_2$, $s_4$ and $s_8$ of $1 + r + r^2 + \cdots$ as functions of the ratio $r$, with the curve $\frac{1}{1-r}$ in green. Inside the shaded strip $-1 < r < 1$ the partial sums close in on the green curve as terms are added. Outside it they move away.
Monocurl source
let fq = Frame(-1.6, 1.6, -1, 6, [0.1, 0.25, 0], 6.3, 3.1)
let poly = |r, n| {
    var total = 0
    for (k in range(0, n)) {
        total = total + r ^ k
    }
    return total
}
let PolyCurve = |n, col| stroke{col, 3.5} Polyline(map(
    filter(map(sample(-1.6, 1.6, 160), |r| [r, poly(r, n)]), |q| q[1] > -1 and q[1] < 6),
    |q| at(fq, q[0], q[1])
))
mesh strip = Region([at(fq, -1, -1), at(fq, 1, -1), at(fq, 1, 6), at(fq, -1, 6)], tint(LEAF, 0.1))
mesh grid = GridLines(fq, [-1.5, -1, -0.5, 0.5, 1, 1.5], [1, 2, 3, 4, 5, 6])
mesh axes = [
    Axes(fq),
    center{[fx(fq, 1.6) - 0.5, fy(fq, -1) - 0.7, 0]} Math("\text{ratio } r", 0.78, SLATE),
    center{[fx(fq, 0) - 0.75, fy(fq, 6) + 0.3, 0]} Math("\text{sum}", 0.78, SLATE)
]
mesh ticks = block {
    for (x in [-1, -0.5, 0, 0.5, 1]) {
        . center{[fx(fq, x), fy(fq, -1) - 0.28, 0]} Math(to_string(x, 1), 0.72, SLATE)
    }
    for (y in [1, 2, 3, 4, 5, 6]) {
        . center{[fx(fq, -1.6) - 0.28, fy(fq, y), 0]} Math(to_string(y), 0.72, SLATE)
    }
}
mesh curves = [
    PolyCurve(2, AZURE),
    PolyCurve(4, PLUM),
    PolyCurve(8, EMBER),
    stroke{LEAF, 5} Polyline(map(sample(-1.6, 0.833, 120), |r| at(fq, r, 1 / (1 - r))))
]
mesh names = [
    center{at(fq, 1.42, 1.95)} Math("s_2", 0.85, AZURE),
    center{at(fq, 1.42, 4.5)} Math("s_4", 0.85, PLUM),
    center{at(fq, 1.06, 5.3)} Math("s_8", 0.85, EMBER),
    center{at(fq, 0.5, 4.6)} Math("\frac{1}{1 - r}", 0.85, LEAF)
]

Uses the shared definitions for this lesson's figures.

Why 0.999... equals 1

What does the notation $0.999\ldots$ mean? A finite decimal such as $0.999$ is shorthand for the sum $\frac{9}{10} + \frac{9}{100} + \frac{9}{1000}$. A decimal that goes on forever cannot be a finite sum, so it is defined as a series:

(Such a series always converges, because its partial sums never decrease and never exceed 1; this uses the monotone convergence theorem, that an increasing sequence which is bounded above has a limit, from the lesson on the number $e$.) With that definition the equation $0.999\ldots = 1$ is a statement that can be proved.

Four number lines, each a tenfold magnification of the right-hand tenth of the one above. The partial sums 0.9, 0.99, 0.999, 0.9999 (orange) each stop short of 1 by the red gap, and the gap shrinks by a factor of 10 from one line to the next.
Four number lines, each a tenfold magnification of the right-hand tenth of the one above. The partial sums $0.9$, $0.99$, $0.999$, $0.9999$ (orange) each stop short of 1 by the red gap, and the gap shrinks by a factor of 10 from one line to the next.
Monocurl source
let XL = -2.9
let XR = 3.0
let LEFT_NAME = ["0", "0.9", "0.99", "0.999"]
let SUM_NAME = ["0.9", "0.99", "0.999", "0.9999"]
let GAP_NAME = ["\frac{1}{10}", "\frac{1}{100}", "\frac{1}{1000}", "\frac{1}{10000}"]
let rowy = |j| 1.5 - 1.08 * j
let XN = XL + 0.9 * (XR - XL)
mesh wedges = block {
    for (j in range(0, 3)) {
        . Region([[XN, rowy(j) - 0.06, 0], [XR, rowy(j) - 0.06, 0], [XR, rowy(j + 1) + 0.52, 0], [XL, rowy(j + 1) + 0.52, 0]], tint(SLATE, 0.13))
        . center{[-2.2, rowy(j) - 0.33, 0]} Math("\times 10", 0.8, SLATE)
    }
}
mesh rows = block {
    for (j in range(0, 4)) {
        let y = rowy(j)
        . Seg([XL, y, 0], [XN, y, 0], SLATE, 3)
        . Seg([XN, y, 0], [XR, y, 0], ROSE, 5)
        for (k in range(0, 11)) {
            . Seg([XL + (XR - XL) * k / 10, y - 0.09, 0], [XL + (XR - XL) * k / 10, y + 0.09, 0], SLATE, 2)
        }
        . Pt([XN, y, 0], EMBER, 0.1)
        . Pt([XR, y, 0], LEAF, 0.1)
        . center{[XL, y + 0.32, 0]} Math(LEFT_NAME[j], 0.74, SLATE)
        . center{[XN - 0.1, y + 0.32, 0]} Math(SUM_NAME[j], 0.74, EMBER)
        . center{[XR + 0.1, y + 0.32, 0]} Math("1", 0.8, LEAF)
        . center{[XR + 0.58, y - 0.05, 0]} Math(GAP_NAME[j], 0.7, ROSE)
    }
}

Uses the shared definitions for this lesson's figures.

The proof is three lines long, and people still resist the conclusion. The objections deserve answers.

”0.999... is always a little less than 1.” Each partial sum $0.9$, $0.99$, $0.999$ is less than 1. But $0.999\ldots$ is none of the partial sums. It is one fixed number, the limit of the partial sums, so nothing about it is still “getting there”, and a limit of numbers below 1 can equal 1.

”The difference is $0.000\ldots1$, infinitely small but not zero.” The gaps between the partial sums and 1 are $\frac{1}{10}, \frac{1}{100}, \frac{1}{1000}, \ldots$ If $1 - 0.999\ldots$ were a positive number $d$, it would have to be smaller than every one of these gaps, because the partial sums increase toward $0.999\ldots$ and never pass it. But Bernoulli’s inequality with $h = 9$ gives $10^n \ge 1 + 9n > n$, so $\frac{1}{10^n} < \frac1n$, and $\frac1n < d$ as soon as $n > \frac1d$. No positive real number is smaller than every $\frac{1}{10^n}$. And no decimal has the form “$0.000\ldots1$”, because every digit of a decimal sits at some finite position, and there is no position after all of them.

”Then one number has two different decimals.” It does. Decimals are names for numbers, and some numbers have two names, just as $\frac12$ and $\frac24$ name one fraction. Every positive number with a terminating decimal has a second one ending in nines: $0.5 = 0.4999\ldots$ and $2 = 1.999\ldots$

”I was shown a quicker proof: $\frac13 = 0.333\ldots$, so multiplying by 3 gives $1 = 0.999\ldots$” That argument is correct once you grant that $\frac13 = 0.333\ldots$ and that an infinite decimal can be multiplied digit by digit. Both are true, and both are facts about limits of the same kind as the one in question. It shows the equation is consistent with the rest of arithmetic; the proof is the one above.

Worked examples

Stepping back: the hard step was deciding what an infinite sum is. Once the sum is defined as the limit of the partial sums, the rest is algebra on finite sums and one limit, $r^n \to 0$. The shortcut $S = 1 + rS$ is still worth knowing, as long as you remember what it assumes. The finite formula has a second life in the next lesson, where $1 - r^n = (1 - r)(1 + r + \cdots + r^{n-1})$ becomes the reason that a root of a polynomial always gives a factor.

Check yourself

What is the sum of $1 + \frac23 + \frac49 + \frac{8}{27} + \cdots$?

Problems

Work these on paper before opening the solutions. The first three repeat the lesson’s moves, the next three combine them, and the last two go further.

Problem 1

Find the sum of each series, or explain why it has none. (a) $1 + \frac14 + \frac1{16} + \cdots$ (b) $5 - \frac53 + \frac59 - \cdots$ (c) $\frac23 + \frac49 + \frac8{27} + \cdots$ (d) $1 - \frac32 + \frac94 - \cdots$

Show solutionHide solution

(a) $a = 1$, $r = \frac14$: sum $\frac{1}{1 - 1/4} = \frac43$. (b) $a = 5$, $r = -\frac13$: sum $\frac{5}{1 + 1/3} = \frac{15}{4}$. (c) $a = \frac23$, $r = \frac23$: sum $\frac{2/3}{1/3} = 2$. (d) $r = -\frac32$, and $|r| = \frac32 \ge 1$, so the series diverges. Its terms $1, -1.5, 2.25, \ldots$ grow in size instead of approaching 0.

Problem 2

Compute $1 + 2 + 4 + \cdots + 2^{10}$ and $1 + 3 + 9 + \cdots + 3^5$ with the finite sum formula.

Show solutionHide solution

The first has 11 terms, from $2^0$ to $2^{10}$, so $n = 11$: $\frac{1 - 2^{11}}{1 - 2} = 2^{11} - 1 = 2047$. The second has 6 terms: $\frac{1 - 3^6}{1 - 3} = \frac{729 - 1}{2} = 364$. Check the second directly: $1 + 3 + 9 + 27 + 81 + 243 = 364$.

Problem 3

Write $0.3181818\ldots$ (the block 18 repeats) as a fraction in lowest terms.

Show solutionHide solution

Split off the part that does not repeat: $0.3181818\ldots = \frac{3}{10} + \frac{18}{1000} + \frac{18}{100000} + \cdots$. The series is geometric with $a = \frac{18}{1000}$ and $r = \frac{1}{100}$, with sum $\frac{18/1000}{99/100} = \frac{18}{990} = \frac{1}{55}$. So the number is $\frac{3}{10} + \frac{1}{55} = \frac{33}{110} + \frac{2}{110} = \frac{35}{110} = \frac{7}{22}$. Check: $7 \div 22 = 0.31818\ldots$

Problem 4

For which real numbers $x$ does the series $1 + 2x + 4x^2 + 8x^3 + \cdots$ converge, and what is its sum? Evaluate the sum at $x = \frac14$ and at $x = -\frac14$.

Show solutionHide solution

The series is geometric with $a = 1$ and ratio $r = 2x$. It converges exactly when $|2x| < 1$, that is $-\frac12 < x < \frac12$, and then its sum is $\frac{1}{1 - 2x}$. At $x = \frac14$ the sum is $\frac{1}{1 - 1/2} = 2$ (the series is $1 + \frac12 + \frac14 + \cdots$). At $x = -\frac14$ it is $\frac{1}{1 + 1/2} = \frac23$ (the series is $1 - \frac12 + \frac14 - \cdots$).

Problem 5

How many terms of $\frac12 + \frac14 + \frac18 + \cdots$ are needed for the partial sum to differ from the sum by less than $10^{-6}$? How many terms of $0.9 + 0.09 + 0.009 + \cdots$?

Show solutionHide solution

For the first series the shortfall after $n$ terms is $\frac{1}{2^n}$, and we need $2^n > 10^6$. Since $2^{19} = 524\,288$ and $2^{20} = 1\,048\,576$, we need 20 terms. For the second the shortfall is $\frac{1}{10^n}$, and $\frac{1}{10^n} < \frac{1}{10^6}$ requires $n \ge 7$: seven terms. The series with the smaller ratio converges faster, because the shortfall is multiplied by $r$ at each step.

Problem 6

Achilles runs at 10 meters per second and a tortoise at 1 meter per second. The tortoise starts 9 meters ahead. Zeno argues: by the time Achilles reaches the tortoise’s starting point, the tortoise has moved on; by the time he reaches that point, it has moved on again; so he never catches it. Find the duration of each stage of this description, add the durations, and compare with the answer from ordinary algebra.

Show solutionHide solution

Stage 1: Achilles covers 9 m in $0.9$ s, during which the tortoise moves $0.9$ m. Stage 2: he covers $0.9$ m in $0.09$ s, and the tortoise moves $0.09$ m. Stage 3 takes $0.009$ s, and so on. Each stage is a tenth as long as the one before, so the total time is $0.9 + 0.09 + 0.009 + \cdots = \frac{0.9}{1 - 0.1} = 1$ second (this is $0.999\ldots$ seconds). By algebra: after $t$ seconds Achilles is at $10t$ and the tortoise at $9 + t$, and $10t = 9 + t$ gives $t = 1$. Zeno’s description is accurate. It cuts the one second before the catch into infinitely many stages, all of which fit inside that second.

Problem 7

Here are three “computations” of $S = 1 - 1 + 1 - 1 + \cdots$. (i) $S = (1 - 1) + (1 - 1) + \cdots = 0$. (ii) $S = 1 + (-1 + 1) + (-1 + 1) + \cdots = 1$. (iii) $S = 1 - (1 - 1 + 1 - \cdots) = 1 - S$, so $S = \frac12$. What does the definition say, and what is the common error?

Show solutionHide solution

The partial sums are $1, 0, 1, 0, \ldots$ For any proposed limit $S$, take $\varepsilon = \frac12$: the partial sums 0 and 1 are a distance 1 apart, so they cannot both be within $\frac12$ of $S$, and both occur beyond every $N$. So the sequence has no limit and the series diverges. All three computations begin by treating $S$ as a number and then apply rules of finite arithmetic (regrouping, factoring out $-1$) to it. Those rules were never established for a symbol that stands for no number, and the three contradictory answers show what happens when they are used anyway. In (i) and (ii) the bracketing also silently selects only the even or only the odd partial sums.

Problem 8

Let $T_n = \frac12 + \frac{2}{4} + \frac{3}{8} + \cdots + \frac{n}{2^n}$. (a) By computing $T_n - \frac12 T_n$, show that $T_n = 2 - \frac{n + 2}{2^n}$. (b) Show that $2^n \ge \frac{n(n-1)}{2}$ for $n \ge 2$, and deduce that the series $\frac12 + \frac24 + \frac38 + \cdots$ converges to 2.

Show solutionHide solution

(a) $\frac12 T_n = \frac14 + \frac28 + \cdots + \frac{n-1}{2^n} + \frac{n}{2^{n+1}}$. Subtracting from $T_n$ term by term, the coefficient of each $\frac{1}{2^k}$ is $k - (k - 1) = 1$, so

$$ \tfrac12 T_n = \frac12 + \frac14 + \cdots + \frac{1}{2^n} - \frac{n}{2^{n+1}} = 1 - \frac{1}{2^n} - \frac{n}{2^{n+1}}. $$

Doubling, $T_n = 2 - \frac{2}{2^n} - \frac{n}{2^n} = 2 - \frac{n+2}{2^n}$. Check: $T_2 = \frac12 + \frac12 = 1 = 2 - \frac44$. (b) $2^n$ is the number of subsets of a set of $n$ things, and $\frac{n(n-1)}{2}$ is the number of subsets with exactly two members, so $2^n \ge \frac{n(n-1)}{2}$. Hence for $n \ge 2$, $\frac{n}{2^n} \le \frac{2}{n - 1}$, and with $\frac{2}{2^n} < \frac2n$ we get $|T_n - 2| = \frac{n+2}{2^n} < \frac{2}{n-1} + \frac{2}{n}$, which is less than any given $\varepsilon$ once $n$ is large enough. So the partial sums have limit 2. The terms $\frac{n}{2^n}$ are not geometric, but the multiply-and-subtract idea reduces the sum to a geometric one.

Shared definitions used by this lesson's figures
# house kit: prepended to every essay figure after the std imports

# palette. AZURE and EMBER are the two actors, LEAF marks an answer, ROSE a problem
let PAPER = [1, 1, 1, 1]
let INK = [0.08, 0.09, 0.11, 1]
let SLATE = [0.40, 0.44, 0.50, 1]
let MIST = [0.85, 0.86, 0.88, 1]
let AZURE = [0.13, 0.42, 0.68, 1]
let EMBER = [0.89, 0.42, 0.12, 1]
let LEAF = [0.13, 0.55, 0.36, 1]
let ROSE = [0.78, 0.22, 0.24, 1]
let PLUM = [0.47, 0.30, 0.66, 1]
let GOLD = [0.85, 0.62, 0.08, 1]

background = PAPER

# an opaque pastel of col; use for fills so overlaps do not darken
let tint = |col, amount = 0.18| [
    1 - (1 - col[0]) * amount,
    1 - (1 - col[1]) * amount,
    1 - (1 - col[2]) * amount,
    1
]

# text: Cap for labels, Note for quiet labels, Math for typeset maths
let Cap = |txt, size = 0.75, col = INK| color{col} Text(txt, size, "Charter")
let Note = |txt, size = 0.65| color{SLATE} Text(txt, size, "Charter")
let Math = |tex, size = 0.9, col = INK| color{col} Tex(tex, size)

# a paper rectangle to sit behind text that crosses lines
let Plate = |pos, size| center{pos} fill{PAPER} stroke{alpha{0} PAPER, 0} Rect(size)

# shapes with fill and stroke both set, so no stray black appears
let Pt = |pos, col = INK, r = 0.075| center{pos} fill{col} stroke{col, 1} Circle(r, 48)
let Ring = |pos, col = INK, r = 0.1, w = 3| center{pos} fill{PAPER} stroke{col, w} Circle(r, 48)
let Seg = |a, b, col = INK, w = 3| stroke{col, w} Line(a, b)
let Path = |pts, col = INK, w = 3| stroke{col, w} Polyline(pts)
let Region = |pts, col| fill{col} stroke{alpha{0} col, 0} Polygon(pts)
let Outline = |pts, col = INK, w = 3| fill{alpha{0} PAPER} stroke{col, w} Polygon(pts)
let Shape = |pts, col, amount = 0.18, w = 3| fill{tint(col, amount)} stroke{col, w} Polygon(pts)
let Disc = |pos, r, col, amount = 0.18, w = 3| center{pos} fill{tint(col, amount)} stroke{col, w} Circle(r, 96)
let Box = |pos, size, col, amount = 0.18, w = 3| center{pos} fill{tint(col, amount)} stroke{col, w} Rect(size)
# a line from a to b ending in a small solid arrowhead
let Tip = |a, b, col = INK, w = 3, head = 0.17| {
    let d = normalize([b[0] - a[0], b[1] - a[1], 0])
    let n = [-d[1], d[0], 0]
    let base = [b[0] - d[0] * head, b[1] - d[1] * head, 0]
    return [
        stroke{col, w} Line(a, base),
        fill{col} stroke{col, 1} Polygon([
            b,
            [base[0] + n[0] * head * 0.42, base[1] + n[1] * head * 0.42, 0],
            [base[0] - n[0] * head * 0.42, base[1] - n[1] * head * 0.42, 0]
        ])
    ]
}

# plots. a frame is [x0, x1, y0, y1, centre, width, height]: a data window and the scene box it fills
let Frame = |x0, x1, y0, y1, c = [0, 0, 0], w = 6.4, h = 3.6| [x0, x1, y0, y1, c, w, h]
let fx = |fr, x| fr[4][0] + (x - (fr[0] + fr[1]) / 2) * fr[5] / (fr[1] - fr[0])
let fy = |fr, y| fr[4][1] + (y - (fr[2] + fr[3]) / 2) * fr[6] / (fr[3] - fr[2])
let at = |fr, x, y| [fx(fr, x), fy(fr, y), 0]

# axes through the data origin (or the nearest edge of the window), with optional names
let Axes = |fr, x_name = "", y_name = "", col = SLATE| {
    let ox = clamp(fr[0], 0, fr[1])
    let oy = clamp(fr[2], 0, fr[3])
    var parts = [
        Tip(at(fr, fr[0], oy), [fx(fr, fr[1]) + 0.25, fy(fr, oy), 0], col, 2.5, 0.15),
        Tip(at(fr, ox, fr[2]), [fx(fr, ox), fy(fr, fr[3]) + 0.25, 0], col, 2.5, 0.15)
    ]
    if (x_name != "") {
        parts .= center{[fx(fr, fr[1]) + 0.5, fy(fr, oy), 0]} Math(x_name, 0.8, col)
    }
    if (y_name != "") {
        parts .= center{[fx(fr, ox), fy(fr, fr[3]) + 0.5, 0]} Math(y_name, 0.8, col)
    }
    return parts
}

# tick marks with numbers at the listed data values
let XTicks = |fr, xs, col = SLATE, places = 0| block {
    let oy = fy(fr, clamp(fr[2], 0, fr[3]))
    for (x in xs) {
        . Seg([fx(fr, x), oy - 0.07, 0], [fx(fr, x), oy + 0.07, 0], col, 2)
        . center{[fx(fr, x), oy - 0.33, 0]} Math(to_string(x, places), 0.72, col)
    }
}
let YTicks = |fr, ys, col = SLATE, places = 0| block {
    let ox = fx(fr, clamp(fr[0], 0, fr[1]))
    for (y in ys) {
        . Seg([ox - 0.07, fy(fr, y), 0], [ox + 0.07, fy(fr, y), 0], col, 2)
        . center{[ox - 0.36, fy(fr, y), 0]} Math(to_string(y, places), 0.72, col)
    }
}

# faint grid lines at the listed data values
let GridLines = |fr, xs, ys, col = MIST| block {
    for (x in xs) {
        . Seg(at(fr, x, fr[2]), at(fr, x, fr[3]), col, 1.5)
    }
    for (y in ys) {
        . Seg(at(fr, fr[0], y), at(fr, fr[1], y), col, 1.5)
    }
}

# the graph of f over [a, b], drawn in the frame. values are clamped to the window
let Graph = |fr, f, a, b, col = AZURE, w = 4, n = 160|
    stroke{col, w} Polyline(map(sample(a, b, n), |x| at(fr, x, clamp(fr[2], f(x), fr[3]))))

# the region between the graph of f and the x axis over [a, b]
let AreaUnder = |fr, f, a, b, col = AZURE, amount = 0.2, n = 80| {
    var pts = [at(fr, a, 0)]
    for (x in sample(a, b, n)) {
        pts .= at(fr, x, clamp(fr[2], f(x), fr[3]))
    }
    pts .= at(fr, b, 0)
    return fill{tint(col, amount)} stroke{alpha{0} col, 0} Polygon(pts)
}

let Dash = |a, b, col = SLATE, w = 2.5| stroke{col, w} dashed{[0.1, 0.08]} Line(a, b)
# an axis-parallel rectangle with lower-left corner [x, y], filled and outlined
let Bar = |x, y, w, h, col, amount = 0.25, lw = 2.5| Shape([[x, y, 0], [x + w, y, 0], [x + w, y + h, 0], [x, y + h, 0]], col, amount, lw)