N = 50
#| $S_1, \ldots, S_n$ by adding the terms one at a time.
def partial_sums(a, r, n) {
"S_1 .. S_n by adding the terms one at a time."
s = zeros(n)
total = 0.0
term = a
for i to n {
total += term
s[i] = total
term = term * r
}
return s
}
#| $S_n = a\,\frac{1 - r^n}{1 - r}$, from $S_n - rS_n = a - ar^n$.
def closed_form(a, r, n) {
"a (1 - r^n) / (1 - r)."
return a * (1.0 - pow(r, n)) / (1.0 - r)
}
half = partial_sums(1.0, 0.5, N)
nine = partial_sums(1.0, 0.9, N)
show half[9]
show nine[N - 1]
ns = linspace(1.0, N, N)
figure("Running sums of a geometric series", "terms added", "sum")
plot(ns, half, "r = 1/2, limit 2")
plot(ns, nine, "r = 0.9, limit 10")
test "the running sum matches a (1 - r^n) / (1 - r) at every n, for both ratios" {
for i to N {
assert_near(half[i], closed_form(1.0, 0.5, i + 1), 1.0e-12)
assert_near(nine[i], closed_form(1.0, 0.9, i + 1), 1.0e-9)
}
}
test "ten terms of 1 + 1/2 + 1/4 + ... come to 2 - 2^-9 = 1.998046875, exactly" {
# the third term is 1/4
assert_near(half[2] - half[1], 0.25, 0.0)
assert_near(half[9], 1.998046875, 1.0e-14)
}
test "the r = 0.9 series takes 44 terms to get within 1% of its limit, and the r = 1/2 series takes 7" {
# the number of terms before the running sum is first within 1% of the limit
nine_terms = 0
half_terms = 0
for i to N {
if nine_terms == 0 and abs(nine[i] - 10.0) < 0.01 * 10.0 {
nine_terms = i + 1
}
if half_terms == 0 and abs(half[i] - 2.0) < 0.01 * 2.0 {
half_terms = i + 1
}
}
assert_near(nine_terms, 44, 0.0)
assert_near(half_terms, 7, 0.0)
}
test "fifty terms of the r = 0.9 series are within 0.6% of the limit 10" {
assert_lt(abs(nine[N - 1] - 10.0) / 10.0, 0.006)
}
test "a = 3, r = 0.2: five terms give 3.7488 and the limit is 3.75" {
s = partial_sums(3.0, 0.2, 5)
assert_near(s[4], 3.7488, 1.0e-12)
assert_near(3.0 / (1.0 - 0.2), 3.75, 1.0e-12)
}
# SPDX-License-Identifier: MIT OR Apache-2.0
# SPDX-FileCopyrightText: 2026 The Viv authors