← Back to Go Examples
Lessons · Go · Mathematics

College Algebra Refresher

It's been years. You remember that algebra had rules, that exponents did something, that there was a formula with a square root in it — and not much else. This page rebuilds college algebra from the ground up in seven lessons, written for an adult who once knew it. Every lesson says the idea in plain words, works one example by hand, then hands you two problems to do before the answer appears. The Go code is there too, tucked in a drop-down under each lesson, because a computer that agrees with your pencil is the best proof you've got it right. Open it after you've done the maths, not instead.

The finish line — what your pencil and the computer will both say
$ go run . 2^x = 50 -> x = ln50/ln2 = 5.6439 check: 2^x = 50.0000 $1000 at 6% monthly for 10 years = $1819.40 doubling time = 11.58 years (rule of 72 guess: 12.00)
Seven lessons ~2 hours
METHOD

How to teach yourself from this page.

Reading maths feels like understanding it. It isn't. The only way to know whether you've got an idea back is to use it with nothing in front of you, and then say it out loud in your own words. Every lesson below is built around that, so follow the same five moves each time:

  1. Read the idea at the top of the lesson. It's short on purpose.
  2. Cover the worked example with your hand and try it yourself first. Then uncover it a line at a time and see where you and the page part ways.
  3. Do the “Now you” problems on paper before you click Reveal. Getting one wrong and seeing why is worth more than getting it right by peeking.
  4. Answer the “Explain it simply” prompt out loud, as if to a teenager. Where you reach for jargon, that's the part you haven't really got back yet. Go re-read that bit.
  5. Then open the Go program. It does the same arithmetic you just did. If your numbers match its output, move on. If they don't, one of you is wrong — and it's usually a sign error.
Why Go is in a drop-down

Code is a great way to check maths and a poor way to learn it. A program will happily compute the wrong thing very precisely. So the maths comes first, on paper, and the program is the answer key. Each one is complete: copy it into a folder, go mod init algebra, go run ., and it prints exactly the output shown. If you haven't installed Go yet, Install Go takes ten minutes.

Pencil first. Reveal second. Explain it out loud third. Code last.

LESSON 01

Exponents and radicals.

An exponent is a count of multiplications: 23 means 2 × 2 × 2. Every exponent rule is a shortcut you could rediscover by writing the multiplications out, which is worth doing once so the rules stop feeling arbitrary. A radical (square root, cube root) is just an exponent that's a fraction, so it obeys the same rules.

RuleSaysBecause
Productam · an = am+nthree 2s times four 2s is seven 2s
Quotientam / an = am−ncancel n of the 2s top and bottom
Power(am)n = amnn groups of m 2s
Zeroa0 = 1an / an = a0, and anything over itself is 1
Negativea−n = 1 / ankeep subtracting past zero and the 2s move to the bottom
Fractionala1/n = n√a    am/n = (n√a)m(a1/2)2 = a1, so a1/2 must be the square root

Worked by hand

Simplify   (2x3)2 · x−4
  1. Power rule on the bracket, and it applies to the 2 as well: (2x3)2 = 22 · x6 = 4x6
  2. Product rule: 4x6 · x−4 = 4x6 + (−4) = 4x2
Simplify   √72
  1. Find the largest perfect square inside: 72 = 36 × 2
  2. Roots split over multiplication: √72 = √36 · √2 = 6√2  (≈ 8.485)
Evaluate   82/3
  1. Read am/n as “nth root, then mth power” — root first keeps the numbers small: 3√8 = 2
  2. Then square it: 22 = 4
✎ Now you — on paper, then reveal
  1. Simplify x5 · x−2 / x
  2. Evaluate 163/4
  3. Simplify √50
Reveal the answers

1. x5 − 2 − 1 = x2.   2. Fourth root of 16 is 2, then cubed: 8.   3. 50 = 25 × 2, so 5√2. If you got 25√2 you forgot to take the root of the 25.

💡 Explain it simply

A teenager says “anything to the power zero is one” is just a rule someone made up. Convince them it couldn't be anything else, using only the quotient rule.

Reveal a plain-language answer

Take 23 divided by 23. That's 8 over 8, which is obviously 1. But the quotient rule says subtract the exponents: 23 − 3 = 20. Both are the same calculation, so 20 has to be 1 — not because anyone decided it, but because the alternative would make the quotient rule lie. The same trick shows why negative exponents mean “one over”: 23 / 25 is 8/32 = 1/4, and the rule says 2−2, so 2−2 must be 1/4.

Check it in Go

math.Pow(a, b) is ab for any real exponent, so every rule in the table can be checked numerically. One thing to notice in the output: 8^(1/3) prints as 1.9999999999999998, not 2. That's not a maths error — it's floating-point arithmetic being honest about the fact that 1/3 can't be stored exactly. math.Cbrt exists precisely so you get an exact 2 when you want a cube root.

