broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    s = x.strip()
    up = s.upper()
    words = s.replace('(', ' ( ').split()
    if not words or words[0] not in ('POLYGON', 'MULTIPOLYGON'):
        return None
    if len(words) == 2 and words[1] == 'EMPTY':
        return []
    if '(' not in s:
        return None
    body = s[s.index('('):]
    stack = [[]]
    cur = ''
    for ch in body:
        if ch == '(':
            stack.append([])
        elif ch in '),':
            if cur.strip():
                nums = [float(t) for t in cur.split()]
                stack[-1].append(nums[:2])
            cur = ''
            if ch == ')':
                done = stack.pop()
                stack[-1].append(done)
        else:
            cur += ch
    tree = stack[0][0]
    polys = [tree] if words[0] == 'POLYGON' else tree
    for poly in polys:
        for ring in poly:
            if len(ring) < 4 or ring[0] != ring[-1]:
                return None
    return polys
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', 'POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0], [0.0, 0.0]]]]), ('control #1', 'POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 2 4, 4 4, 2 2))', [[[[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0], [0.0, 0.0]], [[2.0, 2.0], [2.0, 4.0], [4.0, 4.0], [2.0, 2.0]]]]), ('regression #2', 'POLYGON Z ((0 0 5, 4 0 5, 4 4 6, 0 0 5))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('regression #3', 'POLYGON ZM ((0 0 5 1, 4 0 5 2, 4 4 6 3, 0 0 5 1))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('regression #4', 'POLYGON M ((0 0 7, 4 0 8, 4 4 9, 0 0 7))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('boundary #5', 'POLYGON EMPTY', []), ('boundary #6', 'polygon empty', []), ('regression #7', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((5 5, 6 5, 6 6, 5 5), (5.2 5.1, 5.8 5.8, 5.8 5.1, 5.2 5.1)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]], [[[5.0, 5.0], [6.0, 5.0], [6.0, 6.0], [5.0, 5.0]], [[5.2, 5.1], [5.8, 5.8], [5.8, 5.1], [5.2, 5.1]]]])], [('regression #3', 'POLYGON ZM ((0 0 5 1, 4 0 5 2, 4 4 6 3, 0 0 5 1))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('regression #4', 'POLYGON M ((0 0 7, 4 0 8, 4 4 9, 0 0 7))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('boundary #5', 'POLYGON EMPTY', []), ('boundary #6', 'polygon empty', []), ('regression #7', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((5 5, 6 5, 6 6, 5 5), (5.2 5.1, 5.8 5.8, 5.8 5.1, 5.2 5.1)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]], [[[5.0, 5.0], [6.0, 5.0], [6.0, 6.0], [5.0, 5.0]], [[5.2, 5.1], [5.8, 5.8], [5.8, 5.1], [5.2, 5.1]]]]), ('regression #8', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]]]), ('regression #9', 'multipolygon (((-1.5 -2, 3e2 0, 0 1e1, -1.5 -2)))', [[[[-1.5, -2.0], [300.0, 0.0], [0.0, 10.0], [-1.5, -2.0]]]]), ('boundary #10', 'POLYGON ((0 0, 4 0, 4 4, 0 1))', None)], [('boundary #6', 'polygon empty', []), ('regression #7', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((5 5, 6 5, 6 6, 5 5), (5.2 5.1, 5.8 5.8, 5.8 5.1, 5.2 5.1)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]], [[[5.0, 5.0], [6.0, 5.0], [6.0, 6.0], [5.0, 5.0]], [[5.2, 5.1], [5.8, 5.8], [5.8, 5.1], [5.2, 5.1]]]]), ('regression #8', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]]]), ('regression #9', 'multipolygon (((-1.5 -2, 3e2 0, 0 1e1, -1.5 -2)))', [[[[-1.5, -2.0], [300.0, 0.0], [0.0, 10.0], [-1.5, -2.0]]]]), ('boundary #10', 'POLYGON ((0 0, 4 0, 4 4, 0 1))', None), ('boundary #11', 'POLYGON ((0 0, 4 0, 0 0))', None), ('control #12', 'Polygon((1 1,5 1,5 5,1 1))', [[[[1.0, 1.0], [5.0, 1.0], [5.0, 5.0], [1.0, 1.0]]]]), ('boundary #13', 'LINESTRING (0 0, 1 1)', None)], [('boundary #6', 'polygon empty', []), ('boundary #10', 'POLYGON ((0 0, 4 0, 4 4, 0 1))', None), ('boundary #11', 'POLYGON ((0 0, 4 0, 0 0))', None), ('control #12', 'Polygon((1 1,5 1,5 5,1 1))', [[[[1.0, 1.0], [5.0, 1.0], [5.0, 5.0], [1.0, 1.0]]]]), ('boundary #13', 'LINESTRING (0 0, 1 1)', None), ('boundary #14', 'MULTIPOLYGON EMPTY', []), ('control #15', '  POLYGON ((0 0, -3 0, -3 -3, 0 0))  ', [[[[0.0, 0.0], [-3.0, 0.0], [-3.0, -3.0], [0.0, 0.0]]]]), ('regression #16', 'MULTIPOLYGON Z (((0 0 1, 2 0 1, 2 2 1, 0 0 1)), ((9 9 0, 8 9 0, 8 8 0, 9 9 0)))', [[[[0.0, 0.0], [2.0, 0.0], [2.0, 2.0], [0.0, 0.0]]], [[[9.0, 9.0], [8.0, 9.0], [8.0, 8.0], [9.0, 9.0]]]])], [('control #0', 'POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0], [0.0, 0.0]]]]), ('control #1', 'POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 2 4, 4 4, 2 2))', [[[[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0], [0.0, 0.0]], [[2.0, 2.0], [2.0, 4.0], [4.0, 4.0], [2.0, 2.0]]]]), ('boundary #6', 'polygon empty', []), ('regression #9', 'multipolygon (((-1.5 -2, 3e2 0, 0 1e1, -1.5 -2)))', [[[[-1.5, -2.0], [300.0, 0.0], [0.0, 10.0], [-1.5, -2.0]]]]), ('boundary #14', 'MULTIPOLYGON EMPTY', []), ('control #15', '  POLYGON ((0 0, -3 0, -3 -3, 0 0))  ', [[[[0.0, 0.0], [-3.0, 0.0], [-3.0, -3.0], [0.0, 0.0]]]]), ('regression #16', 'MULTIPOLYGON Z (((0 0 1, 2 0 1, 2 2 1, 0 0 1)), ((9 9 0, 8 9 0, 8 8 0, 9 9 0)))', [[[[0.0, 0.0], [2.0, 0.0], [2.0, 2.0], [0.0, 0.0]]], [[[9.0, 9.0], [8.0, 9.0], [8.0, 8.0], [9.0, 9.0]]]]), ('boundary #17', 'POLYGON ((0 0 1, 4 0 1, 4 4 1, 0 0 2))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 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)

source:
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    s = x.strip()
    up = s.upper()
    words = s.replace('(', ' ( ').split()
    words[:1] = [w.upper() for w in words[:1]]
    if not words or words[0] not in ('POLYGON', 'MULTIPOLYGON'):
        return None
    if len(words) == 2 and words[1] == 'EMPTY':
        return []
    if '(' not in s:
        return None
    body = s[s.index('('):]
    stack = [[]]
    cur = ''
    for ch in body:
        if ch == '(':
            stack.append([])
        elif ch in '),':
            if cur.strip():
                nums = [float(t) for t in cur.split()]
                stack[-1].append(nums[:2])
            cur = ''
            if ch == ')':
                done = stack.pop()
                stack[-1].append(done)
        else:
            cur += ch
    tree = stack[0][0]
    polys = [tree] if words[0] == 'POLYGON' else tree
    for poly in polys:
        for ring in poly:
            if len(ring) < 4 or ring[0] != ring[-1]:
                return None
    return polys
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', 'POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0], [0.0, 0.0]]]]), ('control #1', 'POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 2 4, 4 4, 2 2))', [[[[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0], [0.0, 0.0]], [[2.0, 2.0], [2.0, 4.0], [4.0, 4.0], [2.0, 2.0]]]]), ('regression #2', 'POLYGON Z ((0 0 5, 4 0 5, 4 4 6, 0 0 5))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('regression #3', 'POLYGON ZM ((0 0 5 1, 4 0 5 2, 4 4 6 3, 0 0 5 1))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('regression #4', 'POLYGON M ((0 0 7, 4 0 8, 4 4 9, 0 0 7))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('boundary #5', 'POLYGON EMPTY', []), ('boundary #6', 'polygon empty', []), ('regression #7', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((5 5, 6 5, 6 6, 5 5), (5.2 5.1, 5.8 5.8, 5.8 5.1, 5.2 5.1)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]], [[[5.0, 5.0], [6.0, 5.0], [6.0, 6.0], [5.0, 5.0]], [[5.2, 5.1], [5.8, 5.8], [5.8, 5.1], [5.2, 5.1]]]])], [('regression #3', 'POLYGON ZM ((0 0 5 1, 4 0 5 2, 4 4 6 3, 0 0 5 1))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('regression #4', 'POLYGON M ((0 0 7, 4 0 8, 4 4 9, 0 0 7))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('boundary #5', 'POLYGON EMPTY', []), ('boundary #6', 'polygon empty', []), ('regression #7', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((5 5, 6 5, 6 6, 5 5), (5.2 5.1, 5.8 5.8, 5.8 5.1, 5.2 5.1)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]], [[[5.0, 5.0], [6.0, 5.0], [6.0, 6.0], [5.0, 5.0]], [[5.2, 5.1], [5.8, 5.8], [5.8, 5.1], [5.2, 5.1]]]]), ('regression #8', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]]]), ('regression #9', 'multipolygon (((-1.5 -2, 3e2 0, 0 1e1, -1.5 -2)))', [[[[-1.5, -2.0], [300.0, 0.0], [0.0, 10.0], [-1.5, -2.0]]]]), ('boundary #10', 'POLYGON ((0 0, 4 0, 4 4, 0 1))', None)], [('boundary #6', 'polygon empty', []), ('regression #7', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((5 5, 6 5, 6 6, 5 5), (5.2 5.1, 5.8 5.8, 5.8 5.1, 5.2 5.1)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]], [[[5.0, 5.0], [6.0, 5.0], [6.0, 6.0], [5.0, 5.0]], [[5.2, 5.1], [5.8, 5.8], [5.8, 5.1], [5.2, 5.1]]]]), ('regression #8', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]]]), ('regression #9', 'multipolygon (((-1.5 -2, 3e2 0, 0 1e1, -1.5 -2)))', [[[[-1.5, -2.0], [300.0, 0.0], [0.0, 10.0], [-1.5, -2.0]]]]), ('boundary #10', 'POLYGON ((0 0, 4 0, 4 4, 0 1))', None), ('boundary #11', 'POLYGON ((0 0, 4 0, 0 0))', None), ('control #12', 'Polygon((1 1,5 1,5 5,1 1))', [[[[1.0, 1.0], [5.0, 1.0], [5.0, 5.0], [1.0, 1.0]]]]), ('boundary #13', 'LINESTRING (0 0, 1 1)', None)], [('boundary #6', 'polygon empty', []), ('boundary #10', 'POLYGON ((0 0, 4 0, 4 4, 0 1))', None), ('boundary #11', 'POLYGON ((0 0, 4 0, 0 0))', None), ('control #12', 'Polygon((1 1,5 1,5 5,1 1))', [[[[1.0, 1.0], [5.0, 1.0], [5.0, 5.0], [1.0, 1.0]]]]), ('boundary #13', 'LINESTRING (0 0, 1 1)', None), ('boundary #14', 'MULTIPOLYGON EMPTY', []), ('control #15', '  POLYGON ((0 0, -3 0, -3 -3, 0 0))  ', [[[[0.0, 0.0], [-3.0, 0.0], [-3.0, -3.0], [0.0, 0.0]]]]), ('regression #16', 'MULTIPOLYGON Z (((0 0 1, 2 0 1, 2 2 1, 0 0 1)), ((9 9 0, 8 9 0, 8 8 0, 9 9 0)))', [[[[0.0, 0.0], [2.0, 0.0], [2.0, 2.0], [0.0, 0.0]]], [[[9.0, 9.0], [8.0, 9.0], [8.0, 8.0], [9.0, 9.0]]]])], [('control #0', 'POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0], [0.0, 0.0]]]]), ('control #1', 'POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 2 4, 4 4, 2 2))', [[[[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0], [0.0, 0.0]], [[2.0, 2.0], [2.0, 4.0], [4.0, 4.0], [2.0, 2.0]]]]), ('boundary #6', 'polygon empty', []), ('regression #9', 'multipolygon (((-1.5 -2, 3e2 0, 0 1e1, -1.5 -2)))', [[[[-1.5, -2.0], [300.0, 0.0], [0.0, 10.0], [-1.5, -2.0]]]]), ('boundary #14', 'MULTIPOLYGON EMPTY', []), ('control #15', '  POLYGON ((0 0, -3 0, -3 -3, 0 0))  ', [[[[0.0, 0.0], [-3.0, 0.0], [-3.0, -3.0], [0.0, 0.0]]]]), ('regression #16', 'MULTIPOLYGON Z (((0 0 1, 2 0 1, 2 2 1, 0 0 1)), ((9 9 0, 8 9 0, 8 8 0, 9 9 0)))', [[[[0.0, 0.0], [2.0, 0.0], [2.0, 2.0], [0.0, 0.0]]], [[[9.0, 9.0], [8.0, 9.0], [8.0, 8.0], [9.0, 9.0]]]]), ('boundary #17', 'POLYGON ((0 0 1, 4 0 1, 4 4 1, 0 0 2))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 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)

prompt:
Lower-case WKT from some databases is rejected.

Input: a WKT string. The keyword (case-insensitive) must be POLYGON or MULTIPOLYGON, else None; "<keyword> EMPTY" returns []. Otherwise parse the parenthesised body into polygons -> rings -> positions, keeping only x and y of every position (Z/M ordinates are dropped). Every ring must have at least 4 positions and be closed in x/y, else None. Return a list of polygons (a POLYGON gives a one-element list).

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    s = x.strip()
    up = s.upper()
    words = up.replace('(', ' ( ').split()
    if not words or words[0] not in ('POLYGON', 'MULTIPOLYGON'):
        return None
    if len(words) == 2 and words[1] == 'EMPTY':
        return []
    if '(' not in s:
        return None
    body = s[s.index('('):]
    stack = [[]]
    cur = ''
    for ch in body:
        if ch == '(':
            stack.append([])
        elif ch in '),':
            if cur.strip():
                nums = [float(t) for t in cur.split()]
                stack[-1].append(nums[:2])
            cur = ''
            if ch == ')':
                done = stack.pop()
                stack[-1].append(done)
        else:
            cur += ch
    tree = stack[0][0]
    polys = [tree] if words[0] == 'POLYGON' else tree
    for poly in polys:
        for ring in poly:
            if len(ring) < 4:
                return None
    return polys
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', 'POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0], [0.0, 0.0]]]]), ('control #1', 'POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 2 4, 4 4, 2 2))', [[[[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0], [0.0, 0.0]], [[2.0, 2.0], [2.0, 4.0], [4.0, 4.0], [2.0, 2.0]]]]), ('regression #2', 'POLYGON Z ((0 0 5, 4 0 5, 4 4 6, 0 0 5))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('regression #3', 'POLYGON ZM ((0 0 5 1, 4 0 5 2, 4 4 6 3, 0 0 5 1))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('regression #4', 'POLYGON M ((0 0 7, 4 0 8, 4 4 9, 0 0 7))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('boundary #5', 'POLYGON EMPTY', []), ('boundary #6', 'polygon empty', []), ('boundary #10', 'POLYGON ((0 0, 4 0, 4 4, 0 1))', None)], [('regression #3', 'POLYGON ZM ((0 0 5 1, 4 0 5 2, 4 4 6 3, 0 0 5 1))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('regression #4', 'POLYGON M ((0 0 7, 4 0 8, 4 4 9, 0 0 7))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('boundary #5', 'POLYGON EMPTY', []), ('boundary #6', 'polygon empty', []), ('regression #7', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((5 5, 6 5, 6 6, 5 5), (5.2 5.1, 5.8 5.8, 5.8 5.1, 5.2 5.1)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]], [[[5.0, 5.0], [6.0, 5.0], [6.0, 6.0], [5.0, 5.0]], [[5.2, 5.1], [5.8, 5.8], [5.8, 5.1], [5.2, 5.1]]]]), ('regression #8', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]]]), ('regression #9', 'multipolygon (((-1.5 -2, 3e2 0, 0 1e1, -1.5 -2)))', [[[[-1.5, -2.0], [300.0, 0.0], [0.0, 10.0], [-1.5, -2.0]]]]), ('boundary #10', 'POLYGON ((0 0, 4 0, 4 4, 0 1))', None)], [('boundary #6', 'polygon empty', []), ('regression #7', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((5 5, 6 5, 6 6, 5 5), (5.2 5.1, 5.8 5.8, 5.8 5.1, 5.2 5.1)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]], [[[5.0, 5.0], [6.0, 5.0], [6.0, 6.0], [5.0, 5.0]], [[5.2, 5.1], [5.8, 5.8], [5.8, 5.1], [5.2, 5.1]]]]), ('regression #8', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]]]), ('regression #9', 'multipolygon (((-1.5 -2, 3e2 0, 0 1e1, -1.5 -2)))', [[[[-1.5, -2.0], [300.0, 0.0], [0.0, 10.0], [-1.5, -2.0]]]]), ('boundary #10', 'POLYGON ((0 0, 4 0, 4 4, 0 1))', None), ('boundary #11', 'POLYGON ((0 0, 4 0, 0 0))', None), ('control #12', 'Polygon((1 1,5 1,5 5,1 1))', [[[[1.0, 1.0], [5.0, 1.0], [5.0, 5.0], [1.0, 1.0]]]]), ('boundary #13', 'LINESTRING (0 0, 1 1)', None)], [('regression #9', 'multipolygon (((-1.5 -2, 3e2 0, 0 1e1, -1.5 -2)))', [[[[-1.5, -2.0], [300.0, 0.0], [0.0, 10.0], [-1.5, -2.0]]]]), ('boundary #10', 'POLYGON ((0 0, 4 0, 4 4, 0 1))', None), ('boundary #11', 'POLYGON ((0 0, 4 0, 0 0))', None), ('control #12', 'Polygon((1 1,5 1,5 5,1 1))', [[[[1.0, 1.0], [5.0, 1.0], [5.0, 5.0], [1.0, 1.0]]]]), ('boundary #13', 'LINESTRING (0 0, 1 1)', None), ('boundary #14', 'MULTIPOLYGON EMPTY', []), ('control #15', '  POLYGON ((0 0, -3 0, -3 -3, 0 0))  ', [[[[0.0, 0.0], [-3.0, 0.0], [-3.0, -3.0], [0.0, 0.0]]]]), ('regression #16', 'MULTIPOLYGON Z (((0 0 1, 2 0 1, 2 2 1, 0 0 1)), ((9 9 0, 8 9 0, 8 8 0, 9 9 0)))', [[[[0.0, 0.0], [2.0, 0.0], [2.0, 2.0], [0.0, 0.0]]], [[[9.0, 9.0], [8.0, 9.0], [8.0, 8.0], [9.0, 9.0]]]])], [('control #0', 'POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0], [0.0, 0.0]]]]), ('control #1', 'POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 2 4, 4 4, 2 2))', [[[[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0], [0.0, 0.0]], [[2.0, 2.0], [2.0, 4.0], [4.0, 4.0], [2.0, 2.0]]]]), ('boundary #10', 'POLYGON ((0 0, 4 0, 4 4, 0 1))', None), ('boundary #13', 'LINESTRING (0 0, 1 1)', None), ('boundary #14', 'MULTIPOLYGON EMPTY', []), ('control #15', '  POLYGON ((0 0, -3 0, -3 -3, 0 0))  ', [[[[0.0, 0.0], [-3.0, 0.0], [-3.0, -3.0], [0.0, 0.0]]]]), ('regression #16', 'MULTIPOLYGON Z (((0 0 1, 2 0 1, 2 2 1, 0 0 1)), ((9 9 0, 8 9 0, 8 8 0, 9 9 0)))', [[[[0.0, 0.0], [2.0, 0.0], [2.0, 2.0], [0.0, 0.0]]], [[[9.0, 9.0], [8.0, 9.0], [8.0, 8.0], [9.0, 9.0]]]]), ('boundary #17', 'POLYGON ((0 0 1, 4 0 1, 4 4 1, 0 0 2))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 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)

meaning: Fixture-count band, not measured model difficulty

deps: stdlib

source:
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    s = x.strip()
    up = s.upper()
    words = up.replace('(', ' ( ').split()
    if not words or words[0] not in ('POLYGON', 'MULTIPOLYGON'):
        return None
    if len(words) == 2 and words[1] == 'EMPTY':
        return []
    if '(' not in s:
        return None
    body = s[s.index('('):]
    stack = [[]]
    cur = ''
    for ch in body:
        if ch == '(':
            stack.append([])
        elif ch in '),':
            if cur.strip():
                nums = [float(t) for t in cur.split()]
                stack[-1].append(nums[:2])
            cur = ''
            if ch == ')':
                done = stack.pop()
                stack[-1].append(done)
        else:
            cur += ch
    tree = stack[0][0]
    polys = [tree] if words[0] == 'POLYGON' else tree
    for poly in polys:
        for ring in poly:
            if len(ring) < 4 or ring[0][0] != ring[-1][0]:
                return None
    return polys
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', 'POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0], [0.0, 0.0]]]]), ('control #1', 'POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 2 4, 4 4, 2 2))', [[[[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0], [0.0, 0.0]], [[2.0, 2.0], [2.0, 4.0], [4.0, 4.0], [2.0, 2.0]]]]), ('regression #2', 'POLYGON Z ((0 0 5, 4 0 5, 4 4 6, 0 0 5))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('regression #3', 'POLYGON ZM ((0 0 5 1, 4 0 5 2, 4 4 6 3, 0 0 5 1))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('regression #4', 'POLYGON M ((0 0 7, 4 0 8, 4 4 9, 0 0 7))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('boundary #5', 'POLYGON EMPTY', []), ('boundary #6', 'polygon empty', []), ('boundary #10', 'POLYGON ((0 0, 4 0, 4 4, 0 1))', None)], [('regression #3', 'POLYGON ZM ((0 0 5 1, 4 0 5 2, 4 4 6 3, 0 0 5 1))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('regression #4', 'POLYGON M ((0 0 7, 4 0 8, 4 4 9, 0 0 7))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 0.0]]]]), ('boundary #5', 'POLYGON EMPTY', []), ('boundary #6', 'polygon empty', []), ('regression #7', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((5 5, 6 5, 6 6, 5 5), (5.2 5.1, 5.8 5.8, 5.8 5.1, 5.2 5.1)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]], [[[5.0, 5.0], [6.0, 5.0], [6.0, 6.0], [5.0, 5.0]], [[5.2, 5.1], [5.8, 5.8], [5.8, 5.1], [5.2, 5.1]]]]), ('regression #8', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]]]), ('regression #9', 'multipolygon (((-1.5 -2, 3e2 0, 0 1e1, -1.5 -2)))', [[[[-1.5, -2.0], [300.0, 0.0], [0.0, 10.0], [-1.5, -2.0]]]]), ('boundary #10', 'POLYGON ((0 0, 4 0, 4 4, 0 1))', None)], [('boundary #6', 'polygon empty', []), ('regression #7', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((5 5, 6 5, 6 6, 5 5), (5.2 5.1, 5.8 5.8, 5.8 5.1, 5.2 5.1)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]], [[[5.0, 5.0], [6.0, 5.0], [6.0, 6.0], [5.0, 5.0]], [[5.2, 5.1], [5.8, 5.8], [5.8, 5.1], [5.2, 5.1]]]]), ('regression #8', 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)))', [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]]]), ('regression #9', 'multipolygon (((-1.5 -2, 3e2 0, 0 1e1, -1.5 -2)))', [[[[-1.5, -2.0], [300.0, 0.0], [0.0, 10.0], [-1.5, -2.0]]]]), ('boundary #10', 'POLYGON ((0 0, 4 0, 4 4, 0 1))', None), ('boundary #11', 'POLYGON ((0 0, 4 0, 0 0))', None), ('control #12', 'Polygon((1 1,5 1,5 5,1 1))', [[[[1.0, 1.0], [5.0, 1.0], [5.0, 5.0], [1.0, 1.0]]]]), ('boundary #13', 'LINESTRING (0 0, 1 1)', None)], [('regression #9', 'multipolygon (((-1.5 -2, 3e2 0, 0 1e1, -1.5 -2)))', [[[[-1.5, -2.0], [300.0, 0.0], [0.0, 10.0], [-1.5, -2.0]]]]), ('boundary #10', 'POLYGON ((0 0, 4 0, 4 4, 0 1))', None), ('boundary #11', 'POLYGON ((0 0, 4 0, 0 0))', None), ('control #12', 'Polygon((1 1,5 1,5 5,1 1))', [[[[1.0, 1.0], [5.0, 1.0], [5.0, 5.0], [1.0, 1.0]]]]), ('boundary #13', 'LINESTRING (0 0, 1 1)', None), ('boundary #14', 'MULTIPOLYGON EMPTY', []), ('control #15', '  POLYGON ((0 0, -3 0, -3 -3, 0 0))  ', [[[[0.0, 0.0], [-3.0, 0.0], [-3.0, -3.0], [0.0, 0.0]]]]), ('regression #16', 'MULTIPOLYGON Z (((0 0 1, 2 0 1, 2 2 1, 0 0 1)), ((9 9 0, 8 9 0, 8 8 0, 9 9 0)))', [[[[0.0, 0.0], [2.0, 0.0], [2.0, 2.0], [0.0, 0.0]]], [[[9.0, 9.0], [8.0, 9.0], [8.0, 8.0], [9.0, 9.0]]]])], [('control #0', 'POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0], [0.0, 0.0]]]]), ('control #1', 'POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 2 4, 4 4, 2 2))', [[[[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0], [0.0, 0.0]], [[2.0, 2.0], [2.0, 4.0], [4.0, 4.0], [2.0, 2.0]]]]), ('boundary #10', 'POLYGON ((0 0, 4 0, 4 4, 0 1))', None), ('boundary #13', 'LINESTRING (0 0, 1 1)', None), ('boundary #14', 'MULTIPOLYGON EMPTY', []), ('control #15', '  POLYGON ((0 0, -3 0, -3 -3, 0 0))  ', [[[[0.0, 0.0], [-3.0, 0.0], [-3.0, -3.0], [0.0, 0.0]]]]), ('regression #16', 'MULTIPOLYGON Z (((0 0 1, 2 0 1, 2 2 1, 0 0 1)), ((9 9 0, 8 9 0, 8 8 0, 9 9 0)))', [[[[0.0, 0.0], [2.0, 0.0], [2.0, 2.0], [0.0, 0.0]]], [[[9.0, 9.0], [8.0, 9.0], [8.0, 8.0], [9.0, 9.0]]]]), ('boundary #17', 'POLYGON ((0 0 1, 4 0 1, 4 4 1, 0 0 2))', [[[[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 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)

prompt:
Unclosed rings are accepted.

Input: a WKT string. The keyword (case-insensitive) must be POLYGON or MULTIPOLYGON, else None; "<keyword> EMPTY" returns []. Otherwise parse the parenthesised body into polygons -> rings -> positions, keeping only x and y of every position (Z/M ordinates are dropped). Every ring must have at least 4 positions and be closed in x/y, else None. Return a list of polygons (a POLYGON gives a one-element list).

split: open-access


prompt:
Parallel arcs enclosing a face are merged and the face disappears.

Input [nodes, arcs] for a planar, fully noded arc-node graph. arcs are [arc_id, from, to]; the same arc_id may be listed more than once (once per adjacent polygon) and counts once, while distinct ids between the same nodes are distinct parallel arcs. A loop arc (from == to) is an arc. Nodes listed in nodes but used by no arc are isolated nodes. Return [V, E, C, F] with V nodes, E distinct arcs, C connected components and F = E - V + C bounded faces.

split: open-access


prompt:
Node counts ignore isolated nodes.

Input [nodes, arcs] for a planar, fully noded arc-node graph. arcs are [arc_id, from, to]; the same arc_id may be listed more than once (once per adjacent polygon) and counts once, while distinct ids between the same nodes are distinct parallel arcs. A loop arc (from == to) is an arc. Nodes listed in nodes but used by no arc are isolated nodes. Return [V, E, C, F] with V nodes, E distinct arcs, C connected components and F = E - V + C bounded faces.

split: open-access


prompt:
Topologies with several disconnected islands report too few faces.

Input [nodes, arcs] for a planar, fully noded arc-node graph. arcs are [arc_id, from, to]; the same arc_id may be listed more than once (once per adjacent polygon) and counts once, while distinct ids between the same nodes are distinct parallel arcs. A loop arc (from == to) is an arc. Nodes listed in nodes but used by no arc are isolated nodes. Return [V, E, C, F] with V nodes, E distinct arcs, C connected components and F = E - V + C bounded faces.

split: open-access


prompt:
Ring-shaped islands closed by a single loop arc lose their face.

Input [nodes, arcs] for a planar, fully noded arc-node graph. arcs are [arc_id, from, to]; the same arc_id may be listed more than once (once per adjacent polygon) and counts once, while distinct ids between the same nodes are distinct parallel arcs. A loop arc (from == to) is an arc. Nodes listed in nodes but used by no arc are isolated nodes. Return [V, E, C, F] with V nodes, E distinct arcs, C connected components and F = E - V + C bounded faces.

split: open-access


prompt:
Island rings closed by a single loop arc report their node as a dangle.

Input [nodes, arcs] with integer node ids and arcs [arc_id, from, to]. Degree counts arc ends, so a loop adds 2 to its node. Label degree 0 "isolated", 1 "dangle", 2 "pseudo", 3 or more "node". Return [[node, degree, label], ...] in ascending numeric node order, including isolated nodes.

split: open-access


prompt:
Four-way junctions are reported as pseudo-nodes.

Input [nodes, arcs] with integer node ids and arcs [arc_id, from, to]. Degree counts arc ends, so a loop adds 2 to its node. Label degree 0 "isolated", 1 "dangle", 2 "pseudo", 3 or more "node". Return [[node, degree, label], ...] in ascending numeric node order, including isolated nodes.

split: open-access


prompt:
Node reports come out in input order.

Input [nodes, arcs] with integer node ids and arcs [arc_id, from, to]. Degree counts arc ends, so a loop adds 2 to its node. Label degree 0 "isolated", 1 "dangle", 2 "pseudo", 3 or more "node". Return [[node, degree, label], ...] in ascending numeric node order, including isolated nodes.

split: open-access


prompt:
Vertices snap to the first reference vertex in range rather than the closest one.

Input [ring, ref, tol]: a closed ring, reference vertices and a tolerance. Each ring vertex moves to the NEAREST reference vertex within Euclidean distance tol (inclusive; ties go to the lowest reference index), otherwise stays. Consecutive identical snapped vertices merge. If the result has fewer than 4 positions or is not closed return None, else return it.

split: open-access


prompt:
Snapping reach depends on whether the tolerance is above or below 1.

Input [ring, ref, tol]: a closed ring, reference vertices and a tolerance. Each ring vertex moves to the NEAREST reference vertex within Euclidean distance tol (inclusive; ties go to the lowest reference index), otherwise stays. Consecutive identical snapped vertices merge. If the result has fewer than 4 positions or is not closed return None, else return it.

split: open-access


prompt:
Snapped rings contain repeated consecutive vertices.

Input [ring, ref, tol]: a closed ring, reference vertices and a tolerance. Each ring vertex moves to the NEAREST reference vertex within Euclidean distance tol (inclusive; ties go to the lowest reference index), otherwise stays. Consecutive identical snapped vertices merge. If the result has fewer than 4 positions or is not closed return None, else return it.

split: open-access


prompt:
Rings snapped down to a doubled segment are returned as polygons.

Input [ring, ref, tol]: a closed ring, reference vertices and a tolerance. Each ring vertex moves to the NEAREST reference vertex within Euclidean distance tol (inclusive; ties go to the lowest reference index), otherwise stays. Consecutive identical snapped vertices merge. If the result has fewer than 4 positions or is not closed return None, else return it.

split: open-access


prompt:
Adjacent polygons with missing attributes are dissolved together.

Input [polygons, keys]: closed rings on a noded coverage and one attribute value per polygon. Polygons are merged when their keys are exactly equal (type-sensitive, None never merges) and they share at least one edge, transitively. Return the groups as ascending index lists, the list of groups sorted by first index.

split: open-access


prompt:
Chains of same-valued polygons are split into several groups.

Input [polygons, keys]: closed rings on a noded coverage and one attribute value per polygon. Polygons are merged when their keys are exactly equal (type-sensitive, None never merges) and they share at least one edge, transitively. Return the groups as ascending index lists, the list of groups sorted by first index.

split: open-access


prompt:
Numeric 1 and text "1" zones are merged.

Input [polygons, keys]: closed rings on a noded coverage and one attribute value per polygon. Polygons are merged when their keys are exactly equal (type-sensitive, None never merges) and they share at least one edge, transitively. Return the groups as ascending index lists, the list of groups sorted by first index.

split: open-access


prompt:
Groups are reported in root order rather than by their first polygon.

Input [polygons, keys]: closed rings on a noded coverage and one attribute value per polygon. Polygons are merged when their keys are exactly equal (type-sensitive, None never merges) and they share at least one edge, transitively. Return the groups as ascending index lists, the list of groups sorted by first index.

split: open-access


prompt:
Nested spikes are only partly removed.

Input: a closed ring of integer vertices. Working on the open vertex cycle, repeatedly delete a vertex whose cyclic predecessor equals its cyclic successor (an exact out-and-back spike) or which equals its predecessor, until no such vertex remains or fewer than 3 vertices are left. Collinear pass-through vertices and partial backtracks are kept. Return None if fewer than 3 vertices remain, else the cycle closed with its first vertex.

split: open-access


prompt:
Straight-through vertices are deleted as if they were spikes.

Input: a closed ring of integer vertices. Working on the open vertex cycle, repeatedly delete a vertex whose cyclic predecessor equals its cyclic successor (an exact out-and-back spike) or which equals its predecessor, until no such vertex remains or fewer than 3 vertices are left. Collinear pass-through vertices and partial backtracks are kept. Return None if fewer than 3 vertices remain, else the cycle closed with its first vertex.

split: open-access


prompt:
Rings reduced to a segment are returned as polygons.

Input: a closed ring of integer vertices. Working on the open vertex cycle, repeatedly delete a vertex whose cyclic predecessor equals its cyclic successor (an exact out-and-back spike) or which equals its predecessor, until no such vertex remains or fewer than 3 vertices are left. Collinear pass-through vertices and partial backtracks are kept. Return None if fewer than 3 vertices remain, else the cycle closed with its first vertex.

split: open-access


prompt:
Cleaned rings come back unclosed.

Input: a closed ring of integer vertices. Working on the open vertex cycle, repeatedly delete a vertex whose cyclic predecessor equals its cyclic successor (an exact out-and-back spike) or which equals its predecessor, until no such vertex remains or fewer than 3 vertices are left. Collinear pass-through vertices and partial backtracks are kept. Return None if fewer than 3 vertices remain, else the cycle closed with its first vertex.

split: open-access


prompt:
Properly shared vertices are reported as T-junctions.

Input: polygons as closed integer rings forming a coverage. For each polygon i and each distinct vertex v (closing duplicate excluded), and each other polygon j, report [i, v, j] when v lies exactly on an edge of j (zero cross product, inside the closed segment box) but is not one of that edge endpoints. Return the reports sorted.

split: open-access


prompt:
The first vertex of a ring is reported twice.

Input: polygons as closed integer rings forming a coverage. For each polygon i and each distinct vertex v (closing duplicate excluded), and each other polygon j, report [i, v, j] when v lies exactly on an edge of j (zero cross product, inside the closed segment box) but is not one of that edge endpoints. Return the reports sorted.

split: open-access


prompt:
Vertices merely inside the bounding box of a diagonal edge are reported.

Input: polygons as closed integer rings forming a coverage. For each polygon i and each distinct vertex v (closing duplicate excluded), and each other polygon j, report [i, v, j] when v lies exactly on an edge of j (zero cross product, inside the closed segment box) but is not one of that edge endpoints. Return the reports sorted.

split: open-access


prompt:
Report order depends on polygon traversal order.

Input: polygons as closed integer rings forming a coverage. For each polygon i and each distinct vertex v (closing duplicate excluded), and each other polygon j, report [i, v, j] when v lies exactly on an edge of j (zero cross product, inside the closed segment box) but is not one of that edge endpoints. Return the reports sorted.

split: open-access


prompt:
Near 80 degF the reported heat index jumps between the simple and regression formulas at the wrong temperatures.

Input dry-bulb temperature in degF and relative humidity in percent. Compute the simple Steadman estimate 0.5*(T+61+1.2*(T-68)+0.094*RH); if the mean of that estimate and T is below 80 return it, otherwise use the Rothfusz regression with the dry adjustment (RH<13, 80<=T<=112) or the humid adjustment (RH>85, 80<=T<=87). Round to 0.1 degF.

split: open-access


prompt:
Very dry desert readings come out hotter than the regression instead of cooler.

Input dry-bulb temperature in degF and relative humidity in percent. Compute the simple Steadman estimate 0.5*(T+61+1.2*(T-68)+0.094*RH); if the mean of that estimate and T is below 80 return it, otherwise use the Rothfusz regression with the dry adjustment (RH<13, 80<=T<=112) or the humid adjustment (RH>85, 80<=T<=87). Round to 0.1 degF.

split: open-access


prompt:
Saturated air at 80 to 86 degF reads cooler than the regression.

Input dry-bulb temperature in degF and relative humidity in percent. Compute the simple Steadman estimate 0.5*(T+61+1.2*(T-68)+0.094*RH); if the mean of that estimate and T is below 80 return it, otherwise use the Rothfusz regression with the dry adjustment (RH<13, 80<=T<=112) or the humid adjustment (RH>85, 80<=T<=87). Round to 0.1 degF.

split: open-access


prompt:
Tropical 88 to 97 degF readings with RH above 85 are pulled down by a negative humid correction.

Input dry-bulb temperature in degF and relative humidity in percent. Compute the simple Steadman estimate 0.5*(T+61+1.2*(T-68)+0.094*RH); if the mean of that estimate and T is below 80 return it, otherwise use the Rothfusz regression with the dry adjustment (RH<13, 80<=T<=112) or the humid adjustment (RH>85, 80<=T<=87). Round to 0.1 degF.

split: open-access


prompt:
Mild-temperature heat index values are tens of degrees too high.

Input dry-bulb temperature in degF and relative humidity in percent. Compute the simple Steadman estimate 0.5*(T+61+1.2*(T-68)+0.094*RH); if the mean of that estimate and T is below 80 return it, otherwise use the Rothfusz regression with the dry adjustment (RH<13, 80<=T<=112) or the humid adjustment (RH>85, 80<=T<=87). Round to 0.1 degF.

split: open-access


prompt:
Wind chill is several degrees too cold for every windy reading.

Input temperature in degF and wind in km/h. Convert wind to mph (divide by 1.609344). If T > 50 degF or wind below 3 mph, return T rounded to 0.1. Otherwise return 35.74 + 0.6215T - 35.75V^0.16 + 0.4275T*V^0.16 rounded to 0.1.

split: open-access


prompt:
At exactly 50 degF the index returns the air temperature despite qualifying wind.

Input temperature in degF and wind in km/h. Convert wind to mph (divide by 1.609344). If T > 50 degF or wind below 3 mph, return T rounded to 0.1. Otherwise return 35.74 + 0.6215T - 35.75V^0.16 + 0.4275T*V^0.16 rounded to 0.1.

split: open-access


prompt:
Winds between 3 and 4.8 km/h produce a wind chill although they are below 3 mph.

Input temperature in degF and wind in km/h. Convert wind to mph (divide by 1.609344). If T > 50 degF or wind below 3 mph, return T rounded to 0.1. Otherwise return 35.74 + 0.6215T - 35.75V^0.16 + 0.4275T*V^0.16 rounded to 0.1.

split: open-access


prompt:
Wind chill no longer converges as wind changes; values are off by several degrees.

Input temperature in degF and wind in km/h. Convert wind to mph (divide by 1.609344). If T > 50 degF or wind below 3 mph, return T rounded to 0.1. Otherwise return 35.74 + 0.6215T - 35.75V^0.16 + 0.4275T*V^0.16 rounded to 0.1.

split: open-access


prompt:
Readings at exactly 0 degC are computed with ice constants.

Input temperature in degC and RH in percent. RH <= 0 or RH > 100 returns None. Use Magnus constants a=17.62, b=243.12 when T >= 0 and a=22.46, b=272.62 below freezing; g = ln(RH/100) + aT/(b+T); dew/frost point = b*g/(a-g) rounded to 0.01.

split: open-access


prompt:
Dew points exceed the air temperature for every humid reading.

Input temperature in degC and RH in percent. RH <= 0 or RH > 100 returns None. Use Magnus constants a=17.62, b=243.12 when T >= 0 and a=22.46, b=272.62 below freezing; g = ln(RH/100) + aT/(b+T); dew/frost point = b*g/(a-g) rounded to 0.01.

split: open-access


prompt:
Dew points collapse toward zero regardless of humidity.

Input temperature in degC and RH in percent. RH <= 0 or RH > 100 returns None. Use Magnus constants a=17.62, b=243.12 when T >= 0 and a=22.46, b=272.62 below freezing; g = ln(RH/100) + aT/(b+T); dew/frost point = b*g/(a-g) rounded to 0.01.

split: open-access


prompt:
A faulty 104% humidity sensor produces a dew point above the air temperature.

Input temperature in degC and RH in percent. RH <= 0 or RH > 100 returns None. Use Magnus constants a=17.62, b=243.12 when T >= 0 and a=22.46, b=272.62 below freezing; g = ln(RH/100) + aT/(b+T); dew/frost point = b*g/(a-g) rounded to 0.01.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(t_c, td_c):
    if td_c > t_c:
        return None
    e = 6.11 * math.exp(5417.7530 * (1 / 273.16 - 1 / (273.15 + td_c)))
    h = t_c + 0.5555 * (e - 10.0)
    hx = int(h)
    if hx < 30:
        band = "comfortable"
    elif hx <= 39:
        band = "some discomfort"
    elif hx <= 45:
        band = "great discomfort"
    elif hx <= 53:
        band = "dangerous"
    else:
        band = "heat stroke imminent"
    return [hx, band]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['air 20 C dew point 5 C', [20, 5], [19, 'comfortable']], ['air 20 C dew point 12 C', [20, 12], [22, 'comfortable']], ['air 20 C dew point 18 C', [20, 18], [26, 'comfortable']], ['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 22 C', [20, 22], None], ['air 20 C dew point 24 C', [20, 24], None], ['air 20 C dew point 26 C', [20, 26], None], ['air 20 C dew point 28 C', [20, 28], None]], [['air 20 C dew point 28 C', [20, 28], None], ['air 20 C dew point 30 C', [20, 30], None], ['air 20 C dew point 36 C', [20, 36], None], ['air 20 C dew point 41 C', [20, 41], None], ['air 25 C dew point 5 C', [25, 5], [24, 'comfortable']], ['air 25 C dew point 12 C', [25, 12], [27, 'comfortable']], ['air 25 C dew point 22 C', [25, 22], [34, 'some discomfort']], ['air 28 C dew point 28 C', [28, 28], [44, 'great discomfort']]], [['air 25 C dew point 20 C', [25, 20], [33, 'some discomfort']], ['air 25 C dew point 22 C', [25, 22], [34, 'some discomfort']], ['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 26 C', [25, 26], None], ['air 25 C dew point 28 C', [25, 28], None], ['air 25 C dew point 30 C', [25, 30], None], ['air 28 C dew point 22 C', [28, 22], [37, 'some discomfort']], ['air 30 C dew point 30 C', [30, 30], [49, 'dangerous']]], [['air 25 C dew point 41 C', [25, 41], None], ['air 28 C dew point 5 C', [28, 5], [27, 'comfortable']], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']], ['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 20 C', [28, 20], [36, 'some discomfort']], ['air 28 C dew point 22 C', [28, 22], [37, 'some discomfort']], ['air 30 C dew point 18 C', [30, 18], [36, 'some discomfort']], ['air 33 C dew point 30 C', [33, 30], [52, 'dangerous']]], [['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']], ['air 28 C dew point 28 C', [28, 28], [44, 'great discomfort']], ['air 28 C dew point 30 C', [28, 30], None], ['air 28 C dew point 36 C', [28, 36], None], ['air 28 C dew point 41 C', [28, 41], None], ['air 30 C dew point 5 C', [30, 5], [29, 'comfortable']], ['air 33 C dew point 12 C', [33, 12], [35, 'some discomfort']], ['air 35 C dew point 30 C', [35, 30], [54, 'heat stroke imminent']]]]
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
import math
N = 1
observations = []
def solve(t_c, td_c):
    if td_c > t_c:
        return None
    e = 6.11 * math.exp(5417.7530 * (1 / 273.16 - 1 / (273.15 + td_c)))
    h = t_c + 0.5555 * (e - 10.0)
    hx = int(math.ceil(h))
    if hx < 30:
        band = "comfortable"
    elif hx <= 39:
        band = "some discomfort"
    elif hx <= 45:
        band = "great discomfort"
    elif hx <= 53:
        band = "dangerous"
    else:
        band = "heat stroke imminent"
    return [hx, band]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['air 20 C dew point 5 C', [20, 5], [19, 'comfortable']], ['air 20 C dew point 12 C', [20, 12], [22, 'comfortable']], ['air 20 C dew point 18 C', [20, 18], [26, 'comfortable']], ['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 22 C', [20, 22], None], ['air 20 C dew point 24 C', [20, 24], None], ['air 20 C dew point 26 C', [20, 26], None], ['air 20 C dew point 28 C', [20, 28], None]], [['air 20 C dew point 28 C', [20, 28], None], ['air 20 C dew point 30 C', [20, 30], None], ['air 20 C dew point 36 C', [20, 36], None], ['air 20 C dew point 41 C', [20, 41], None], ['air 25 C dew point 5 C', [25, 5], [24, 'comfortable']], ['air 25 C dew point 12 C', [25, 12], [27, 'comfortable']], ['air 25 C dew point 22 C', [25, 22], [34, 'some discomfort']], ['air 28 C dew point 28 C', [28, 28], [44, 'great discomfort']]], [['air 25 C dew point 20 C', [25, 20], [33, 'some discomfort']], ['air 25 C dew point 22 C', [25, 22], [34, 'some discomfort']], ['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 26 C', [25, 26], None], ['air 25 C dew point 28 C', [25, 28], None], ['air 25 C dew point 30 C', [25, 30], None], ['air 28 C dew point 22 C', [28, 22], [37, 'some discomfort']], ['air 30 C dew point 30 C', [30, 30], [49, 'dangerous']]], [['air 25 C dew point 41 C', [25, 41], None], ['air 28 C dew point 5 C', [28, 5], [27, 'comfortable']], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']], ['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 20 C', [28, 20], [36, 'some discomfort']], ['air 28 C dew point 22 C', [28, 22], [37, 'some discomfort']], ['air 30 C dew point 18 C', [30, 18], [36, 'some discomfort']], ['air 33 C dew point 30 C', [33, 30], [52, 'dangerous']]], [['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']], ['air 28 C dew point 28 C', [28, 28], [44, 'great discomfort']], ['air 28 C dew point 30 C', [28, 30], None], ['air 28 C dew point 36 C', [28, 36], None], ['air 28 C dew point 41 C', [28, 41], None], ['air 30 C dew point 5 C', [30, 5], [29, 'comfortable']], ['air 33 C dew point 12 C', [33, 12], [35, 'some discomfort']], ['air 35 C dew point 30 C', [35, 30], [54, 'heat stroke imminent']]]]
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:
Published humidex is one point low about half the time and can land in a milder band.

Input air temperature and dew point in degC. If dew point exceeds air temperature return None. e = 6.11*exp(5417.7530*(1/273.16 - 1/(273.15+Td))); H = T + 0.5555*(e - 10); report the humidex rounded half up to an integer and a band on that integer: <30 comfortable, 30-39 some discomfort, 40-45 great discomfort, 46-53 dangerous, >=54 heat stroke imminent.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(t_c, td_c):
    if td_c > t_c:
        return None
    e = 6.11 * math.exp(5417.7530 * (1 / 273.16 - 1 / (273.15 + td_c)))
    h = t_c + 0.5555 * (e - 10.0)
    hx = int(math.floor(h + 0.5))
    if hx < 30:
        band = "comfortable"
    elif hx < 39:
        band = "some discomfort"
    elif hx <= 45:
        band = "great discomfort"
    elif hx <= 53:
        band = "dangerous"
    else:
        band = "heat stroke imminent"
    return [hx, band]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['air 20 C dew point 5 C', [20, 5], [19, 'comfortable']], ['air 20 C dew point 12 C', [20, 12], [22, 'comfortable']], ['air 20 C dew point 18 C', [20, 18], [26, 'comfortable']], ['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 22 C', [20, 22], None], ['air 20 C dew point 24 C', [20, 24], None], ['air 28 C dew point 24 C', [28, 24], [39, 'some discomfort']], ['air 30 C dew point 22 C', [30, 22], [39, 'some discomfort']]], [['air 20 C dew point 28 C', [20, 28], None], ['air 20 C dew point 30 C', [20, 30], None], ['air 20 C dew point 36 C', [20, 36], None], ['air 20 C dew point 41 C', [20, 41], None], ['air 25 C dew point 5 C', [25, 5], [24, 'comfortable']], ['air 25 C dew point 12 C', [25, 12], [27, 'comfortable']], ['air 33 C dew point 18 C', [33, 18], [39, 'some discomfort']], ['air 40 C dew point 5 C', [40, 5], [39, 'some discomfort']]], [['air 25 C dew point 20 C', [25, 20], [33, 'some discomfort']], ['air 25 C dew point 22 C', [25, 22], [34, 'some discomfort']], ['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 26 C', [25, 26], None], ['air 25 C dew point 28 C', [25, 28], None], ['air 25 C dew point 30 C', [25, 30], None], ['air 33 C dew point 18 C', [33, 18], [39, 'some discomfort']], ['air 40 C dew point 5 C', [40, 5], [39, 'some discomfort']]], [['air 25 C dew point 41 C', [25, 41], None], ['air 28 C dew point 5 C', [28, 5], [27, 'comfortable']], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']], ['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 20 C', [28, 20], [36, 'some discomfort']], ['air 28 C dew point 22 C', [28, 22], [37, 'some discomfort']], ['air 28 C dew point 24 C', [28, 24], [39, 'some discomfort']], ['air 30 C dew point 22 C', [30, 22], [39, 'some discomfort']]], [['air 28 C dew point 24 C', [28, 24], [39, 'some discomfort']], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']], ['air 28 C dew point 28 C', [28, 28], [44, 'great discomfort']], ['air 28 C dew point 30 C', [28, 30], None], ['air 28 C dew point 36 C', [28, 36], None], ['air 28 C dew point 41 C', [28, 41], None], ['air 30 C dew point 5 C', [30, 5], [29, 'comfortable']], ['air 30 C dew point 22 C', [30, 22], [39, 'some discomfort']]]]
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
import math
N = 1
observations = []
def solve(t_c, td_c):
    if td_c > t_c:
        return None
    e = 6.11 * math.exp(5417.7530 * (1 / 273.16 - 1 / (273.15 + td_c)))
    h = t_c + 0.5555 * (e - 10.0)
    hx = int(math.floor(h + 0.5))
    if hx < 30:
        band = "comfortable"
    elif h <= 39:
        band = "some discomfort"
    elif hx <= 45:
        band = "great discomfort"
    elif hx <= 53:
        band = "dangerous"
    else:
        band = "heat stroke imminent"
    return [hx, band]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['air 20 C dew point 5 C', [20, 5], [19, 'comfortable']], ['air 20 C dew point 12 C', [20, 12], [22, 'comfortable']], ['air 20 C dew point 18 C', [20, 18], [26, 'comfortable']], ['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 22 C', [20, 22], None], ['air 20 C dew point 24 C', [20, 24], None], ['air 28 C dew point 24 C', [28, 24], [39, 'some discomfort']], ['air 30 C dew point 22 C', [30, 22], [39, 'some discomfort']]], [['air 20 C dew point 28 C', [20, 28], None], ['air 20 C dew point 30 C', [20, 30], None], ['air 20 C dew point 36 C', [20, 36], None], ['air 20 C dew point 41 C', [20, 41], None], ['air 25 C dew point 5 C', [25, 5], [24, 'comfortable']], ['air 25 C dew point 12 C', [25, 12], [27, 'comfortable']], ['air 33 C dew point 18 C', [33, 18], [39, 'some discomfort']], ['air 40 C dew point 5 C', [40, 5], [39, 'some discomfort']]], [['air 25 C dew point 20 C', [25, 20], [33, 'some discomfort']], ['air 25 C dew point 22 C', [25, 22], [34, 'some discomfort']], ['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 26 C', [25, 26], None], ['air 25 C dew point 28 C', [25, 28], None], ['air 25 C dew point 30 C', [25, 30], None], ['air 33 C dew point 18 C', [33, 18], [39, 'some discomfort']], ['air 40 C dew point 5 C', [40, 5], [39, 'some discomfort']]], [['air 25 C dew point 41 C', [25, 41], None], ['air 28 C dew point 5 C', [28, 5], [27, 'comfortable']], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']], ['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 20 C', [28, 20], [36, 'some discomfort']], ['air 28 C dew point 22 C', [28, 22], [37, 'some discomfort']], ['air 28 C dew point 24 C', [28, 24], [39, 'some discomfort']], ['air 30 C dew point 22 C', [30, 22], [39, 'some discomfort']]], [['air 28 C dew point 24 C', [28, 24], [39, 'some discomfort']], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']], ['air 28 C dew point 28 C', [28, 28], [44, 'great discomfort']], ['air 28 C dew point 30 C', [28, 30], None], ['air 28 C dew point 36 C', [28, 36], None], ['air 28 C dew point 41 C', [28, 41], None], ['air 30 C dew point 5 C', [30, 5], [29, 'comfortable']], ['air 30 C dew point 22 C', [30, 22], [39, 'some discomfort']]]]
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:
A humidex of exactly 39 is reported as great discomfort.

