{"abstract":"Points inside the second or later hole are reported as interior.","category":"GIS polygon topology","checks":8,"contract":"Input [point, rings]: exterior ring then hole rings, closed, integer coordinates. A point on any ring edge (exact cross product 0 and inside the closed segment bounding box) is \"boundary\". Otherwise a crossing-number test with the half-open rule (a.y > py) != (b.y > py) and ray toward +x decides containment: outside the exterior or inside any hole is \"exterior\", else \"interior\".","evaluation_group":"w2-gis-polygon-topology-point-in-polygon-with-holes","failed_approach":"Only the last hole is tested, so points in earlier holes are still interior.","family":"w2-gis-polygon-topology-point-in-polygon-with-holes-every-hole-consulted","id":"FA-70391","implementations":{"attempt":{"sha256":"5705c74f0e31477f04ae29d8ac2a20e7177b67ea8a95f524de0a9b0feb7df4d4","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\nimport math\nN = 1\nobservations = []\ndef solve(x):\n    pt, rings = x\n    px, py = pt\n    def on_seg(a, b):\n        cross = (b[0] - a[0]) * (py - a[1]) - (b[1] - a[1]) * (px - a[0])\n        if cross != 0:\n            return False\n        return min(a[0], b[0]) <= px <= max(a[0], b[0]) and min(a[1], b[1]) <= py <= max(a[1], b[1])\n    def inside(ring):\n        c = False\n        for a, b in zip(ring, ring[1:]):\n            if (a[1] > py) != (b[1] > py):\n                xi = a[0] + (py - a[1]) * (b[0] - a[0]) / (b[1] - a[1])\n                if px < xi:\n                    c = not c\n        return c\n    for ring in rings:\n        for a, b in zip(ring, ring[1:]):\n            if on_seg(a, b):\n                return 'boundary'\n    if not inside(rings[0]):\n        return 'exterior'\n    for hole in rings[-1:]:\n        if inside(hole):\n            return 'exterior'\n    return 'interior'\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[('control #0', [[1, 1], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #1', [[3, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #2', [[8, 5], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #3', [[6, 6], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #4', [[12, 6], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #5', [[0, 0], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #6', [[2, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #7', [[7, 9], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary')], [('control #3', [[6, 6], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #4', [[12, 6], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #5', [[0, 0], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #6', [[2, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #7', [[7, 9], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #8', [[8, 9], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #25', [[4, 2], [[[0, 0], [8, 0], [4, 6], [0, 0]]]], 'interior'), ('regression #30', [[3, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]]]], 'exterior')], [('control #6', [[2, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #7', [[7, 9], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #8', [[8, 9], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #9', [[11, 11], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #10', [[13, 5], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #11', [[-1, 12], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('regression #31', [[8, 5], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]], [[1, 7], [1, 10], [3, 10], [3, 7], [1, 7]]]], 'exterior'), ('regression #38', [[2, 5], [[[5, 0], [10, 5], [5, 10], [0, 5], [5, 0]]]], 'interior')], [('control #9', [[11, 11], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #10', [[13, 5], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #11', [[-1, 12], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #12', [[6, 12], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #13', [[5, 2], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #14', [[4, 2], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('regression #35', [[2, 8], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]], [[1, 7], [1, 10], [3, 10], [3, 7], [1, 7]]]], 'exterior'), ('regression #38', [[2, 5], [[[5, 0], [10, 5], [5, 10], [0, 5], [5, 0]]]], 'interior')], [('control #2', [[8, 5], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #13', [[5, 2], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #14', [[4, 2], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #15', [[6, 2], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #16', [[9, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #17', [[10, 12], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #18', [[12, 12], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('regression #39', [[4, 5], [[[5, 0], [10, 5], [5, 10], [0, 5], [5, 0]]]], 'interior')]]\nfor label, args, expected in fixtures[N-1]:\n    check(label, solve(args), expected)\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"},"broken":{"sha256":"051e3799ce4271c61c9275f1647038af54de219db8a4200e59a7f418f90bdc79","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\nimport math\nN = 1\nobservations = []\ndef solve(x):\n    pt, rings = x\n    px, py = pt\n    def on_seg(a, b):\n        cross = (b[0] - a[0]) * (py - a[1]) - (b[1] - a[1]) * (px - a[0])\n        if cross != 0:\n            return False\n        return min(a[0], b[0]) <= px <= max(a[0], b[0]) and min(a[1], b[1]) <= py <= max(a[1], b[1])\n    def inside(ring):\n        c = False\n        for a, b in zip(ring, ring[1:]):\n            if (a[1] > py) != (b[1] > py):\n                xi = a[0] + (py - a[1]) * (b[0] - a[0]) / (b[1] - a[1])\n                if px < xi:\n                    c = not c\n        return c\n    for ring in rings:\n        for a, b in zip(ring, ring[1:]):\n            if on_seg(a, b):\n                return 'boundary'\n    if not inside(rings[0]):\n        return 'exterior'\n    for hole in rings[1:2]:\n        if inside(hole):\n            return 'exterior'\n    return 'interior'\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[('control #0', [[1, 1], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #1', [[3, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #2', [[8, 5], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #3', [[6, 6], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #4', [[12, 6], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #5', [[0, 0], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #6', [[2, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #7', [[7, 9], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary')], [('control #3', [[6, 6], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #4', [[12, 6], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #5', [[0, 0], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #6', [[2, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #7', [[7, 9], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #8', [[8, 9], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #25', [[4, 2], [[[0, 0], [8, 0], [4, 6], [0, 0]]]], 'interior'), ('regression #30', [[3, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]]]], 'exterior')], [('control #6', [[2, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #7', [[7, 9], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #8', [[8, 9], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #9', [[11, 11], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #10', [[13, 5], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #11', [[-1, 12], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('regression #31', [[8, 5], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]], [[1, 7], [1, 10], [3, 10], [3, 7], [1, 7]]]], 'exterior'), ('regression #38', [[2, 5], [[[5, 0], [10, 5], [5, 10], [0, 5], [5, 0]]]], 'interior')], [('control #9', [[11, 11], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #10', [[13, 5], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #11', [[-1, 12], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #12', [[6, 12], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #13', [[5, 2], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #14', [[4, 2], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('regression #35', [[2, 8], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]], [[1, 7], [1, 10], [3, 10], [3, 7], [1, 7]]]], 'exterior'), ('regression #38', [[2, 5], [[[5, 0], [10, 5], [5, 10], [0, 5], [5, 0]]]], 'interior')], [('control #2', [[8, 5], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #13', [[5, 2], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #14', [[4, 2], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #15', [[6, 2], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #16', [[9, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #17', [[10, 12], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #18', [[12, 12], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('regression #39', [[4, 5], [[[5, 0], [10, 5], [5, 10], [0, 5], [5, 0]]]], 'interior')]]\nfor label, args, expected in fixtures[N-1]:\n    check(label, solve(args), expected)\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"},"fixed":{"sha256":"5991fa65febfda0f0db23fd400056b2b133b2b28df26f52631f5d6a473dd30d5","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\nimport math\nN = 1\nobservations = []\ndef solve(x):\n    pt, rings = x\n    px, py = pt\n    def on_seg(a, b):\n        cross = (b[0] - a[0]) * (py - a[1]) - (b[1] - a[1]) * (px - a[0])\n        if cross != 0:\n            return False\n        return min(a[0], b[0]) <= px <= max(a[0], b[0]) and min(a[1], b[1]) <= py <= max(a[1], b[1])\n    def inside(ring):\n        c = False\n        for a, b in zip(ring, ring[1:]):\n            if (a[1] > py) != (b[1] > py):\n                xi = a[0] + (py - a[1]) * (b[0] - a[0]) / (b[1] - a[1])\n                if px < xi:\n                    c = not c\n        return c\n    for ring in rings:\n        for a, b in zip(ring, ring[1:]):\n            if on_seg(a, b):\n                return 'boundary'\n    if not inside(rings[0]):\n        return 'exterior'\n    for hole in rings[1:]:\n        if inside(hole):\n            return 'exterior'\n    return 'interior'\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[('control #0', [[1, 1], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #1', [[3, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #2', [[8, 5], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #3', [[6, 6], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #4', [[12, 6], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #5', [[0, 0], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #6', [[2, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #7', [[7, 9], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary')], [('control #3', [[6, 6], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #4', [[12, 6], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #5', [[0, 0], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #6', [[2, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #7', [[7, 9], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #8', [[8, 9], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #25', [[4, 2], [[[0, 0], [8, 0], [4, 6], [0, 0]]]], 'interior'), ('regression #30', [[3, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]]]], 'exterior')], [('control #6', [[2, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #7', [[7, 9], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #8', [[8, 9], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #9', [[11, 11], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #10', [[13, 5], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #11', [[-1, 12], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('regression #31', [[8, 5], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]], [[1, 7], [1, 10], [3, 10], [3, 7], [1, 7]]]], 'exterior'), ('regression #38', [[2, 5], [[[5, 0], [10, 5], [5, 10], [0, 5], [5, 0]]]], 'interior')], [('control #9', [[11, 11], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #10', [[13, 5], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #11', [[-1, 12], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #12', [[6, 12], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #13', [[5, 2], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #14', [[4, 2], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('regression #35', [[2, 8], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]], [[1, 7], [1, 10], [3, 10], [3, 7], [1, 7]]]], 'exterior'), ('regression #38', [[2, 5], [[[5, 0], [10, 5], [5, 10], [0, 5], [5, 0]]]], 'interior')], [('control #2', [[8, 5], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'exterior'), ('control #13', [[5, 2], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #14', [[4, 2], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #15', [[6, 2], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'interior'), ('control #16', [[9, 3], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #17', [[10, 12], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('control #18', [[12, 12], [[[0, 0], [12, 0], [12, 12], [0, 12], [0, 0]], [[2, 2], [2, 5], [5, 5], [5, 2], [2, 2]], [[7, 3], [7, 9], [10, 9], [10, 3], [7, 3]]]], 'boundary'), ('regression #39', [[4, 5], [[[5, 0], [10, 5], [5, 10], [0, 5], [5, 0]]]], 'interior')]]\nfor label, args, expected in fixtures[N-1]:\n    check(label, solve(args), expected)\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"}},"limitations":"Stipulated deterministic toy contract on a bounded input domain; results are rounded as stated and no conformance with any published standard or library is claimed. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.","method":"Deterministic executable model with adversarial boundary fixtures.","provenance":{"created_by":"Failure Map","dependencies":"Python standard library","family":"w2-gis-polygon-topology-point-in-polygon-with-holes-every-hole-consulted","generated_at":"2026-09-29T14:48:20.419332+00:00","license":"CC0-1.0","python":"3.12.14","seed":1,"split":"open-access"},"relevance":"Parcel lookup, geofencing and spatial joins classify points against polygons with holes; lakes and courtyards must not count as inside.","repair":"At the every hole consulted step restore `for hole in rings[1:]:`, leaving the rest of the model unchanged.","root_cause":"Only the first hole is tested for containment.","sha256":"d127b471eac35e0254c3662e816397e6d0dcbbe89ea045568857c520752ac8ea","title":"Point classification against a polygon with holes: every hole consulted · case 01","variant":1,"variant_policy":"Five numbered records share a model and may reuse boundary fixtures.","verification":{"attempt":{"elapsed_ms":42.566,"exit_code":1,"observations":[{"actual":"interior","check":"control #0","expected":"interior","passed":true},{"actual":"interior","check":"control #1","expected":"exterior","passed":false},{"actual":"exterior","check":"control #2","expected":"exterior","passed":true},{"actual":"interior","check":"control #3","expected":"interior","passed":true},{"actual":"boundary","check":"control #4","expected":"boundary","passed":true},{"actual":"boundary","check":"control #5","expected":"boundary","passed":true},{"actual":"boundary","check":"control #6","expected":"boundary","passed":true},{"actual":"boundary","check":"control #7","expected":"boundary","passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"control #0\", \"actual\": \"interior\", \"expected\": \"interior\", \"passed\": true}, {\"check\": \"control #1\", \"actual\": \"interior\", \"expected\": \"exterior\", \"passed\": false}, {\"check\": \"control #2\", \"actual\": \"exterior\", \"expected\": \"exterior\", \"passed\": true}, {\"check\": \"control #3\", \"actual\": \"interior\", \"expected\": \"interior\", \"passed\": true}, {\"check\": \"control #4\", \"actual\": \"boundary\", \"expected\": \"boundary\", \"passed\": true}, {\"check\": \"control #5\", \"actual\": \"boundary\", \"expected\": \"boundary\", \"passed\": true}, {\"check\": \"control #6\", \"actual\": \"boundary\", \"expected\": \"boundary\", \"passed\": true}, {\"check\": \"control #7\", \"actual\": \"boundary\", \"expected\": \"boundary\", \"passed\": true}], \"passed\": false}\n"},"broken":{"elapsed_ms":41.58,"exit_code":1,"observations":[{"actual":"interior","check":"control #0","expected":"interior","passed":true},{"actual":"exterior","check":"control #1","expected":"exterior","passed":true},{"actual":"interior","check":"control #2","expected":"exterior","passed":false},{"actual":"interior","check":"control #3","expected":"interior","passed":true},{"actual":"boundary","check":"control #4","expected":"boundary","passed":true},{"actual":"boundary","check":"control #5","expected":"boundary","passed":true},{"actual":"boundary","check":"control #6","expected":"boundary","passed":true},{"actual":"boundary","check":"control #7","expected":"boundary","passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"control #0\", \"actual\": \"interior\", \"expected\": \"interior\", \"passed\": true}, {\"check\": \"control #1\", \"actual\": \"exterior\", \"expected\": \"exterior\", \"passed\": true}, {\"check\": \"control #2\", \"actual\": \"interior\", \"expected\": \"exterior\", \"passed\": false}, {\"check\": \"control #3\", \"actual\": \"interior\", \"expected\": \"interior\", \"passed\": true}, {\"check\": \"control #4\", \"actual\": \"boundary\", \"expected\": \"boundary\", \"passed\": true}, {\"check\": \"control #5\", \"actual\": \"boundary\", \"expected\": \"boundary\", \"passed\": true}, {\"check\": \"control #6\", \"actual\": \"boundary\", \"expected\": \"boundary\", \"passed\": true}, {\"check\": \"control #7\", \"actual\": \"boundary\", \"expected\": \"boundary\", \"passed\": true}], \"passed\": false}\n"},"fixed":{"elapsed_ms":41.045,"exit_code":0,"observations":[{"actual":"interior","check":"control #0","expected":"interior","passed":true},{"actual":"exterior","check":"control #1","expected":"exterior","passed":true},{"actual":"exterior","check":"control #2","expected":"exterior","passed":true},{"actual":"interior","check":"control #3","expected":"interior","passed":true},{"actual":"boundary","check":"control #4","expected":"boundary","passed":true},{"actual":"boundary","check":"control #5","expected":"boundary","passed":true},{"actual":"boundary","check":"control #6","expected":"boundary","passed":true},{"actual":"boundary","check":"control #7","expected":"boundary","passed":true}],"passed":true,"stderr":"","stdout":"{\"observations\": [{\"check\": \"control #0\", \"actual\": \"interior\", \"expected\": \"interior\", \"passed\": true}, {\"check\": \"control #1\", \"actual\": \"exterior\", \"expected\": \"exterior\", \"passed\": true}, {\"check\": \"control #2\", \"actual\": \"exterior\", \"expected\": \"exterior\", \"passed\": true}, {\"check\": \"control #3\", \"actual\": \"interior\", \"expected\": \"interior\", \"passed\": true}, {\"check\": \"control #4\", \"actual\": \"boundary\", \"expected\": \"boundary\", \"passed\": true}, {\"check\": \"control #5\", \"actual\": \"boundary\", \"expected\": \"boundary\", \"passed\": true}, {\"check\": \"control #6\", \"actual\": \"boundary\", \"expected\": \"boundary\", \"passed\": true}, {\"check\": \"control #7\", \"actual\": \"boundary\", \"expected\": \"boundary\", \"passed\": true}], \"passed\": true}\n"}},"verified":true,"visibility":"public"}