<!-- canonical: efficientnewlanguage.org/ai/examples/911-the-two-points-were-joined-by-a-line | ai_layer_version: 0.1.0 | updated: 2026-09-18 -->

# Example 911 — The two points were joined by a line

`the_two_points_were_joined_by_a_line.eml` - Case counts are read on day zero and day three, both readings are exact, and a forecast for day thirty is drawn by extending the line through them. What kind of curve the two points lie on is computed below.

## EML

```eml
# Self-authored for the EML case corpus (no external origin). Case counts are
# read on day zero and day three, both readings are exact, and a forecast for
# day thirty is drawn by extending the line through them. What kind of curve the
# two points lie on is computed below.
#
# The forecast is careful. Both readings are real counts; the rise between them
# is computed exactly; the line is extended arithmetically, with no guesswork;
# and the intent is exactly 'how many cases on day thirty'.
#
# The count doubled in three days, and a thing that doubles every three days is
# not on a line: it doubles ten times by day thirty, and the straight-line
# forecast is short by a factor of ninety.

100 => cases_day_zero
200 => cases_day_three
3 => doubling_time_days
30 => forecast_day
1024 => two_to_the_tenth

cases_day_three - cases_day_zero => rise_in_three_days
int(forecast_day / doubling_time_days) => three_day_periods_to_forecast
cases_day_zero + rise_in_three_days * three_day_periods_to_forecast => linear_forecast
cases_day_zero * two_to_the_tenth => doubling_forecast
int(doubling_forecast / linear_forecast) => shortfall_factor
doubling_forecast - linear_forecast => cases_the_line_missed

"cases, day 0                    : " + str(cases_day_zero) ^0
"cases, day 3                    : " + str(cases_day_three) ^0
"rise in three days              : " + str(rise_in_three_days) ^0
"" ^0
"line extended to day " + str(forecast_day) + "         : " + str(cases_day_zero) + " + " + str(rise_in_three_days) + " x " + str(three_day_periods_to_forecast) + " = " + str(linear_forecast) ^0
"doubling extended to day " + str(forecast_day) + "     : " + str(cases_day_zero) + " x 2 to the " + str(three_day_periods_to_forecast) + " = " + str(doubling_forecast) ^0
"the line is short by            : " + str(cases_the_line_missed) + " cases, a factor of " + str(shortfall_factor) ^0
"" ^0

# ---- what the forecast verified ----

"the two-point forecast" ^0
"  readings : real counts on day 0 and day 3" ^0
"  rise : computed exactly, " + str(rise_in_three_days) ^0
"  extension : pure arithmetic, no guesswork" ^0
"  intent : how many cases on day " + str(forecast_day) ^0
"  readings misrecorded : 0" ^0
"  verdict : " + str(linear_forecast) + " CASES BY DAY 30" ^0
"" ^0
"  using two real readings and exact arithmetic is the part" ^0
"  done right here, and it is why the line is precisely the" ^0
"  line through those two points" ^0
"" ^0

# ---- what curve the two points lie on ----

"doubling" ^0
"  the two points fit : a line rising " + str(rise_in_three_days) + " per three days," ^0
"    and also a curve doubling every three days" ^0
"  with two points : the two are indistinguishable" ^0
"  by day " + str(forecast_day) + " : the line adds " + str(rise_in_three_days) + " ten times; the curve" ^0
"    doubles ten times" ^0
"  so the forecasts : " + str(linear_forecast) + " against " + str(doubling_forecast) ^0
"  which one an epidemic follows : the curve, until it runs" ^0
"    out of people" ^0
"" ^0

# ---- what the planners got ----

"the plan" ^0
"  capacity prepared for : " + str(linear_forecast) ^0
"  cases that arrived : " + str(doubling_forecast) ^0
"  is either reading wrong : no" ^0
"  is a line the only curve through two points : no; it is" ^0
"    the one that assumes the rise stays the same size" ^0
"" ^0

# ---- null control ----

# The same two readings extended as a doubling (or with a third reading, which
# separates the line from the curve at once).
1100 => nc_forecast_by_the_line
102400 => nc_forecast_by_doubling
101300 => nc_cases_the_doubling_model_accounts_for

"null control - extend the doubling, not the line" ^0
"  forecast by the line : " + str(nc_forecast_by_the_line) ^0
"  forecast by doubling : " + str(nc_forecast_by_doubling) ^0
"  cases the doubling model accounts for : " + str(nc_cases_the_doubling_model_accounts_for) ^0
"  no reading changed; the extension stopped assuming the" ^0
"  rise stays the same size" ^0
"" ^0

# ---- the rule ----

"what a line through two exact readings guarantees" ^0
"  the line passes through both readings : exactly, real" ^0
"    counts, exact rise" ^0
"  the line is where the counts are going : not addressed;" ^0
"    the counts doubled in three days, and ten more" ^0
"    doublings give " + str(doubling_forecast) + " where the line gives " + str(linear_forecast) ^0
"" ^0

"two points fit a line and fit a doubling equally well, and a rise that is" ^0
"constant in size and a rise that is constant in ratio part company only" ^0
"after the points run out; the forecast chose the one that is easy to draw" ^0
"" ^0

"Two exact readings, an exact rise, a line drawn without guesswork - the line" ^0
"is right about the two points. But the count doubled in three days, and ten" ^0
"more doublings by day " + str(forecast_day) + " give " + str(doubling_forecast) + " where the line gives " + str(linear_forecast) + ", short by a" ^0
"factor of " + str(shortfall_factor) + ", until the extension follows the ratio and not the difference." ^0
```