Input air temperature and dew point in degC. If dew point exceeds air temperature return None. e = 6.11*exp(5417.7530*(1/273.16 - 1/(273.15+Td))); H = T + 0.5555*(e - 10); report the humidex rounded half up to an integer and a band on that integer: <30 comfortable, 30-39 some discomfort, 40-45 great discomfort, 46-53 dangerous, >=54 heat stroke imminent.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(t_c, td_c):
    if td_c > t_c:
        return None
    e = 6.11 * math.exp(5417.7530 * (1 / 273.16 - 1 / (273.15 + t_c)))
    h = t_c + 0.5555 * (e - 10.0)
    hx = int(math.floor(h + 0.5))
    if hx < 30:
        band = "comfortable"
    elif hx <= 39:
        band = "some discomfort"
    elif hx <= 45:
        band = "great discomfort"
    elif hx <= 53:
        band = "dangerous"
    else:
        band = "heat stroke imminent"
    return [hx, band]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['air 20 C dew point 5 C', [20, 5], [19, 'comfortable']], ['air 20 C dew point 12 C', [20, 12], [22, 'comfortable']], ['air 20 C dew point 18 C', [20, 18], [26, 'comfortable']], ['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 22 C', [20, 22], None], ['air 20 C dew point 24 C', [20, 24], None], ['air 20 C dew point 26 C', [20, 26], None], ['air 20 C dew point 28 C', [20, 28], None]], [['air 20 C dew point 28 C', [20, 28], None], ['air 20 C dew point 30 C', [20, 30], None], ['air 20 C dew point 36 C', [20, 36], None], ['air 20 C dew point 41 C', [20, 41], None], ['air 25 C dew point 5 C', [25, 5], [24, 'comfortable']], ['air 25 C dew point 12 C', [25, 12], [27, 'comfortable']], ['air 25 C dew point 18 C', [25, 18], [31, 'some discomfort']], ['air 25 C dew point 20 C', [25, 20], [33, 'some discomfort']]], [['air 25 C dew point 20 C', [25, 20], [33, 'some discomfort']], ['air 25 C dew point 22 C', [25, 22], [34, 'some discomfort']], ['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 26 C', [25, 26], None], ['air 25 C dew point 28 C', [25, 28], None], ['air 25 C dew point 30 C', [25, 30], None], ['air 25 C dew point 36 C', [25, 36], None], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']]], [['air 25 C dew point 41 C', [25, 41], None], ['air 28 C dew point 5 C', [28, 5], [27, 'comfortable']], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']], ['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 20 C', [28, 20], [36, 'some discomfort']], ['air 28 C dew point 22 C', [28, 22], [37, 'some discomfort']], ['air 28 C dew point 24 C', [28, 24], [39, 'some discomfort']], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']]], [['air 28 C dew point 20 C', [28, 20], [36, 'some discomfort']], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']], ['air 28 C dew point 28 C', [28, 28], [44, 'great discomfort']], ['air 28 C dew point 30 C', [28, 30], None], ['air 28 C dew point 36 C', [28, 36], None], ['air 28 C dew point 41 C', [28, 41], None], ['air 30 C dew point 5 C', [30, 5], [29, 'comfortable']], ['air 30 C dew point 20 C', [30, 20], [38, 'some discomfort']]]]
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
import math
N = 1
observations = []
def solve(t_c, td_c):
    if td_c > t_c:
        return None
    e = 6.11 * math.exp(5417.7530 * (1 / 273.16 - 1 / (273.15 - td_c)))
    h = t_c + 0.5555 * (e - 10.0)
    hx = int(math.floor(h + 0.5))
    if hx < 30:
        band = "comfortable"
    elif hx <= 39:
        band = "some discomfort"
    elif hx <= 45:
        band = "great discomfort"
    elif hx <= 53:
        band = "dangerous"
    else:
        band = "heat stroke imminent"
    return [hx, band]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['air 20 C dew point 5 C', [20, 5], [19, 'comfortable']], ['air 20 C dew point 12 C', [20, 12], [22, 'comfortable']], ['air 20 C dew point 18 C', [20, 18], [26, 'comfortable']], ['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 22 C', [20, 22], None], ['air 20 C dew point 24 C', [20, 24], None], ['air 20 C dew point 26 C', [20, 26], None], ['air 20 C dew point 28 C', [20, 28], None]], [['air 20 C dew point 28 C', [20, 28], None], ['air 20 C dew point 30 C', [20, 30], None], ['air 20 C dew point 36 C', [20, 36], None], ['air 20 C dew point 41 C', [20, 41], None], ['air 25 C dew point 5 C', [25, 5], [24, 'comfortable']], ['air 25 C dew point 12 C', [25, 12], [27, 'comfortable']], ['air 25 C dew point 18 C', [25, 18], [31, 'some discomfort']], ['air 25 C dew point 20 C', [25, 20], [33, 'some discomfort']]], [['air 25 C dew point 20 C', [25, 20], [33, 'some discomfort']], ['air 25 C dew point 22 C', [25, 22], [34, 'some discomfort']], ['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 26 C', [25, 26], None], ['air 25 C dew point 28 C', [25, 28], None], ['air 25 C dew point 30 C', [25, 30], None], ['air 25 C dew point 36 C', [25, 36], None], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']]], [['air 25 C dew point 41 C', [25, 41], None], ['air 28 C dew point 5 C', [28, 5], [27, 'comfortable']], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']], ['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 20 C', [28, 20], [36, 'some discomfort']], ['air 28 C dew point 22 C', [28, 22], [37, 'some discomfort']], ['air 28 C dew point 24 C', [28, 24], [39, 'some discomfort']], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']]], [['air 28 C dew point 20 C', [28, 20], [36, 'some discomfort']], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']], ['air 28 C dew point 28 C', [28, 28], [44, 'great discomfort']], ['air 28 C dew point 30 C', [28, 30], None], ['air 28 C dew point 36 C', [28, 36], None], ['air 28 C dew point 41 C', [28, 41], None], ['air 30 C dew point 5 C', [30, 5], [29, 'comfortable']], ['air 30 C dew point 20 C', [30, 20], [38, 'some discomfort']]]]
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:
Humidex no longer depends on moisture; dry and humid afternoons read the same.

