FA-68846 / Tide and marine navigation tables / Open access
Rule of twelfths: Tide hour index rounds to the nearest hour · case 01
Heights between whole tide hours jump by a whole twelfth-step early.
ROOT CAUSE
The current tide hour is chosen with round(u) instead of floor, so the second half of each hour uses the next segment.
VERIFIED REPAIR
Take the floor of the tide-hour position and clamp to the last segment.
Unsuccessful approach: Clamping the floor to segment 4 extrapolates the fifth-hour slope through the final tide hour.
Case contract
Input [t1,h1,t2,h2,t] in hours/metres for consecutive turning points t1<t2 (rise or fall). The interval is split into six equal tide hours with cumulative twelfths 0,1,3,6,9,11,12 and linear interpolation inside a tide hour. Return height rounded to 4 decimals, or None when t2<=t1 or t lies outside [t1,t2].
Why this case matters
Tide tables and passage plans turn published predictions into go/no-go decisions about depth, clearance and timing.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
t1,h1,t2,h2,t=x
if t2<=t1: return None
if t<t1 or t>t2: return None
u=(t-t1)*6/(t2-t1)
k=min(round(u),5)
cum=[0,1,3,6,9,11,12]
c=cum[k]+(cum[k+1]-cum[k])*(u-k)
return round(h1+(h2-h1)*c/12,4)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('rule of twelfths [0, 0.5, 6, 4.1, 1.5]', [0, 0.5, 6, 4.1, 1.5], 1.1), ('rule of twelfths [0, 0.5, 6, 4.1, 6]', [0, 0.5, 6, 4.1, 6], 4.1), ('rule of twelfths [0, 0.5, 6, 4.1, 0]', [0, 0.5, 6, 4.1, 0], 0.5), ('rule of twelfths [0, 0.5, 6, 4.1, 1]', [0, 0.5, 6, 4.1, 1], 0.8), ('rule of twelfths [0, 0.5, 6, 4.1, 2]', [0, 0.5, 6, 4.1, 2], 1.4), ('rule of twelfths [0, 0.5, 6, 4.1, 3]', [0, 0.5, 6, 4.1, 3], 2.3), ('rule of twelfths [0, 0.5, 6, 4.1, 4.5]', [0, 0.5, 6, 4.1, 4.5], 3.5)], [('rule of twelfths [2, 5.2, 8.5, 0.8, 4]', [2, 5.2, 8.5, 0.8, 4], 4.2128), ('rule of twelfths [2, 5.2, 8.5, 0.8, 8.5]', [2, 5.2, 8.5, 0.8, 8.5], 0.8), ('rule of twelfths [0, 0.5, 6, 4.1, 2]', [0, 0.5, 6, 4.1, 2], 1.4), ('rule of twelfths [0, 0.5, 6, 4.1, 3]', [0, 0.5, 6, 4.1, 3], 2.3), ('rule of twelfths [0, 0.5, 6, 4.1, 4.5]', [0, 0.5, 6, 4.1, 4.5], 3.5), ('rule of twelfths [0, 0.5, 6, 4.1, 5]', [0, 0.5, 6, 4.1, 5], 3.8), ('rule of twelfths [0, 0.5, 6, 4.1, 6]', [0, 0.5, 6, 4.1, 6], 4.1)], [('rule of twelfths [2, 5.2, 8.5, 0.8, 7]', [2, 5.2, 8.5, 0.8, 7], 1.4487), ('rule of twelfths [0, 0.5, 6, 4.1, 6]', [0, 0.5, 6, 4.1, 6], 4.1), ('rule of twelfths [0, 0.5, 6, 4.1, 5]', [0, 0.5, 6, 4.1, 5], 3.8), ('rule of twelfths [0, 0.5, 6, 4.1, 6.5]', [0, 0.5, 6, 4.1, 6.5], None), ('rule of twelfths [0, 0.5, 6, 4.1, 7.25]', [0, 0.5, 6, 4.1, 7.25], None), ('rule of twelfths [2, 5.2, 8.5, 0.8, 2]', [2, 5.2, 8.5, 0.8, 2], 5.2), ('rule of twelfths [2, 5.2, 8.5, 0.8, 3.1]', [2, 5.2, 8.5, 0.8, 3.1], 4.8221)], [('rule of twelfths [0, 0.0, 6, 6.0, 0.6]', [0, 0.0, 6, 6.0, 0.6], 0.3), ('rule of twelfths [2, 5.2, 8.5, 0.8, 8.5]', [2, 5.2, 8.5, 0.8, 8.5], 0.8), ('rule of twelfths [0, 0.5, 6, 4.1, 7.25]', [0, 0.5, 6, 4.1, 7.25], None), ('rule of twelfths [2, 5.2, 8.5, 0.8, 2]', [2, 5.2, 8.5, 0.8, 2], 5.2), ('rule of twelfths [2, 5.2, 8.5, 0.8, 3.1]', [2, 5.2, 8.5, 0.8, 3.1], 4.8221), ('rule of twelfths [2, 5.2, 8.5, 0.8, 4]', [2, 5.2, 8.5, 0.8, 4], 4.2128), ('rule of twelfths [2, 5.2, 8.5, 0.8, 5.25]', [2, 5.2, 8.5, 0.8, 5.25], 3.0)], [('rule of twelfths [0, 0.5, 6, 4.1, 1.5]', [0, 0.5, 6, 4.1, 1.5], 1.1), ('rule of twelfths [0, 0.5, 6, 4.1, 6]', [0, 0.5, 6, 4.1, 6], 4.1), ('rule of twelfths [2, 5.2, 8.5, 0.8, 4]', [2, 5.2, 8.5, 0.8, 4], 4.2128), ('rule of twelfths [2, 5.2, 8.5, 0.8, 5.25]', [2, 5.2, 8.5, 0.8, 5.25], 3.0), ('rule of twelfths [2, 5.2, 8.5, 0.8, 7]', [2, 5.2, 8.5, 0.8, 7], 1.4487), ('rule of twelfths [2, 5.2, 8.5, 0.8, 8.5]', [2, 5.2, 8.5, 0.8, 8.5], 0.8), ('rule of twelfths [2, 5.2, 8.5, 0.8, 9]', [2, 5.2, 8.5, 0.8, 9], None)]]
for label, args, expected in fixtures[N-1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| rule of twelfths [0, 0.5, 6, 4.1, 1.5] | 0.95 | 1.1 | Failed |
| rule of twelfths [0, 0.5, 6, 4.1, 6] | 4.1 | 4.1 | Passed |
| rule of twelfths [0, 0.5, 6, 4.1, 0] | 0.5 | 0.5 | Passed |
| rule of twelfths [0, 0.5, 6, 4.1, 1] | 0.8 | 0.8 | Passed |
| rule of twelfths [0, 0.5, 6, 4.1, 2] | 1.4 | 1.4 | Passed |
| rule of twelfths [0, 0.5, 6, 4.1, 3] | 2.3 | 2.3 | Passed |
| rule of twelfths [0, 0.5, 6, 4.1, 4.5] | 3.5 | 3.5 | Passed |
SHA-256 / c9343a34f58b7454319cc1bbe3e41d5fcd2977a02e83b3a1f9a97b851fdd4c7d
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
t1,h1,t2,h2,t=x
if t2<=t1: return None
if t<t1 or t>t2: return None
u=(t-t1)*6/(t2-t1)
k=min(int(u),4)
cum=[0,1,3,6,9,11,12]
c=cum[k]+(cum[k+1]-cum[k])*(u-k)
return round(h1+(h2-h1)*c/12,4)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('rule of twelfths [0, 0.5, 6, 4.1, 1.5]', [0, 0.5, 6, 4.1, 1.5], 1.1), ('rule of twelfths [0, 0.5, 6, 4.1, 6]', [0, 0.5, 6, 4.1, 6], 4.1), ('rule of twelfths [0, 0.5, 6, 4.1, 0]', [0, 0.5, 6, 4.1, 0], 0.5), ('rule of twelfths [0, 0.5, 6, 4.1, 1]', [0, 0.5, 6, 4.1, 1], 0.8), ('rule of twelfths [0, 0.5, 6, 4.1, 2]', [0, 0.5, 6, 4.1, 2], 1.4), ('rule of twelfths [0, 0.5, 6, 4.1, 3]', [0, 0.5, 6, 4.1, 3], 2.3), ('rule of twelfths [0, 0.5, 6, 4.1, 4.5]', [0, 0.5, 6, 4.1, 4.5], 3.5)], [('rule of twelfths [2, 5.2, 8.5, 0.8, 4]', [2, 5.2, 8.5, 0.8, 4], 4.2128), ('rule of twelfths [2, 5.2, 8.5, 0.8, 8.5]', [2, 5.2, 8.5, 0.8, 8.5], 0.8), ('rule of twelfths [0, 0.5, 6, 4.1, 2]', [0, 0.5, 6, 4.1, 2], 1.4), ('rule of twelfths [0, 0.5, 6, 4.1, 3]', [0, 0.5, 6, 4.1, 3], 2.3), ('rule of twelfths [0, 0.5, 6, 4.1, 4.5]', [0, 0.5, 6, 4.1, 4.5], 3.5), ('rule of twelfths [0, 0.5, 6, 4.1, 5]', [0, 0.5, 6, 4.1, 5], 3.8), ('rule of twelfths [0, 0.5, 6, 4.1, 6]', [0, 0.5, 6, 4.1, 6], 4.1)], [('rule of twelfths [2, 5.2, 8.5, 0.8, 7]', [2, 5.2, 8.5, 0.8, 7], 1.4487), ('rule of twelfths [0, 0.5, 6, 4.1, 6]', [0, 0.5, 6, 4.1, 6], 4.1), ('rule of twelfths [0, 0.5, 6, 4.1, 5]', [0, 0.5, 6, 4.1, 5], 3.8), ('rule of twelfths [0, 0.5, 6, 4.1, 6.5]', [0, 0.5, 6, 4.1, 6.5], None), ('rule of twelfths [0, 0.5, 6, 4.1, 7.25]', [0, 0.5, 6, 4.1, 7.25], None), ('rule of twelfths [2, 5.2, 8.5, 0.8, 2]', [2, 5.2, 8.5, 0.8, 2], 5.2), ('rule of twelfths [2, 5.2, 8.5, 0.8, 3.1]', [2, 5.2, 8.5, 0.8, 3.1], 4.8221)], [('rule of twelfths [0, 0.0, 6, 6.0, 0.6]', [0, 0.0, 6, 6.0, 0.6], 0.3), ('rule of twelfths [2, 5.2, 8.5, 0.8, 8.5]', [2, 5.2, 8.5, 0.8, 8.5], 0.8), ('rule of twelfths [0, 0.5, 6, 4.1, 7.25]', [0, 0.5, 6, 4.1, 7.25], None), ('rule of twelfths [2, 5.2, 8.5, 0.8, 2]', [2, 5.2, 8.5, 0.8, 2], 5.2), ('rule of twelfths [2, 5.2, 8.5, 0.8, 3.1]', [2, 5.2, 8.5, 0.8, 3.1], 4.8221), ('rule of twelfths [2, 5.2, 8.5, 0.8, 4]', [2, 5.2, 8.5, 0.8, 4], 4.2128), ('rule of twelfths [2, 5.2, 8.5, 0.8, 5.25]', [2, 5.2, 8.5, 0.8, 5.25], 3.0)], [('rule of twelfths [0, 0.5, 6, 4.1, 1.5]', [0, 0.5, 6, 4.1, 1.5], 1.1), ('rule of twelfths [0, 0.5, 6, 4.1, 6]', [0, 0.5, 6, 4.1, 6], 4.1), ('rule of twelfths [2, 5.2, 8.5, 0.8, 4]', [2, 5.2, 8.5, 0.8, 4], 4.2128), ('rule of twelfths [2, 5.2, 8.5, 0.8, 5.25]', [2, 5.2, 8.5, 0.8, 5.25], 3.0), ('rule of twelfths [2, 5.2, 8.5, 0.8, 7]', [2, 5.2, 8.5, 0.8, 7], 1.4487), ('rule of twelfths [2, 5.2, 8.5, 0.8, 8.5]', [2, 5.2, 8.5, 0.8, 8.5], 0.8), ('rule of twelfths [2, 5.2, 8.5, 0.8, 9]', [2, 5.2, 8.5, 0.8, 9], None)]]
for label, args, expected in fixtures[N-1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| rule of twelfths [0, 0.5, 6, 4.1, 1.5] | 1.1 | 1.1 | Passed |
| rule of twelfths [0, 0.5, 6, 4.1, 6] | 4.4 | 4.1 | Failed |
| rule of twelfths [0, 0.5, 6, 4.1, 0] | 0.5 | 0.5 | Passed |
| rule of twelfths [0, 0.5, 6, 4.1, 1] | 0.8 | 0.8 | Passed |
| rule of twelfths [0, 0.5, 6, 4.1, 2] | 1.4 | 1.4 | Passed |
| rule of twelfths [0, 0.5, 6, 4.1, 3] | 2.3 | 2.3 | Passed |
| rule of twelfths [0, 0.5, 6, 4.1, 4.5] | 3.5 | 3.5 | Passed |
SHA-256 / 12ff1f8196e7a04fdaa38c03d08c8e59eb1a1ffba7365efb44fe0e0cce0c3cc5
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
t1,h1,t2,h2,t=x
if t2<=t1: return None
if t<t1 or t>t2: return None
u=(t-t1)*6/(t2-t1)
k=min(int(u),5)
cum=[0,1,3,6,9,11,12]
c=cum[k]+(cum[k+1]-cum[k])*(u-k)
return round(h1+(h2-h1)*c/12,4)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('rule of twelfths [0, 0.5, 6, 4.1, 1.5]', [0, 0.5, 6, 4.1, 1.5], 1.1), ('rule of twelfths [0, 0.5, 6, 4.1, 6]', [0, 0.5, 6, 4.1, 6], 4.1), ('rule of twelfths [0, 0.5, 6, 4.1, 0]', [0, 0.5, 6, 4.1, 0], 0.5), ('rule of twelfths [0, 0.5, 6, 4.1, 1]', [0, 0.5, 6, 4.1, 1], 0.8), ('rule of twelfths [0, 0.5, 6, 4.1, 2]', [0, 0.5, 6, 4.1, 2], 1.4), ('rule of twelfths [0, 0.5, 6, 4.1, 3]', [0, 0.5, 6, 4.1, 3], 2.3), ('rule of twelfths [0, 0.5, 6, 4.1, 4.5]', [0, 0.5, 6, 4.1, 4.5], 3.5)], [('rule of twelfths [2, 5.2, 8.5, 0.8, 4]', [2, 5.2, 8.5, 0.8, 4], 4.2128), ('rule of twelfths [2, 5.2, 8.5, 0.8, 8.5]', [2, 5.2, 8.5, 0.8, 8.5], 0.8), ('rule of twelfths [0, 0.5, 6, 4.1, 2]', [0, 0.5, 6, 4.1, 2], 1.4), ('rule of twelfths [0, 0.5, 6, 4.1, 3]', [0, 0.5, 6, 4.1, 3], 2.3), ('rule of twelfths [0, 0.5, 6, 4.1, 4.5]', [0, 0.5, 6, 4.1, 4.5], 3.5), ('rule of twelfths [0, 0.5, 6, 4.1, 5]', [0, 0.5, 6, 4.1, 5], 3.8), ('rule of twelfths [0, 0.5, 6, 4.1, 6]', [0, 0.5, 6, 4.1, 6], 4.1)], [('rule of twelfths [2, 5.2, 8.5, 0.8, 7]', [2, 5.2, 8.5, 0.8, 7], 1.4487), ('rule of twelfths [0, 0.5, 6, 4.1, 6]', [0, 0.5, 6, 4.1, 6], 4.1), ('rule of twelfths [0, 0.5, 6, 4.1, 5]', [0, 0.5, 6, 4.1, 5], 3.8), ('rule of twelfths [0, 0.5, 6, 4.1, 6.5]', [0, 0.5, 6, 4.1, 6.5], None), ('rule of twelfths [0, 0.5, 6, 4.1, 7.25]', [0, 0.5, 6, 4.1, 7.25], None), ('rule of twelfths [2, 5.2, 8.5, 0.8, 2]', [2, 5.2, 8.5, 0.8, 2], 5.2), ('rule of twelfths [2, 5.2, 8.5, 0.8, 3.1]', [2, 5.2, 8.5, 0.8, 3.1], 4.8221)], [('rule of twelfths [0, 0.0, 6, 6.0, 0.6]', [0, 0.0, 6, 6.0, 0.6], 0.3), ('rule of twelfths [2, 5.2, 8.5, 0.8, 8.5]', [2, 5.2, 8.5, 0.8, 8.5], 0.8), ('rule of twelfths [0, 0.5, 6, 4.1, 7.25]', [0, 0.5, 6, 4.1, 7.25], None), ('rule of twelfths [2, 5.2, 8.5, 0.8, 2]', [2, 5.2, 8.5, 0.8, 2], 5.2), ('rule of twelfths [2, 5.2, 8.5, 0.8, 3.1]', [2, 5.2, 8.5, 0.8, 3.1], 4.8221), ('rule of twelfths [2, 5.2, 8.5, 0.8, 4]', [2, 5.2, 8.5, 0.8, 4], 4.2128), ('rule of twelfths [2, 5.2, 8.5, 0.8, 5.25]', [2, 5.2, 8.5, 0.8, 5.25], 3.0)], [('rule of twelfths [0, 0.5, 6, 4.1, 1.5]', [0, 0.5, 6, 4.1, 1.5], 1.1), ('rule of twelfths [0, 0.5, 6, 4.1, 6]', [0, 0.5, 6, 4.1, 6], 4.1), ('rule of twelfths [2, 5.2, 8.5, 0.8, 4]', [2, 5.2, 8.5, 0.8, 4], 4.2128), ('rule of twelfths [2, 5.2, 8.5, 0.8, 5.25]', [2, 5.2, 8.5, 0.8, 5.25], 3.0), ('rule of twelfths [2, 5.2, 8.5, 0.8, 7]', [2, 5.2, 8.5, 0.8, 7], 1.4487), ('rule of twelfths [2, 5.2, 8.5, 0.8, 8.5]', [2, 5.2, 8.5, 0.8, 8.5], 0.8), ('rule of twelfths [2, 5.2, 8.5, 0.8, 9]', [2, 5.2, 8.5, 0.8, 9], None)]]
for label, args, expected in fixtures[N-1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| rule of twelfths [0, 0.5, 6, 4.1, 1.5] | 1.1 | 1.1 | Passed |
| rule of twelfths [0, 0.5, 6, 4.1, 6] | 4.1 | 4.1 | Passed |
| rule of twelfths [0, 0.5, 6, 4.1, 0] | 0.5 | 0.5 | Passed |
| rule of twelfths [0, 0.5, 6, 4.1, 1] | 0.8 | 0.8 | Passed |
| rule of twelfths [0, 0.5, 6, 4.1, 2] | 1.4 | 1.4 | Passed |
| rule of twelfths [0, 0.5, 6, 4.1, 3] | 2.3 | 2.3 | Passed |
| rule of twelfths [0, 0.5, 6, 4.1, 4.5] | 3.5 | 3.5 | Passed |
SHA-256 / af4ee2abb9048a3a41a895bb8f6cb347f3b7da2207a3b95f754a68a8c7b7ad74
Verification & scope
A deterministic toy model with stipulated rules and constants; not certified hydrographic software or a substitute for official tide tables. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:48:06.094329+00:00.
Case digest / 447ea6b9b021f8a57b1dbf321740828167fb2c9fe9ad4cee2777a4918cb7e34a