▶ Show the Go program lesson1.go · 27 lines
package main

import (
	"fmt"
	"math"
)

func main() {
	// product rule: a^m * a^n = a^(m+n)
	fmt.Println("2^3 * 2^4 =", math.Pow(2, 3)*math.Pow(2, 4), "= 2^7 =", math.Pow(2, 7))

	// quotient rule: a^m / a^n = a^(m-n)
	fmt.Println("2^7 / 2^5 =", math.Pow(2, 7)/math.Pow(2, 5), "= 2^2 =", math.Pow(2, 2))

	// power rule: (a^m)^n = a^(m*n)
	fmt.Println("(3^2)^3 =", math.Pow(math.Pow(3, 2), 3), "= 3^6 =", math.Pow(3, 6))

	// negative exponent: a^-n = 1 / a^n
	fmt.Println("5^-2 =", math.Pow(5, -2), "= 1/25 =", 1.0/25)

	// fractional exponent: a^(1/n) is the nth root
	fmt.Println("8^(1/3) =", math.Pow(8, 1.0/3), " cube root:", math.Cbrt(8))
	fmt.Println("8^(2/3) =", math.Pow(8, 2.0/3), " (cube root, then squared:", math.Pow(math.Cbrt(8), 2), ")")

	// simplify a radical: sqrt(72) = sqrt(36 * 2) = 6 * sqrt(2)
	fmt.Printf("sqrt(72) = %.4f   6*sqrt(2) = %.4f\n", math.Sqrt(72), 6*math.Sqrt(2))
}
go run . — output
2^3 * 2^4 = 128 = 2^7 = 128 2^7 / 2^5 = 4 = 2^2 = 4 (3^2)^3 = 729 = 3^6 = 729 5^-2 = 0.04 = 1/25 = 0.04 8^(1/3) = 1.9999999999999998 cube root: 2 8^(2/3) = 4 (cube root, then squared: 4 ) sqrt(72) = 8.4853 6*sqrt(2) = 8.4853
LESSON 02

Factoring — multiplication in reverse.

Expanding is easy: (x + 2)(x + 3) = x2 + 5x + 6, you just multiply everything by everything. Factoring is the same equation read right-to-left, and it's harder because you're guessing what was multiplied. It matters for one reason above all others: if two things multiply to zero, one of them is zero. Factor an expression that equals zero and you've solved the equation.

Four patterns cover almost everything at this level. Always try them in this order:

1. Look forPatternExample
A common factorab + ac = a(b + c)3x2 − 12x = 3x(x − 4)
A difference of squaresa2 − b2 = (a − b)(a + b)x2 − 25 = (x − 5)(x + 5)
A perfect squarea2 ± 2ab + b2 = (a ± b)2x2 + 6x + 9 = (x + 3)2
A trinomial x2 + bx + c(x + p)(x + q) where p·q = c and p + q = bx2 + 5x + 6 = (x + 2)(x + 3)

Worked by hand

Factor   x2 − 7x + 12
  1. No common factor, not a difference of squares. It's a trinomial, so: two numbers that multiply to 12 and add to −7.
  2. Pairs for 12: (1, 12), (2, 6), (3, 4). Adding to −7 means both negative: (−3, −4). Check: (−3)(−4) = 12 ✓, −3 + (−4) = −7 ✓.
  3. So x2 − 7x + 12 = (x − 3)(x − 4). Expand it back to be sure.
Solve   2x2 + 8x + 6 = 0
  1. Common factor first, always: 2(x2 + 4x + 3) = 0
  2. Trinomial: multiply to 3, add to 4 → (1, 3). So 2(x + 1)(x + 3) = 0
  3. Zero product: 2 isn't zero, so either x + 1 = 0 or x + 3 = 0. x = −1 or x = −3.
  4. Check one: 2(−1)2 + 8(−1) + 6 = 2 − 8 + 6 = 0 ✓
✎ Now you — on paper, then reveal
  1. Factor x2 + 2x − 15
  2. Factor 9x2 − 25
  3. Solve x2 − 5x = 0
Reveal the answers

1. Multiply to −15, add to 2: (5, −3). (x + 5)(x − 3).   2. Both terms are squares, 9x2 = (3x)2 and 25 = 52: (3x − 5)(3x + 5).   3. Common factor x: x(x − 5) = 0, so x = 0 or x = 5. The classic mistake is dividing both sides by x and losing the x = 0 answer — never divide by something that might be zero.

💡 Explain it simply

Why does rewriting x2 − 7x + 12 as (x − 3)(x − 4) suddenly make it easy to solve when it equals zero, but do nothing useful when it equals 5?

Reveal a plain-language answer