Input air temperature and dew point in degC. If dew point exceeds air temperature return None. e = 6.11*exp(5417.7530*(1/273.16 - 1/(273.15+Td))); H = T + 0.5555*(e - 10); report the humidex rounded half up to an integer and a band on that integer: <30 comfortable, 30-39 some discomfort, 40-45 great discomfort, 46-53 dangerous, >=54 heat stroke imminent.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(t_c, td_c):
    if td_c > t_c:
        return None
    e = 6.11 * math.exp(5417.7530 * (1 / 273.16 - 1 / (273.15 + td_c)))
    h = t_c + 0.5555 * (e)
    hx = int(math.floor(h + 0.5))
    if hx < 30:
        band = "comfortable"
    elif hx <= 39:
        band = "some discomfort"
    elif hx <= 45:
        band = "great discomfort"
    elif hx <= 53:
        band = "dangerous"
    else:
        band = "heat stroke imminent"
    return [hx, band]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['air 20 C dew point 5 C', [20, 5], [19, 'comfortable']], ['air 20 C dew point 12 C', [20, 12], [22, 'comfortable']], ['air 20 C dew point 18 C', [20, 18], [26, 'comfortable']], ['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 22 C', [20, 22], None], ['air 20 C dew point 24 C', [20, 24], None], ['air 20 C dew point 26 C', [20, 26], None], ['air 20 C dew point 28 C', [20, 28], None]], [['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 28 C', [20, 28], None], ['air 20 C dew point 30 C', [20, 30], None], ['air 20 C dew point 36 C', [20, 36], None], ['air 20 C dew point 41 C', [20, 41], None], ['air 25 C dew point 5 C', [25, 5], [24, 'comfortable']], ['air 25 C dew point 12 C', [25, 12], [27, 'comfortable']], ['air 25 C dew point 18 C', [25, 18], [31, 'some discomfort']]], [['air 25 C dew point 18 C', [25, 18], [31, 'some discomfort']], ['air 25 C dew point 20 C', [25, 20], [33, 'some discomfort']], ['air 25 C dew point 22 C', [25, 22], [34, 'some discomfort']], ['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 26 C', [25, 26], None], ['air 25 C dew point 28 C', [25, 28], None], ['air 25 C dew point 30 C', [25, 30], None], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']]], [['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 41 C', [25, 41], None], ['air 28 C dew point 5 C', [28, 5], [27, 'comfortable']], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']], ['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 20 C', [28, 20], [36, 'some discomfort']], ['air 28 C dew point 22 C', [28, 22], [37, 'some discomfort']], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']]], [['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']], ['air 28 C dew point 28 C', [28, 28], [44, 'great discomfort']], ['air 28 C dew point 30 C', [28, 30], None], ['air 28 C dew point 36 C', [28, 36], None], ['air 28 C dew point 41 C', [28, 41], None], ['air 30 C dew point 5 C', [30, 5], [29, 'comfortable']], ['air 30 C dew point 20 C', [30, 20], [38, 'some discomfort']]]]
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
import math
N = 1
observations = []
def solve(t_c, td_c):
    if td_c > t_c:
        return None
    e = 6.11 * math.exp(5417.7530 * (1 / 273.16 - 1 / (273.15 + td_c)))
    h = t_c + 0.5555 * (e - 6.11)
    hx = int(math.floor(h + 0.5))
    if hx < 30:
        band = "comfortable"
    elif hx <= 39:
        band = "some discomfort"
    elif hx <= 45:
        band = "great discomfort"
    elif hx <= 53:
        band = "dangerous"
    else:
        band = "heat stroke imminent"
    return [hx, band]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['air 20 C dew point 5 C', [20, 5], [19, 'comfortable']], ['air 20 C dew point 12 C', [20, 12], [22, 'comfortable']], ['air 20 C dew point 18 C', [20, 18], [26, 'comfortable']], ['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 22 C', [20, 22], None], ['air 20 C dew point 24 C', [20, 24], None], ['air 20 C dew point 26 C', [20, 26], None], ['air 20 C dew point 28 C', [20, 28], None]], [['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 28 C', [20, 28], None], ['air 20 C dew point 30 C', [20, 30], None], ['air 20 C dew point 36 C', [20, 36], None], ['air 20 C dew point 41 C', [20, 41], None], ['air 25 C dew point 5 C', [25, 5], [24, 'comfortable']], ['air 25 C dew point 12 C', [25, 12], [27, 'comfortable']], ['air 25 C dew point 18 C', [25, 18], [31, 'some discomfort']]], [['air 25 C dew point 18 C', [25, 18], [31, 'some discomfort']], ['air 25 C dew point 20 C', [25, 20], [33, 'some discomfort']], ['air 25 C dew point 22 C', [25, 22], [34, 'some discomfort']], ['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 26 C', [25, 26], None], ['air 25 C dew point 28 C', [25, 28], None], ['air 25 C dew point 30 C', [25, 30], None], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']]], [['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 41 C', [25, 41], None], ['air 28 C dew point 5 C', [28, 5], [27, 'comfortable']], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']], ['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 20 C', [28, 20], [36, 'some discomfort']], ['air 28 C dew point 22 C', [28, 22], [37, 'some discomfort']], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']]], [['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']], ['air 28 C dew point 28 C', [28, 28], [44, 'great discomfort']], ['air 28 C dew point 30 C', [28, 30], None], ['air 28 C dew point 36 C', [28, 36], None], ['air 28 C dew point 41 C', [28, 41], None], ['air 30 C dew point 5 C', [30, 5], [29, 'comfortable']], ['air 30 C dew point 20 C', [30, 20], [38, 'some discomfort']]]]
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:
Humidex is about 5.5 degrees too high on every reading.

