Script: Mathematical Reference Tables Generator

Recovery Library — Source Code

This Python script generates the Mathematical Reference Tables used by several Category 2 (Reference Data) documents. It produces tables of logarithms, trigonometric functions, square roots, reciprocals, factorials, powers, unit conversions, and physical constants.

Requirements: Python 3.6+ with standard library only (no external dependencies).

Usage:

python scripts/generate_math_tables.py [output_file]
# Default output: tables-mathematical.md

Output: tables-mathematical.md — approximately 1,500 lines of markdown tables, converted to HTML by the site build process.


Source Code

#!/usr/bin/env python3
"""
Generate mathematical reference tables for the Recovery Library.

Produces tables-mathematical.md with:
- 4-figure common logarithms (1.00–9.99)
- Trigonometric functions (sin, cos, tan at 1° intervals)
- Natural logarithms (1.0–10.0)
- Square roots (1–100)
- Reciprocals (1–100)
- Powers of 2, e, 10
- Unit conversion factors
- Physical and mathematical constants

All values computed using Python's math module (IEEE 754 double precision).
Output is markdown formatted for pandoc conversion to HTML.

Usage:
    python generate_math_tables.py [output_file]
    Default output: ../tables-mathematical.md
"""

import math
import sys
import os
from datetime import datetime


def header():
    return f"""---
title: "Mathematical Reference Tables"
subtitle: "Recovery Library — Computed Reference Data"
date: "{datetime.now().strftime('%Y-%m-%d')}"
---

# Mathematical Reference Tables

*Recovery Library — Computed Reference Data*

**Computation method:** All values computed using Python's `math` module (IEEE 754 double-precision floating point, ~15 significant digits). Tables are rounded to the displayed precision. Any computer with Python (or equivalent) can reproduce and extend these tables by running the generation script (`scripts/generate_math_tables.py`).

**Verification:** Values can be checked against any standard mathematical reference. The generation script is included in the library for reproducibility.

---

"""


def log_tables():
    """4-figure common logarithms for 1.00 to 9.99."""
    out = "## Common Logarithms (Base 10)\n\n"
    out += "log₁₀(N) for N = 1.00 to 9.99. To find log₁₀ of any number, "
    out += "use log₁₀(a × 10ⁿ) = log₁₀(a) + n.\n\n"

    # Header row
    out += "| N | .00 | .01 | .02 | .03 | .04 | .05 | .06 | .07 | .08 | .09 |\n"
    out += "|---|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|\n"

    for row in range(10, 100):  # 1.0 to 9.9
        n_base = row / 10.0
        out += f"| {n_base:.1f}"
        for col in range(10):
            n = n_base + col / 100.0
            val = math.log10(n)
            out += f" | {val:.4f}"
        out += " |\n"

    out += "\n---\n\n"
    return out


def trig_tables():
    """Trigonometric functions at 1° intervals."""
    out = "## Trigonometric Functions\n\n"
    out += "Values of sin, cos, and tan for 0° to 90° in 1° increments.\n\n"

    out += "| Deg | sin | cos | tan | Deg | sin | cos | tan |\n"
    out += "|-----|------|------|------|-----|------|------|------|\n"

    for deg in range(0, 46):
        rad = math.radians(deg)
        sin_v = math.sin(rad)
        cos_v = math.cos(rad)
        tan_v = math.tan(rad) if deg != 90 else "—"

        deg2 = deg + 45
        if deg2 <= 90:
            rad2 = math.radians(deg2)
            sin_v2 = math.sin(rad2)
            cos_v2 = math.cos(rad2)
            if deg2 == 90:
                tan_v2 = "∞"
            else:
                tan_v2 = f"{math.tan(rad2):.4f}"

            if isinstance(tan_v, float):
                tan_v = f"{tan_v:.4f}"

            out += f"| {deg}° | {sin_v:.4f} | {cos_v:.4f} | {tan_v} | {deg2}° | {sin_v2:.4f} | {cos_v2:.4f} | {tan_v2} |\n"
        else:
            if isinstance(tan_v, float):
                tan_v = f"{tan_v:.4f}"
            out += f"| {deg}° | {sin_v:.4f} | {cos_v:.4f} | {tan_v} | | | | |\n"

    out += "\n---\n\n"
    return out