Zero is special: it's the only number you can't get by multiplying two non-zero numbers together. So if (something) × (something else) = 0, you know one of the two brackets is zero, and each bracket is a one-step equation. If the product is 5 instead, the brackets could be 1 and 5, or 2.5 and 2, or a million pairs — the factored form tells you nothing. That's why the first move in solving any quadratic is to get zero on one side.

Check it in Go

The program does exactly the by-hand search: for x2 + bx + c it tries every integer p that divides c, sets q = c/p, and stops when p + q = b. When nothing works it says so — which is the honest answer for x2 + x + 1, and your cue to reach for the quadratic formula in the next lesson.

▶ Show the Go program lesson2.go · 46 lines
package main

import "fmt"

// factor x^2 + bx + c by finding two integers whose product is c
// and whose sum is b — exactly the by-hand method.
func factor(b, c int) (int, int, bool) {
	for p := -100; p <= 100; p++ {
		if p == 0 {
			continue
		}
		if c%p == 0 {
			q := c / p
			if p+q == b {
				return p, q, true
			}
		}
	}
	return 0, 0, false
}

// signed prints "+ 4" or "- 4" so the output reads like the textbook
func signed(n int) string {
	if n < 0 {
		return fmt.Sprintf("- %d", -n)
	}
	return fmt.Sprintf("+ %d", n)
}

// terms writes the "+ bx + c" part of the polynomial
func terms(b, c int) string {
	return fmt.Sprintf("%sx %s", signed(b), signed(c))
}

func main() {
	problems := [][2]int{{5, 6}, {-7, 12}, {2, -15}, {1, 1}}
	for _, pr := range problems {
		b, c := pr[0], pr[1]
		p, q, ok := factor(b, c)
		if ok {
			fmt.Printf("x^2 %s  =  (x %s)(x %s)\n", terms(b, c), signed(p), signed(q))
		} else {
			fmt.Printf("x^2 %s  does not factor over the integers\n", terms(b, c))
		}
	}
}
go run . — output
x^2 + 5x + 6 = (x + 2)(x + 3) x^2 - 7x + 12 = (x - 4)(x - 3) x^2 + 2x - 15 = (x - 3)(x + 5) x^2 + 1x + 1 does not factor over the integers
LESSON 03

Quadratics: three forms, one parabola.

A quadratic is anything with an x2 in it and nothing higher, and its graph is always a parabola — a U, or an upside-down U if the x2 coefficient is negative. The same parabola can be written three ways, and each form makes one fact obvious:

FormLooks likeShows you at a glance
Standardy = ax2 + bx + cthe y-intercept (it's c)
Factoredy = a(x − r1)(x − r2)the roots, where it crosses the x-axis (r1 and r2)
Vertexy = a(x − h)2 + kthe turning point (h, k) — the lowest or highest point

Lesson 02 got you from standard to factored. Completing the square gets you from standard to vertex form, and it's the technique the quadratic formula is secretly made of.

Worked by hand — completing the square

Write   y = x2 − 6x + 5   in vertex form
  1. Take half the x coefficient and square it: half of −6 is −3, squared is 9. That's the number that would make x2 − 6x a perfect square.
  2. Add it and take it away again, so nothing changes: y = (x2 − 6x + 9) − 9 + 5
  3. The bracket is now a perfect square: y = (x − 3)2 − 4
  4. Read the vertex straight off: (3, −4). Since (x − 3)2 can't be negative, y is smallest when it's zero, at x = 3, and that smallest value is −4.

The shortcut, once you've done it by hand a few times: the vertex is always at x = −b / 2a. Here that's 6/2 = 3. Plug it in for y.

The discriminant — how many roots before you find them

Inside the quadratic formula x = (−b ± √(b2 − 4ac)) / 2a, the part under the root, b2 − 4ac, decides everything:

  • Positive → two real roots. The parabola crosses the x-axis twice.
  • Zero → one repeated root. The vertex sits exactly on the x-axis.
  • Negative → no real roots. You'd need the square root of a negative; the parabola floats above (or below) the axis without touching it.

For x2 − 6x + 5: b2 − 4ac = 36 − 20 = 16, positive, so two roots: x = (6 ± 4)/2 = 5 and 1. Which matches the factoring: (x − 5)(x − 1). Three forms, one parabola.

✎ Now you — on paper, then reveal
  1. Write y = x2 + 4x − 1 in vertex form and state the vertex.
  2. How many real roots does x2 + 2x + 5 = 0 have?
  3. A ball's height is h = −5t2 + 20t. When is it highest, and how high?
Reveal the answers

1. Half of 4 is 2, squared is 4: (x2 + 4x + 4) − 4 − 1 = (x + 2)2 − 5, vertex (−2, −5). Watch the sign: (x + 2) means h = −2.   2. 4 − 20 = −16, negative: none.   3. Vertex at t = −20 / (2 × −5) = 2 seconds; h = −20 + 40 = 20. The negative a means it's an upside-down U, so the vertex is a maximum — the top of the throw.

💡 Explain it simply

Without mentioning a formula, explain why a quadratic can have two solutions, or one, or none — using only the picture of a U-shaped curve and a horizontal line.

Reveal a plain-language answer

Solving ax2 + bx + c = 0 means asking where the U crosses the floor (the x-axis). A U that dips below the floor crosses it on the way down and again on the way up — two answers. A U whose bottom just kisses the floor touches it once — one answer. A U that floats above the floor never touches it — no answers. The discriminant is just a number that tells you which of those three pictures you're looking at, without drawing it.

Check it in Go

The program computes the vertex with −b/2a, then classifies the discriminant with a switch — the three-way branch is the same three cases as the picture above. Change a, b, c at the top to the coefficients from the “Now you” problems and run it again.

▶ Show the Go program lesson3.go · 31 lines
package main

import (
	"fmt"
	"math"
)

func main() {
	// y = ax^2 + bx + c
	a, b, c := 1.0, -6.0, 5.0

	// vertex: x = -b / 2a, then plug in for y
	vx := -b / (2 * a)
	vy := a*vx*vx + b*vx + c
	fmt.Printf("vertex at (%g, %g)\n", vx, vy)
	fmt.Printf("vertex form: y = %g(x - %g)^2 - %g\n", a, vx, -vy)

	// discriminant decides how many real roots there are
	d := b*b - 4*a*c
	fmt.Printf("discriminant b^2 - 4ac = %g\n", d)
	switch {
	case d > 0:
		r1 := (-b + math.Sqrt(d)) / (2 * a)
		r2 := (-b - math.Sqrt(d)) / (2 * a)
		fmt.Printf("two real roots: x = %g and x = %g\n", r1, r2)
	case d == 0:
		fmt.Printf("one repeated root: x = %g\n", -b/(2*a))
	default:
		fmt.Println("no real roots (the parabola never touches the x-axis)")
	}
}
go run . — output
vertex at (3, -4) vertex form: y = 1(x - 3)^2 - 4 discriminant b^2 - 4ac = 16 two real roots: x = 5 and x = 1
LESSON 04

Functions: notation, domain, composition, inverse.

A function is a machine: put a number in, get exactly one number out. f(x) = 2x + 3 is the machine's name and its recipe, and f(4) means “put 4 in”: f(4) = 2(4) + 3 = 11. The notation trips people up because f(x) looks like multiplication. It isn't. Read the brackets as “of”: f of 4.

Four things college algebra asks about any function:

  • Evaluate it — substitute and compute. f(−1), f(a + 1), f(x2) all work the same way: replace every x with the thing in the brackets.
  • Its domain — which inputs are allowed. Two things break: dividing by zero, and taking the square root of a negative. The domain is every real number that avoids both.
  • Compose it with another — feed one machine's output into the next. f(g(x)) means do g first, then f. Written (f ∘ g)(x).
  • Invert it — build the machine that undoes it, f−1. If f turns 4 into 11, f−1 turns 11 back into 4.

Worked by hand

f(x) = 2x + 3,   g(x) = x2.   Find f(g(4)) and g(f(4)).
  1. f(g(4)): inside first. g(4) = 16. Then f(16) = 2(16) + 3 = 35.
  2. g(f(4)): inside first. f(4) = 11. Then g(11) = 121.
  3. Different answers. Order matters in composition — “square then double-plus-three” is not “double-plus-three then square.”
Find the inverse of   f(x) = 2x + 3
  1. Write it as y = 2x + 3.
  2. Swap x and y — that's the whole trick, because an inverse swaps inputs and outputs: x = 2y + 3.
  3. Solve for y: x − 3 = 2y, so y = (x − 3)/2. Hence f−1(x) = (x − 3)/2.
  4. Check: f(4) = 11, and f−1(11) = 8/2 = 4 ✓. An inverse always brings you home.
Find the domain of   h(x) = √(x − 2) / (x − 5)
  1. The root needs x − 2 ≥ 0, so x ≥ 2.
  2. The bottom can't be zero, so x ≠ 5.
  3. Domain: x ≥ 2 and x ≠ 5. In interval notation, [2, 5) ∪ (5, ∞).
✎ Now you — on paper, then reveal
  1. f(x) = 3x − 1. Find f−1(x), and check it with f(2).
  2. What is the domain of 1 / (x2 − 9)?
  3. f(x) = x + 1, g(x) = 2x. Find (f ∘ g)(2) and (g ∘ f)(2).
Reveal the answers

1. x = 3y − 1 → y = (x + 1)/3, so f−1(x) = (x + 1)/3. f(2) = 5, f−1(5) = 2 ✓.   2. x2 − 9 = (x − 3)(x + 3) is zero at 3 and −3, so the domain is all real x except 3 and −3. Lesson 02 paid off.   3. (f ∘ g)(2) = f(g(2)) = f(4) = 5; (g ∘ f)(2) = g(f(2)) = g(3) = 6.

💡 Explain it simply

Explain to someone who's never seen f(x) what a function is, why f(4) isn't “f times 4,” and what makes something not a function.

Reveal a plain-language answer

A function is a rule that takes a number and gives back exactly one number — like a vending machine where every button gives one specific thing. f is the machine's name and f(4) means “press the 4 button”; the answer is whatever comes out, and that has nothing to do with multiplying. The one rule: the same button must always give the same thing. “The square root of 16” is a function if you agree it means 4, but a rule that says “4 or −4, your choice” isn't one, because pressing the button doesn't tell you what you'll get.

Check it in Go

Go functions are values, so f and g are ordinary variables and f(g(4)) is written exactly as in maths. The domain question becomes an error return: h refuses inputs it can't handle and says why, which is the domain made executable.

▶ Show the Go program lesson4.go · 40 lines
package main

import (
	"fmt"
	"math"
)

func main() {
	f := func(x float64) float64 { return 2*x + 3 }
	g := func(x float64) float64 { return x * x }

	fmt.Println("f(4) =", f(4))
	fmt.Println("g(4) =", g(4))

	// composition: (f o g)(x) = f(g(x)) — do g first, then f
	fmt.Println("f(g(4)) =", f(g(4))) // 2*16 + 3
	fmt.Println("g(f(4)) =", g(f(4))) // (2*4+3)^2 — order matters

	// inverse of f: swap x and y in y = 2x + 3, solve for y -> (x - 3) / 2
	fInv := func(x float64) float64 { return (x - 3) / 2 }
	fmt.Println("fInv(f(4)) =", fInv(f(4)), "  (an inverse undoes the function)")

	// domain: h(x) = sqrt(x - 2) / (x - 5) only makes sense for some x
	h := func(x float64) (float64, error) {
		if x-2 < 0 {
			return 0, fmt.Errorf("x=%g: square root of a negative", x)
		}
		if x == 5 {
			return 0, fmt.Errorf("x=%g: division by zero", x)
		}
		return math.Sqrt(x-2) / (x - 5), nil
	}
	for _, x := range []float64{1, 3, 5, 11} {
		if v, err := h(x); err != nil {
			fmt.Println("h(x) undefined:", err)
		} else {
			fmt.Printf("h(%g) = %g\n", x, v)
		}
	}
}
go run . — output
f(4) = 11 g(4) = 16 f(g(4)) = 35 g(f(4)) = 121 fInv(f(4)) = 4 (an inverse undoes the function) h(x) undefined: x=1: square root of a negative h(3) = -0.5 h(x) undefined: x=5: division by zero h(11) = 0.5
LESSON 05

Exponentials and logarithms.

An exponential equation has the unknown in the exponent: 2x = 50. You can't get x down with the tools so far. A logarithm is the tool: logb(y) is the exponent you'd need to put on b to get y. log2(8) = 3 because 23 = 8. That's the whole definition; everything else follows from it.

logb(y) = x    means exactly    bx = y

Two bases matter in practice. ln is log base e (e ≈ 2.718, the base that makes calculus tidy), and log without a subscript usually means base 10. Any other base you reach through the change-of-base rule. The three log rules are the three exponent rules from Lesson 01, seen from the other side:

Log ruleComes from the exponent rule
log(ab) = log a + log bbm · bn = bm+n
log(a/b) = log a − log bbm / bn = bm−n
log(an) = n · log a(bm)n = bmn
logb(y) = ln y / ln bchange of base — lets a calculator with only ln do any base

Worked by hand

Solve   2x = 50
  1. Take the log of both sides. Any base works; use ln: ln(2x) = ln 50
  2. The power rule pulls the x down in front: x · ln 2 = ln 50
  3. Divide: x = ln 50 / ln 2 = 3.912 / 0.693 ≈ 5.644
  4. Sanity check: 25 = 32 and 26 = 64, so 50 needs an x between 5 and 6 ✓
$1,000 at 6% a year, compounded monthly, for 10 years
  1. The formula: A = P(1 + r/n)nt, with P = 1000, r = 0.06, n = 12, t = 10.
  2. Monthly rate 0.06/12 = 0.005; number of months 120: A = 1000 × 1.005120
  3. 1.005120 ≈ 1.8194, so A ≈ $1,819.40
How long until it doubles?
  1. Set A = 2P and cancel P: 2 = 1.00512t
  2. Logs of both sides, power rule: ln 2 = 12t · ln 1.005
  3. t = ln 2 / (12 × ln 1.005) = 0.6931 / (12 × 0.004988) ≈ 11.58 years
  4. The banker's shortcut “rule of 72” says 72/6 = 12 years. Close — now you know where it comes from.
✎ Now you — on paper, then reveal
  1. log2(32) = ?
  2. Solve 10x = 1000, then solve ex = 20 (leave it as a log, then estimate).
  3. Simplify ln(e3) and log5(1).
Reveal the answers

1. 2? = 32 → 5.   2. 103 = 1000 so x = 3; ex = 20 gives x = ln 20 ≈ 3.0 (e3 ≈ 20.1, so just under 3).   3. ln(e3) = 3 — ln and e undo each other, they're inverses in the Lesson 04 sense. log5(1) = 0, because 50 = 1; the log of 1 is 0 in every base.

💡 Explain it simply

Someone says logarithms are the most confusing thing in algebra. Explain what question a logarithm is asking, in one sentence, and then why it's the thing that solves 2x = 50.

Reveal a plain-language answer

A logarithm asks one question: “how many times do I multiply by this base to reach that number?” log2(50) is “how many doublings get you to 50?” — and 2x = 50 is the exact same question written the other way round. So the log isn't a new trick; it's the name for the answer you were already looking for. Five doublings is 32, six is 64, so the answer is a bit over five and a half, and the log just tells you precisely how much.

Check it in Go

math.Log is ln; math.Log10 and math.Log2 exist for those bases, and any other base is math.Log(y) / math.Log(b). The program solves 2x = 50, checks all three log rules numerically, and runs the interest and doubling-time calculations from the worked example.

▶ Show the Go program lesson5.go · 27 lines
package main

import (
	"fmt"
	"math"
)

func main() {
	// solve 2^x = 50: take the log of both sides, x = ln(50) / ln(2)
	x := math.Log(50) / math.Log(2)
	fmt.Printf("2^x = 50  ->  x = ln50/ln2 = %.4f   check: 2^x = %.4f\n", x, math.Pow(2, x))

	// the three log rules, checked numerically
	a, b := 8.0, 32.0
	fmt.Printf("log(ab) = log a + log b :  %.4f = %.4f\n", math.Log(a*b), math.Log(a)+math.Log(b))
	fmt.Printf("log(a/b) = log a - log b:  %.4f = %.4f\n", math.Log(a/b), math.Log(a)-math.Log(b))
	fmt.Printf("log(a^3) = 3 log a      :  %.4f = %.4f\n", math.Log(math.Pow(a, 3)), 3*math.Log(a))

	// compound interest: A = P(1 + r/n)^(nt)
	P, r, n, t := 1000.0, 0.06, 12.0, 10.0
	A := P * math.Pow(1+r/n, n*t)
	fmt.Printf("$%.0f at 6%% monthly for %g years = $%.2f\n", P, t, A)

	// how long to double? solve 2 = (1 + r/n)^(nt) for t
	years := math.Log(2) / (n * math.Log(1+r/n))
	fmt.Printf("doubling time = %.2f years   (rule of 72 guess: %.2f)\n", years, 72/6.0)
}
go run . — output
2^x = 50 -> x = ln50/ln2 = 5.6439 check: 2^x = 50.0000 log(ab) = log a + log b : 5.5452 = 5.5452 log(a/b) = log a - log b: -1.3863 = -1.3863 log(a^3) = 3 log a : 6.2383 = 6.2383 $1000 at 6% monthly for 10 years = $1819.40 doubling time = 11.58 years (rule of 72 guess: 12.00)
LESSON 06

Inequalities and absolute value.

An inequality is solved like an equation — do the same thing to both sides — with one exception that causes nearly every error: multiplying or dividing both sides by a negative number flips the sign. 2 < 3 is true; multiply both sides by −1 and −2 < −3 is false. You have to write −2 > −3.

Absolute value, |x|, is distance from zero, and |x − 3| is distance from 3. Reading it as a distance turns every absolute-value inequality into a sentence:

  • |x − a| < d — “x is within d of a” → a − d < x < a + d. One interval.
  • |x − a| > d — “x is more than d away from a” → x < a − d or x > a + d. Two pieces.

Worked by hand

Solve   −3x + 6 > 15
  1. Subtract 6: −3x > 9
  2. Divide by −3 — and flip: x < −3
  3. Test a value to be sure. x = −4: −3(−4) + 6 = 18 > 15 ✓. x = 0: 6 > 15? No ✓. The flip was right.
Solve   |x − 3| < 5
  1. Read it: x is within 5 of 3.
  2. So 3 − 5 < x < 3 + 5, i.e. −2 < x < 8. Interval notation: (−2, 8).
Solve   |2x + 1| ≥ 7
  1. “Greater than” splits into two cases: 2x + 1 ≥ 7  or  2x + 1 ≤ −7
  2. First: 2x ≥ 6, x ≥ 3. Second: 2x ≤ −8, x ≤ −4.
  3. x ≤ −4 or x ≥ 3. Interval notation: (−∞, −4] ∪ [3, ∞). Square bracket means the endpoint is included (≥), round means it isn't (>).
✎ Now you — on paper, then reveal
  1. Solve 5 − 2x ≤ 11
  2. Solve |x + 1| < 4
  3. Solve |x − 2| > 1, and write it in interval notation
Reveal the answers

1. −2x ≤ 6, divide by −2 and flip: x ≥ −3.   2. Within 4 of −1 (note: x + 1 is x − (−1)): −5 < x < 3.   3. More than 1 away from 2: x < 1 or x > 3, i.e. (−∞, 1) ∪ (3, ∞).

💡 Explain it simply

Explain why dividing both sides of an inequality by a negative number flips the sign, using a number line and no algebra.

Reveal a plain-language answer

Multiplying by a negative number is a mirror: it reflects every number across zero. Something on the right ends up on the left and vice versa. “2 is less than 3” means 2 sits to the left of 3 on the line. Reflect them both and −2 sits to the right of −3. Their order swapped, so the sign that describes their order has to swap too. Adding or subtracting just slides both numbers along together, which is why those never flip anything.

Check it in Go

You can't “solve” an inequality in code the way you solve an equation, but you can do something better for a refresher: test candidate values against the original inequality and against your solution, side by side. If the two columns ever disagree, your solution is wrong. math.Abs is absolute value.

▶ Show the Go program lesson6.go · 28 lines
package main

import (
	"fmt"
	"math"
)

func main() {
	// -3x + 6 > 15  ->  -3x > 9  ->  x < -3   (dividing by -3 FLIPS the sign)
	// test a few values against the ORIGINAL inequality to confirm
	fmt.Println("-3x + 6 > 15 ?")
	for _, x := range []float64{-5, -4, -3, -2, 0} {
		fmt.Printf("  x = %2g: %-5v  (x < -3 says %v)\n", x, -3*x+6 > 15, x < -3)
	}

	// |x - 3| < 5  means x is within 5 of 3  ->  -2 < x < 8
	fmt.Println("|x - 3| < 5 ?")
	for _, x := range []float64{-3, -2, 0, 3, 7, 8, 9} {
		fmt.Printf("  x = %2g: %-5v  (-2 < x < 8 says %v)\n", x, math.Abs(x-3) < 5, -2 < x && x < 8)
	}

	// |2x + 1| >= 7  splits into two cases: 2x+1 >= 7  or  2x+1 <= -7
	//                                        x >= 3     or  x <= -4
	fmt.Println("|2x + 1| >= 7 ?")
	for _, x := range []float64{-5, -4, -3, 0, 2, 3, 4} {
		fmt.Printf("  x = %2g: %-5v  (x <= -4 or x >= 3 says %v)\n", x, math.Abs(2*x+1) >= 7, x <= -4 || x >= 3)
	}
}
go run . — output
-3x + 6 > 15 ? x = -5: true (x < -3 says true) x = -4: true (x < -3 says true) x = -3: false (x < -3 says false) x = -2: false (x < -3 says false) x = 0: false (x < -3 says false) |x - 3| < 5 ? x = -3: false (-2 < x < 8 says false) x = -2: false (-2 < x < 8 says false) x = 0: true (-2 < x < 8 says true) x = 3: true (-2 < x < 8 says true) x = 7: true (-2 < x < 8 says true) x = 8: false (-2 < x < 8 says false) x = 9: false (-2 < x < 8 says false) |2x + 1| >= 7 ? x = -5: true (x <= -4 or x >= 3 says true) x = -4: true (x <= -4 or x >= 3 says true) x = -3: false (x <= -4 or x >= 3 says false) x = 0: false (x <= -4 or x >= 3 says false) x = 2: false (x <= -4 or x >= 3 says false) x = 3: true (x <= -4 or x >= 3 says true) x = 4: true (x <= -4 or x >= 3 says true)
LESSON 07

Sequences and series.

A sequence is a list of numbers made by a rule; a series is what you get when you add them up. College algebra covers the two rules that come up everywhere — in loans, in savings, in anything that grows by the same amount or the same factor each step:

Arithmetic — add d each timeGeometric — multiply by r each time
Example5, 8, 11, 14, … (d = 3)3, 6, 12, 24, … (r = 2)
nth terman = a1 + (n − 1)dan = a1 · rn−1
Sum of n termsSn = n(a1 + an) / 2Sn = a1(1 − rn) / (1 − r)
Sum of infinitely manynever finiteS = a1 / (1 − r), only if |r| < 1

The arithmetic sum formula has a story worth knowing: it's “number of terms times the average term,” and the average of an evenly spaced list is just the average of the first and last. That's the whole formula. The geometric one is less obvious, so the Go program checks it with a plain loop.

Worked by hand

5, 8, 11, …   Find the 20th term and the sum of the first 20.
  1. a1 = 5, d = 3. The 20th term has had 19 steps of 3 added: a20 = 5 + 19 × 3 = 62
  2. Sum: 20 terms, average of first and last is (5 + 62)/2 = 33.5, so S20 = 20 × 33.5 = 670
3, 6, 12, …   Find the 10th term and the sum of the first 10.
  1. a1 = 3, r = 2. Nine doublings: a10 = 3 × 29 = 3 × 512 = 1536
  2. S10 = 3(1 − 210) / (1 − 2) = 3(1 − 1024) / (−1) = 3 × 1023 = 3069
1 + ½ + ¼ + ⅛ + …
  1. Geometric with a1 = 1, r = ½. Since |r| < 1, the infinite sum is finite.
  2. S = 1 / (1 − ½) = 2. Each term closes half the remaining gap to 2 and never crosses it.
✎ Now you — on paper, then reveal
  1. 2, 7, 12, … — what is the 15th term?
  2. Add the first 10 odd numbers, 1 + 3 + 5 + … + 19, with the formula.
  3. 0.9 + 0.09 + 0.009 + … = ? (This one settles an old argument.)
Reveal the answers

1. d = 5, so 2 + 14 × 5 = 72.   2. First 1, last 19, ten terms: 10 × (1 + 19)/2 = 100. (The first n odd numbers always sum to n2.)   3. a1 = 0.9, r = 0.1: S = 0.9 / 0.9 = 1. Which is to say, 0.999… repeating is exactly 1, not “almost.”

💡 Explain it simply

How can adding up infinitely many positive numbers give a finite answer like 2? Explain it without the formula.

Reveal a plain-language answer

Stand two metres from a wall and take a step that covers half the remaining distance. Then half of what's left. Then half again. You take infinitely many steps, every one of them a real forward step, and you never reach the wall — but you also never get past two metres, and you get as close as anyone cares to measure. The total of all those steps is exactly the distance to the wall. That only works because the steps shrink fast enough; if each step were the same size, or shrank too slowly, you'd walk forever. “|r| less than 1” is the rule for shrinking fast enough.

Check it in Go

The program applies each formula and then does the same sum with a for loop, so the formula and the brute force sit next to each other in the output. The last line shows the infinite series creeping up on 2: twenty terms get you to 1.999998.

▶ Show the Go program lesson7.go · 41 lines
package main

import "fmt"

func main() {
	// arithmetic: 5, 8, 11, ...  a1 = 5, d = 3
	a1, d := 5.0, 3.0
	n := 20.0
	an := a1 + (n-1)*d       // nth term
	sum := n / 2 * (a1 + an) // sum of the first n terms
	fmt.Printf("arithmetic: 20th term = %g, sum of 20 terms = %g\n", an, sum)

	// check the sum formula with a plain loop
	loop := 0.0
	for i := 0; i < 20; i++ {
		loop += a1 + float64(i)*d
	}
	fmt.Printf("  loop says %g\n", loop)

	// geometric: 3, 6, 12, ...  a1 = 3, r = 2
	g1, r := 3.0, 2.0
	gn := g1 * pow(r, 10-1)                 // 10th term
	gsum := g1 * (1 - pow(r, 10)) / (1 - r) // sum of the first 10 terms
	fmt.Printf("geometric: 10th term = %g, sum of 10 terms = %g\n", gn, gsum)

	// infinite geometric series converges when |r| < 1: S = a1 / (1 - r)
	fmt.Printf("1 + 1/2 + 1/4 + 1/8 + ... = %g\n", 1/(1-0.5))
	partial := 0.0
	for i := 0; i < 20; i++ {
		partial += pow(0.5, float64(i))
	}
	fmt.Printf("  first 20 terms add to %.6f\n", partial)
}

func pow(base, exp float64) float64 {
	result := 1.0
	for i := 0; i < int(exp); i++ {
		result *= base
	}
	return result
}
go run . — output
arithmetic: 20th term = 62, sum of 20 terms = 670 loop says 670 geometric: 10th term = 1536, sum of 10 terms = 3069 1 + 1/2 + 1/4 + 1/8 + ... = 2 first 20 terms add to 1.999998
NEXT

Where to go from here.

Seven lessons is a refresher, not a course. If a lesson felt shaky, the fix is the same one this page is built on: close it, take a blank sheet, and re-derive the worked example from memory. What you can rebuild, you own.

1

Learning Go with Algebra

The gentler companion page: linear equations, 2×2 systems, the quadratic formula and lines, each worked by hand and then in Go. Good if Lesson 02 or 03 here moved too fast.

2

Big O Notation, by Example

Where the exponents and logs from Lessons 01 and 05 turn up in programming: why a log n algorithm barely notices a million items and an n2 one chokes on ten thousand.

3

Learning to Code by Bowling

If the Go in the drop-downs made you curious rather than nervous, this is the place to actually learn the language — by building something and then rebuilding it smaller.