## Python (deterministic transpilation)

```python
cases_day_zero = 100
cases_day_three = 200
doubling_time_days = 3
forecast_day = 30
two_to_the_tenth = 1024
rise_in_three_days = cases_day_three - cases_day_zero
three_day_periods_to_forecast = int(forecast_day / doubling_time_days)
linear_forecast = cases_day_zero + rise_in_three_days * three_day_periods_to_forecast
doubling_forecast = cases_day_zero * two_to_the_tenth
shortfall_factor = int(doubling_forecast / linear_forecast)
cases_the_line_missed = doubling_forecast - linear_forecast
print("cases, day 0                    : " + str(cases_day_zero))
print("cases, day 3                    : " + str(cases_day_three))
print("rise in three days              : " + str(rise_in_three_days))
print("")
print("line extended to day " + str(forecast_day) + "         : " + str(cases_day_zero) + " + " + str(rise_in_three_days) + " x " + str(three_day_periods_to_forecast) + " = " + str(linear_forecast))
print("doubling extended to day " + str(forecast_day) + "     : " + str(cases_day_zero) + " x 2 to the " + str(three_day_periods_to_forecast) + " = " + str(doubling_forecast))
print("the line is short by            : " + str(cases_the_line_missed) + " cases, a factor of " + str(shortfall_factor))
print("")
print("the two-point forecast")
print("  readings : real counts on day 0 and day 3")
print("  rise : computed exactly, " + str(rise_in_three_days))
print("  extension : pure arithmetic, no guesswork")
print("  intent : how many cases on day " + str(forecast_day))
print("  readings misrecorded : 0")
print("  verdict : " + str(linear_forecast) + " CASES BY DAY 30")
print("")
print("  using two real readings and exact arithmetic is the part")
print("  done right here, and it is why the line is precisely the")
print("  line through those two points")
print("")
print("doubling")
print("  the two points fit : a line rising " + str(rise_in_three_days) + " per three days,")
print("    and also a curve doubling every three days")
print("  with two points : the two are indistinguishable")
print("  by day " + str(forecast_day) + " : the line adds " + str(rise_in_three_days) + " ten times; the curve")
print("    doubles ten times")
print("  so the forecasts : " + str(linear_forecast) + " against " + str(doubling_forecast))
print("  which one an epidemic follows : the curve, until it runs")
print("    out of people")
print("")
print("the plan")
print("  capacity prepared for : " + str(linear_forecast))
print("  cases that arrived : " + str(doubling_forecast))
print("  is either reading wrong : no")
print("  is a line the only curve through two points : no; it is")
print("    the one that assumes the rise stays the same size")
print("")
nc_forecast_by_the_line = 1100
nc_forecast_by_doubling = 102400
nc_cases_the_doubling_model_accounts_for = 101300
print("null control - extend the doubling, not the line")
print("  forecast by the line : " + str(nc_forecast_by_the_line))
print("  forecast by doubling : " + str(nc_forecast_by_doubling))
print("  cases the doubling model accounts for : " + str(nc_cases_the_doubling_model_accounts_for))
print("  no reading changed; the extension stopped assuming the")
print("  rise stays the same size")
print("")
print("what a line through two exact readings guarantees")
print("  the line passes through both readings : exactly, real")
print("    counts, exact rise")
print("  the line is where the counts are going : not addressed;")
print("    the counts doubled in three days, and ten more")
print("    doublings give " + str(doubling_forecast) + " where the line gives " + str(linear_forecast))
print("")
print("two points fit a line and fit a doubling equally well, and a rise that is")
print("constant in size and a rise that is constant in ratio part company only")
print("after the points run out; the forecast chose the one that is easy to draw")
print("")
print("Two exact readings, an exact rise, a line drawn without guesswork - the line")
print("is right about the two points. But the count doubled in three days, and ten")
print("more doublings by day " + str(forecast_day) + " give " + str(doubling_forecast) + " where the line gives " + str(linear_forecast) + ", short by a")
print("factor of " + str(shortfall_factor) + ", until the extension follows the ratio and not the difference.")
```

