Update build process and enhance curriculum content: Add a new script for generating site metadata to improve SEO with automatic sitemap and feed generation. Modify the build command to include this new script, ensuring it runs before the Zola build. Update the math curriculum to reflect the status change of the "Linear inequalities in one unknown" chapter to live, and enhance the chapter content with detailed explanations and examples. Adjust contributor roles in the JSON data for clarity.

This commit is contained in:
Hananoshika Yomaru 2026-05-20 23:54:57 -07:00
parent 89b6b6c41a
commit 353b129533
13 changed files with 1565 additions and 6 deletions

View file

@ -33,6 +33,7 @@ frontend/ → catalog-app, Mermaid map view, site chrome
```bash
bun install
bun run build:js # after frontend/ changes
bun run generate:site-metadata # regenerate static/sitemap.xml + static/feed.xml from content + git log
bun run index:search # once per dev session (copies public/pagefind → static/pagefind)
zola serve # http://127.0.0.1:1111
bun run build # production: JS + Zola + pagefind → public/
@ -59,10 +60,20 @@ bun run build # production: JS + Zola + pagefind → public/
| New subject (sidebar) | `data/catalog.json` → `subjects[]`; `data/{id}-curriculum.json` (catalog template auto-loads by `id`) |
| New chapter (planned) | `{subject}-curriculum.json` strand entry; optional `graph.edges` |
| New chapter (live) | Above + `content/{subject}/{slug}/_index.md` + HTML partial + `chapter.html` wiring + `main.tsx` widget mount if interactive |
| Sitemap/feed coverage | `scripts/generate-site-metadata.ts`, `static/sitemap.xml`, `static/feed.xml`, `package.json` build script |
| Homepage subject card | `themes/.../templates/index.html` (not yet data-driven) |
| Browse / nav URL | `zola.toml` `[extra] browse_url` + `base.html` site-config + `main.tsx` fallback |
| Search UI / index | `base.html` (Pagefind assets + modal), `content/search.md`, `_search.scss`; `bun run index:search` for dev |
## Sitemap / feed auto-generation
- `bun run generate:site-metadata` generates:
- `static/sitemap.xml` (all user-facing built pages + live chapters)
- `static/feed.xml` (RSS 2.0 for pages + live chapters)
- URL timestamps come from `git log -1` on content-source paths. If a page/chapter has no commit yet, generator falls back to filesystem mtime.
- Build pipeline runs this automatically before `zola build` via `package.json`.
- Include only resolvable pages; planned chapters without live content are excluded.
## Search (Pagefind)
| Piece | Path |

View file

@ -0,0 +1,234 @@
+++
title = "Laws of indices"
description = "Apply index laws to simplify expressions with integer and rational exponents."
template = "chapter.html"
weight = 12
[extra]
subject = "math"
chapter_id = "laws-of-indices"
strand = "Number & Algebra"
+++
<div class="book-prose">
<p>
An <strong>index</strong> (or exponent) tells you how many times a base is used as a factor.
For example, \(2^4 = 2 \times 2 \times 2 \times 2\). The laws of indices let us simplify
expressions efficiently and work with very large or very small quantities.
</p>
<h2 id="basic-meaning" class="book-prose__heading">Meaning of indices</h2>
<div class="book-table-wrap">
<table class="book-table">
<thead>
<tr>
<th scope="col">Expression</th>
<th scope="col">Meaning</th>
<th scope="col">Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>\(5^3\)</td>
<td>\(5 \times 5 \times 5\)</td>
<td>\(125\)</td>
</tr>
<tr>
<td>\(a^2\)</td>
<td>\(a \times a\)</td>
<td>depends on \(a\)</td>
</tr>
<tr>
<td>\(10^0\)</td>
<td>zero index</td>
<td>\(1\)</td>
</tr>
</tbody>
</table>
</div>
<h2 id="core-laws" class="book-prose__heading">Core laws of indices</h2>
<div class="book-callout book-callout--formula" role="note">
<p class="book-callout__label">Index laws (for non-zero base where required)</p>
<p class="book-formula">
\[
a^m \cdot a^n = a^{m+n}, \quad
\frac{a^m}{a^n} = a^{m-n}, \quad
(a^m)^n = a^{mn}
\]
\[
(ab)^n = a^n b^n, \quad
\left(\frac{a}{b}\right)^n = \frac{a^n}{b^n}, \quad
a^0 = 1
\]
</p>
</div>
<h2 id="negative-indices" class="book-prose__heading">Negative indices</h2>
<p>
A negative index means reciprocal:
\[
a^{-n} = \frac{1}{a^n}, \qquad a \neq 0.
\]
</p>
<p><strong>Example.</strong> \(2^{-3} = \frac{1}{2^3} = \frac{1}{8}\).</p>
<p><strong>Example.</strong> \(x^{-2}y^3 = \frac{y^3}{x^2}\).</p>
<h2 id="fractional-indices" class="book-prose__heading">Fractional indices</h2>
<p>
Fractional indices represent roots:
\[
a^{1/n} = \sqrt[n]{a}, \qquad
a^{m/n} = \sqrt[n]{a^m}.
\]
</p>
<p><strong>Example.</strong> \(27^{1/3} = 3\).</p>
<p><strong>Example.</strong> \(16^{3/4} = (\sqrt[4]{16})^3 = 2^3 = 8\).</p>
<h2 id="simplification-examples" class="book-prose__heading">Simplification examples</h2>
<p><strong>Example 1.</strong> Simplify \(a^5 \cdot a^{-2}\).</p>
<p>
\(a^{5+(-2)} = a^3\).
</p>
<p><strong>Example 2.</strong> Simplify \(\dfrac{3x^4y^{-1}}{6x^2}\).</p>
<p>
\[
\frac{3x^4y^{-1}}{6x^2}
= \frac{1}{2}x^{4-2}y^{-1}
= \frac{x^2}{2y}.
\]
</p>
<p><strong>Example 3.</strong> Simplify \((2a^3b^{-2})^2\).</p>
<p>
\(2^2 a^{6} b^{-4} = \dfrac{4a^6}{b^4}\).
</p>
<h2 id="scientific-notation" class="book-prose__heading">Scientific notation link</h2>
<p>
Indices are central to scientific notation:
\[
N = k \times 10^n, \qquad 1 \leq k < 10.
\]
Positive \(n\) gives large numbers, negative \(n\) gives small decimals.
</p>
<p><strong>Example.</strong> \(0.00045 = 4.5 \times 10^{-4}\).</p>
</div>
<div class="book-prose">
<h2 id="history" class="book-prose__heading">History</h2>
<p>
Exponent ideas developed over centuries as mathematicians searched for compact ways to write
repeated multiplication. Ancient methods used words or special marks, but a systematic notation
appeared gradually in early modern Europe.
</p>
<p>
In the 17th century, mathematicians such as <strong>Descartes</strong> helped popularize writing
powers with superscripts (like \(x^2\), \(x^3\)). Later work by
<strong>Wallis</strong> and <strong>Newton</strong> expanded these ideas to fractional and
negative exponents. This notation became a foundation for algebra, calculus, and scientific
notation.
</p>
<h2 id="derivation" class="book-prose__heading">Derivation</h2>
<h3 id="why-zero-index" class="book-prose__heading">Why \(a^0 = 1\)</h3>
<p>
Using the quotient law with \(a \neq 0\):
\[
\frac{a^m}{a^m} = a^{m-m} = a^0.
\]
But \(\dfrac{a^m}{a^m} = 1\), so \(a^0 = 1\).
</p>
<h3 id="why-negative-index" class="book-prose__heading">Why \(a^{-n} = \dfrac{1}{a^n}\)</h3>
<p>
Again from the quotient law:
\[
\frac{a^0}{a^n} = a^{0-n} = a^{-n}.
\]
Since \(a^0 = 1\), we get \(a^{-n} = \dfrac{1}{a^n}\).
</p>
<h3 id="why-fractional-index" class="book-prose__heading">Why \(a^{1/n} = \sqrt[n]{a}\)</h3>
<p>
Let \(a^{1/n} = t\). Raise both sides to power \(n\):
\[
(a^{1/n})^n = t^n \Rightarrow a = t^n.
\]
So \(t\) is an \(n\)-th root of \(a\), giving \(a^{1/n} = \sqrt[n]{a}\).
</p>
<h2 id="checkpoints" class="book-prose__heading">Checkpoints</h2>
<p>
Pause and reason before continuing. Discuss with a classmate or write your reasoning in a
notebook.
</p>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Simplify \(x^7 \div x^3\). Which index law did you use?
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Rewrite \(5^{-2}\) without a negative index, then evaluate.
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Simplify \((a^2b^{-1})^3\) and express your final answer with positive indices.
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Evaluate \(81^{3/4}\) by rewriting it in root form first.
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Write \(0.000072\) in scientific notation and state the index on 10.
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
True or false: \((x+y)^2 = x^2 + y^2\). Explain why index laws do or do not support this.
</p>
</div>
<h2 id="applications" class="book-prose__heading">Applications</h2>
<h3 id="science-scales" class="book-prose__heading">Large and small scales in science</h3>
<p>
Distances in astronomy and sizes of atoms are usually written with powers of 10 to keep numbers
readable. For example, \(1.5 \times 10^{11}\) m is much clearer than writing 150000000000 m.
</p>
<h3 id="growth-decay" class="book-prose__heading">Repeated growth and decay</h3>
<p>
Compound growth often uses exponent models such as
\(A = P(1+r)^t\). Exponents track repeated multiplication over equal time steps.
</p>
<h3 id="computing-bits" class="book-prose__heading">Computing and binary powers</h3>
<p>
Computer memory sizes are tied to powers of 2. For example, \(2^{10} = 1024\), so one kibibyte
is 1024 bytes and larger units build from further powers.
</p>
<h2 id="references" class="book-prose__heading">References</h2>
<ul>
<li>Descartes, R. (1637). <em>La Geometrie</em> (superscript power notation usage).</li>
<li>Wallis, J. (1685). <em>A Treatise of Algebra</em> (negative and fractional powers).</li>
<li>Secondary school algebra curricula on laws of indices and scientific notation.</li>
</ul>
</div>

View file

@ -0,0 +1,103 @@
<div class="book-prose">
<p>
An <strong>index</strong> (or exponent) tells you how many times a base is used as a factor.
For example, \(2^4 = 2 \times 2 \times 2 \times 2\). The laws of indices let us simplify
expressions efficiently and work with very large or very small quantities.
</p>
<h2 id="basic-meaning" class="book-prose__heading">Meaning of indices</h2>
<div class="book-table-wrap">
<table class="book-table">
<thead>
<tr>
<th scope="col">Expression</th>
<th scope="col">Meaning</th>
<th scope="col">Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>\(5^3\)</td>
<td>\(5 \times 5 \times 5\)</td>
<td>\(125\)</td>
</tr>
<tr>
<td>\(a^2\)</td>
<td>\(a \times a\)</td>
<td>depends on \(a\)</td>
</tr>
<tr>
<td>\(10^0\)</td>
<td>zero index</td>
<td>\(1\)</td>
</tr>
</tbody>
</table>
</div>
<h2 id="core-laws" class="book-prose__heading">Core laws of indices</h2>
<div class="book-callout book-callout--formula" role="note">
<p class="book-callout__label">Index laws (for non-zero base where required)</p>
<p class="book-formula">
\[
a^m \cdot a^n = a^{m+n}, \quad
\frac{a^m}{a^n} = a^{m-n}, \quad
(a^m)^n = a^{mn}
\]
\[
(ab)^n = a^n b^n, \quad
\left(\frac{a}{b}\right)^n = \frac{a^n}{b^n}, \quad
a^0 = 1
\]
</p>
</div>
<h2 id="negative-indices" class="book-prose__heading">Negative indices</h2>
<p>
A negative index means reciprocal:
\[
a^{-n} = \frac{1}{a^n}, \qquad a \neq 0.
\]
</p>
<p><strong>Example.</strong> \(2^{-3} = \frac{1}{2^3} = \frac{1}{8}\).</p>
<p><strong>Example.</strong> \(x^{-2}y^3 = \frac{y^3}{x^2}\).</p>
<h2 id="fractional-indices" class="book-prose__heading">Fractional indices</h2>
<p>
Fractional indices represent roots:
\[
a^{1/n} = \sqrt[n]{a}, \qquad
a^{m/n} = \sqrt[n]{a^m}.
\]
</p>
<p><strong>Example.</strong> \(27^{1/3} = 3\).</p>
<p><strong>Example.</strong> \(16^{3/4} = (\sqrt[4]{16})^3 = 2^3 = 8\).</p>
<h2 id="simplification-examples" class="book-prose__heading">Simplification examples</h2>
<p><strong>Example 1.</strong> Simplify \(a^5 \cdot a^{-2}\).</p>
<p>
\(a^{5+(-2)} = a^3\).
</p>
<p><strong>Example 2.</strong> Simplify \(\dfrac{3x^4y^{-1}}{6x^2}\).</p>
<p>
\[
\frac{3x^4y^{-1}}{6x^2}
= \frac{1}{2}x^{4-2}y^{-1}
= \frac{x^2}{2y}.
\]
</p>
<p><strong>Example 3.</strong> Simplify \((2a^3b^{-2})^2\).</p>
<p>
\(2^2 a^{6} b^{-4} = \dfrac{4a^6}{b^4}\).
</p>
<h2 id="scientific-notation" class="book-prose__heading">Scientific notation link</h2>
<p>
Indices are central to scientific notation:
\[
N = k \times 10^n, \qquad 1 \leq k < 10.
\]
Positive \(n\) gives large numbers, negative \(n\) gives small decimals.
</p>
<p><strong>Example.</strong> \(0.00045 = 4.5 \times 10^{-4}\).</p>
</div>

View file

@ -0,0 +1,119 @@
<div class="book-prose">
<h2 id="history" class="book-prose__heading">History</h2>
<p>
Exponent ideas developed over centuries as mathematicians searched for compact ways to write
repeated multiplication. Ancient methods used words or special marks, but a systematic notation
appeared gradually in early modern Europe.
</p>
<p>
In the 17th century, mathematicians such as <strong>Descartes</strong> helped popularize writing
powers with superscripts (like \(x^2\), \(x^3\)). Later work by
<strong>Wallis</strong> and <strong>Newton</strong> expanded these ideas to fractional and
negative exponents. This notation became a foundation for algebra, calculus, and scientific
notation.
</p>
<h2 id="derivation" class="book-prose__heading">Derivation</h2>
<h3 id="why-zero-index" class="book-prose__heading">Why \(a^0 = 1\)</h3>
<p>
Using the quotient law with \(a \neq 0\):
\[
\frac{a^m}{a^m} = a^{m-m} = a^0.
\]
But \(\dfrac{a^m}{a^m} = 1\), so \(a^0 = 1\).
</p>
<h3 id="why-negative-index" class="book-prose__heading">Why \(a^{-n} = \dfrac{1}{a^n}\)</h3>
<p>
Again from the quotient law:
\[
\frac{a^0}{a^n} = a^{0-n} = a^{-n}.
\]
Since \(a^0 = 1\), we get \(a^{-n} = \dfrac{1}{a^n}\).
</p>
<h3 id="why-fractional-index" class="book-prose__heading">Why \(a^{1/n} = \sqrt[n]{a}\)</h3>
<p>
Let \(a^{1/n} = t\). Raise both sides to power \(n\):
\[
(a^{1/n})^n = t^n \Rightarrow a = t^n.
\]
So \(t\) is an \(n\)-th root of \(a\), giving \(a^{1/n} = \sqrt[n]{a}\).
</p>
<h2 id="checkpoints" class="book-prose__heading">Checkpoints</h2>
<p>
Pause and reason before continuing. Discuss with a classmate or write your reasoning in a
notebook.
</p>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Simplify \(x^7 \div x^3\). Which index law did you use?
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Rewrite \(5^{-2}\) without a negative index, then evaluate.
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Simplify \((a^2b^{-1})^3\) and express your final answer with positive indices.
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Evaluate \(81^{3/4}\) by rewriting it in root form first.
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Write \(0.000072\) in scientific notation and state the index on 10.
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
True or false: \((x+y)^2 = x^2 + y^2\). Explain why index laws do or do not support this.
</p>
</div>
<h2 id="applications" class="book-prose__heading">Applications</h2>
<h3 id="science-scales" class="book-prose__heading">Large and small scales in science</h3>
<p>
Distances in astronomy and sizes of atoms are usually written with powers of 10 to keep numbers
readable. For example, \(1.5 \times 10^{11}\) m is much clearer than writing 150000000000 m.
</p>
<h3 id="growth-decay" class="book-prose__heading">Repeated growth and decay</h3>
<p>
Compound growth often uses exponent models such as
\(A = P(1+r)^t\). Exponents track repeated multiplication over equal time steps.
</p>
<h3 id="computing-bits" class="book-prose__heading">Computing and binary powers</h3>
<p>
Computer memory sizes are tied to powers of 2. For example, \(2^{10} = 1024\), so one kibibyte
is 1024 bytes and larger units build from further powers.
</p>
<h2 id="references" class="book-prose__heading">References</h2>
<ul>
<li>Descartes, R. (1637). <em>La Geometrie</em> (superscript power notation usage).</li>
<li>Wallis, J. (1685). <em>A Treatise of Algebra</em> (negative and fractional powers).</li>
<li>Secondary school algebra curricula on laws of indices and scientific notation.</li>
</ul>
</div>

View file

@ -8,3 +8,251 @@ subject = "math"
chapter_id = "linear-inequalities-one-unknown"
strand = "Number & Algebra"
+++
<div class="book-prose">
<p>
A <strong>linear inequality in one unknown</strong> compares a linear expression with a value or
another linear expression using symbols such as
\(<\), \(>\), \(\leq\), and \(\geq\). Instead of finding one exact value, we usually find a
<strong>set of values</strong> that make the statement true.
</p>
<h2 id="inequality-symbols" class="book-prose__heading">Inequality symbols</h2>
<div class="book-table-wrap">
<table class="book-table">
<thead>
<tr>
<th scope="col">Symbol</th>
<th scope="col">Meaning</th>
<th scope="col">Example</th>
</tr>
</thead>
<tbody>
<tr>
<td>\(<\)</td>
<td>Strictly less than</td>
<td>\(x < 4\)</td>
</tr>
<tr>
<td>\(>\)</td>
<td>Strictly greater than</td>
<td>\(x > -2\)</td>
</tr>
<tr>
<td>\(\leq\)</td>
<td>Less than or equal to</td>
<td>\(x \leq 7\)</td>
</tr>
<tr>
<td>\(\geq\)</td>
<td>Greater than or equal to</td>
<td>\(x \geq 0\)</td>
</tr>
</tbody>
</table>
</div>
<h2 id="solving" class="book-prose__heading">Solving linear inequalities</h2>
<p>
Solve them much like linear equations: expand brackets when needed, collect like terms, then
isolate the variable.
</p>
<p><strong>Example.</strong> Solve \(3x - 5 \leq 16\).</p>
<p>
Add \(5\) to both sides: \(3x \leq 21\). Divide by \(3\): \(x \leq 7\).
</p>
<h2 id="sign-flip-rule" class="book-prose__heading">The sign-flip rule</h2>
<p>
When you multiply or divide <em>both sides</em> of an inequality by a negative number, the
inequality sign must reverse.
</p>
<div class="book-callout book-callout--formula" role="note">
<p class="book-callout__label">Key rule</p>
<p class="book-formula">
\[
a < b \;\Rightarrow\; -a > -b
\]
</p>
</div>
<p><strong>Example.</strong> Solve \(-2x + 1 > 9\).</p>
<p>
Subtract \(1\): \(-2x > 8\). Divide by \(-2\), so reverse \(>\) to \(<\):
\(x < -4\).
</p>
<h2 id="number-line-intervals" class="book-prose__heading">Number line and interval notation</h2>
<p>
The solution set can be written on a number line or in interval notation:
</p>
<div class="book-table-wrap">
<table class="book-table">
<thead>
<tr>
<th scope="col">Inequality</th>
<th scope="col">Interval notation</th>
<th scope="col">Endpoint style</th>
</tr>
</thead>
<tbody>
<tr>
<td>\(x < 3\)</td>
<td>\((-\infty, 3)\)</td>
<td>Open at \(3\)</td>
</tr>
<tr>
<td>\(x \leq 3\)</td>
<td>\((-\infty, 3]\)</td>
<td>Closed at \(3\)</td>
</tr>
<tr>
<td>\(x > -1\)</td>
<td>\((-1, \infty)\)</td>
<td>Open at \(-1\)</td>
</tr>
<tr>
<td>\(x \geq -1\)</td>
<td>\([-1, \infty)\)</td>
<td>Closed at \(-1\)</td>
</tr>
</tbody>
</table>
</div>
<h2 id="compound-inequalities" class="book-prose__heading">Compound inequalities</h2>
<p>
Compound inequalities combine two inequalities with <strong>and</strong> or <strong>or</strong>.
</p>
<p><strong>Example (and).</strong> Solve \(2 < x + 1 \leq 6\).</p>
<p>
Subtract \(1\) from all three parts:
\(1 < x \leq 5\). Interval form: \((1, 5]\).
</p>
<p><strong>Example (or).</strong> Solve \(x - 4 < -7\) or \(3x + 1 \geq 10\).</p>
<p>
First: \(x < -3\). Second: \(3x \geq 9\), so \(x \geq 3\).
Final set: \((-\infty, -3) \cup [3, \infty)\).
</p>
<h2 id="word-problems" class="book-prose__heading">Word problems</h2>
<p>
Translate the condition into an inequality, solve, and interpret the answer in context.
</p>
<p><strong>Example.</strong> A bus ticket costs \$12. You have at most \$90. How many tickets \(n\)
can you buy?</p>
<p>
\(12n \leq 90 \Rightarrow n \leq 7.5\). Since \(n\) is a whole number of tickets, the maximum is
\(7\).
</p>
</div>
<div class="book-prose">
<h2 id="history" class="book-prose__heading">History</h2>
<p>
Ideas behind inequalities are ancient. Greek mathematicians such as <strong>Euclid</strong>
compared magnitudes and proved strict order relationships long before modern algebraic symbols.
In India and the Islamic world, later algebra texts worked with bounds and constraints in
practical problems involving trade, measurement, and inheritance.
</p>
<p>
The modern symbols \(<\) and \(>\) were introduced in the 17th century by
<strong>Thomas Harriot</strong>. As algebra notation became standard, inequalities became a
compact way to describe ranges of possible values rather than one exact answer.
Today, the same language appears in budgeting, engineering safety margins, and optimization.
</p>
<h2 id="derivation" class="book-prose__heading">Derivation</h2>
<h3 id="why-sign-flips" class="book-prose__heading">Why does the sign flip with a negative?</h3>
<p>
Suppose \(a < b\). Subtract \(b\) from both sides to get \(a-b < 0\). Multiplying by \(-1\)
gives \(b-a > 0\), so \(-a > -b\). Geometrically, multiplying by \(-1\) reflects numbers
across zero on the number line, reversing left-right order. That is why dividing by a negative
must reverse \(<\) to \(>\) (and \(\leq\) to \(\geq\)).
</p>
<h3 id="interval-logic" class="book-prose__heading">Why intervals match inequality endpoints</h3>
<p>
A strict inequality such as \(x < c\) excludes \(c\), so interval notation uses an open bracket
\((\cdot)\). A non-strict inequality such as \(x \leq c\) includes \(c\), so interval notation
uses a closed bracket \([\cdot]\). This bracket choice encodes exactly the same endpoint logic
as open and closed dots on number lines.
</p>
<h2 id="checkpoints" class="book-prose__heading">Checkpoints</h2>
<p>
Pause and reason before moving on. Discuss with a classmate or write your reasoning in a
notebook.
</p>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Solve \(4x - 3 &lt; 9\). Write your answer as both an inequality and an interval.
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Solve \(-5x \geq 20\). Which step forces a sign flip, and what is the final solution?
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Is \(x = -2\) a solution of both \(x \leq -2\) and \(x &lt; -2\)? Explain the difference.
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Solve \(1 \leq 2x + 3 &lt; 9\). Then write the solution in interval notation.
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Write and solve an inequality for this condition: "A package mass \(m\) must be at least
0.5 kg and less than 2 kg."
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Solve and sketch on a number line: \(x-6 &lt; -10\) or \(2x+1 \geq 7\).
</p>
</div>
<h2 id="applications" class="book-prose__heading">Applications</h2>
<h3 id="budgeting" class="book-prose__heading">Budgeting and spending limits</h3>
<p>
If each notebook costs \$3 and you have at most \$24, the purchase count \(n\) satisfies
\(3n \leq 24\). Inequalities model spending caps in daily life, event planning, and inventory
decisions.
</p>
<h3 id="safety-limits" class="book-prose__heading">Safety and engineering limits</h3>
<p>
Many systems run within allowable ranges, such as pressure \(P \leq P_{\max}\) or temperature
\(T_{\min} \leq T \leq T_{\max}\). Inequalities represent these safe operating regions directly.
</p>
<h3 id="grades-and-thresholds" class="book-prose__heading">Grades and performance thresholds</h3>
<p>
A pass condition might be "score at least 50," written as \(s \geq 50\). Scholarship criteria
and qualification cutoffs are also inequality-based rules.
</p>
<h2 id="references" class="book-prose__heading">References</h2>
<ul>
<li>Harriot, T. (1631). <em>Artis analyticae praxis</em> (early use of \(<\) and \(>\)).</li>
<li>Euclid, <em>Elements</em>, Book V (order and comparison of magnitudes).</li>
<li>Secondary school algebra curricula on linear inequalities and number-line representation.</li>
</ul>
</div>

View file

@ -0,0 +1,136 @@
<div class="book-prose">
<p>
A <strong>linear inequality in one unknown</strong> compares a linear expression with a value or
another linear expression using symbols such as
\(<\), \(>\), \(\leq\), and \(\geq\). Instead of finding one exact value, we usually find a
<strong>set of values</strong> that make the statement true.
</p>
<h2 id="inequality-symbols" class="book-prose__heading">Inequality symbols</h2>
<div class="book-table-wrap">
<table class="book-table">
<thead>
<tr>
<th scope="col">Symbol</th>
<th scope="col">Meaning</th>
<th scope="col">Example</th>
</tr>
</thead>
<tbody>
<tr>
<td>\(<\)</td>
<td>Strictly less than</td>
<td>\(x < 4\)</td>
</tr>
<tr>
<td>\(>\)</td>
<td>Strictly greater than</td>
<td>\(x > -2\)</td>
</tr>
<tr>
<td>\(\leq\)</td>
<td>Less than or equal to</td>
<td>\(x \leq 7\)</td>
</tr>
<tr>
<td>\(\geq\)</td>
<td>Greater than or equal to</td>
<td>\(x \geq 0\)</td>
</tr>
</tbody>
</table>
</div>
<h2 id="solving" class="book-prose__heading">Solving linear inequalities</h2>
<p>
Solve them much like linear equations: expand brackets when needed, collect like terms, then
isolate the variable.
</p>
<p><strong>Example.</strong> Solve \(3x - 5 \leq 16\).</p>
<p>
Add \(5\) to both sides: \(3x \leq 21\). Divide by \(3\): \(x \leq 7\).
</p>
<h2 id="sign-flip-rule" class="book-prose__heading">The sign-flip rule</h2>
<p>
When you multiply or divide <em>both sides</em> of an inequality by a negative number, the
inequality sign must reverse.
</p>
<div class="book-callout book-callout--formula" role="note">
<p class="book-callout__label">Key rule</p>
<p class="book-formula">
\[
a < b \;\Rightarrow\; -a > -b
\]
</p>
</div>
<p><strong>Example.</strong> Solve \(-2x + 1 > 9\).</p>
<p>
Subtract \(1\): \(-2x > 8\). Divide by \(-2\), so reverse \(>\) to \(<\):
\(x < -4\).
</p>
<h2 id="number-line-intervals" class="book-prose__heading">Number line and interval notation</h2>
<p>
The solution set can be written on a number line or in interval notation:
</p>
<div class="book-table-wrap">
<table class="book-table">
<thead>
<tr>
<th scope="col">Inequality</th>
<th scope="col">Interval notation</th>
<th scope="col">Endpoint style</th>
</tr>
</thead>
<tbody>
<tr>
<td>\(x < 3\)</td>
<td>\((-\infty, 3)\)</td>
<td>Open at \(3\)</td>
</tr>
<tr>
<td>\(x \leq 3\)</td>
<td>\((-\infty, 3]\)</td>
<td>Closed at \(3\)</td>
</tr>
<tr>
<td>\(x > -1\)</td>
<td>\((-1, \infty)\)</td>
<td>Open at \(-1\)</td>
</tr>
<tr>
<td>\(x \geq -1\)</td>
<td>\([-1, \infty)\)</td>
<td>Closed at \(-1\)</td>
</tr>
</tbody>
</table>
</div>
<h2 id="compound-inequalities" class="book-prose__heading">Compound inequalities</h2>
<p>
Compound inequalities combine two inequalities with <strong>and</strong> or <strong>or</strong>.
</p>
<p><strong>Example (and).</strong> Solve \(2 < x + 1 \leq 6\).</p>
<p>
Subtract \(1\) from all three parts:
\(1 < x \leq 5\). Interval form: \((1, 5]\).
</p>
<p><strong>Example (or).</strong> Solve \(x - 4 < -7\) or \(3x + 1 \geq 10\).</p>
<p>
First: \(x < -3\). Second: \(3x \geq 9\), so \(x \geq 3\).
Final set: \((-\infty, -3) \cup [3, \infty)\).
</p>
<h2 id="word-problems" class="book-prose__heading">Word problems</h2>
<p>
Translate the condition into an inequality, solve, and interpret the answer in context.
</p>
<p><strong>Example.</strong> A bus ticket costs \$12. You have at most \$90. How many tickets \(n\)
can you buy?</p>
<p>
\(12n \leq 90 \Rightarrow n \leq 7.5\). Since \(n\) is a whole number of tickets, the maximum is
\(7\).
</p>
</div>

View file

@ -0,0 +1,110 @@
<div class="book-prose">
<h2 id="history" class="book-prose__heading">History</h2>
<p>
Ideas behind inequalities are ancient. Greek mathematicians such as <strong>Euclid</strong>
compared magnitudes and proved strict order relationships long before modern algebraic symbols.
In India and the Islamic world, later algebra texts worked with bounds and constraints in
practical problems involving trade, measurement, and inheritance.
</p>
<p>
The modern symbols \(<\) and \(>\) were introduced in the 17th century by
<strong>Thomas Harriot</strong>. As algebra notation became standard, inequalities became a
compact way to describe ranges of possible values rather than one exact answer.
Today, the same language appears in budgeting, engineering safety margins, and optimization.
</p>
<h2 id="derivation" class="book-prose__heading">Derivation</h2>
<h3 id="why-sign-flips" class="book-prose__heading">Why does the sign flip with a negative?</h3>
<p>
Suppose \(a < b\). Subtract \(b\) from both sides to get \(a-b < 0\). Multiplying by \(-1\)
gives \(b-a > 0\), so \(-a > -b\). Geometrically, multiplying by \(-1\) reflects numbers
across zero on the number line, reversing left-right order. That is why dividing by a negative
must reverse \(<\) to \(>\) (and \(\leq\) to \(\geq\)).
</p>
<h3 id="interval-logic" class="book-prose__heading">Why intervals match inequality endpoints</h3>
<p>
A strict inequality such as \(x < c\) excludes \(c\), so interval notation uses an open bracket
\((\cdot)\). A non-strict inequality such as \(x \leq c\) includes \(c\), so interval notation
uses a closed bracket \([\cdot]\). This bracket choice encodes exactly the same endpoint logic
as open and closed dots on number lines.
</p>
<h2 id="checkpoints" class="book-prose__heading">Checkpoints</h2>
<p>
Pause and reason before moving on. Discuss with a classmate or write your reasoning in a
notebook.
</p>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Solve \(4x - 3 &lt; 9\). Write your answer as both an inequality and an interval.
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Solve \(-5x \geq 20\). Which step forces a sign flip, and what is the final solution?
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Is \(x = -2\) a solution of both \(x \leq -2\) and \(x &lt; -2\)? Explain the difference.
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Solve \(1 \leq 2x + 3 &lt; 9\). Then write the solution in interval notation.
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Write and solve an inequality for this condition: "A package mass \(m\) must be at least
0.5 kg and less than 2 kg."
</p>
</div>
<div class="book-callout" role="note">
<p class="book-callout__label">Checkpoint</p>
<p>
Solve and sketch on a number line: \(x-6 &lt; -10\) or \(2x+1 \geq 7\).
</p>
</div>
<h2 id="applications" class="book-prose__heading">Applications</h2>
<h3 id="budgeting" class="book-prose__heading">Budgeting and spending limits</h3>
<p>
If each notebook costs \$3 and you have at most \$24, the purchase count \(n\) satisfies
\(3n \leq 24\). Inequalities model spending caps in daily life, event planning, and inventory
decisions.
</p>
<h3 id="safety-limits" class="book-prose__heading">Safety and engineering limits</h3>
<p>
Many systems run within allowable ranges, such as pressure \(P \leq P_{\max}\) or temperature
\(T_{\min} \leq T \leq T_{\max}\). Inequalities represent these safe operating regions directly.
</p>
<h3 id="grades-and-thresholds" class="book-prose__heading">Grades and performance thresholds</h3>
<p>
A pass condition might be "score at least 50," written as \(s \geq 50\). Scholarship criteria
and qualification cutoffs are also inequality-based rules.
</p>
<h2 id="references" class="book-prose__heading">References</h2>
<ul>
<li>Harriot, T. (1631). <em>Artis analyticae praxis</em> (early use of \(<\) and \(>\)).</li>
<li>Euclid, <em>Elements</em>, Book V (order and comparison of magnitudes).</li>
<li>Secondary school algebra curricula on linear inequalities and number-line representation.</li>
</ul>
</div>

View file

@ -7,10 +7,9 @@
"job_title": "EdTech specialist / software engineer",
"education": "BEng in Computer Science (HKUST) + BBA (HKUST)",
"contributions": [
"Founder",
"Project Creator",
"Math textbook",
"Physics textbook",
"Chemistry textbook"
"Physics textbook"
],
"socials": [
{ "type": "email", "url": "manlung.yeung@outlook.com" },

View file

@ -466,7 +466,7 @@
"slug": "linear-inequalities-one-unknown",
"title": "Linear inequalities in one unknown",
"description": "Solve and represent linear inequalities on the number line and in word problems.",
"status": "planned",
"status": "live",
"curriculumCoverage": {
"DSE": "core",
"IGCSE": "core",
@ -483,7 +483,7 @@
"slug": "laws-of-indices",
"title": "Laws of indices",
"description": "Apply index laws to simplify expressions with integer and rational exponents.",
"status": "planned",
"status": "live",
"curriculumCoverage": {
"DSE": "core",
"IGCSE": "core",

View file

@ -5,8 +5,9 @@
"type": "module",
"scripts": {
"sync:chapters": "bun scripts/sync-chapters.ts",
"generate:site-metadata": "bun scripts/generate-site-metadata.ts",
"build:js": "bun run sync:chapters && vite build",
"build": "bun run check:static-html && bun run validate:curriculum && rm -f themes/openfreebooks/static/main.css public/main.css && bun run build:js && zola build && bun run index:search",
"build": "bun run check:static-html && bun run validate:curriculum && rm -f themes/openfreebooks/static/main.css public/main.css && bun run build:js && bun run generate:site-metadata && zola build && bun run index:search",
"index:search": "pagefind --site public --glob \"**/index.html\" && rm -rf static/pagefind && cp -R public/pagefind static/pagefind",
"dev:js": "bun run sync:chapters && vite build --watch",
"dev": "bash scripts/dev.sh",

View file

@ -0,0 +1,322 @@
import { existsSync } from "node:fs";
import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
import { dirname, join, relative, sep } from "node:path";
import { $ } from "bun";
type FrontMatter = {
title?: string;
description?: string;
template?: string;
};
type CurriculumChapter = {
slug?: unknown;
status?: unknown;
title?: unknown;
description?: unknown;
};
type CurriculumStrand = {
chapters?: unknown;
};
type CurriculumFile = {
strands?: unknown;
};
type UrlEntry = {
urlPath: string;
title: string;
description: string;
sourcePaths: string[];
lastmod: Date | null;
};
const ROOT = join(import.meta.dir, "..");
const CONTENT_DIR = join(ROOT, "content");
const DATA_DIR = join(ROOT, "data");
const STATIC_DIR = join(ROOT, "static");
const SITEMAP_PATH = join(STATIC_DIR, "sitemap.xml");
const FEED_PATH = join(STATIC_DIR, "feed.xml");
function xmlEscape(value: string): string {
return value
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&apos;");
}
function isNonEmptyString(value: unknown): value is string {
return typeof value === "string" && value.trim().length > 0;
}
function toPosixPath(path: string): string {
return path.split(sep).join("/");
}
function normalizeBaseUrl(rawBaseUrl: string): string {
return rawBaseUrl.replace(/\/+$/, "");
}
function ensureTrailingSlash(path: string): string {
return path.endsWith("/") ? path : `${path}/`;
}
function parseFrontMatter(markdown: string): FrontMatter {
const match = markdown.match(/^\+\+\+([\s\S]*?)\+\+\+/);
if (!match) return {};
const block = match[1];
const readKey = (key: string): string | undefined => {
const lineMatch = block.match(new RegExp(`^\\s*${key}\\s*=\\s*"([^"]*)"\\s*$`, "m"));
return lineMatch?.[1]?.trim();
};
return {
title: readKey("title"),
description: readKey("description"),
template: readKey("template"),
};
}
async function readBaseUrlFromConfig(): Promise<string> {
const zolaPath = join(ROOT, "zola.toml");
const raw = await readFile(zolaPath, "utf8");
const match = raw.match(/^\s*base_url\s*=\s*"([^"]+)"\s*$/m);
if (!match) {
throw new Error("Could not read base_url from zola.toml");
}
return normalizeBaseUrl(match[1]);
}
async function discoverMarkdownFiles(dir: string): Promise<string[]> {
const output: string[] = [];
const entries = await readdir(dir, { withFileTypes: true });
for (const entry of entries) {
if (entry.name.startsWith(".")) continue;
const abs = join(dir, entry.name);
if (entry.isDirectory()) {
output.push(...(await discoverMarkdownFiles(abs)));
continue;
}
if (entry.isFile() && entry.name.endsWith(".md")) {
output.push(abs);
}
}
return output;
}
async function readLiveChapterKeys(): Promise<Set<string>> {
const keys = new Set<string>();
const entries = await readdir(DATA_DIR, { withFileTypes: true });
for (const entry of entries) {
if (!entry.isFile() || !entry.name.endsWith("-curriculum.json")) continue;
const subject = entry.name.replace(/-curriculum\.json$/, "");
const jsonPath = join(DATA_DIR, entry.name);
const raw = await readFile(jsonPath, "utf8");
const data = JSON.parse(raw) as CurriculumFile;
if (!Array.isArray(data.strands)) continue;
for (const strand of data.strands as CurriculumStrand[]) {
if (!Array.isArray(strand.chapters)) continue;
for (const chapter of strand.chapters as CurriculumChapter[]) {
if (!isNonEmptyString(chapter.slug)) continue;
if (chapter.status !== "live") continue;
keys.add(`${subject}/${chapter.slug}`);
}
}
}
return keys;
}
async function readGitLastmod(sourcePaths: string[]): Promise<Date | null> {
const relPaths = sourcePaths
.map((sourcePath) => toPosixPath(relative(ROOT, sourcePath)))
.filter((rel) => rel.length > 0);
if (relPaths.length === 0) return null;
const proc = await $`git --no-pager log -1 --format=%cI -- ${relPaths}`.quiet();
if (proc.exitCode !== 0) return null;
const raw = proc.stdout.toString().trim();
if (!raw) return null;
const parsed = new Date(raw);
return Number.isNaN(parsed.getTime()) ? null : parsed;
}
async function readFilesystemLastmod(sourcePaths: string[]): Promise<Date | null> {
let latestMs = 0;
const visit = async (path: string): Promise<void> => {
const info = await stat(path);
if (info.isDirectory()) {
const entries = await readdir(path, { withFileTypes: true });
for (const entry of entries) {
if (entry.name.startsWith(".")) continue;
await visit(join(path, entry.name));
}
return;
}
if (info.mtimeMs > latestMs) {
latestMs = info.mtimeMs;
}
};
for (const sourcePath of sourcePaths) {
if (!existsSync(sourcePath)) continue;
await visit(sourcePath);
}
if (!latestMs) return null;
return new Date(latestMs);
}
function toUrlPathFromMarkdown(contentRelativePath: string): string {
const rel = toPosixPath(contentRelativePath);
if (rel === "_index.md") return "/";
if (rel.endsWith("/_index.md")) {
return ensureTrailingSlash(`/${rel.replace(/\/_index\.md$/, "")}`);
}
return ensureTrailingSlash(`/${rel.replace(/\.md$/, "")}`);
}
function isChapterIndex(contentRelativePath: string, fm: FrontMatter): boolean {
return contentRelativePath.endsWith("/_index.md") && fm.template === "chapter.html";
}
function chapterKeyFromContentPath(contentRelativePath: string): string | null {
const parts = toPosixPath(contentRelativePath).split("/");
if (parts.length !== 3 || parts[2] !== "_index.md") return null;
return `${parts[0]}/${parts[1]}`;
}
async function buildEntries(): Promise<UrlEntry[]> {
const markdownFiles = await discoverMarkdownFiles(CONTENT_DIR);
const liveChapterKeys = await readLiveChapterKeys();
const entries: UrlEntry[] = [];
for (const absMarkdownPath of markdownFiles) {
const relPath = toPosixPath(relative(CONTENT_DIR, absMarkdownPath));
const raw = await readFile(absMarkdownPath, "utf8");
const fm = parseFrontMatter(raw);
if (isChapterIndex(relPath, fm)) {
const key = chapterKeyFromContentPath(relPath);
if (!key || !liveChapterKeys.has(key)) continue;
const chapterDir = join(CONTENT_DIR, key);
const sourcePaths = [chapterDir];
const lastmod = (await readGitLastmod(sourcePaths)) ?? (await readFilesystemLastmod(sourcePaths));
entries.push({
urlPath: toUrlPathFromMarkdown(relPath),
title: fm.title ?? key.split("/")[1],
description: fm.description ?? fm.title ?? key.split("/")[1],
sourcePaths,
lastmod,
});
continue;
}
const urlPath = toUrlPathFromMarkdown(relPath);
if (urlPath === "/404/") continue;
const sourcePaths = [absMarkdownPath];
const lastmod = (await readGitLastmod(sourcePaths)) ?? (await readFilesystemLastmod(sourcePaths));
const fallbackTitle =
urlPath === "/"
? "Open Free Books"
: urlPath
.split("/")
.filter(Boolean)
.at(-1)
?.replaceAll("-", " ") ?? "Page";
entries.push({
urlPath,
title: fm.title ?? fallbackTitle,
description: fm.description ?? fm.title ?? fallbackTitle,
sourcePaths,
lastmod,
});
}
const dedup = new Map<string, UrlEntry>();
for (const entry of entries) {
if (!dedup.has(entry.urlPath)) {
dedup.set(entry.urlPath, entry);
}
}
const result = [...dedup.values()];
result.sort((a, b) => a.urlPath.localeCompare(b.urlPath));
return result;
}
function renderSitemap(baseUrl: string, entries: UrlEntry[]): string {
const lines = ['<?xml version="1.0" encoding="UTF-8"?>', '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'];
for (const entry of entries) {
const loc = `${baseUrl}${entry.urlPath}`;
lines.push(" <url>");
lines.push(` <loc>${xmlEscape(loc)}</loc>`);
if (entry.lastmod) {
lines.push(` <lastmod>${entry.lastmod.toISOString()}</lastmod>`);
}
lines.push(" </url>");
}
lines.push("</urlset>", "");
return lines.join("\n");
}
function renderFeed(baseUrl: string, entries: UrlEntry[]): string {
const sorted = [...entries].sort((a, b) => {
const aMs = a.lastmod?.getTime() ?? 0;
const bMs = b.lastmod?.getTime() ?? 0;
return bMs - aMs;
});
const latest = sorted.find((entry) => entry.lastmod)?.lastmod ?? new Date();
const lines = [
'<?xml version="1.0" encoding="UTF-8"?>',
'<rss version="2.0">',
" <channel>",
" <title>Open Free Books</title>",
` <link>${xmlEscape(`${baseUrl}/`)}</link>`,
" <description>Recent updates to Open Free Books pages and live chapters.</description>",
` <lastBuildDate>${latest.toUTCString()}</lastBuildDate>`,
];
for (const entry of sorted) {
const link = `${baseUrl}${entry.urlPath}`;
const pubDate = (entry.lastmod ?? latest).toUTCString();
lines.push(" <item>");
lines.push(` <title>${xmlEscape(entry.title)}</title>`);
lines.push(` <link>${xmlEscape(link)}</link>`);
lines.push(` <guid isPermaLink="true">${xmlEscape(link)}</guid>`);
lines.push(` <pubDate>${xmlEscape(pubDate)}</pubDate>`);
lines.push(` <description>${xmlEscape(entry.description)}</description>`);
lines.push(" </item>");
}
lines.push(" </channel>", "</rss>", "");
return lines.join("\n");
}
async function main() {
const baseUrl = await readBaseUrlFromConfig();
const entries = await buildEntries();
await mkdir(dirname(SITEMAP_PATH), { recursive: true });
await writeFile(SITEMAP_PATH, renderSitemap(baseUrl, entries));
await writeFile(FEED_PATH, renderFeed(baseUrl, entries));
if (!existsSync(SITEMAP_PATH) || !existsSync(FEED_PATH)) {
throw new Error("Failed to generate sitemap/feed outputs");
}
console.log(`Generated ${toPosixPath(relative(ROOT, SITEMAP_PATH))} with ${entries.length} URL(s).`);
console.log(`Generated ${toPosixPath(relative(ROOT, FEED_PATH))} with ${entries.length} item(s).`);
}
main();