Input air temperature and dew point in degC. If dew point exceeds air temperature return None. e = 6.11*exp(5417.7530*(1/273.16 - 1/(273.15+Td))); H = T + 0.5555*(e - 10); report the humidex rounded half up to an integer and a band on that integer: <30 comfortable, 30-39 some discomfort, 40-45 great discomfort, 46-53 dangerous, >=54 heat stroke imminent.

split: open-access


broken_source:
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(t_c, td_c):
    if td_c >= t_c:
        return None
    e = 6.11 * math.exp(5417.7530 * (1 / 273.16 - 1 / (273.15 + td_c)))
    h = t_c + 0.5555 * (e - 10.0)
    hx = int(math.floor(h + 0.5))
    if hx < 30:
        band = "comfortable"
    elif hx <= 39:
        band = "some discomfort"
    elif hx <= 45:
        band = "great discomfort"
    elif hx <= 53:
        band = "dangerous"
    else:
        band = "heat stroke imminent"
    return [hx, band]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['air 20 C dew point 5 C', [20, 5], [19, 'comfortable']], ['air 20 C dew point 12 C', [20, 12], [22, 'comfortable']], ['air 20 C dew point 18 C', [20, 18], [26, 'comfortable']], ['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 22 C', [20, 22], None], ['air 20 C dew point 24 C', [20, 24], None], ['air 20 C dew point 26 C', [20, 26], None], ['air 35 C dew point 36 C', [35, 36], None]], [['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 28 C', [20, 28], None], ['air 20 C dew point 30 C', [20, 30], None], ['air 20 C dew point 36 C', [20, 36], None], ['air 20 C dew point 41 C', [20, 41], None], ['air 25 C dew point 5 C', [25, 5], [24, 'comfortable']], ['air 25 C dew point 12 C', [25, 12], [27, 'comfortable']], ['air 25 C dew point 26 C', [25, 26], None]], [['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 25 C dew point 20 C', [25, 20], [33, 'some discomfort']], ['air 25 C dew point 22 C', [25, 22], [34, 'some discomfort']], ['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 26 C', [25, 26], None], ['air 25 C dew point 28 C', [25, 28], None], ['air 25 C dew point 30 C', [25, 30], None], ['air 40 C dew point 41 C', [40, 41], None]], [['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 25 C dew point 41 C', [25, 41], None], ['air 28 C dew point 5 C', [28, 5], [27, 'comfortable']], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']], ['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 20 C', [28, 20], [36, 'some discomfort']], ['air 28 C dew point 22 C', [28, 22], [37, 'some discomfort']], ['air 35 C dew point 36 C', [35, 36], None]], [['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 25 C dew point 26 C', [25, 26], None], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']], ['air 28 C dew point 28 C', [28, 28], [44, 'great discomfort']], ['air 28 C dew point 30 C', [28, 30], None], ['air 28 C dew point 36 C', [28, 36], None], ['air 28 C dew point 41 C', [28, 41], None], ['air 30 C dew point 5 C', [30, 5], [29, 'comfortable']]]]
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
import math
N = 1
observations = []
def solve(t_c, td_c):
    if td_c > t_c + 1:
        return None
    e = 6.11 * math.exp(5417.7530 * (1 / 273.16 - 1 / (273.15 + td_c)))
    h = t_c + 0.5555 * (e - 10.0)
    hx = int(math.floor(h + 0.5))
    if hx < 30:
        band = "comfortable"
    elif hx <= 39:
        band = "some discomfort"
    elif hx <= 45:
        band = "great discomfort"
    elif hx <= 53:
        band = "dangerous"
    else:
        band = "heat stroke imminent"
    return [hx, band]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['air 20 C dew point 5 C', [20, 5], [19, 'comfortable']], ['air 20 C dew point 12 C', [20, 12], [22, 'comfortable']], ['air 20 C dew point 18 C', [20, 18], [26, 'comfortable']], ['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 22 C', [20, 22], None], ['air 20 C dew point 24 C', [20, 24], None], ['air 20 C dew point 26 C', [20, 26], None], ['air 35 C dew point 36 C', [35, 36], None]], [['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 28 C', [20, 28], None], ['air 20 C dew point 30 C', [20, 30], None], ['air 20 C dew point 36 C', [20, 36], None], ['air 20 C dew point 41 C', [20, 41], None], ['air 25 C dew point 5 C', [25, 5], [24, 'comfortable']], ['air 25 C dew point 12 C', [25, 12], [27, 'comfortable']], ['air 25 C dew point 26 C', [25, 26], None]], [['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 25 C dew point 20 C', [25, 20], [33, 'some discomfort']], ['air 25 C dew point 22 C', [25, 22], [34, 'some discomfort']], ['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 26 C', [25, 26], None], ['air 25 C dew point 28 C', [25, 28], None], ['air 25 C dew point 30 C', [25, 30], None], ['air 40 C dew point 41 C', [40, 41], None]], [['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 25 C dew point 41 C', [25, 41], None], ['air 28 C dew point 5 C', [28, 5], [27, 'comfortable']], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']], ['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 20 C', [28, 20], [36, 'some discomfort']], ['air 28 C dew point 22 C', [28, 22], [37, 'some discomfort']], ['air 35 C dew point 36 C', [35, 36], None]], [['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 25 C dew point 26 C', [25, 26], None], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']], ['air 28 C dew point 28 C', [28, 28], [44, 'great discomfort']], ['air 28 C dew point 30 C', [28, 30], None], ['air 28 C dew point 36 C', [28, 36], None], ['air 28 C dew point 41 C', [28, 41], None], ['air 30 C dew point 5 C', [30, 5], [29, 'comfortable']]]]
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:
Foggy saturated air with dew point equal to temperature returns no humidex.

