FA-78956 / Image orientation metadata / Open access
Counter-clockwise button rotates clockwise · case 01
Both rotate buttons turn the photo the same way.
ROOT CAUSE
The ccw branch adds one quarter turn instead of three.
VERIFIED REPAIR
Add three quarter turns (subtract one modulo 4).
Unsuccessful approach: abs(k - 1) maps 0 to 1 instead of wrapping to 3.
Case contract
A photo editor rotates and flips losslessly by rewriting only the orientation tag. An orientation is modelled as (k, m): mirror horizontally m times, then rotate k quarter turns clockwise (tag table 1:(0,0) 2:(0,1) 3:(2,0) 4:(2,1) 5:(3,1) 6:(1,0) 7:(1,1) 8:(3,0)). Editor operations act on the displayed image: cw, ccw, r180, flip_h (mirror left-right) and flip_v (mirror top-bottom). Invalid starting tags count as 1; an unknown operation returns {"error": "unknown-op", "op": op}. Return the final tag.
Why this case matters
Camera, phone and scanner images carry an orientation hint separately from the stored pixels; galleries, thumbnailers, editors and upload pipelines must interpret it consistently or photos appear sideways, mirrored or doubly rotated.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(tag, ops):
to_km = {1: (0, 0), 2: (0, 1), 3: (2, 0), 4: (2, 1), 5: (3, 1), 6: (1, 0), 7: (1, 1), 8: (3, 0)}
from_km = {v: t for t, v in to_km.items()}
if tag not in to_km:
tag = 1
k, m = to_km[tag]
for op in ops:
if op == 'cw':
k = (k + 1) % 4
elif op == 'ccw':
k = (k + 1) % 4
elif op == 'r180':
k = (k + 2) % 4
elif op == 'flip_h':
k, m = (-k) % 4, 1 - m
elif op == 'flip_v':
k, m = (2 - k) % 4, 1 - m
else:
return {'error': 'unknown-op', 'op': op}
return from_km[(k, m)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[(7, ['flip_v', 'ccw', 'r180']), 3], [(4, ['cw', 'cw', 'ccw', 'flip_h']), 6], [(0, ['ccw', 'flip_v', 'r180', 'cw']), 4], [(8, ['ccw', 'flip_h', 'ccw']), 7], [(3, ['rotate']), {'error': 'unknown-op', 'op': 'rotate'}], [(6, []), 6], [(0, ['flip_v']), 4], [(8, ['ccw', 'cw']), 8]], [[(2, ['flip_v', 'ccw', 'cw']), 3], [(1, ['ccw', 'flip_v']), 5], [(11, ['ccw', 'flip_h']), 7], [(8, ['CW']), {'error': 'unknown-op', 'op': 'CW'}], [(0, []), 1], [(4, ['mirror', 'flip_v', 'cw', 'r180']), {'error': 'unknown-op', 'op': 'mirror'}], [(5, ['ccw', 'mirror']), {'error': 'unknown-op', 'op': 'mirror'}], [(7, ['r180', 'ccw', 'flip_v', 'cw']), 6]], [[(6, ['flip_h', 'flip_h', 'ccw', 'r180']), 3], [(9, ['ccw', 'ccw', 'cw', 'r180']), 6], [(9, ['r180', 'r180', 'ccw', 'flip_v']), 5], [(11, ['r180', 'cw', 'flip_v', 'cw']), 2], [(9, ['cw', 'r180', 'flip_h']), 7], [(11, ['cw', 'flip_v', 'flip_h']), 8], [(5, ['flip_h']), 6], [(6, ['r180', 'flip_v', 'ccw']), 4]], [[(4, ['ccw']), 7], [(0, ['ccw']), 8], [(1, ['ccw']), 8], [(8, ['rotate']), {'error': 'unknown-op', 'op': 'rotate'}], [(11, ['r180']), 3], [(1, ['r180', 'flip_h', 'cw', 'flip_v']), 8], [(5, ['flip_h', 'flip_v', 'r180']), 5], [(0, ['r180', 'ccw', 'cw', 'cw']), 8]], [[(8, ['ccw']), 3], [(9, ['ccw', 'flip_v', 'cw']), 2], [(0, ['ccw', 'flip_h']), 7], [(2, ['flip_v', 'flip_v']), 2], [(1, []), 1], [(5, ['r180']), 7], [(9, ['flip_v']), 4], [(6, ['ccw']), 1]]]
labels = ["regression: counter-clockwise step", "repair trap", "combined fault", "control", "control", "boundary", "boundary", "control"]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("%s %d" % (labels[i % len(labels)], i), solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: counter-clockwise step 0 | 1 | 3 | Failed |
| repair trap 1 | 8 | 6 | Failed |
| combined fault 2 | 2 | 4 | Failed |
| control 3 | 7 | 7 | Passed |
| control 4 | {'error': 'unknown-op', 'op': 'rotate'} | {'error': 'unknown-op', 'op': 'rotate'} | Passed |
| boundary 5 | 6 | 6 | Passed |
| boundary 6 | 4 | 4 | Passed |
| control 7 | 6 | 8 | Failed |
SHA-256 / aa450a10ba53ee102d042624f931b7e7ff949e3ea6da25bab71c8a8ccc6b017e
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(tag, ops):
to_km = {1: (0, 0), 2: (0, 1), 3: (2, 0), 4: (2, 1), 5: (3, 1), 6: (1, 0), 7: (1, 1), 8: (3, 0)}
from_km = {v: t for t, v in to_km.items()}
if tag not in to_km:
tag = 1
k, m = to_km[tag]
for op in ops:
if op == 'cw':
k = (k + 1) % 4
elif op == 'ccw':
k = abs(k - 1) % 4
elif op == 'r180':
k = (k + 2) % 4
elif op == 'flip_h':
k, m = (-k) % 4, 1 - m
elif op == 'flip_v':
k, m = (2 - k) % 4, 1 - m
else:
return {'error': 'unknown-op', 'op': op}
return from_km[(k, m)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[(7, ['flip_v', 'ccw', 'r180']), 3], [(4, ['cw', 'cw', 'ccw', 'flip_h']), 6], [(0, ['ccw', 'flip_v', 'r180', 'cw']), 4], [(8, ['ccw', 'flip_h', 'ccw']), 7], [(3, ['rotate']), {'error': 'unknown-op', 'op': 'rotate'}], [(6, []), 6], [(0, ['flip_v']), 4], [(8, ['ccw', 'cw']), 8]], [[(2, ['flip_v', 'ccw', 'cw']), 3], [(1, ['ccw', 'flip_v']), 5], [(11, ['ccw', 'flip_h']), 7], [(8, ['CW']), {'error': 'unknown-op', 'op': 'CW'}], [(0, []), 1], [(4, ['mirror', 'flip_v', 'cw', 'r180']), {'error': 'unknown-op', 'op': 'mirror'}], [(5, ['ccw', 'mirror']), {'error': 'unknown-op', 'op': 'mirror'}], [(7, ['r180', 'ccw', 'flip_v', 'cw']), 6]], [[(6, ['flip_h', 'flip_h', 'ccw', 'r180']), 3], [(9, ['ccw', 'ccw', 'cw', 'r180']), 6], [(9, ['r180', 'r180', 'ccw', 'flip_v']), 5], [(11, ['r180', 'cw', 'flip_v', 'cw']), 2], [(9, ['cw', 'r180', 'flip_h']), 7], [(11, ['cw', 'flip_v', 'flip_h']), 8], [(5, ['flip_h']), 6], [(6, ['r180', 'flip_v', 'ccw']), 4]], [[(4, ['ccw']), 7], [(0, ['ccw']), 8], [(1, ['ccw']), 8], [(8, ['rotate']), {'error': 'unknown-op', 'op': 'rotate'}], [(11, ['r180']), 3], [(1, ['r180', 'flip_h', 'cw', 'flip_v']), 8], [(5, ['flip_h', 'flip_v', 'r180']), 5], [(0, ['r180', 'ccw', 'cw', 'cw']), 8]], [[(8, ['ccw']), 3], [(9, ['ccw', 'flip_v', 'cw']), 2], [(0, ['ccw', 'flip_h']), 7], [(2, ['flip_v', 'flip_v']), 2], [(1, []), 1], [(5, ['r180']), 7], [(9, ['flip_v']), 4], [(6, ['ccw']), 1]]]
labels = ["regression: counter-clockwise step", "repair trap", "combined fault", "control", "control", "boundary", "boundary", "control"]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("%s %d" % (labels[i % len(labels)], i), solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: counter-clockwise step 0 | 3 | 3 | Passed |
| repair trap 1 | 8 | 6 | Failed |
| combined fault 2 | 2 | 4 | Failed |
| control 3 | 7 | 7 | Passed |
| control 4 | {'error': 'unknown-op', 'op': 'rotate'} | {'error': 'unknown-op', 'op': 'rotate'} | Passed |
| boundary 5 | 6 | 6 | Passed |
| boundary 6 | 4 | 4 | Passed |
| control 7 | 8 | 8 | Passed |
SHA-256 / 353d2d29219ebd68110338cb2f4b346d13f0f70a019798987e7224600e88452c
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(tag, ops):
to_km = {1: (0, 0), 2: (0, 1), 3: (2, 0), 4: (2, 1), 5: (3, 1), 6: (1, 0), 7: (1, 1), 8: (3, 0)}
from_km = {v: t for t, v in to_km.items()}
if tag not in to_km:
tag = 1
k, m = to_km[tag]
for op in ops:
if op == 'cw':
k = (k + 1) % 4
elif op == 'ccw':
k = (k + 3) % 4
elif op == 'r180':
k = (k + 2) % 4
elif op == 'flip_h':
k, m = (-k) % 4, 1 - m
elif op == 'flip_v':
k, m = (2 - k) % 4, 1 - m
else:
return {'error': 'unknown-op', 'op': op}
return from_km[(k, m)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[(7, ['flip_v', 'ccw', 'r180']), 3], [(4, ['cw', 'cw', 'ccw', 'flip_h']), 6], [(0, ['ccw', 'flip_v', 'r180', 'cw']), 4], [(8, ['ccw', 'flip_h', 'ccw']), 7], [(3, ['rotate']), {'error': 'unknown-op', 'op': 'rotate'}], [(6, []), 6], [(0, ['flip_v']), 4], [(8, ['ccw', 'cw']), 8]], [[(2, ['flip_v', 'ccw', 'cw']), 3], [(1, ['ccw', 'flip_v']), 5], [(11, ['ccw', 'flip_h']), 7], [(8, ['CW']), {'error': 'unknown-op', 'op': 'CW'}], [(0, []), 1], [(4, ['mirror', 'flip_v', 'cw', 'r180']), {'error': 'unknown-op', 'op': 'mirror'}], [(5, ['ccw', 'mirror']), {'error': 'unknown-op', 'op': 'mirror'}], [(7, ['r180', 'ccw', 'flip_v', 'cw']), 6]], [[(6, ['flip_h', 'flip_h', 'ccw', 'r180']), 3], [(9, ['ccw', 'ccw', 'cw', 'r180']), 6], [(9, ['r180', 'r180', 'ccw', 'flip_v']), 5], [(11, ['r180', 'cw', 'flip_v', 'cw']), 2], [(9, ['cw', 'r180', 'flip_h']), 7], [(11, ['cw', 'flip_v', 'flip_h']), 8], [(5, ['flip_h']), 6], [(6, ['r180', 'flip_v', 'ccw']), 4]], [[(4, ['ccw']), 7], [(0, ['ccw']), 8], [(1, ['ccw']), 8], [(8, ['rotate']), {'error': 'unknown-op', 'op': 'rotate'}], [(11, ['r180']), 3], [(1, ['r180', 'flip_h', 'cw', 'flip_v']), 8], [(5, ['flip_h', 'flip_v', 'r180']), 5], [(0, ['r180', 'ccw', 'cw', 'cw']), 8]], [[(8, ['ccw']), 3], [(9, ['ccw', 'flip_v', 'cw']), 2], [(0, ['ccw', 'flip_h']), 7], [(2, ['flip_v', 'flip_v']), 2], [(1, []), 1], [(5, ['r180']), 7], [(9, ['flip_v']), 4], [(6, ['ccw']), 1]]]
labels = ["regression: counter-clockwise step", "repair trap", "combined fault", "control", "control", "boundary", "boundary", "control"]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("%s %d" % (labels[i % len(labels)], i), solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: counter-clockwise step 0 | 3 | 3 | Passed |
| repair trap 1 | 6 | 6 | Passed |
| combined fault 2 | 4 | 4 | Passed |
| control 3 | 7 | 7 | Passed |
| control 4 | {'error': 'unknown-op', 'op': 'rotate'} | {'error': 'unknown-op', 'op': 'rotate'} | Passed |
| boundary 5 | 6 | 6 | Passed |
| boundary 6 | 4 | 4 | Passed |
| control 7 | 8 | 8 | Passed |
SHA-256 / 5bc59ce40e467d36428519fbb41b4ad4185e983531fa165f5c2830dab95c2876
Verification & scope
A deterministic bounded teaching model with a stipulated contract; it makes no claim of conformance to any published specification. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:49:39.925258+00:00.
Case digest / 1f6040a8831c73704a756af3220af97f1aca6e9dc00a39bab2a728c5af0f0444