177
static/feed.xml Normal file
View file

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>Open Free Books</title>
<link>https://openfreebooks.org/</link>
<description>Recent updates to Open Free Books pages and live chapters.</description>
<lastBuildDate>Thu, 21 May 2026 06:54:29 GMT</lastBuildDate>
<item>
<title>Laws of indices</title>
<link>https://openfreebooks.org/math/laws-of-indices/</link>
<guid isPermaLink="true">https://openfreebooks.org/math/laws-of-indices/</guid>
<pubDate>Thu, 21 May 2026 06:54:29 GMT</pubDate>
<description>Apply index laws to simplify expressions with integer and rational exponents.</description>
</item>
<item>
<title>Linear inequalities in one unknown</title>
<link>https://openfreebooks.org/math/linear-inequalities-one-unknown/</link>
<guid isPermaLink="true">https://openfreebooks.org/math/linear-inequalities-one-unknown/</guid>
<pubDate>Thu, 21 May 2026 06:33:43 GMT</pubDate>
<description>Solve, represent, and apply linear inequalities in one unknown using number lines and interval notation.</description>
</item>
<item>
<title>Counting and numbers</title>
<link>https://openfreebooks.org/math/counting-and-numbers/</link>
<guid isPermaLink="true">https://openfreebooks.org/math/counting-and-numbers/</guid>
<pubDate>Thu, 21 May 2026 05:05:38 GMT</pubDate>
<description>Count objects, spot patterns, add, multiply, and divide; then meet place value and big numbers from hundreds to billions.</description>
</item>
<item>
<title>Linear equations in two unknowns</title>
<link>https://openfreebooks.org/math/linear-equations-two-unknowns/</link>
<guid isPermaLink="true">https://openfreebooks.org/math/linear-equations-two-unknowns/</guid>
<pubDate>Thu, 21 May 2026 05:05:38 GMT</pubDate>
<description>Solve pairs of linear equations by substitution, elimination, and graphs; classify unique, parallel, and coincident cases.</description>
</item>
<item>
<title>Written calculation</title>
<link>https://openfreebooks.org/math/written-calculation/</link>
<guid isPermaLink="true">https://openfreebooks.org/math/written-calculation/</guid>
<pubDate>Thu, 21 May 2026 05:05:38 GMT</pubDate>
<description>Add, subtract, multiply, and divide whole numbers using vertical written form: align columns, regroup, and read the result.</description>
</item>
<item>
<title>English</title>
<link>https://openfreebooks.org/english/</link>
<guid isPermaLink="true">https://openfreebooks.org/english/</guid>
<pubDate>Thu, 21 May 2026 02:10:55 GMT</pubDate>
<description>Browse English chapters in the curriculum catalog.</description>
</item>
<item>
<title>Reading comprehension foundations</title>
<link>https://openfreebooks.org/english/reading-comprehension-foundations/</link>
<guid isPermaLink="true">https://openfreebooks.org/english/reading-comprehension-foundations/</guid>
<pubDate>Thu, 21 May 2026 02:10:55 GMT</pubDate>
<description>Find the main idea, key details, and evidence in short texts.</description>
</item>
<item>
<title>FAQ</title>
<link>https://openfreebooks.org/faq/</link>
<guid isPermaLink="true">https://openfreebooks.org/faq/</guid>
<pubDate>Thu, 21 May 2026 02:10:55 GMT</pubDate>
<description>FAQ</description>
</item>
<item>
<title>Physics</title>
<link>https://openfreebooks.org/physics/</link>
<guid isPermaLink="true">https://openfreebooks.org/physics/</guid>
<pubDate>Thu, 21 May 2026 02:10:55 GMT</pubDate>
<description>Browse physics chapters in the curriculum catalog.</description>
</item>
<item>
<title>Force and Newton&apos;s laws</title>
<link>https://openfreebooks.org/physics/force-newtons-laws/</link>
<guid isPermaLink="true">https://openfreebooks.org/physics/force-newtons-laws/</guid>
<pubDate>Thu, 21 May 2026 02:10:55 GMT</pubDate>
<description>Use free-body diagrams, F = ma, and motion graphs to predict how objects move.</description>
</item>
<item>
<title>Basic math notation</title>
<link>https://openfreebooks.org/math/basic-math-notation/</link>
<guid isPermaLink="true">https://openfreebooks.org/math/basic-math-notation/</guid>
<pubDate>Thu, 21 May 2026 01:07:39 GMT</pubDate>
<description>Learn the names and meanings of plus, minus, times, divide, equals, brackets, and other signs used in number sentences.</description>
</item>
<item>
<title>Zero and one</title>
<link>https://openfreebooks.org/math/zero-and-one/</link>
<guid isPermaLink="true">https://openfreebooks.org/math/zero-and-one/</guid>
<pubDate>Thu, 21 May 2026 01:07:39 GMT</pubDate>
<description>0 is nothing; 1 is one. Learn to tell them apart — the same skill as telling false from true — before counting larger amounts.</description>
</item>
<item>
<title>Algebraic fractions &amp; formulas</title>
<link>https://openfreebooks.org/math/algebraic-fractions-formulas/</link>
<guid isPermaLink="true">https://openfreebooks.org/math/algebraic-fractions-formulas/</guid>
<pubDate>Wed, 20 May 2026 20:17:25 GMT</pubDate>
<description>Simplify and combine algebraic fractions; rearrange formulas in science contexts.</description>
</item>
<item>
<title>Directed numbers, rates &amp; ratios</title>
<link>https://openfreebooks.org/math/directed-numbers-rates/</link>
<guid isPermaLink="true">https://openfreebooks.org/math/directed-numbers-rates/</guid>
<pubDate>Wed, 20 May 2026 20:17:25 GMT</pubDate>
<description>Operate with negative numbers and solve ratio, rate, and proportion problems in context.</description>
</item>
<item>
<title>Polynomials: expansion &amp; factorisation</title>
<link>https://openfreebooks.org/math/polynomials-expansion-factorisation/</link>
<guid isPermaLink="true">https://openfreebooks.org/math/polynomials-expansion-factorisation/</guid>
<pubDate>Wed, 20 May 2026 20:17:25 GMT</pubDate>
<description>Expand products, collect like terms, and factorise expressions using common factors and identities.</description>
</item>
<item>
<title>Properties of operations</title>
<link>https://openfreebooks.org/math/properties-of-operations/</link>
<guid isPermaLink="true">https://openfreebooks.org/math/properties-of-operations/</guid>
<pubDate>Wed, 20 May 2026 20:17:25 GMT</pubDate>
<description>See why order, grouping, and distribution can change how you write a sum or product without changing the result — with interactive area and dot models.</description>
</item>
<item>
<title>Quadratic equations</title>
<link>https://openfreebooks.org/math/quadratic-equations/</link>
<guid isPermaLink="true">https://openfreebooks.org/math/quadratic-equations/</guid>
<pubDate>Wed, 20 May 2026 20:17:25 GMT</pubDate>
<description>The discriminant, nature of roots, and the graph of y = ax² + bx + c.</description>
</item>
<item>
<title>Search</title>
<link>https://openfreebooks.org/search/</link>
<guid isPermaLink="true">https://openfreebooks.org/search/</guid>
<pubDate>Tue, 19 May 2026 19:29:49 GMT</pubDate>
<description>Search</description>
</item>
<item>
<title>Contribute</title>
<link>https://openfreebooks.org/contributing/</link>
<guid isPermaLink="true">https://openfreebooks.org/contributing/</guid>
<pubDate>Tue, 19 May 2026 12:34:25 GMT</pubDate>
<description>Contribute</description>
</item>
<item>
<title>Mathematics</title>
<link>https://openfreebooks.org/math/</link>
<guid isPermaLink="true">https://openfreebooks.org/math/</guid>
<pubDate>Tue, 19 May 2026 10:55:50 GMT</pubDate>
<description>Browse mathematics chapters in the curriculum catalog.</description>
</item>
<item>
<title>Catalog</title>
<link>https://openfreebooks.org/catalog/</link>
<guid isPermaLink="true">https://openfreebooks.org/catalog/</guid>
<pubDate>Tue, 19 May 2026 10:40:53 GMT</pubDate>
<description>Catalog</description>
</item>
<item>
<title>Manifesto</title>
<link>https://openfreebooks.org/about/</link>
<guid isPermaLink="true">https://openfreebooks.org/about/</guid>
<pubDate>Tue, 19 May 2026 06:35:42 GMT</pubDate>
<description>Manifesto</description>
</item>
<item>
<title>Credits</title>
<link>https://openfreebooks.org/credits/</link>
<guid isPermaLink="true">https://openfreebooks.org/credits/</guid>
<pubDate>Tue, 19 May 2026 05:33:20 GMT</pubDate>
<description>Credits</description>
</item>
<item>
<title>Open Free Books</title>
<link>https://openfreebooks.org/</link>
<guid isPermaLink="true">https://openfreebooks.org/</guid>
<pubDate>Tue, 19 May 2026 04:06:12 GMT</pubDate>
<description>Free, open-source textbooks for every learner — elementary through university. Read the full book online at no cost, or contribute on GitHub.</description>
</item>
</channel>
</rss>