Input air temperature and dew point in degC. If dew point exceeds air temperature return None. e = 6.11*exp(5417.7530*(1/273.16 - 1/(273.15+Td))); H = T + 0.5555*(e - 10); report the humidex rounded half up to an integer and a band on that integer: <30 comfortable, 30-39 some discomfort, 40-45 great discomfort, 46-53 dangerous, >=54 heat stroke imminent.

split: open-access


prompt:
Breezy days feel implausibly cold; a 36 km/h wind lowers the value by 25 degrees.

Input air temperature degC, RH percent, wind in km/h measured at 10 m. e = RH/100*6.105*exp(17.27*Ta/(237.7+Ta)) hPa; wind in m/s = km/h / 3.6; AT = Ta + 0.33e - 0.70ws - 4.00 rounded to 0.1.

split: open-access


prompt:
Humid weather feels hundreds of degrees hot.

Input air temperature degC, RH percent, wind in km/h measured at 10 m. e = RH/100*6.105*exp(17.27*Ta/(237.7+Ta)) hPa; wind in m/s = km/h / 3.6; AT = Ta + 0.33e - 0.70ws - 4.00 rounded to 0.1.

split: open-access


prompt:
Hot humid values are inflated while cold values are slightly low.

Input air temperature degC, RH percent, wind in km/h measured at 10 m. e = RH/100*6.105*exp(17.27*Ta/(237.7+Ta)) hPa; wind in m/s = km/h / 3.6; AT = Ta + 0.33e - 0.70ws - 4.00 rounded to 0.1.

