FAILURE MAP
← Case archive

FA-71016 / Weather index computation / Open access

Monthly heating and cooling degree days: half-degree rounding · case 01

Days with an odd tmax+tmin are consistently one degree too cool.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The daily mean is truncated instead of rounded half up.

VERIFIED REPAIR

Use floor(mean + 0.5).

Unsuccessful approach: Python round() rounds halves to even, so 64.5 still becomes 64.

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 = int(mean)
        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 5', [[[83, 71], [69, 53], [79, 74], [80, 71], [47, 46], [83, 64], [74, 51], [52, 52], [None, None], [48, 37]]], [72, 44]], ['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 3', [[[None, None], [None, 60], [65, 60], [74, 72], [93, 69], [76, 73], [58, 43], [54, 35], [74, 52], [55, 49]]], [51, 34]], ['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 8', [[[71, 60], [75, 67], [77, 54], [84, 71], [62, 45], [None, None], [None, None], [90, 75], [None, 60], [65, 51]]], [40, 57]], ['month 9', [[[None, None], [None, None], [67, 66], [64, 51], [None, None], [None, None], [96, 73], [None, None], [75, 50], [72, 67]]], [23, 47]], ['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 11', [[[74, 51], [None, 60], [52, 44], [82, 69], [64, 55], [None, None], [55, 53], [81, 73], [77, 63], [45, 37]]], [66, 28]], ['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 6', [[[93, 68], [80, 74], [80, 59], [71, 70], [74, 73], [59, 57], [91, 71], [60, 51], [62, 61], [74, 73]]], [19, 73]], ['month 11', [[[74, 51], [None, 60], [52, 44], [82, 69], [64, 55], [None, None], [55, 53], [81, 73], [77, 63], [45, 37]]], [66, 28]], ['month 14', [[[47, 42], [None, 60], [60, 48], [64, 63], [50, 46], [None, None], [68, 50], [52, 48], [71, 55], [67, 48]]], [116, 0]], ['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 18', [[[52, 38], [66, 61], [67, 54], [87, 75], [60, 47], [58, 35], [61, 40], [79, 57], [50, 35], [54, 39]]], [108, 19]], ['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 9', [[[None, None], [None, None], [67, 66], [64, 51], [None, None], [None, None], [96, 73], [None, None], [75, 50], [72, 67]]], [23, 47]], ['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 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 24', [[[67, 65], [58, 56], [None, None], [61, 57], [84, 60], [78, 71], [56, 42], [70, 64], [None, 60], [67, 66]]], [38, 24]], ['month 25', [[[61, 43], [None, None], [57, 50], [76, 73], [38, 36], [60, 43], [78, 63], [55, 35], [64, 47], [70, 59]]], [107, 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 12', [[[60, 59], [62, 55], [66, 63], [60, 56], [None, 60], [68, 45], [63, 38], [61, 55], [None, 60], [50, 50]]], [76, 0]], ['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 28', [[[69, 55], [63, 40], [86, 63], [64, 47], [79, 69], [63, 55], [80, 67], [52, 42], [51, 46], [53, 42]]], [82, 28]], ['month 29', [[[70, 54], [68, 60], [54, 39], [None, 60], [None, None], [63, 47], [69, 63], [55, 41], [None, None], [99, 74]]], [102, 23]], ['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]]]]
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 fixtureActualExpectedOutcome
month 0[71, 73][68, 76]Failed
month 1[104, 25][98, 26]Failed
month 2[74, 23][72, 25]Failed
month 3[54, 33][51, 34]Failed
month 4[73, 6][69, 7]Failed
month 5[75, 41][72, 44]Failed
month 6[21, 68][19, 73]Failed
month 7[76, 38][73, 40]Failed

SHA-256 / d14627044459c5e39a7fa5fd75363cf89152489c3ad22da145f1f316a5ca3ef8

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 = round(mean)
        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 5', [[[83, 71], [69, 53], [79, 74], [80, 71], [47, 46], [83, 64], [74, 51], [52, 52], [None, None], [48, 37]]], [72, 44]], ['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 3', [[[None, None], [None, 60], [65, 60], [74, 72], [93, 69], [76, 73], [58, 43], [54, 35], [74, 52], [55, 49]]], [51, 34]], ['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 8', [[[71, 60], [75, 67], [77, 54], [84, 71], [62, 45], [None, None], [None, None], [90, 75], [None, 60], [65, 51]]], [40, 57]], ['month 9', [[[None, None], [None, None], [67, 66], [64, 51], [None, None], [None, None], [96, 73], [None, None], [75, 50], [72, 67]]], [23, 47]], ['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 11', [[[74, 51], [None, 60], [52, 44], [82, 69], [64, 55], [None, None], [55, 53], [81, 73], [77, 63], [45, 37]]], [66, 28]], ['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 6', [[[93, 68], [80, 74], [80, 59], [71, 70], [74, 73], [59, 57], [91, 71], [60, 51], [62, 61], [74, 73]]], [19, 73]], ['month 11', [[[74, 51], [None, 60], [52, 44], [82, 69], [64, 55], [None, None], [55, 53], [81, 73], [77, 63], [45, 37]]], [66, 28]], ['month 14', [[[47, 42], [None, 60], [60, 48], [64, 63], [50, 46], [None, None], [68, 50], [52, 48], [71, 55], [67, 48]]], [116, 0]], ['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 18', [[[52, 38], [66, 61], [67, 54], [87, 75], [60, 47], [58, 35], [61, 40], [79, 57], [50, 35], [54, 39]]], [108, 19]], ['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 9', [[[None, None], [None, None], [67, 66], [64, 51], [None, None], [None, None], [96, 73], [None, None], [75, 50], [72, 67]]], [23, 47]], ['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 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 24', [[[67, 65], [58, 56], [None, None], [61, 57], [84, 60], [78, 71], [56, 42], [70, 64], [None, 60], [67, 66]]], [38, 24]], ['month 25', [[[61, 43], [None, None], [57, 50], [76, 73], [38, 36], [60, 43], [78, 63], [55, 35], [64, 47], [70, 59]]], [107, 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 12', [[[60, 59], [62, 55], [66, 63], [60, 56], [None, 60], [68, 45], [63, 38], [61, 55], [None, 60], [50, 50]]], [76, 0]], ['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 28', [[[69, 55], [63, 40], [86, 63], [64, 47], [79, 69], [63, 55], [80, 67], [52, 42], [51, 46], [53, 42]]], [82, 28]], ['month 29', [[[70, 54], [68, 60], [54, 39], [None, 60], [None, None], [63, 47], [69, 63], [55, 41], [None, None], [99, 74]]], [102, 23]], ['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]]]]
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 fixtureActualExpectedOutcome
month 0[69, 75][68, 76]Failed
month 1[103, 25][98, 26]Failed
month 2[73, 24][72, 25]Failed
month 3[54, 33][51, 34]Failed
month 4[71, 7][69, 7]Failed
month 5[75, 43][72, 44]Failed
month 6[19, 71][19, 73]Failed
month 7[76, 39][73, 40]Failed

SHA-256 / 9b40f2f8cfb84a7ba8941ff9ad9ce2b5a96740f1a70c25a7b0695b7871916920

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 5', [[[83, 71], [69, 53], [79, 74], [80, 71], [47, 46], [83, 64], [74, 51], [52, 52], [None, None], [48, 37]]], [72, 44]], ['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 3', [[[None, None], [None, 60], [65, 60], [74, 72], [93, 69], [76, 73], [58, 43], [54, 35], [74, 52], [55, 49]]], [51, 34]], ['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 8', [[[71, 60], [75, 67], [77, 54], [84, 71], [62, 45], [None, None], [None, None], [90, 75], [None, 60], [65, 51]]], [40, 57]], ['month 9', [[[None, None], [None, None], [67, 66], [64, 51], [None, None], [None, None], [96, 73], [None, None], [75, 50], [72, 67]]], [23, 47]], ['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 11', [[[74, 51], [None, 60], [52, 44], [82, 69], [64, 55], [None, None], [55, 53], [81, 73], [77, 63], [45, 37]]], [66, 28]], ['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 6', [[[93, 68], [80, 74], [80, 59], [71, 70], [74, 73], [59, 57], [91, 71], [60, 51], [62, 61], [74, 73]]], [19, 73]], ['month 11', [[[74, 51], [None, 60], [52, 44], [82, 69], [64, 55], [None, None], [55, 53], [81, 73], [77, 63], [45, 37]]], [66, 28]], ['month 14', [[[47, 42], [None, 60], [60, 48], [64, 63], [50, 46], [None, None], [68, 50], [52, 48], [71, 55], [67, 48]]], [116, 0]], ['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 18', [[[52, 38], [66, 61], [67, 54], [87, 75], [60, 47], [58, 35], [61, 40], [79, 57], [50, 35], [54, 39]]], [108, 19]], ['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 9', [[[None, None], [None, None], [67, 66], [64, 51], [None, None], [None, None], [96, 73], [None, None], [75, 50], [72, 67]]], [23, 47]], ['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 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 24', [[[67, 65], [58, 56], [None, None], [61, 57], [84, 60], [78, 71], [56, 42], [70, 64], [None, 60], [67, 66]]], [38, 24]], ['month 25', [[[61, 43], [None, None], [57, 50], [76, 73], [38, 36], [60, 43], [78, 63], [55, 35], [64, 47], [70, 59]]], [107, 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 12', [[[60, 59], [62, 55], [66, 63], [60, 56], [None, 60], [68, 45], [63, 38], [61, 55], [None, 60], [50, 50]]], [76, 0]], ['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 28', [[[69, 55], [63, 40], [86, 63], [64, 47], [79, 69], [63, 55], [80, 67], [52, 42], [51, 46], [53, 42]]], [82, 28]], ['month 29', [[[70, 54], [68, 60], [54, 39], [None, 60], [None, None], [63, 47], [69, 63], [55, 41], [None, None], [99, 74]]], [102, 23]], ['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]]]]
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 fixtureActualExpectedOutcome
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 5[72, 44][72, 44]Passed
month 6[19, 73][19, 73]Passed
month 7[73, 40][73, 40]Passed

SHA-256 / 5eed6907fa84c6710cbcdf6f80dadf3217463e59181ea7025bb975f42842eb9f

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:25.975597+00:00.

Case digest / 69631cf557f220befc3bbabd01ff5035cb079f23ed7147134679b37a4eff8377