## stdout (executed)

```text
cases, day 0                    : 100
cases, day 3                    : 200
rise in three days              : 100

line extended to day 30         : 100 + 100 x 10 = 1100
doubling extended to day 30     : 100 x 2 to the 10 = 102400
the line is short by            : 101300 cases, a factor of 93

the two-point forecast
  readings : real counts on day 0 and day 3
  rise : computed exactly, 100
  extension : pure arithmetic, no guesswork
  intent : how many cases on day 30
  readings misrecorded : 0
  verdict : 1100 CASES BY DAY 30

  using two real readings and exact arithmetic is the part
  done right here, and it is why the line is precisely the
  line through those two points

doubling
  the two points fit : a line rising 100 per three days,
    and also a curve doubling every three days
  with two points : the two are indistinguishable
  by day 30 : the line adds 100 ten times; the curve
    doubles ten times
  so the forecasts : 1100 against 102400
  which one an epidemic follows : the curve, until it runs
    out of people

the plan
  capacity prepared for : 1100
  cases that arrived : 102400
  is either reading wrong : no
  is a line the only curve through two points : no; it is
    the one that assumes the rise stays the same size

null control - extend the doubling, not the line
  forecast by the line : 1100
  forecast by doubling : 102400
  cases the doubling model accounts for : 101300
  no reading changed; the extension stopped assuming the
  rise stays the same size

what a line through two exact readings guarantees
  the line passes through both readings : exactly, real
    counts, exact rise
  the line is where the counts are going : not addressed;
    the counts doubled in three days, and ten more
    doublings give 102400 where the line gives 1100

two points fit a line and fit a doubling equally well, and a rise that is
constant in size and a rise that is constant in ratio part company only
after the points run out; the forecast chose the one that is easy to draw

Two exact readings, an exact rise, a line drawn without guesswork - the line
is right about the two points. But the count doubled in three days, and ten
more doublings by day 30 give 102400 where the line gives 1100, short by a
factor of 93, until the extension follows the ratio and not the difference.
```

## Round-trip

`ok: true` — round-trip fixpoint reached (python1 == python2)

## Trace event types

eml:run:start · eml:assign · eml:output · eml:run:done