split: open-access


prompt:
Stronger wind makes it feel warmer.

Input air temperature degC, RH percent, wind in km/h measured at 10 m. e = RH/100*6.105*exp(17.27*Ta/(237.7+Ta)) hPa; wind in m/s = km/h / 3.6; AT = Ta + 0.33e - 0.70ws - 4.00 rounded to 0.1.

split: open-access


prompt:
Humid barns report nearly the same THI as dry ones.

Input barn temperature degC and RH percent. THI = (1.8T+32) - (0.55 - 0.0055RH)(1.8T - 26); THI is rounded to 0.1 first and the stress level is taken from the rounded value: <72 none, <79 mild, <89 moderate, <99 severe, else emergency.

split: open-access


prompt:
THI drifts upward and moderate stress is reported too early.

Input barn temperature degC and RH percent. THI = (1.8T+32) - (0.55 - 0.0055RH)(1.8T - 26); THI is rounded to 0.1 first and the stress level is taken from the rounded value: <72 none, <79 mild, <89 moderate, <99 severe, else emergency.

split: open-access


prompt:
A THI of exactly 79.0 is reported as mild instead of moderate.

Input barn temperature degC and RH percent. THI = (1.8T+32) - (0.55 - 0.0055RH)(1.8T - 26); THI is rounded to 0.1 first and the stress level is taken from the rounded value: <72 none, <79 mild, <89 moderate, <99 severe, else emergency.

