FAILURE MAP
← Case archive

FA-71021 / Weather index computation / Open access

Monthly heating and cooling degree days: daily mean arithmetic · case 01

Half-degree means lose the half before the rounding rule sees them.

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

ROOT CAUSE

Integer floor division of tmax+tmin discards the .5 of odd sums.

VERIFIED REPAIR

Use true division (tmax+tmin)/2.

Unsuccessful approach: Dropping the parentheses halves only tmin.

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 - 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 16', [[[None, None], [42, 35], [49, 35], [81, 65], [50, 49], [54, 44], [64, 47], [88, 67], [71, 58], [45, 37]]], [113, 21]], ['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 21', [[[54, 43], [73, 63], [69, 50], [76, 62], [75, 67], [49, 37], [72, 66], [55, 49], [82, 62], [58, 56]]], [64, 24]], ['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 / 50d91c9abb62e2b174d0200e2670102d31d4efe0d830578847cdd40826ff59e9

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 - 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 16', [[[None, None], [42, 35], [49, 35], [81, 65], [50, 49], [54, 44], [64, 47], [88, 67], [71, 58], [45, 37]]], [113, 21]], ['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 21', [[[54, 43], [73, 63], [69, 50], [76, 62], [75, 67], [49, 37], [72, 66], [55, 49], [82, 62], [58, 56]]], [64, 24]], ['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[0, 373][68, 76]Failed
month 1[9, 246][98, 26]Failed
month 2[0, 287][72, 25]Failed
month 3[0, 258][51, 34]Failed
month 4[0, 224][69, 7]Failed
month 5[0, 305][72, 44]Failed
month 6[0, 426][19, 73]Failed
month 7[5, 315][73, 40]Failed

SHA-256 / 999b3a9b8c4c306d47352a176541e1170ba9974b534d3f46c93b578fc699c642

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 16', [[[None, None], [42, 35], [49, 35], [81, 65], [50, 49], [54, 44], [64, 47], [88, 67], [71, 58], [45, 37]]], [113, 21]], ['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 21', [[[54, 43], [73, 63], [69, 50], [76, 62], [75, 67], [49, 37], [72, 66], [55, 49], [82, 62], [58, 56]]], [64, 24]], ['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 / d6cc0d66b064b9a9884d35ff4eb78ac6be7df91b57f61f0b0b7b90fa7b4ee26a

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

Case digest / 726e1ff1fd64fa324ace79f56ea45863e99ff120806c3783f0c5c7d294d54dd7