broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(heat_sp, cool_sp, temps):
    diff = 10
    if cool_sp - heat_sp < 30:
        cool_sp = heat_sp + 30
    state = 'idle'
    last_heat_end = None
    last_cool_end = None
    out = []
    for minute, temp in enumerate(temps):
        if state == 'heat' and temp >= heat_sp:
            state = 'idle'
            last_heat_end = minute
        elif state == 'cool' and temp <= cool_sp:
            state = 'idle'
            last_cool_end = minute
        if state == 'idle':
            if temp < heat_sp and (last_cool_end is None or minute - last_cool_end >= 5):
                state = 'heat'
            elif temp > cool_sp + diff and (last_heat_end is None or minute - last_heat_end >= 5):
                state = 'cool'
        out.append(state)
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['regression: scenario 6',
   [680,
    720,
    [666, 650, 675, 664, 671, 693, 693, 695, 674, 675, 657, 635, 629, 632, 629, 632, 636, 632, 620]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['regression: scenario 1',
   [680, 690, [693, 687, 709, 709, 685, 704, 684, 680, 686, 687, 679]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: scenario 2',
   [680, 700, [681, 686, 698, 703, 724, 744, 752, 732, 755, 742, 762, 772]],
   ['idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['control: scenario 3',
   [680,
    740,
    [649,
     667,
     643,
     622,
     637,
     657,
     634,
     649,
     674,
     654,
     672,
     675,
     698,
     687,
     666,
     654,
     670,
     645,
     654,
     637,
     651,
     672]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']]],
 [['regression: scenario 2',
   [680, 710, [674, 681, 688, 699, 718, 698, 716, 709, 691, 672, 678, 701, 724, 699, 683, 698, 691, 701]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['regression: scenario 9',
   [680, 720, [674, 698, 687, 683, 659, 647, 658, 656, 658, 674, 664, 656, 672, 650]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 700, [687, 695, 710, 721, 712, 724, 736, 738, 737, 740, 748]],
   ['idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['regression: scenario 3',
   [680, 690, [686, 679, 704, 696, 678, 658, 635, 648, 643, 657, 650, 661, 672, 691, 687, 685, 708, 687]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']]],
 [['regression: scenario 5',
   [680, 740, [676, 673, 693, 690, 714, 709, 715, 740, 727, 735, 738, 752, 754, 729, 752, 746]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'idle',
    'cool',
    'cool']],
  ['regression: scenario 16',
   [680, 740, [684, 671, 648, 650, 634, 633, 624, 643, 667, 647, 630, 639]],
   ['idle', 'idle', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 710, [758, 778, 769, 788, 780, 787, 790, 773, 789, 766, 785, 779, 790, 790, 790, 777, 790]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['control: scenario 2',
   [680, 740, [646, 654, 672, 672, 692, 717, 736, 759, 769, 790, 790]],
   ['heat', 'heat', 'heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']]],
 [['regression: scenario 11',
   [680, 690, [661, 683, 677, 656, 633, 621, 620, 620, 620, 629, 635, 658, 682, 657, 659, 665]],
   ['heat',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'heat',
    'heat',
    'heat']],
  ['regression: scenario 12',
   [680, 710, [710, 715, 700, 681, 698, 674, 698, 701, 681, 675, 664]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'heat']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['regression: scenario 1',
   [680,
    690,
    [671, 646, 636, 635, 620, 625, 635, 653, 659, 636, 645, 633, 620, 628, 620, 638, 653, 655, 666, 678]],
   ['idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: scenario 2',
   [680, 740, [655, 638, 663, 641, 622, 636, 628, 621, 621, 620, 635, 632, 644, 651, 656, 675, 687]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle']]],
 [['regression: scenario 9',
   [680, 690, [673, 694, 686, 686, 699, 720, 727, 736, 726, 719]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool']],
  ['regression: scenario 34',
   [680,
    700,
    [735,
     716,
     727,
     705,
     684,
     664,
     652,
     673,
     670,
     688,
     692,
     705,
     701,
     720,
     743,
     746,
     741,
     746,
     763,
     788,
     790]],
   ['cool',
    'cool',
    'cool',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 720, [774, 779, 785, 790, 790, 781, 778, 777, 770, 767, 743, 730, 708, 690]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle',
    'idle']],
  ['regression: scenario 2',
   [680,
    710,
    [701,
     679,
     702,
     721,
     727,
     748,
     743,
     720,
     741,
     738,
     762,
     769,
     758,
     740,
     744,
     725,
     748,
     728,
     742,
     755,
     769]],
   ['idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']]]]
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)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(heat_sp, cool_sp, temps):
    diff = 10
    if cool_sp - heat_sp < 30:
        cool_sp = heat_sp + 30
    state = 'idle'
    last_heat_end = None
    last_cool_end = None
    out = []
    for minute, temp in enumerate(temps):
        if state == 'heat' and temp >= heat_sp:
            state = 'idle'
            last_heat_end = minute
        elif state == 'cool' and temp <= cool_sp:
            state = 'idle'
            last_cool_end = minute
        if state == 'idle':
            if temp < heat_sp - diff // 2 and (last_cool_end is None or minute - last_cool_end >= 5):
                state = 'heat'
            elif temp > cool_sp + diff and (last_heat_end is None or minute - last_heat_end >= 5):
                state = 'cool'
        out.append(state)
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['regression: scenario 6',
   [680,
    720,
    [666, 650, 675, 664, 671, 693, 693, 695, 674, 675, 657, 635, 629, 632, 629, 632, 636, 632, 620]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['regression: scenario 1',
   [680, 690, [693, 687, 709, 709, 685, 704, 684, 680, 686, 687, 679]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: scenario 2',
   [680, 700, [681, 686, 698, 703, 724, 744, 752, 732, 755, 742, 762, 772]],
   ['idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['control: scenario 3',
   [680,
    740,
    [649,
     667,
     643,
     622,
     637,
     657,
     634,
     649,
     674,
     654,
     672,
     675,
     698,
     687,
     666,
     654,
     670,
     645,
     654,
     637,
     651,
     672]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']]],
 [['regression: scenario 2',
   [680, 710, [674, 681, 688, 699, 718, 698, 716, 709, 691, 672, 678, 701, 724, 699, 683, 698, 691, 701]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['regression: scenario 9',
   [680, 720, [674, 698, 687, 683, 659, 647, 658, 656, 658, 674, 664, 656, 672, 650]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 700, [687, 695, 710, 721, 712, 724, 736, 738, 737, 740, 748]],
   ['idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['regression: scenario 3',
   [680, 690, [686, 679, 704, 696, 678, 658, 635, 648, 643, 657, 650, 661, 672, 691, 687, 685, 708, 687]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']]],
 [['regression: scenario 5',
   [680, 740, [676, 673, 693, 690, 714, 709, 715, 740, 727, 735, 738, 752, 754, 729, 752, 746]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'idle',
    'cool',
    'cool']],
  ['regression: scenario 16',
   [680, 740, [684, 671, 648, 650, 634, 633, 624, 643, 667, 647, 630, 639]],
   ['idle', 'idle', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 710, [758, 778, 769, 788, 780, 787, 790, 773, 789, 766, 785, 779, 790, 790, 790, 777, 790]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['control: scenario 2',
   [680, 740, [646, 654, 672, 672, 692, 717, 736, 759, 769, 790, 790]],
   ['heat', 'heat', 'heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']]],
 [['regression: scenario 11',
   [680, 690, [661, 683, 677, 656, 633, 621, 620, 620, 620, 629, 635, 658, 682, 657, 659, 665]],
   ['heat',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'heat',
    'heat',
    'heat']],
  ['regression: scenario 12',
   [680, 710, [710, 715, 700, 681, 698, 674, 698, 701, 681, 675, 664]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'heat']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['regression: scenario 1',
   [680,
    690,
    [671, 646, 636, 635, 620, 625, 635, 653, 659, 636, 645, 633, 620, 628, 620, 638, 653, 655, 666, 678]],
   ['idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: scenario 2',
   [680, 740, [655, 638, 663, 641, 622, 636, 628, 621, 621, 620, 635, 632, 644, 651, 656, 675, 687]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle']]],
 [['regression: scenario 9',
   [680, 690, [673, 694, 686, 686, 699, 720, 727, 736, 726, 719]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool']],
  ['regression: scenario 34',
   [680,
    700,
    [735,
     716,
     727,
     705,
     684,
     664,
     652,
     673,
     670,
     688,
     692,
     705,
     701,
     720,
     743,
     746,
     741,
     746,
     763,
     788,
     790]],
   ['cool',
    'cool',
    'cool',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 720, [774, 779, 785, 790, 790, 781, 778, 777, 770, 767, 743, 730, 708, 690]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle',
    'idle']],
  ['regression: scenario 2',
   [680,
    710,
    [701,
     679,
     702,
     721,
     727,
     748,
     743,
     720,
     741,
     738,
     762,
     769,
     758,
     740,
     744,
     725,
     748,
     728,
     742,
     755,
     769]],
   ['idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']]]]
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)

prompt:
The furnace starts as soon as the room dips below setpoint and short-cycles around it.

Auto heat/cool thermostat sampled once per minute (temps in tenths F). If the cooling setpoint is less than 3.0 F above the heating setpoint it is pushed up to heat_sp + 30 (the heating setpoint wins). Heating starts below heat_sp - 1.0 F and ends at temp >= heat_sp; cooling starts above cool_sp + 1.0 F and ends at temp <= cool_sp. After a heating call ends, cooling may not start until 5 minutes later, and vice versa. Return heat, cool or idle per minute.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(heat_sp, cool_sp, temps):
    diff = 10
    if cool_sp - heat_sp < 30:
        cool_sp = heat_sp + 30
    state = 'idle'
    last_heat_end = None
    last_cool_end = None
    out = []
    for minute, temp in enumerate(temps):
        if state == 'heat' and temp >= heat_sp - diff:
            state = 'idle'
            last_heat_end = minute
        elif state == 'cool' and temp <= cool_sp:
            state = 'idle'
            last_cool_end = minute
        if state == 'idle':
            if temp < heat_sp - diff and (last_cool_end is None or minute - last_cool_end >= 5):
                state = 'heat'
            elif temp > cool_sp + diff and (last_heat_end is None or minute - last_heat_end >= 5):
                state = 'cool'
        out.append(state)
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: scenario 3',
   [680,
    740,
    [649,
     667,
     643,
     622,
     637,
     657,
     634,
     649,
     674,
     654,
     672,
     675,
     698,
     687,
     666,
     654,
     670,
     645,
     654,
     637,
     651,
     672]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 690, [693, 687, 709, 709, 685, 704, 684, 680, 686, 687, 679]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: scenario 2',
   [680, 700, [681, 686, 698, 703, 724, 744, 752, 732, 755, 742, 762, 772]],
   ['idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['control: scenario 4',
   [680, 720, [678, 693, 708, 706, 691, 698, 702, 725, 720, 725, 717, 703, 717, 731, 714, 728, 740, 735]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'cool',
    'cool']]],
 [['regression: scenario 6',
   [680,
    690,
    [694, 696, 693, 697, 678, 655, 668, 684, 659, 671, 693, 680, 655, 660, 640, 655, 657, 634, 622, 620]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'heat',
    'heat',
    'idle',
    'heat',
    'heat',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['regression: scenario 29',
   [680, 700, [694, 696, 676, 693, 679, 684, 666, 676, 671, 659, 680, 680, 658, 650, 662, 677]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 700, [687, 695, 710, 721, 712, 724, 736, 738, 737, 740, 748]],
   ['idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['control: scenario 2',
   [680, 710, [674, 681, 688, 699, 718, 698, 716, 709, 691, 672, 678, 701, 724, 699, 683, 698, 691, 701]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']]],
 [['regression: scenario 10',
   [680, 700, [693, 681, 656, 676, 697, 710, 694, 681, 682, 707, 717, 711, 736, 731, 735]],
   ['idle',
    'idle',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool']],
  ['regression: scenario 52',
   [680, 700, [670, 669, 652, 663, 640, 620, 634, 649, 660, 680]],
   ['idle', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'idle']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 710, [758, 778, 769, 788, 780, 787, 790, 773, 789, 766, 785, 779, 790, 790, 790, 777, 790]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['regression: scenario 2',
   [680, 740, [646, 654, 672, 672, 692, 717, 736, 759, 769, 790, 790]],
   ['heat', 'heat', 'heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']]],
 [['regression: scenario 9',
   [680, 690, [659, 637, 655, 663, 655, 654, 639, 635, 631, 656, 667, 672, 683, 704, 683, 696, 718]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['regression: scenario 35',
   [680,
    690,
    [712,
     690,
     682,
     662,
     680,
     655,
     665,
     643,
     622,
     636,
     657,
     681,
     658,
     663,
     656,
     663,
     649,
     661,
     657,
     643,
     667]],
   ['idle',
    'idle',
    'idle',
    'heat',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['regression: scenario 1',
   [680,
    690,
    [671, 646, 636, 635, 620, 625, 635, 653, 659, 636, 645, 633, 620, 628, 620, 638, 653, 655, 666, 678]],
   ['idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['regression: scenario 2',
   [680, 740, [655, 638, 663, 641, 622, 636, 628, 621, 621, 620, 635, 632, 644, 651, 656, 675, 687]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle']]],
 [['regression: scenario 19',
   [680,
    690,
    [644, 668, 674, 684, 659, 674, 661, 653, 633, 642, 656, 675, 656, 679, 675, 677, 700, 714, 720]],
   ['heat',
    'heat',
    'heat',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 720, [774, 779, 785, 790, 790, 781, 778, 777, 770, 767, 743, 730, 708, 690]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle',
    'idle']],
  ['control: scenario 2',
   [680,
    710,
    [701,
     679,
     702,
     721,
     727,
     748,
     743,
     720,
     741,
     738,
     762,
     769,
     758,
     740,
     744,
     725,
     748,
     728,
     742,
     755,
     769]],
   ['idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['control: scenario 3',
   [680, 720, [645, 664, 650, 632, 620, 628, 652, 666, 661, 660, 644]],
   ['heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat']]]]
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)

meaning: Fixture-count band, not measured model difficulty

deps: stdlib

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(heat_sp, cool_sp, temps):
    diff = 10
    if cool_sp - heat_sp < 30:
        cool_sp = heat_sp + 30
    state = 'idle'
    last_heat_end = None
    last_cool_end = None
    out = []
    for minute, temp in enumerate(temps):
        if state == 'heat' and temp > heat_sp:
            state = 'idle'
            last_heat_end = minute
        elif state == 'cool' and temp <= cool_sp:
            state = 'idle'
            last_cool_end = minute
        if state == 'idle':
            if temp < heat_sp - diff and (last_cool_end is None or minute - last_cool_end >= 5):
                state = 'heat'
            elif temp > cool_sp + diff and (last_heat_end is None or minute - last_heat_end >= 5):
                state = 'cool'
        out.append(state)
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: scenario 3',
   [680,
    740,
    [649,
     667,
     643,
     622,
     637,
     657,
     634,
     649,
     674,
     654,
     672,
     675,
     698,
     687,
     666,
     654,
     670,
     645,
     654,
     637,
     651,
     672]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 690, [693, 687, 709, 709, 685, 704, 684, 680, 686, 687, 679]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: scenario 2',
   [680, 700, [681, 686, 698, 703, 724, 744, 752, 732, 755, 742, 762, 772]],
   ['idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['control: scenario 4',
   [680, 720, [678, 693, 708, 706, 691, 698, 702, 725, 720, 725, 717, 703, 717, 731, 714, 728, 740, 735]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'cool',
    'cool']]],
 [['regression: scenario 6',
   [680,
    690,
    [694, 696, 693, 697, 678, 655, 668, 684, 659, 671, 693, 680, 655, 660, 640, 655, 657, 634, 622, 620]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'heat',
    'heat',
    'idle',
    'heat',
    'heat',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['regression: scenario 29',
   [680, 700, [694, 696, 676, 693, 679, 684, 666, 676, 671, 659, 680, 680, 658, 650, 662, 677]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 700, [687, 695, 710, 721, 712, 724, 736, 738, 737, 740, 748]],
   ['idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['control: scenario 2',
   [680, 710, [674, 681, 688, 699, 718, 698, 716, 709, 691, 672, 678, 701, 724, 699, 683, 698, 691, 701]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']]],
 [['regression: scenario 10',
   [680, 700, [693, 681, 656, 676, 697, 710, 694, 681, 682, 707, 717, 711, 736, 731, 735]],
   ['idle',
    'idle',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool']],
  ['regression: scenario 52',
   [680, 700, [670, 669, 652, 663, 640, 620, 634, 649, 660, 680]],
   ['idle', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'idle']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 710, [758, 778, 769, 788, 780, 787, 790, 773, 789, 766, 785, 779, 790, 790, 790, 777, 790]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['regression: scenario 2',
   [680, 740, [646, 654, 672, 672, 692, 717, 736, 759, 769, 790, 790]],
   ['heat', 'heat', 'heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']]],
 [['regression: scenario 9',
   [680, 690, [659, 637, 655, 663, 655, 654, 639, 635, 631, 656, 667, 672, 683, 704, 683, 696, 718]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['regression: scenario 35',
   [680,
    690,
    [712,
     690,
     682,
     662,
     680,
     655,
     665,
     643,
     622,
     636,
     657,
     681,
     658,
     663,
     656,
     663,
     649,
     661,
     657,
     643,
     667]],
   ['idle',
    'idle',
    'idle',
    'heat',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['regression: scenario 1',
   [680,
    690,
    [671, 646, 636, 635, 620, 625, 635, 653, 659, 636, 645, 633, 620, 628, 620, 638, 653, 655, 666, 678]],
   ['idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['regression: scenario 2',
   [680, 740, [655, 638, 663, 641, 622, 636, 628, 621, 621, 620, 635, 632, 644, 651, 656, 675, 687]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle']]],
 [['regression: scenario 19',
   [680,
    690,
    [644, 668, 674, 684, 659, 674, 661, 653, 633, 642, 656, 675, 656, 679, 675, 677, 700, 714, 720]],
   ['heat',
    'heat',
    'heat',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle']],
  ['regression: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 720, [774, 779, 785, 790, 790, 781, 778, 777, 770, 767, 743, 730, 708, 690]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle',
    'idle']],
  ['control: scenario 2',
   [680,
    710,
    [701,
     679,
     702,
     721,
     727,
     748,
     743,
     720,
     741,
     738,
     762,
     769,
     758,
     740,
     744,
     725,
     748,
     728,
     742,
     755,
     769]],
   ['idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['control: scenario 3',
   [680, 720, [645, 664, 650, 632, 620, 628, 652, 666, 661, 660, 644]],
   ['heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat']]]]
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)

prompt:
Rooms never reach the heating setpoint and the call chatters at the bottom of the band.

Auto heat/cool thermostat sampled once per minute (temps in tenths F). If the cooling setpoint is less than 3.0 F above the heating setpoint it is pushed up to heat_sp + 30 (the heating setpoint wins). Heating starts below heat_sp - 1.0 F and ends at temp >= heat_sp; cooling starts above cool_sp + 1.0 F and ends at temp <= cool_sp. After a heating call ends, cooling may not start until 5 minutes later, and vice versa. Return heat, cool or idle per minute.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(heat_sp, cool_sp, temps):
    diff = 10
    if cool_sp - heat_sp < 30:
        cool_sp = heat_sp + 30
    state = 'idle'
    last_heat_end = None
    last_cool_end = None
    out = []
    for minute, temp in enumerate(temps):
        if state == 'heat' and temp >= heat_sp:
            state = 'idle'
            last_heat_end = minute
        elif state == 'cool' and temp < cool_sp:
            state = 'idle'
            last_cool_end = minute
        if state == 'idle':
            if temp < heat_sp - diff and (last_cool_end is None or minute - last_cool_end >= 5):
                state = 'heat'
            elif temp > cool_sp + diff and (last_heat_end is None or minute - last_heat_end >= 5):
                state = 'cool'
        out.append(state)
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: scenario 1',
   [680, 690, [693, 687, 709, 709, 685, 704, 684, 680, 686, 687, 679]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: scenario 2',
   [680, 700, [681, 686, 698, 703, 724, 744, 752, 732, 755, 742, 762, 772]],
   ['idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['control: scenario 3',
   [680,
    740,
    [649,
     667,
     643,
     622,
     637,
     657,
     634,
     649,
     674,
     654,
     672,
     675,
     698,
     687,
     666,
     654,
     670,
     645,
     654,
     637,
     651,
     672]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: scenario 4',
   [680, 720, [678, 693, 708, 706, 691, 698, 702, 725, 720, 725, 717, 703, 717, 731, 714, 728, 740, 735]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'cool',
    'cool']]],
 [['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['regression: scenario 1',
   [680, 700, [687, 695, 710, 721, 712, 724, 736, 738, 737, 740, 748]],
   ['idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: scenario 2',
   [680, 710, [674, 681, 688, 699, 718, 698, 716, 709, 691, 672, 678, 701, 724, 699, 683, 698, 691, 701]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['control: scenario 3',
   [680, 690, [686, 679, 704, 696, 678, 658, 635, 648, 643, 657, 650, 661, 672, 691, 687, 685, 708, 687]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['control: scenario 4',
   [680, 710, [747, 771, 775, 769, 770, 766, 790, 790, 790, 789, 781]],
   ['cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']]],
 [['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: scenario 6',
   [680, 700, [757, 740, 724, 721, 702, 699, 724, 717, 742, 721, 723, 700]],
   ['cool', 'cool', 'cool', 'cool', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 710, [758, 778, 769, 788, 780, 787, 790, 773, 789, 766, 785, 779, 790, 790, 790, 777, 790]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['control: scenario 2',
   [680, 740, [646, 654, 672, 672, 692, 717, 736, 759, 769, 790, 790]],
   ['heat', 'heat', 'heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: scenario 3',
   [680, 710, [676, 683, 703, 725, 749, 746, 749, 735, 740, 755, 765, 742, 727, 737, 745, 751, 762]],
   ['idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']]],
 [['regression: scenario 34',
   [680, 700, [682, 703, 712, 736, 754, 777, 775, 764, 775, 787, 763, 766, 750, 731, 729, 719, 710]],
   ['idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle']],
  ['regression: scenario 13',
   [680, 700, [732, 729, 742, 749, 733, 737, 714, 714, 716, 713, 725, 733, 737, 726, 729, 723, 745]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680,
    690,
    [671, 646, 636, 635, 620, 625, 635, 653, 659, 636, 645, 633, 620, 628, 620, 638, 653, 655, 666, 678]],
   ['idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: scenario 2',
   [680, 740, [655, 638, 663, 641, 622, 636, 628, 621, 621, 620, 635, 632, 644, 651, 656, 675, 687]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle']]],
 [['regression: scenario 33',
   [680, 720, [682, 688, 681, 690, 702, 715, 721, 732, 753, 738, 741, 733, 720, 700, 712, 689, 710, 729]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['regression: scenario 9',
   [680, 690, [673, 694, 686, 686, 699, 720, 727, 736, 726, 719]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['regression: scenario 1',
   [680, 720, [774, 779, 785, 790, 790, 781, 778, 777, 770, 767, 743, 730, 708, 690]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle',
    'idle']],
  ['regression: scenario 2',
   [680,
    710,
    [701,
     679,
     702,
     721,
     727,
     748,
     743,
     720,
     741,
     738,
     762,
     769,
     758,
     740,
     744,
     725,
     748,
     728,
     742,
     755,
     769]],
   ['idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']]]]
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)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(heat_sp, cool_sp, temps):
    diff = 10
    if cool_sp - heat_sp < 30:
        cool_sp = heat_sp + 30
    state = 'idle'
    last_heat_end = None
    last_cool_end = None
    out = []
    for minute, temp in enumerate(temps):
        if state == 'heat' and temp >= heat_sp:
            state = 'idle'
            last_heat_end = minute
        elif state == 'cool' and temp <= cool_sp + diff:
            state = 'idle'
            last_cool_end = minute
        if state == 'idle':
            if temp < heat_sp - diff and (last_cool_end is None or minute - last_cool_end >= 5):
                state = 'heat'
            elif temp > cool_sp + diff and (last_heat_end is None or minute - last_heat_end >= 5):
                state = 'cool'
        out.append(state)
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: scenario 1',
   [680, 690, [693, 687, 709, 709, 685, 704, 684, 680, 686, 687, 679]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: scenario 2',
   [680, 700, [681, 686, 698, 703, 724, 744, 752, 732, 755, 742, 762, 772]],
   ['idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['control: scenario 3',
   [680,
    740,
    [649,
     667,
     643,
     622,
     637,
     657,
     634,
     649,
     674,
     654,
     672,
     675,
     698,
     687,
     666,
     654,
     670,
     645,
     654,
     637,
     651,
     672]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: scenario 4',
   [680, 720, [678, 693, 708, 706, 691, 698, 702, 725, 720, 725, 717, 703, 717, 731, 714, 728, 740, 735]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'cool',
    'cool']]],
 [['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['regression: scenario 1',
   [680, 700, [687, 695, 710, 721, 712, 724, 736, 738, 737, 740, 748]],
   ['idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: scenario 2',
   [680, 710, [674, 681, 688, 699, 718, 698, 716, 709, 691, 672, 678, 701, 724, 699, 683, 698, 691, 701]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['control: scenario 3',
   [680, 690, [686, 679, 704, 696, 678, 658, 635, 648, 643, 657, 650, 661, 672, 691, 687, 685, 708, 687]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['control: scenario 4',
   [680, 710, [747, 771, 775, 769, 770, 766, 790, 790, 790, 789, 781]],
   ['cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']]],
 [['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: scenario 6',
   [680, 700, [757, 740, 724, 721, 702, 699, 724, 717, 742, 721, 723, 700]],
   ['cool', 'cool', 'cool', 'cool', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 710, [758, 778, 769, 788, 780, 787, 790, 773, 789, 766, 785, 779, 790, 790, 790, 777, 790]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['control: scenario 2',
   [680, 740, [646, 654, 672, 672, 692, 717, 736, 759, 769, 790, 790]],
   ['heat', 'heat', 'heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: scenario 3',
   [680, 710, [676, 683, 703, 725, 749, 746, 749, 735, 740, 755, 765, 742, 727, 737, 745, 751, 762]],
   ['idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']]],
 [['regression: scenario 34',
   [680, 700, [682, 703, 712, 736, 754, 777, 775, 764, 775, 787, 763, 766, 750, 731, 729, 719, 710]],
   ['idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle']],
  ['regression: scenario 13',
   [680, 700, [732, 729, 742, 749, 733, 737, 714, 714, 716, 713, 725, 733, 737, 726, 729, 723, 745]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680,
    690,
    [671, 646, 636, 635, 620, 625, 635, 653, 659, 636, 645, 633, 620, 628, 620, 638, 653, 655, 666, 678]],
   ['idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: scenario 2',
   [680, 740, [655, 638, 663, 641, 622, 636, 628, 621, 621, 620, 635, 632, 644, 651, 656, 675, 687]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle']]],
 [['regression: scenario 33',
   [680, 720, [682, 688, 681, 690, 702, 715, 721, 732, 753, 738, 741, 733, 720, 700, 712, 689, 710, 729]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['regression: scenario 9',
   [680, 690, [673, 694, 686, 686, 699, 720, 727, 736, 726, 719]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['regression: scenario 1',
   [680, 720, [774, 779, 785, 790, 790, 781, 778, 777, 770, 767, 743, 730, 708, 690]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle',
    'idle']],
  ['regression: scenario 2',
   [680,
    710,
    [701,
     679,
     702,
     721,
     727,
     748,
     743,
     720,
     741,
     738,
     762,
     769,
     758,
     740,
     744,
     725,
     748,
     728,
     742,
     755,
     769]],
   ['idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']]]]
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)

prompt:
Air conditioner runs one extra sample past setpoint on every cycle.

Auto heat/cool thermostat sampled once per minute (temps in tenths F). If the cooling setpoint is less than 3.0 F above the heating setpoint it is pushed up to heat_sp + 30 (the heating setpoint wins). Heating starts below heat_sp - 1.0 F and ends at temp >= heat_sp; cooling starts above cool_sp + 1.0 F and ends at temp <= cool_sp. After a heating call ends, cooling may not start until 5 minutes later, and vice versa. Return heat, cool or idle per minute.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(heat_sp, cool_sp, temps):
    diff = 10
    if cool_sp - heat_sp < 30:
        cool_sp = heat_sp + 30
    state = 'idle'
    last_heat_end = None
    last_cool_end = None
    out = []
    for minute, temp in enumerate(temps):
        if state == 'heat' and temp >= heat_sp:
            state = 'idle'
        elif state == 'cool' and temp <= cool_sp:
            state = 'idle'
            last_cool_end = minute
        if state == 'idle':
            if temp < heat_sp - diff and (last_cool_end is None or minute - last_cool_end >= 5):
                state = 'heat'
                last_heat_end = minute
            elif temp > cool_sp + diff and (last_heat_end is None or minute - last_heat_end >= 5):
                state = 'cool'
        out.append(state)
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 690, [693, 687, 709, 709, 685, 704, 684, 680, 686, 687, 679]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: scenario 2',
   [680, 700, [681, 686, 698, 703, 724, 744, 752, 732, 755, 742, 762, 772]],
   ['idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['control: scenario 3',
   [680,
    740,
    [649,
     667,
     643,
     622,
     637,
     657,
     634,
     649,
     674,
     654,
     672,
     675,
     698,
     687,
     666,
     654,
     670,
     645,
     654,
     637,
     651,
     672]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: scenario 4',
   [680, 720, [678, 693, 708, 706, 691, 698, 702, 725, 720, 725, 717, 703, 717, 731, 714, 728, 740, 735]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'cool',
    'cool']]],
 [['regression: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: scenario 25',
   [680, 710, [674, 651, 640, 644, 663, 686, 668, 681, 706, 727, 723, 727, 746, 737, 716, 713, 725, 747]],
   ['idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 700, [687, 695, 710, 721, 712, 724, 736, 738, 737, 740, 748]],
   ['idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['control: scenario 2',
   [680, 710, [674, 681, 688, 699, 718, 698, 716, 709, 691, 672, 678, 701, 724, 699, 683, 698, 691, 701]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['control: scenario 3',
   [680, 690, [686, 679, 704, 696, 678, 658, 635, 648, 643, 657, 650, 661, 672, 691, 687, 685, 708, 687]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']]],
 [['regression: scenario 2',
   [680, 740, [646, 654, 672, 672, 692, 717, 736, 759, 769, 790, 790]],
   ['heat', 'heat', 'heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: scenario 14',
   [680, 710, [678, 658, 672, 689, 668, 655, 667, 652, 671, 687, 710, 730, 739, 757, 736, 715]],
   ['idle',
    'heat',
    'heat',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['regression: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 710, [758, 778, 769, 788, 780, 787, 790, 773, 789, 766, 785, 779, 790, 790, 790, 777, 790]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['control: scenario 3',
   [680, 710, [676, 683, 703, 725, 749, 746, 749, 735, 740, 755, 765, 742, 727, 737, 745, 751, 762]],
   ['idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']]],
 [['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: scenario 4',
   [680,
    690,
    [646, 652, 631, 639, 659, 675, 688, 663, 665, 676, 683, 703, 716, 725, 740, 750, 730, 725, 738]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['regression: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680,
    690,
    [671, 646, 636, 635, 620, 625, 635, 653, 659, 636, 645, 633, 620, 628, 620, 638, 653, 655, 666, 678]],
   ['idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: scenario 2',
   [680, 740, [655, 638, 663, 641, 622, 636, 628, 621, 621, 620, 635, 632, 644, 651, 656, 675, 687]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle']],
  ['control: scenario 3',
   [680, 690, [720, 710, 717, 702, 708, 715, 732, 753, 772, 753, 757]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool']]],
 [['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: scenario 28',
   [680, 710, [657, 667, 661, 671, 681, 698, 707, 731, 740, 738, 726, 715, 719, 716, 694, 717]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle',
    'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['regression: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 720, [774, 779, 785, 790, 790, 781, 778, 777, 770, 767, 743, 730, 708, 690]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle',
    'idle']],
  ['control: scenario 2',
   [680,
    710,
    [701,
     679,
     702,
     721,
     727,
     748,
     743,
     720,
     741,
     738,
     762,
     769,
     758,
     740,
     744,
     725,
     748,
     728,
     742,
     755,
     769]],
   ['idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['control: scenario 3',
   [680, 720, [645, 664, 650, 632, 620, 628, 652, 666, 661, 660, 644]],
   ['heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat']]]]
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)

source:
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(heat_sp, cool_sp, temps):
    diff = 10
    if cool_sp - heat_sp < 30:
        cool_sp = heat_sp + 30
    state = 'idle'
    last_heat_end = None
    last_cool_end = None
    out = []
    for minute, temp in enumerate(temps):
        if state == 'heat' and temp >= heat_sp:
            state = 'idle'
            last_heat_end = minute - 1
        elif state == 'cool' and temp <= cool_sp:
            state = 'idle'
            last_cool_end = minute
        if state == 'idle':
            if temp < heat_sp - diff and (last_cool_end is None or minute - last_cool_end >= 5):
                state = 'heat'
            elif temp > cool_sp + diff and (last_heat_end is None or minute - last_heat_end >= 5):
                state = 'cool'
        out.append(state)
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 690, [693, 687, 709, 709, 685, 704, 684, 680, 686, 687, 679]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: scenario 2',
   [680, 700, [681, 686, 698, 703, 724, 744, 752, 732, 755, 742, 762, 772]],
   ['idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['control: scenario 3',
   [680,
    740,
    [649,
     667,
     643,
     622,
     637,
     657,
     634,
     649,
     674,
     654,
     672,
     675,
     698,
     687,
     666,
     654,
     670,
     645,
     654,
     637,
     651,
     672]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: scenario 4',
   [680, 720, [678, 693, 708, 706, 691, 698, 702, 725, 720, 725, 717, 703, 717, 731, 714, 728, 740, 735]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'cool',
    'cool']]],
 [['regression: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: scenario 25',
   [680, 710, [674, 651, 640, 644, 663, 686, 668, 681, 706, 727, 723, 727, 746, 737, 716, 713, 725, 747]],
   ['idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 700, [687, 695, 710, 721, 712, 724, 736, 738, 737, 740, 748]],
   ['idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['control: scenario 2',
   [680, 710, [674, 681, 688, 699, 718, 698, 716, 709, 691, 672, 678, 701, 724, 699, 683, 698, 691, 701]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['control: scenario 3',
   [680, 690, [686, 679, 704, 696, 678, 658, 635, 648, 643, 657, 650, 661, 672, 691, 687, 685, 708, 687]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']]],
 [['regression: scenario 2',
   [680, 740, [646, 654, 672, 672, 692, 717, 736, 759, 769, 790, 790]],
   ['heat', 'heat', 'heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: scenario 14',
   [680, 710, [678, 658, 672, 689, 668, 655, 667, 652, 671, 687, 710, 730, 739, 757, 736, 715]],
   ['idle',
    'heat',
    'heat',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['regression: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 710, [758, 778, 769, 788, 780, 787, 790, 773, 789, 766, 785, 779, 790, 790, 790, 777, 790]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['control: scenario 3',
   [680, 710, [676, 683, 703, 725, 749, 746, 749, 735, 740, 755, 765, 742, 727, 737, 745, 751, 762]],
   ['idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']]],
 [['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: scenario 4',
   [680,
    690,
    [646, 652, 631, 639, 659, 675, 688, 663, 665, 676, 683, 703, 716, 725, 740, 750, 730, 725, 738]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['regression: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680,
    690,
    [671, 646, 636, 635, 620, 625, 635, 653, 659, 636, 645, 633, 620, 628, 620, 638, 653, 655, 666, 678]],
   ['idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: scenario 2',
   [680, 740, [655, 638, 663, 641, 622, 636, 628, 621, 621, 620, 635, 632, 644, 651, 656, 675, 687]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle']],
  ['control: scenario 3',
   [680, 690, [720, 710, 717, 702, 708, 715, 732, 753, 772, 753, 757]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool']]],
 [['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: scenario 28',
   [680, 710, [657, 667, 661, 671, 681, 698, 707, 731, 740, 738, 726, 715, 719, 716, 694, 717]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle',
    'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['regression: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 720, [774, 779, 785, 790, 790, 781, 778, 777, 770, 767, 743, 730, 708, 690]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle',
    'idle']],
  ['control: scenario 2',
   [680,
    710,
    [701,
     679,
     702,
     721,
     727,
     748,
     743,
     720,
     741,
     738,
     762,
     769,
     758,
     740,
     744,
     725,
     748,
     728,
     742,
     755,
     769]],
   ['idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['control: scenario 3',
   [680, 720, [645, 664, 650, 632, 620, 628, 652, 666, 661, 660, 644]],
   ['heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat']]]]
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)

prompt:
Cooling can start immediately after a long heating call because the lockout already expired.

Auto heat/cool thermostat sampled once per minute (temps in tenths F). If the cooling setpoint is less than 3.0 F above the heating setpoint it is pushed up to heat_sp + 30 (the heating setpoint wins). Heating starts below heat_sp - 1.0 F and ends at temp >= heat_sp; cooling starts above cool_sp + 1.0 F and ends at temp <= cool_sp. After a heating call ends, cooling may not start until 5 minutes later, and vice versa. Return heat, cool or idle per minute.

split: open-access