FA-71031 / Weather index computation / Open access
Monthly heating and cooling degree days: heating accumulation operand · case 01
HDD totals carry half-degree fractions and disagree with published integer totals.
ROOT CAUSE
The heating accumulation uses the unrounded daily mean.
VERIFIED REPAIR
Accumulate 65 minus the rounded mean.
Unsuccessful approach: Using the daily minimum overstates HDD and fails on carried-forward days.
Case contract
daily entries are [tmax_F, tmin_F] with None for missing. A day with a missing value reuses the previous valid daily mean; missing days before any valid day are skipped. Round the daily mean half up to a whole degree, then HDD += 65 - m if m < 65, CDD += m - 65 if m > 65. Return [HDD, CDD].
Why this case matters
Energy billing normalisation and utility demand models use monthly HDD/CDD.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(daily):
hdd = 0
cdd = 0
prev = None
for hi, lo in daily:
if hi is None or lo is None:
if prev is None:
continue
mean = prev
else:
mean = (hi + lo) / 2
prev = mean
m = math.floor(mean + 0.5)
if m < 65:
hdd += 65 - mean
elif m > 65:
cdd += m - 65
return [hdd, cdd]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['month 0', [[[57, 57], [81, 68], [50, 45], [None, 60], [63, 43], [60, 41], [91, 67], [None, None], [92, 75], [None, 60]]], [68, 76]], ['month 1', [[[83, 66], [71, 50], [49, 42], [46, 43], [92, 70], [55, 54], [55, 46], [70, 55], [64, 64], [38, 36]]], [98, 26]], ['month 2', [[[86, 69], [48, 47], [63, 47], [70, 60], [63, 42], [56, 36], [59, 43], [72, 70], [74, 59], [75, 63]]], [72, 25]], ['month 3', [[[None, None], [None, 60], [65, 60], [74, 72], [93, 69], [76, 73], [58, 43], [54, 35], [74, 52], [55, 49]]], [51, 34]], ['month 4', [[[None, 60], [64, 63], [61, 54], [73, 49], [47, 38], [49, 40], [58, 46], [77, 58], [72, 54], [74, 64]]], [69, 7]], ['month 6', [[[93, 68], [80, 74], [80, 59], [71, 70], [74, 73], [59, 57], [91, 71], [60, 51], [62, 61], [74, 73]]], [19, 73]], ['month 7', [[[78, 54], [83, 68], [57, 36], [81, 75], [47, 41], [86, 63], [81, 59], [41, 37], [67, 50], [65, 60]]], [73, 40]], ['month 10', [[[68, 47], [71, 53], [93, 68], [78, 65], [52, 47], [53, 43], [77, 72], [84, 72], [80, 71], [67, 58]]], [44, 57]]], [['month 3', [[[None, None], [None, 60], [65, 60], [74, 72], [93, 69], [76, 73], [58, 43], [54, 35], [74, 52], [55, 49]]], [51, 34]], ['month 7', [[[78, 54], [83, 68], [57, 36], [81, 75], [47, 41], [86, 63], [81, 59], [41, 37], [67, 50], [65, 60]]], [73, 40]], ['month 10', [[[68, 47], [71, 53], [93, 68], [78, 65], [52, 47], [53, 43], [77, 72], [84, 72], [80, 71], [67, 58]]], [44, 57]], ['month 12', [[[60, 59], [62, 55], [66, 63], [60, 56], [None, 60], [68, 45], [63, 38], [61, 55], [None, 60], [50, 50]]], [76, 0]], ['month 13', [[[77, 62], [51, 47], [None, 60], [None, 60], [83, 65], [54, 43], [67, 67], [72, 47], [71, 49], [48, 47]]], [91, 16]], ['month 15', [[[71, 61], [None, None], [None, 60], [None, 60], [None, None], [73, 67], [61, 41], [86, 74], [None, 60], [None, 60]]], [14, 55]], ['month 16', [[[None, None], [42, 35], [49, 35], [81, 65], [50, 49], [54, 44], [64, 47], [88, 67], [71, 58], [45, 37]]], [113, 21]], ['month 17', [[[59, 59], [84, 70], [83, 63], [64, 56], [77, 66], [44, 42], [84, 62], [49, 40], [36, 36], [59, 35]]], [100, 35]]], [['month 7', [[[78, 54], [83, 68], [57, 36], [81, 75], [47, 41], [86, 63], [81, 59], [41, 37], [67, 50], [65, 60]]], [73, 40]], ['month 16', [[[None, None], [42, 35], [49, 35], [81, 65], [50, 49], [54, 44], [64, 47], [88, 67], [71, 58], [45, 37]]], [113, 21]], ['month 19', [[[62, 37], [93, 74], [45, 39], [56, 53], [70, 45], [94, 75], [57, 47], [95, 72], [49, 35], [71, 66]]], [91, 62]], ['month 20', [[[77, 75], [58, 55], [63, 40], [76, 57], [94, 70], [60, 45], [72, 66], [None, None], [68, 51], [72, 58]]], [38, 38]], ['month 21', [[[54, 43], [73, 63], [69, 50], [76, 62], [75, 67], [49, 37], [72, 66], [55, 49], [82, 62], [58, 56]]], [64, 24]], ['month 22', [[[65, 61], [None, 60], [75, 71], [92, 75], [76, 69], [81, 75], [81, 70], [85, 65], [58, 55], [68, 59]]], [13, 69]], ['month 23', [[[46, 42], [55, 38], [73, 63], [72, 61], [62, 37], [60, 58], [55, 52], [38, 36], [82, 69], [51, 47]]], [115, 16]], ['month 26', [[[None, 60], [None, 60], [62, 41], [85, 67], [60, 58], [65, 42], [58, 54], [60, 37], [74, 60], [84, 63]]], [55, 22]]], [['month 0', [[[57, 57], [81, 68], [50, 45], [None, 60], [63, 43], [60, 41], [91, 67], [None, None], [92, 75], [None, 60]]], [68, 76]], ['month 1', [[[83, 66], [71, 50], [49, 42], [46, 43], [92, 70], [55, 54], [55, 46], [70, 55], [64, 64], [38, 36]]], [98, 26]], ['month 13', [[[77, 62], [51, 47], [None, 60], [None, 60], [83, 65], [54, 43], [67, 67], [72, 47], [71, 49], [48, 47]]], [91, 16]], ['month 21', [[[54, 43], [73, 63], [69, 50], [76, 62], [75, 67], [49, 37], [72, 66], [55, 49], [82, 62], [58, 56]]], [64, 24]], ['odd sums', [[[70, 59], [66, 63], [80, 51]]], [0, 1]], ['first day missing', [[[None, None], [70, 50]]], [5, 0]], ['carry forward', [[[90, 71], [None, None], [None, 50]]], [0, 48]], ['base day', [[[65, 65]]], [0, 0]]], [['month 3', [[[None, None], [None, 60], [65, 60], [74, 72], [93, 69], [76, 73], [58, 43], [54, 35], [74, 52], [55, 49]]], [51, 34]], ['month 4', [[[None, 60], [64, 63], [61, 54], [73, 49], [47, 38], [49, 40], [58, 46], [77, 58], [72, 54], [74, 64]]], [69, 7]], ['month 6', [[[93, 68], [80, 74], [80, 59], [71, 70], [74, 73], [59, 57], [91, 71], [60, 51], [62, 61], [74, 73]]], [19, 73]], ['month 7', [[[78, 54], [83, 68], [57, 36], [81, 75], [47, 41], [86, 63], [81, 59], [41, 37], [67, 50], [65, 60]]], [73, 40]], ['month 10', [[[68, 47], [71, 53], [93, 68], [78, 65], [52, 47], [53, 43], [77, 72], [84, 72], [80, 71], [67, 58]]], [44, 57]], ['month 12', [[[60, 59], [62, 55], [66, 63], [60, 56], [None, 60], [68, 45], [63, 38], [61, 55], [None, 60], [50, 50]]], [76, 0]], ['month 18', [[[52, 38], [66, 61], [67, 54], [87, 75], [60, 47], [58, 35], [61, 40], [79, 57], [50, 35], [54, 39]]], [108, 19]], ['first day missing', [[[None, None], [70, 50]]], [5, 0]]]]
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 |
|---|---|---|---|
| month 0 | [69.5, 76] | [68, 76] | Failed |
| month 1 | [101.0, 26] | [98, 26] | Failed |
| month 2 | [73.0, 25] | [72, 25] | Failed |
| month 3 | [52.5, 34] | [51, 34] | Failed |
| month 4 | [71.0, 7] | [69, 7] | Failed |
| month 6 | [20.0, 73] | [19, 73] | Failed |
| month 7 | [74.5, 40] | [73, 40] | Failed |
| month 10 | [45.5, 57] | [44, 57] | Failed |
SHA-256 / 30e6067503d28fb55c51228b584028fb10e11fa698e93bc672cfb79ba024c707
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(daily):
hdd = 0
cdd = 0
prev = None
for hi, lo in daily:
if hi is None or lo is None:
if prev is None:
continue
mean = prev
else:
mean = (hi + lo) / 2
prev = mean
m = math.floor(mean + 0.5)
if m < 65:
hdd += 65 - lo
elif m > 65:
cdd += m - 65
return [hdd, cdd]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['month 0', [[[57, 57], [81, 68], [50, 45], [None, 60], [63, 43], [60, 41], [91, 67], [None, None], [92, 75], [None, 60]]], [68, 76]], ['month 1', [[[83, 66], [71, 50], [49, 42], [46, 43], [92, 70], [55, 54], [55, 46], [70, 55], [64, 64], [38, 36]]], [98, 26]], ['month 2', [[[86, 69], [48, 47], [63, 47], [70, 60], [63, 42], [56, 36], [59, 43], [72, 70], [74, 59], [75, 63]]], [72, 25]], ['month 3', [[[None, None], [None, 60], [65, 60], [74, 72], [93, 69], [76, 73], [58, 43], [54, 35], [74, 52], [55, 49]]], [51, 34]], ['month 4', [[[None, 60], [64, 63], [61, 54], [73, 49], [47, 38], [49, 40], [58, 46], [77, 58], [72, 54], [74, 64]]], [69, 7]], ['month 6', [[[93, 68], [80, 74], [80, 59], [71, 70], [74, 73], [59, 57], [91, 71], [60, 51], [62, 61], [74, 73]]], [19, 73]], ['month 7', [[[78, 54], [83, 68], [57, 36], [81, 75], [47, 41], [86, 63], [81, 59], [41, 37], [67, 50], [65, 60]]], [73, 40]], ['month 10', [[[68, 47], [71, 53], [93, 68], [78, 65], [52, 47], [53, 43], [77, 72], [84, 72], [80, 71], [67, 58]]], [44, 57]]], [['month 3', [[[None, None], [None, 60], [65, 60], [74, 72], [93, 69], [76, 73], [58, 43], [54, 35], [74, 52], [55, 49]]], [51, 34]], ['month 7', [[[78, 54], [83, 68], [57, 36], [81, 75], [47, 41], [86, 63], [81, 59], [41, 37], [67, 50], [65, 60]]], [73, 40]], ['month 10', [[[68, 47], [71, 53], [93, 68], [78, 65], [52, 47], [53, 43], [77, 72], [84, 72], [80, 71], [67, 58]]], [44, 57]], ['month 12', [[[60, 59], [62, 55], [66, 63], [60, 56], [None, 60], [68, 45], [63, 38], [61, 55], [None, 60], [50, 50]]], [76, 0]], ['month 13', [[[77, 62], [51, 47], [None, 60], [None, 60], [83, 65], [54, 43], [67, 67], [72, 47], [71, 49], [48, 47]]], [91, 16]], ['month 15', [[[71, 61], [None, None], [None, 60], [None, 60], [None, None], [73, 67], [61, 41], [86, 74], [None, 60], [None, 60]]], [14, 55]], ['month 16', [[[None, None], [42, 35], [49, 35], [81, 65], [50, 49], [54, 44], [64, 47], [88, 67], [71, 58], [45, 37]]], [113, 21]], ['month 17', [[[59, 59], [84, 70], [83, 63], [64, 56], [77, 66], [44, 42], [84, 62], [49, 40], [36, 36], [59, 35]]], [100, 35]]], [['month 7', [[[78, 54], [83, 68], [57, 36], [81, 75], [47, 41], [86, 63], [81, 59], [41, 37], [67, 50], [65, 60]]], [73, 40]], ['month 16', [[[None, None], [42, 35], [49, 35], [81, 65], [50, 49], [54, 44], [64, 47], [88, 67], [71, 58], [45, 37]]], [113, 21]], ['month 19', [[[62, 37], [93, 74], [45, 39], [56, 53], [70, 45], [94, 75], [57, 47], [95, 72], [49, 35], [71, 66]]], [91, 62]], ['month 20', [[[77, 75], [58, 55], [63, 40], [76, 57], [94, 70], [60, 45], [72, 66], [None, None], [68, 51], [72, 58]]], [38, 38]], ['month 21', [[[54, 43], [73, 63], [69, 50], [76, 62], [75, 67], [49, 37], [72, 66], [55, 49], [82, 62], [58, 56]]], [64, 24]], ['month 22', [[[65, 61], [None, 60], [75, 71], [92, 75], [76, 69], [81, 75], [81, 70], [85, 65], [58, 55], [68, 59]]], [13, 69]], ['month 23', [[[46, 42], [55, 38], [73, 63], [72, 61], [62, 37], [60, 58], [55, 52], [38, 36], [82, 69], [51, 47]]], [115, 16]], ['month 26', [[[None, 60], [None, 60], [62, 41], [85, 67], [60, 58], [65, 42], [58, 54], [60, 37], [74, 60], [84, 63]]], [55, 22]]], [['month 0', [[[57, 57], [81, 68], [50, 45], [None, 60], [63, 43], [60, 41], [91, 67], [None, None], [92, 75], [None, 60]]], [68, 76]], ['month 1', [[[83, 66], [71, 50], [49, 42], [46, 43], [92, 70], [55, 54], [55, 46], [70, 55], [64, 64], [38, 36]]], [98, 26]], ['month 13', [[[77, 62], [51, 47], [None, 60], [None, 60], [83, 65], [54, 43], [67, 67], [72, 47], [71, 49], [48, 47]]], [91, 16]], ['month 21', [[[54, 43], [73, 63], [69, 50], [76, 62], [75, 67], [49, 37], [72, 66], [55, 49], [82, 62], [58, 56]]], [64, 24]], ['odd sums', [[[70, 59], [66, 63], [80, 51]]], [0, 1]], ['first day missing', [[[None, None], [70, 50]]], [5, 0]], ['carry forward', [[[90, 71], [None, None], [None, 50]]], [0, 48]], ['base day', [[[65, 65]]], [0, 0]]], [['month 3', [[[None, None], [None, 60], [65, 60], [74, 72], [93, 69], [76, 73], [58, 43], [54, 35], [74, 52], [55, 49]]], [51, 34]], ['month 4', [[[None, 60], [64, 63], [61, 54], [73, 49], [47, 38], [49, 40], [58, 46], [77, 58], [72, 54], [74, 64]]], [69, 7]], ['month 6', [[[93, 68], [80, 74], [80, 59], [71, 70], [74, 73], [59, 57], [91, 71], [60, 51], [62, 61], [74, 73]]], [19, 73]], ['month 7', [[[78, 54], [83, 68], [57, 36], [81, 75], [47, 41], [86, 63], [81, 59], [41, 37], [67, 50], [65, 60]]], [73, 40]], ['month 10', [[[68, 47], [71, 53], [93, 68], [78, 65], [52, 47], [53, 43], [77, 72], [84, 72], [80, 71], [67, 58]]], [44, 57]], ['month 12', [[[60, 59], [62, 55], [66, 63], [60, 56], [None, 60], [68, 45], [63, 38], [61, 55], [None, 60], [50, 50]]], [76, 0]], ['month 18', [[[52, 38], [66, 61], [67, 54], [87, 75], [60, 47], [58, 35], [61, 40], [79, 57], [50, 35], [54, 39]]], [108, 19]], ['first day missing', [[[None, None], [70, 50]]], [5, 0]]]]
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 |
|---|---|---|---|
| month 0 | [79, 76] | [68, 76] | Failed |
| month 1 | [130, 26] | [98, 26] | Failed |
| month 2 | [110, 25] | [72, 25] | Failed |
| month 3 | [86, 34] | [51, 34] | Failed |
| month 4 | [111, 7] | [69, 7] | Failed |
| month 6 | [26, 73] | [19, 73] | Failed |
| month 7 | [101, 40] | [73, 40] | Failed |
| month 10 | [77, 57] | [44, 57] | Failed |
SHA-256 / 6dd5dee1bdbdacdc6d73f56b80d9e1ad83c14594467442006f25151a641a9666
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(daily):
hdd = 0
cdd = 0
prev = None
for hi, lo in daily:
if hi is None or lo is None:
if prev is None:
continue
mean = prev
else:
mean = (hi + lo) / 2
prev = mean
m = math.floor(mean + 0.5)
if m < 65:
hdd += 65 - m
elif m > 65:
cdd += m - 65
return [hdd, cdd]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['month 0', [[[57, 57], [81, 68], [50, 45], [None, 60], [63, 43], [60, 41], [91, 67], [None, None], [92, 75], [None, 60]]], [68, 76]], ['month 1', [[[83, 66], [71, 50], [49, 42], [46, 43], [92, 70], [55, 54], [55, 46], [70, 55], [64, 64], [38, 36]]], [98, 26]], ['month 2', [[[86, 69], [48, 47], [63, 47], [70, 60], [63, 42], [56, 36], [59, 43], [72, 70], [74, 59], [75, 63]]], [72, 25]], ['month 3', [[[None, None], [None, 60], [65, 60], [74, 72], [93, 69], [76, 73], [58, 43], [54, 35], [74, 52], [55, 49]]], [51, 34]], ['month 4', [[[None, 60], [64, 63], [61, 54], [73, 49], [47, 38], [49, 40], [58, 46], [77, 58], [72, 54], [74, 64]]], [69, 7]], ['month 6', [[[93, 68], [80, 74], [80, 59], [71, 70], [74, 73], [59, 57], [91, 71], [60, 51], [62, 61], [74, 73]]], [19, 73]], ['month 7', [[[78, 54], [83, 68], [57, 36], [81, 75], [47, 41], [86, 63], [81, 59], [41, 37], [67, 50], [65, 60]]], [73, 40]], ['month 10', [[[68, 47], [71, 53], [93, 68], [78, 65], [52, 47], [53, 43], [77, 72], [84, 72], [80, 71], [67, 58]]], [44, 57]]], [['month 3', [[[None, None], [None, 60], [65, 60], [74, 72], [93, 69], [76, 73], [58, 43], [54, 35], [74, 52], [55, 49]]], [51, 34]], ['month 7', [[[78, 54], [83, 68], [57, 36], [81, 75], [47, 41], [86, 63], [81, 59], [41, 37], [67, 50], [65, 60]]], [73, 40]], ['month 10', [[[68, 47], [71, 53], [93, 68], [78, 65], [52, 47], [53, 43], [77, 72], [84, 72], [80, 71], [67, 58]]], [44, 57]], ['month 12', [[[60, 59], [62, 55], [66, 63], [60, 56], [None, 60], [68, 45], [63, 38], [61, 55], [None, 60], [50, 50]]], [76, 0]], ['month 13', [[[77, 62], [51, 47], [None, 60], [None, 60], [83, 65], [54, 43], [67, 67], [72, 47], [71, 49], [48, 47]]], [91, 16]], ['month 15', [[[71, 61], [None, None], [None, 60], [None, 60], [None, None], [73, 67], [61, 41], [86, 74], [None, 60], [None, 60]]], [14, 55]], ['month 16', [[[None, None], [42, 35], [49, 35], [81, 65], [50, 49], [54, 44], [64, 47], [88, 67], [71, 58], [45, 37]]], [113, 21]], ['month 17', [[[59, 59], [84, 70], [83, 63], [64, 56], [77, 66], [44, 42], [84, 62], [49, 40], [36, 36], [59, 35]]], [100, 35]]], [['month 7', [[[78, 54], [83, 68], [57, 36], [81, 75], [47, 41], [86, 63], [81, 59], [41, 37], [67, 50], [65, 60]]], [73, 40]], ['month 16', [[[None, None], [42, 35], [49, 35], [81, 65], [50, 49], [54, 44], [64, 47], [88, 67], [71, 58], [45, 37]]], [113, 21]], ['month 19', [[[62, 37], [93, 74], [45, 39], [56, 53], [70, 45], [94, 75], [57, 47], [95, 72], [49, 35], [71, 66]]], [91, 62]], ['month 20', [[[77, 75], [58, 55], [63, 40], [76, 57], [94, 70], [60, 45], [72, 66], [None, None], [68, 51], [72, 58]]], [38, 38]], ['month 21', [[[54, 43], [73, 63], [69, 50], [76, 62], [75, 67], [49, 37], [72, 66], [55, 49], [82, 62], [58, 56]]], [64, 24]], ['month 22', [[[65, 61], [None, 60], [75, 71], [92, 75], [76, 69], [81, 75], [81, 70], [85, 65], [58, 55], [68, 59]]], [13, 69]], ['month 23', [[[46, 42], [55, 38], [73, 63], [72, 61], [62, 37], [60, 58], [55, 52], [38, 36], [82, 69], [51, 47]]], [115, 16]], ['month 26', [[[None, 60], [None, 60], [62, 41], [85, 67], [60, 58], [65, 42], [58, 54], [60, 37], [74, 60], [84, 63]]], [55, 22]]], [['month 0', [[[57, 57], [81, 68], [50, 45], [None, 60], [63, 43], [60, 41], [91, 67], [None, None], [92, 75], [None, 60]]], [68, 76]], ['month 1', [[[83, 66], [71, 50], [49, 42], [46, 43], [92, 70], [55, 54], [55, 46], [70, 55], [64, 64], [38, 36]]], [98, 26]], ['month 13', [[[77, 62], [51, 47], [None, 60], [None, 60], [83, 65], [54, 43], [67, 67], [72, 47], [71, 49], [48, 47]]], [91, 16]], ['month 21', [[[54, 43], [73, 63], [69, 50], [76, 62], [75, 67], [49, 37], [72, 66], [55, 49], [82, 62], [58, 56]]], [64, 24]], ['odd sums', [[[70, 59], [66, 63], [80, 51]]], [0, 1]], ['first day missing', [[[None, None], [70, 50]]], [5, 0]], ['carry forward', [[[90, 71], [None, None], [None, 50]]], [0, 48]], ['base day', [[[65, 65]]], [0, 0]]], [['month 3', [[[None, None], [None, 60], [65, 60], [74, 72], [93, 69], [76, 73], [58, 43], [54, 35], [74, 52], [55, 49]]], [51, 34]], ['month 4', [[[None, 60], [64, 63], [61, 54], [73, 49], [47, 38], [49, 40], [58, 46], [77, 58], [72, 54], [74, 64]]], [69, 7]], ['month 6', [[[93, 68], [80, 74], [80, 59], [71, 70], [74, 73], [59, 57], [91, 71], [60, 51], [62, 61], [74, 73]]], [19, 73]], ['month 7', [[[78, 54], [83, 68], [57, 36], [81, 75], [47, 41], [86, 63], [81, 59], [41, 37], [67, 50], [65, 60]]], [73, 40]], ['month 10', [[[68, 47], [71, 53], [93, 68], [78, 65], [52, 47], [53, 43], [77, 72], [84, 72], [80, 71], [67, 58]]], [44, 57]], ['month 12', [[[60, 59], [62, 55], [66, 63], [60, 56], [None, 60], [68, 45], [63, 38], [61, 55], [None, 60], [50, 50]]], [76, 0]], ['month 18', [[[52, 38], [66, 61], [67, 54], [87, 75], [60, 47], [58, 35], [61, 40], [79, 57], [50, 35], [54, 39]]], [108, 19]], ['first day missing', [[[None, None], [70, 50]]], [5, 0]]]]
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 |
|---|---|---|---|
| month 0 | [68, 76] | [68, 76] | Passed |
| month 1 | [98, 26] | [98, 26] | Passed |
| month 2 | [72, 25] | [72, 25] | Passed |
| month 3 | [51, 34] | [51, 34] | Passed |
| month 4 | [69, 7] | [69, 7] | Passed |
| month 6 | [19, 73] | [19, 73] | Passed |
| month 7 | [73, 40] | [73, 40] | Passed |
| month 10 | [44, 57] | [44, 57] | Passed |
SHA-256 / c03f67f931b6faaeadb4321e1d3f7a7157617e5b93886a44b77bc0d6b154ec72
Verification & scope
Stipulated deterministic teaching model of an operational weather index; coefficients and thresholds are fixed by the contract and no claim of standards conformance is made. 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:26.056739+00:00.
Case digest / 5c784ad139fd4a978ae4e7ae6afb501356a1d6f1fecfa833a2c1ac5465ca2ad3