99
static/sitemap.xml Normal file
View file

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://openfreebooks.org/</loc>
<lastmod>2026-05-19T04:06:12.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/about/</loc>
<lastmod>2026-05-19T06:35:42.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/catalog/</loc>
<lastmod>2026-05-19T10:40:53.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/contributing/</loc>
<lastmod>2026-05-19T12:34:25.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/credits/</loc>
<lastmod>2026-05-19T05:33:20.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/english/</loc>
<lastmod>2026-05-21T02:10:55.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/english/reading-comprehension-foundations/</loc>
<lastmod>2026-05-21T02:10:55.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/faq/</loc>
<lastmod>2026-05-21T02:10:55.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/math/</loc>
<lastmod>2026-05-19T10:55:50.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/math/algebraic-fractions-formulas/</loc>
<lastmod>2026-05-20T20:17:25.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/math/basic-math-notation/</loc>
<lastmod>2026-05-21T01:07:39.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/math/counting-and-numbers/</loc>
<lastmod>2026-05-21T05:05:38.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/math/directed-numbers-rates/</loc>
<lastmod>2026-05-20T20:17:25.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/math/laws-of-indices/</loc>
<lastmod>2026-05-21T06:54:29.553Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/math/linear-equations-two-unknowns/</loc>
<lastmod>2026-05-21T05:05:38.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/math/linear-inequalities-one-unknown/</loc>
<lastmod>2026-05-21T06:33:43.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/math/polynomials-expansion-factorisation/</loc>
<lastmod>2026-05-20T20:17:25.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/math/properties-of-operations/</loc>
<lastmod>2026-05-20T20:17:25.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/math/quadratic-equations/</loc>
<lastmod>2026-05-20T20:17:25.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/math/written-calculation/</loc>
<lastmod>2026-05-21T05:05:38.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/math/zero-and-one/</loc>
<lastmod>2026-05-21T01:07:39.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/physics/</loc>
<lastmod>2026-05-21T02:10:55.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/physics/force-newtons-laws/</loc>
<lastmod>2026-05-21T02:10:55.000Z</lastmod>
</url>
<url>
<loc>https://openfreebooks.org/search/</loc>
<lastmod>2026-05-19T19:29:49.000Z</lastmod>
</url>
</urlset>