def trig_fine_tables():
    """Trigonometric functions at 0.1° intervals for navigation use."""
    out = "## Trigonometric Functions — Fine Resolution (0.1°)\n\n"
    out += "For celestial navigation sight reduction. Values at 0.1° intervals, 0°–90°.\n\n"

    out += "| Deg | sin | cos | tan |\n"
    out += "|------|--------|--------|--------|\n"

    for i in range(0, 901):
        deg = i / 10.0
        rad = math.radians(deg)
        sin_v = math.sin(rad)
        cos_v = math.cos(rad)
        if deg == 90.0:
            tan_v = "∞"
        else:
            tan_v = f"{math.tan(rad):.6f}"

        out += f"| {deg:5.1f}° | {sin_v:.6f} | {cos_v:.6f} | {tan_v} |\n"

    out += "\n---\n\n"
    return out


def natural_log_table():
    """Natural logarithms for 1.0 to 10.0 in steps of 0.1."""
    out = "## Natural Logarithms (ln)\n\n"
    out += "ln(N) for N = 0.1 to 10.0. Use ln(a × eⁿ) = ln(a) + n.\n\n"

    out += "| N | ln(N) | N | ln(N) | N | ln(N) | N | ln(N) |\n"
    out += "|-----|--------|-----|--------|-----|--------|-----|--------|\n"

    values = []
    for i in range(1, 101):
        n = i / 10.0
        values.append((n, math.log(n)))

    # 4 columns
    rows = (len(values) + 3) // 4
    for r in range(rows):
        parts = []
        for c in range(4):
            idx = r + c * rows
            if idx < len(values):
                n, ln_n = values[idx]
                parts.append(f"| {n:5.1f} | {ln_n:7.4f}")
            else:
                parts.append("|       |        ")
        out += " ".join(parts) + " |\n"

    out += "\n---\n\n"
    return out


def sqrt_table():
    """Square roots of 1 to 200."""
    out = "## Square Roots\n\n"
    out += "√N for N = 1 to 200.\n\n"

    out += "| N | √N | N | √N | N | √N | N | √N |\n"
    out += "|---|------|---|------|---|------|---|------|\n"

    rows = 50
    for r in range(rows):
        parts = []
        for c in range(4):
            n = r + 1 + c * rows
            if n <= 200:
                parts.append(f"| {n:3d} | {math.sqrt(n):.4f}")
            else:
                parts.append("|     |       ")
        out += " ".join(parts) + " |\n"

    out += "\n---\n\n"
    return out


def reciprocal_table():
    """Reciprocals of 1 to 200."""
    out = "## Reciprocals\n\n"
    out += "1/N for N = 1 to 200.\n\n"

    out += "| N | 1/N | N | 1/N | N | 1/N | N | 1/N |\n"
    out += "|---|--------|---|--------|---|--------|---|--------|\n"

    rows = 50
    for r in range(rows):
        parts = []
        for c in range(4):
            n = r + 1 + c * rows
            if n <= 200:
                parts.append(f"| {n:3d} | {1/n:.6f}")
            else:
                parts.append("|     |         ")
        out += " ".join(parts) + " |\n"

    out += "\n---\n\n"
    return out


def powers_table():
    """Powers of 2, e, and 10."""
    out = "## Powers and Exponentials\n\n"

    out += "### Powers of 2\n\n"
    out += "| n | 2ⁿ | n | 2ⁿ |\n"
    out += "|---|-----|---|-----|\n"
    for i in range(0, 17):
        j = i + 17
        if j <= 32:
            out += f"| {i} | {2**i:,} | {j} | {2**j:,} |\n"
        else:
            out += f"| {i} | {2**i:,} | | |\n"

    out += "\n### Powers of e\n\n"
    out += "| x | eˣ | x | eˣ |\n"
    out += "|-----|----------|-----|----------|\n"
    e_values = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9,
                1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 6, 7, 8, 9, 10]
    half = (len(e_values) + 1) // 2
    for i in range(half):
        x1 = e_values[i]
        v1 = math.exp(x1)
        if i + half < len(e_values):
            x2 = e_values[i + half]
            v2 = math.exp(x2)
            out += f"| {x1} | {v1:.6f} | {x2} | {v2:.4f} |\n"
        else:
            out += f"| {x1} | {v1:.6f} | | |\n"

    out += "\n### Powers of 10\n\n"
    out += "| n | 10ⁿ |\n"
    out += "|---|-----|\n"
    for i in range(-10, 11):
        if i < 0:
            out += f"| {i} | {10**i:.10f} |\n"
        else:
            out += f"| {i} | {10**i:,} |\n"

    out += "\n---\n\n"
    return out