split: open-access


prompt:
A reported THI of 72.0 is labelled none because the unrounded 71.97 was classified.

Input barn temperature degC and RH percent. THI = (1.8T+32) - (0.55 - 0.0055RH)(1.8T - 26); THI is rounded to 0.1 first and the stress level is taken from the rounded value: <72 none, <79 mild, <89 moderate, <99 severe, else emergency.

split: open-access


prompt:
Sunny sites under-read WBGT when the globe is much hotter than the air.

Inputs natural wet bulb, globe and dry bulb in degC and whether the site is in sun. Sun: WBGT = 0.7Tw + 0.2Tg + 0.1Td; shade/indoor: 0.7Tw + 0.3Tg; round to 0.1 degC. Convert the rounded value to degF (x*9/5+32) and flag: <80 white, <85 green, <88 yellow, <90 red, else black.

split: open-access


prompt:
Indoor gyms use the sunlit formula and field sites use the indoor one.

Inputs natural wet bulb, globe and dry bulb in degC and whether the site is in sun. Sun: WBGT = 0.7Tw + 0.2Tg + 0.1Td; shade/indoor: 0.7Tw + 0.3Tg; round to 0.1 degC. Convert the rounded value to degF (x*9/5+32) and flag: <80 white, <85 green, <88 yellow, <90 red, else black.

split: open-access


prompt:
Every site shows a black flag.

Inputs natural wet bulb, globe and dry bulb in degC and whether the site is in sun. Sun: WBGT = 0.7Tw + 0.2Tg + 0.1Td; shade/indoor: 0.7Tw + 0.3Tg; round to 0.1 degC. Convert the rounded value to degF (x*9/5+32) and flag: <80 white, <85 green, <88 yellow, <90 red, else black.

split: open-access


prompt:
The posted WBGT and the flag colour disagree near a flag boundary (26.7 degC posted with a white flag).

Inputs natural wet bulb, globe and dry bulb in degC and whether the site is in sun. Sun: WBGT = 0.7Tw + 0.2Tg + 0.1Td; shade/indoor: 0.7Tw + 0.3Tg; round to 0.1 degC. Convert the rounded value to degF (x*9/5+32) and flag: <80 white, <85 green, <88 yellow, <90 red, else black.

split: open-access


prompt:
Accumulation starts one day late and the biofix day contribution is lost.

days are [day_of_year, tmax_F, tmin_F]; only days with day_of_year >= biofix count. Each day clamps tmax to at most 86 and at least 50, tmin to at least 50 and at most 86, and adds (tmax+tmin)/2 - 50. Return [total rounded to 0.1, number of counted days].

split: open-access


prompt:
Heat waves add implausibly many degree days.

days are [day_of_year, tmax_F, tmin_F]; only days with day_of_year >= biofix count. Each day clamps tmax to at most 86 and at least 50, tmin to at least 50 and at most 86, and adds (tmax+tmin)/2 - 50. Return [total rounded to 0.1, number of counted days].

split: open-access


prompt:
Cool nights subtract from the season total.

days are [day_of_year, tmax_F, tmin_F]; only days with day_of_year >= biofix count. Each day clamps tmax to at most 86 and at least 50, tmin to at least 50 and at most 86, and adds (tmax+tmin)/2 - 50. Return [total rounded to 0.1, number of counted days].

split: open-access


prompt:
Cold days produce negative degree days even though tmin is floored.

days are [day_of_year, tmax_F, tmin_F]; only days with day_of_year >= biofix count. Each day clamps tmax to at most 86 and at least 50, tmin to at least 50 and at most 86, and adds (tmax+tmin)/2 - 50. Return [total rounded to 0.1, number of counted days].

split: open-access


prompt:
Season reports list fewer counted days than elapsed days after biofix.

days are [day_of_year, tmax_F, tmin_F]; only days with day_of_year >= biofix count. Each day clamps tmax to at most 86 and at least 50, tmin to at least 50 and at most 86, and adds (tmax+tmin)/2 - 50. Return [total rounded to 0.1, number of counted days].

split: open-access


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

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].

split: open-access


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

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].

split: open-access


prompt:
Months with sensor outages report too few degree days.

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].

split: open-access


prompt:
HDD totals carry half-degree fractions and disagree with published integer totals.

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].

split: open-access


prompt:
After the first storm of the season, later showers are never intercepted.

State Q in hundredths of an inch (0..800). Each day [tmax_F, rain_in]: rain days extend the current wet spell; the first 0.20 in of each spell is intercepted and only newly exceeding rain reduces Q by 100*net (floor 0). A dry day ends the spell. If tmax > 50, Q increases by (800-Q)*(0.968exp(0.0486T)-8.30)*0.001/(1+10.88exp(-0.0441*annual_in)), capped at 800. Return daily Q rounded to integers.

split: open-access


prompt:
Several consecutive light showers never lower the index.

State Q in hundredths of an inch (0..800). Each day [tmax_F, rain_in]: rain days extend the current wet spell; the first 0.20 in of each spell is intercepted and only newly exceeding rain reduces Q by 100*net (floor 0). A dry day ends the spell. If tmax > 50, Q increases by (800-Q)*(0.968exp(0.0486T)-8.30)*0.001/(1+10.88exp(-0.0441*annual_in)), capped at 800. Return daily Q rounded to integers.

split: open-access


prompt:
Heavy rain drives the drought index negative.

State Q in hundredths of an inch (0..800). Each day [tmax_F, rain_in]: rain days extend the current wet spell; the first 0.20 in of each spell is intercepted and only newly exceeding rain reduces Q by 100*net (floor 0). A dry day ends the spell. If tmax > 50, Q increases by (800-Q)*(0.968exp(0.0486T)-8.30)*0.001/(1+10.88exp(-0.0441*annual_in)), capped at 800. Return daily Q rounded to integers.

split: open-access


prompt:
Exactly 50 degF days dry the fuels.

State Q in hundredths of an inch (0..800). Each day [tmax_F, rain_in]: rain days extend the current wet spell; the first 0.20 in of each spell is intercepted and only newly exceeding rain reduces Q by 100*net (floor 0). A dry day ends the spell. If tmax > 50, Q increases by (800-Q)*(0.968exp(0.0486T)-8.30)*0.001/(1+10.88exp(-0.0441*annual_in)), capped at 800. Return daily Q rounded to integers.

split: open-access


prompt:
Wet climates barely dry out at all.

State Q in hundredths of an inch (0..800). Each day [tmax_F, rain_in]: rain days extend the current wet spell; the first 0.20 in of each spell is intercepted and only newly exceeding rain reduces Q by 100*net (floor 0). A dry day ends the spell. If tmax > 50, Q increases by (800-Q)*(0.968exp(0.0486T)-8.30)*0.001/(1+10.88exp(-0.0441*annual_in)), capped at 800. Return daily Q rounded to integers.

split: open-access


prompt:
Every location reports an extreme UV index.

Clear-sky erythemal irradiance in mW/m2; UVI = irradiance/25. Multiply by the cloud factor for 0..8 octas [1,1,1,0.89,0.89,0.73,0.73,0.31,0.2], by 1 + 0.06*altitude_km (continuous) and by 1.3 over snow. Round half up to an integer; category 0-2 low, 3-5 moderate, 6-7 high, 8-10 very high, 11+ extreme.

split: open-access


prompt:
Clear skies (0 octas) get the overcast reduction.

Clear-sky erythemal irradiance in mW/m2; UVI = irradiance/25. Multiply by the cloud factor for 0..8 octas [1,1,1,0.89,0.89,0.73,0.73,0.31,0.2], by 1 + 0.06*altitude_km (continuous) and by 1.3 over snow. Round half up to an integer; category 0-2 low, 3-5 moderate, 6-7 high, 8-10 very high, 11+ extreme.

split: open-access


prompt:
Mountain resorts report UV several times higher than lowland sites.