def conversion_table():
    """Unit conversion factors."""
    out = "## Unit Conversion Factors\n\n"

    out += "### Length\n\n"
    out += "| From | To | Multiply by |\n"
    out += "|------|------|-------------|\n"
    conversions_length = [
        ("metres", "feet", 3.28084),
        ("metres", "inches", 39.3701),
        ("metres", "yards", 1.09361),
        ("kilometres", "miles", 0.621371),
        ("kilometres", "nautical miles", 0.539957),
        ("nautical miles", "kilometres", 1.852),
        ("nautical miles", "statute miles", 1.15078),
        ("inches", "millimetres", 25.4),
        ("feet", "metres", 0.3048),
        ("miles", "kilometres", 1.60934),
        ("fathoms", "metres", 1.8288),
    ]
    for f, t, v in conversions_length:
        out += f"| {f} | {t} | {v} |\n"

    out += "\n### Mass\n\n"
    out += "| From | To | Multiply by |\n"
    out += "|------|------|-------------|\n"
    conversions_mass = [
        ("kilograms", "pounds", 2.20462),
        ("pounds", "kilograms", 0.453592),
        ("tonnes (metric)", "short tons", 1.10231),
        ("tonnes (metric)", "long tons", 0.984207),
        ("ounces", "grams", 28.3495),
        ("troy ounces", "grams", 31.1035),
    ]
    for f, t, v in conversions_mass:
        out += f"| {f} | {t} | {v} |\n"

    out += "\n### Volume\n\n"
    out += "| From | To | Multiply by |\n"
    out += "|------|------|-------------|\n"
    conversions_vol = [
        ("litres", "US gallons", 0.264172),
        ("litres", "imperial gallons", 0.219969),
        ("US gallons", "litres", 3.78541),
        ("imperial gallons", "litres", 4.54609),
        ("cubic metres", "litres", 1000),
        ("cubic feet", "litres", 28.3168),
    ]
    for f, t, v in conversions_vol:
        out += f"| {f} | {t} | {v} |\n"

    out += "\n### Temperature\n\n"
    out += "°F = °C × 9/5 + 32  \n"
    out += "°C = (°F − 32) × 5/9  \n"
    out += "K = °C + 273.15\n\n"

    out += "| °C | °F | K |\n"
    out += "|----|-----|------|\n"
    temps = [-40, -30, -20, -10, 0, 5, 10, 15, 20, 25, 30, 37, 40, 50,
             60, 80, 100, 150, 200, 500, 1000]
    for c in temps:
        f = c * 9 / 5 + 32
        k = c + 273.15
        out += f"| {c} | {f:.1f} | {k:.2f} |\n"

    out += "\n### Pressure\n\n"
    out += "| From | To | Multiply by |\n"
    out += "|------|------|-------------|\n"
    conversions_press = [
        ("atmospheres", "kPa", 101.325),
        ("atmospheres", "psi", 14.696),
        ("bar", "kPa", 100),
        ("psi", "kPa", 6.89476),
        ("mmHg (torr)", "kPa", 0.133322),
        ("inHg", "kPa", 3.38639),
    ]
    for f, t, v in conversions_press:
        out += f"| {f} | {t} | {v} |\n"

    out += "\n### Energy\n\n"
    out += "| From | To | Multiply by |\n"
    out += "|------|------|-------------|\n"
    conversions_energy = [
        ("kWh", "MJ", 3.6),
        ("kWh", "BTU", 3412.14),
        ("MJ", "kWh", 0.277778),
        ("calories", "joules", 4.184),
        ("BTU", "kJ", 1.05506),
        ("horsepower-hours", "kWh", 0.7457),
    ]
    for f, t, v in conversions_energy:
        out += f"| {f} | {t} | {v} |\n"

    out += "\n---\n\n"
    return out