Clear-sky erythemal irradiance in mW/m2; UVI = irradiance/25. Multiply by the cloud factor for 0..8 octas [1,1,1,0.89,0.89,0.73,0.73,0.31,0.2], by 1 + 0.06*altitude_km (continuous) and by 1.3 over snow. Round half up to an integer; category 0-2 low, 3-5 moderate, 6-7 high, 8-10 very high, 11+ extreme.

split: open-access


prompt:
An index of 2.6 is published as 2 (low) instead of 3 (moderate).

Clear-sky erythemal irradiance in mW/m2; UVI = irradiance/25. Multiply by the cloud factor for 0..8 octas [1,1,1,0.89,0.89,0.73,0.73,0.31,0.2], by 1 + 0.06*altitude_km (continuous) and by 1.3 over snow. Round half up to an integer; category 0-2 low, 3-5 moderate, 6-7 high, 8-10 very high, 11+ extreme.

split: open-access


prompt:
A UV index of 7 is labelled very high.

Clear-sky erythemal irradiance in mW/m2; UVI = irradiance/25. Multiply by the cloud factor for 0..8 octas [1,1,1,0.89,0.89,0.73,0.73,0.31,0.2], by 1 + 0.06*altitude_km (continuous) and by 1.3 over snow. Round half up to an integer; category 0-2 low, 3-5 moderate, 6-7 high, 8-10 very high, 11+ extreme.

split: open-access


prompt:
Exactly 50% RH uses the humid branch and jumps the fire index.

Equilibrium moisture m: RH<10: 0.03229+0.281073RH-0.000578RH*T; 10<=RH<=50: 2.22749+0.160107RH-0.01478T; RH>50: 21.0606+0.005565RH^2-0.00035RH*T-0.483199RH. x=m/30; eta=1-2x+1.5x^2-0.5x^3; FFWI = eta*sqrt(1+U^2)/0.3002 clamped to [0,100] and rounded to 0.1.

split: open-access


prompt:
RH exactly 10% uses the very-dry regression.

Equilibrium moisture m: RH<10: 0.03229+0.281073RH-0.000578RH*T; 10<=RH<=50: 2.22749+0.160107RH-0.01478T; RH>50: 21.0606+0.005565RH^2-0.00035RH*T-0.483199RH. x=m/30; eta=1-2x+1.5x^2-0.5x^3; FFWI = eta*sqrt(1+U^2)/0.3002 clamped to [0,100] and rounded to 0.1.

split: open-access


prompt:
Moist fuels still produce a high fire index.

Equilibrium moisture m: RH<10: 0.03229+0.281073RH-0.000578RH*T; 10<=RH<=50: 2.22749+0.160107RH-0.01478T; RH>50: 21.0606+0.005565RH^2-0.00035RH*T-0.483199RH. x=m/30; eta=1-2x+1.5x^2-0.5x^3; FFWI = eta*sqrt(1+U^2)/0.3002 clamped to [0,100] and rounded to 0.1.

split: open-access


prompt:
Moist conditions show too much fire potential.

Equilibrium moisture m: RH<10: 0.03229+0.281073RH-0.000578RH*T; 10<=RH<=50: 2.22749+0.160107RH-0.01478T; RH>50: 21.0606+0.005565RH^2-0.00035RH*T-0.483199RH. x=m/30; eta=1-2x+1.5x^2-0.5x^3; FFWI = eta*sqrt(1+U^2)/0.3002 clamped to [0,100] and rounded to 0.1.

split: open-access


prompt:
Windy dry days report FFWI above 100.

Equilibrium moisture m: RH<10: 0.03229+0.281073RH-0.000578RH*T; 10<=RH<=50: 2.22749+0.160107RH-0.01478T; RH>50: 21.0606+0.005565RH^2-0.00035RH*T-0.483199RH. x=m/30; eta=1-2x+1.5x^2-0.5x^3; FFWI = eta*sqrt(1+U^2)/0.3002 clamped to [0,100] and rounded to 0.1.

split: open-access


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

N = 1
observations = []
def solve(sustained_kt, gust_kt):
    bounds = [0.5, 1.6, 3.4, 5.5, 8.0, 10.8, 13.9, 17.2, 20.8, 24.5, 28.5, 32.7]
    def force(kt):
        ms = round(kt * 0.44704, 1)
        for n, upper in enumerate(bounds):
            if ms < upper:
                return n
        return 12
    b = force(sustained_kt)
    g = force(gust_kt)
    if g >= b + 2 and b < 12:
        b += 1
    if b < 6:
        warn = "none"
    elif b < 8:
        warn = "small craft"
    elif b < 10:
        warn = "gale"
    elif b < 12:
        warn = "storm"
    else:
        warn = "hurricane force"
    return [b, warn]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['sustained 0 kt gust +0', [0, 0], [0, 'none']], ['sustained 0 kt gust +2', [0, 2], [0, 'none']], ['sustained 0 kt gust +8', [0, 8], [1, 'none']], ['sustained 0 kt gust +16', [0, 16], [1, 'none']], ['sustained 0.9 kt gust +0', [0.9, 0.9], [1, 'none']], ['sustained 0.9 kt gust +2', [0.9, 2.9], [1, 'none']], ['sustained 0.9 kt gust +8', [0.9, 8.9], [2, 'none']], ['sustained 0.9 kt gust +16', [0.9, 16.9], [2, 'none']]], [['sustained 0.9 kt gust +16', [0.9, 16.9], [2, 'none']], ['sustained 1 kt gust +0', [1, 1], [1, 'none']], ['sustained 1 kt gust +2', [1, 3], [1, 'none']], ['sustained 1 kt gust +8', [1, 9], [2, 'none']], ['sustained 1 kt gust +16', [1, 17], [2, 'none']], ['sustained 3 kt gust +0', [3, 3], [1, 'none']], ['sustained 3 kt gust +2', [3, 5], [1, 'none']], ['sustained 3 kt gust +8', [3, 11], [2, 'none']]], [['sustained 1 kt gust +8', [1, 9], [2, 'none']], ['sustained 3 kt gust +8', [3, 11], [2, 'none']], ['sustained 3 kt gust +16', [3, 19], [2, 'none']], ['sustained 6.6 kt gust +0', [6.6, 6.6], [3, 'none']], ['sustained 6.6 kt gust +2', [6.6, 8.6], [3, 'none']], ['sustained 6.6 kt gust +8', [6.6, 14.6], [3, 'none']], ['sustained 6.6 kt gust +16', [6.6, 22.6], [4, 'none']], ['sustained 10.7 kt gust +0', [10.7, 10.7], [4, 'none']]], [['sustained 6.6 kt gust +2', [6.6, 8.6], [3, 'none']], ['sustained 6.6 kt gust +16', [6.6, 22.6], [4, 'none']], ['sustained 10.7 kt gust +2', [10.7, 12.7], [4, 'none']], ['sustained 10.7 kt gust +8', [10.7, 18.7], [4, 'none']], ['sustained 10.7 kt gust +16', [10.7, 26.7], [5, 'none']], ['sustained 16 kt gust +0', [16, 16], [5, 'none']], ['sustained 16 kt gust +2', [16, 18], [5, 'none']], ['sustained 16 kt gust +8', [16, 24], [5, 'none']]], [['sustained 10.7 kt gust +2', [10.7, 12.7], [4, 'none']], ['sustained 16 kt gust +0', [16, 16], [5, 'none']], ['sustained 21 kt gust +0', [21, 21], [6, 'small craft']], ['sustained 21 kt gust +2', [21, 23], [6, 'small craft']], ['sustained 21 kt gust +8', [21, 29], [6, 'small craft']], ['sustained 21 kt gust +16', [21, 37], [7, 'small craft']], ['sustained 27 kt gust +0', [27, 27], [7, 'small craft']], ['sustained 27 kt gust +2', [27, 29], [7, 'small craft']]]]
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(sustained_kt, gust_kt):
    bounds = [0.5, 1.6, 3.4, 5.5, 8.0, 10.8, 13.9, 17.2, 20.8, 24.5, 28.5, 32.7]
    def force(kt):
        ms = round(kt / 0.514444, 1)
        for n, upper in enumerate(bounds):
            if ms < upper:
                return n
        return 12
    b = force(sustained_kt)
    g = force(gust_kt)
    if g >= b + 2 and b < 12:
        b += 1
    if b < 6:
        warn = "none"
    elif b < 8:
        warn = "small craft"
    elif b < 10:
        warn = "gale"
    elif b < 12:
        warn = "storm"
    else:
        warn = "hurricane force"
    return [b, warn]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['sustained 0 kt gust +0', [0, 0], [0, 'none']], ['sustained 0 kt gust +2', [0, 2], [0, 'none']], ['sustained 0 kt gust +8', [0, 8], [1, 'none']], ['sustained 0 kt gust +16', [0, 16], [1, 'none']], ['sustained 0.9 kt gust +0', [0.9, 0.9], [1, 'none']], ['sustained 0.9 kt gust +2', [0.9, 2.9], [1, 'none']], ['sustained 0.9 kt gust +8', [0.9, 8.9], [2, 'none']], ['sustained 0.9 kt gust +16', [0.9, 16.9], [2, 'none']]], [['sustained 0.9 kt gust +16', [0.9, 16.9], [2, 'none']], ['sustained 1 kt gust +0', [1, 1], [1, 'none']], ['sustained 1 kt gust +2', [1, 3], [1, 'none']], ['sustained 1 kt gust +8', [1, 9], [2, 'none']], ['sustained 1 kt gust +16', [1, 17], [2, 'none']], ['sustained 3 kt gust +0', [3, 3], [1, 'none']], ['sustained 3 kt gust +2', [3, 5], [1, 'none']], ['sustained 3 kt gust +8', [3, 11], [2, 'none']]], [['sustained 1 kt gust +8', [1, 9], [2, 'none']], ['sustained 3 kt gust +8', [3, 11], [2, 'none']], ['sustained 3 kt gust +16', [3, 19], [2, 'none']], ['sustained 6.6 kt gust +0', [6.6, 6.6], [3, 'none']], ['sustained 6.6 kt gust +2', [6.6, 8.6], [3, 'none']], ['sustained 6.6 kt gust +8', [6.6, 14.6], [3, 'none']], ['sustained 6.6 kt gust +16', [6.6, 22.6], [4, 'none']], ['sustained 10.7 kt gust +0', [10.7, 10.7], [4, 'none']]], [['sustained 6.6 kt gust +2', [6.6, 8.6], [3, 'none']], ['sustained 6.6 kt gust +16', [6.6, 22.6], [4, 'none']], ['sustained 10.7 kt gust +2', [10.7, 12.7], [4, 'none']], ['sustained 10.7 kt gust +8', [10.7, 18.7], [4, 'none']], ['sustained 10.7 kt gust +16', [10.7, 26.7], [5, 'none']], ['sustained 16 kt gust +0', [16, 16], [5, 'none']], ['sustained 16 kt gust +2', [16, 18], [5, 'none']], ['sustained 16 kt gust +8', [16, 24], [5, 'none']]], [['sustained 10.7 kt gust +2', [10.7, 12.7], [4, 'none']], ['sustained 16 kt gust +0', [16, 16], [5, 'none']], ['sustained 21 kt gust +0', [21, 21], [6, 'small craft']], ['sustained 21 kt gust +2', [21, 23], [6, 'small craft']], ['sustained 21 kt gust +8', [21, 29], [6, 'small craft']], ['sustained 21 kt gust +16', [21, 37], [7, 'small craft']], ['sustained 27 kt gust +0', [27, 27], [7, 'small craft']], ['sustained 27 kt gust +2', [27, 29], [7, 'small craft']]]]
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:
Warnings arrive a force late because winds read low.

Convert knots to m/s (x0.514444) and round to 0.1 m/s. Beaufort force n is the first n with speed < bounds[n], bounds=[0.5,1.6,3.4,5.5,8.0,10.8,13.9,17.2,20.8,24.5,28.5,32.7], else 12. If the gust force is at least sustained force + 2 (and force < 12) upgrade by one. Warning: <6 none, <8 small craft, <10 gale, <12 storm, 12 hurricane force. Return [force, warning].

split: open-access