def constants_table():
    """Physical and mathematical constants."""
    out = "## Physical and Mathematical Constants\n\n"

    out += "### Mathematical Constants\n\n"
    out += "| Constant | Symbol | Value |\n"
    out += "|----------|--------|-------|\n"
    out += f"| Pi | π | {math.pi:.15f} |\n"
    out += f"| Euler's number | e | {math.e:.15f} |\n"
    out += f"| √2 | | {math.sqrt(2):.15f} |\n"
    out += f"| √3 | | {math.sqrt(3):.15f} |\n"
    out += f"| ln(2) | | {math.log(2):.15f} |\n"
    out += f"| ln(10) | | {math.log(10):.15f} |\n"
    out += f"| log₁₀(2) | | {math.log10(2):.15f} |\n"
    out += f"| log₁₀(e) | | {math.log10(math.e):.15f} |\n"
    out += f"| 1 radian | | {math.degrees(1):.10f}° |\n"
    out += f"| 1 degree | | {math.radians(1):.15f} rad |\n"

    out += "\n### Physical Constants\n\n"
    out += "| Constant | Symbol | Value | Unit |\n"
    out += "|----------|--------|-------|------|\n"
    phys_constants = [
        ("Speed of light in vacuum", "c", "299,792,458", "m/s"),
        ("Gravitational constant", "G", "6.674 × 10⁻¹¹", "N⋅m²/kg²"),
        ("Planck constant", "h", "6.626 × 10⁻³⁴", "J⋅s"),
        ("Boltzmann constant", "k_B", "1.381 × 10⁻²³", "J/K"),
        ("Avogadro's number", "N_A", "6.022 × 10²³", "mol⁻¹"),
        ("Gas constant", "R", "8.314", "J/(mol⋅K)"),
        ("Stefan-Boltzmann constant", "σ", "5.670 × 10⁻⁸", "W/(m²⋅K⁴)"),
        ("Elementary charge", "e", "1.602 × 10⁻¹⁹", "C"),
        ("Electron mass", "m_e", "9.109 × 10⁻³¹", "kg"),
        ("Proton mass", "m_p", "1.673 × 10⁻²⁷", "kg"),
        ("Standard gravity", "g", "9.80665", "m/s²"),
        ("Standard atmosphere", "atm", "101,325", "Pa"),
        ("Absolute zero", "", "−273.15", "°C"),
        ("Water triple point", "", "273.16", "K"),
        ("Solar constant", "", "1,361", "W/m²"),
    ]
    for name, sym, val, unit in phys_constants:
        out += f"| {name} | {sym} | {val} | {unit} |\n"

    out += "\n### Astronomical Constants\n\n"
    out += "| Constant | Value | Unit |\n"
    out += "|----------|-------|------|\n"
    astro = [
        ("Earth equatorial radius", "6,378.137", "km"),
        ("Earth polar radius", "6,356.752", "km"),
        ("Earth mean radius", "6,371.0", "km"),
        ("Earth mass", "5.972 × 10²⁴", "kg"),
        ("Earth-Sun mean distance (1 AU)", "149,597,870.7", "km"),
        ("Earth orbital period (sidereal year)", "365.25636", "days"),
        ("Earth axial tilt (J2000)", "23.4393°", ""),
        ("Moon mean distance", "384,400", "km"),
        ("Moon orbital period (sidereal)", "27.322", "days"),
        ("Moon synodic period", "29.531", "days"),
        ("Solar radius", "695,700", "km"),
        ("Solar luminosity", "3.828 × 10²⁶", "W"),
    ]
    for name, val, unit in astro:
        out += f"| {name} | {val} | {unit} |\n"

    out += "\n---\n\n"
    return out


def factorial_table():
    """Factorials and common combinatorial values."""
    out = "## Factorials\n\n"
    out += "| n | n! | ln(n!) |\n"
    out += "|---|------|--------|\n"
    for n in range(0, 26):
        fact = math.factorial(n)
        ln_fact = math.lgamma(n + 1)
        out += f"| {n} | {fact:,} | {ln_fact:.4f} |\n"

    out += "\n---\n\n"
    return out


def main():
    output_file = sys.argv[1] if len(sys.argv) > 1 else os.path.join(
        os.path.dirname(os.path.abspath(__file__)), "..", "tables-mathematical.md"
    )

    content = header()
    content += log_tables()
    content += trig_tables()
    content += trig_fine_tables()
    content += natural_log_table()
    content += sqrt_table()
    content += reciprocal_table()
    content += factorial_table()
    content += powers_table()
    content += conversion_table()
    content += constants_table()

    content += "*Generated by `scripts/generate_math_tables.py` using Python's "
    content += "`math` module (IEEE 754 double precision).*\n"

    with open(output_file, 'w') as f:
        f.write(content)

    print(f"Written to {output_file}")
    print(f"Total lines: {content.count(chr(10))}")


if __name__ == "__main__":
    main()