FA-79201 / Image orientation metadata / Open access
Print size swaps pixel dimensions but not their resolutions · case 01
Rotated fax and scanner images with different X and Y resolutions print with the wrong physical size.
ROOT CAUSE
Only the pixel counts are exchanged, so the displayed width is divided by the resolution of the other axis.
VERIFIED REPAIR
Exchange the resolutions together with the pixel dimensions.
Unsuccessful approach: Exchanging only the resolutions pairs each axis with the wrong pixel count.
Case contract
Input {w, h, tag?, unit?, xres: [num, den], yres?: [num, den]}. ResolutionUnit 2 is inches (25.4 mm), 3 is centimetres (10 mm), a missing unit defaults to 2 and any other unit has no physical size (None). A missing yres equals xres; non-positive rational parts give None. For tags 5..8 the displayed width comes from the stored height at YResolution. Return displayed [width, height] in tenths of a millimetre, each rounded half up from the exact value.
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
import math
from fractions import Fraction
N = 1
observations = []
def solve(d):
w, h = d['w'], d['h']
tag = d.get('tag', 1)
unit = d.get('unit', 2)
per = {2: Fraction(254, 10), 3: Fraction(10)}.get(unit)
if per is None:
return None
xn, xd = d['xres']
yn, yd = d.get('yres', d['xres'])
if xn <= 0 or xd <= 0 or yn <= 0 or yd <= 0:
return None
xr = Fraction(xn, xd)
yr = Fraction(yn, yd)
if tag in (5, 6, 7, 8):
w, h = h, w
def tenths(px, r):
return math.floor(px * per * 10 / r + Fraction(1, 2))
return [tenths(w, xr), tenths(h, yr)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[{'w': 2676, 'h': 1814, 'tag': 6, 'unit': 3, 'xres': [300, 1], 'yres': [600, 1]}, [302, 892]], [{'w': 2250, 'h': 3721, 'tag': 6, 'xres': [300, 1]}, [3150, 1905]], [{'w': 1712, 'h': 2976, 'tag': 8, 'unit': 3, 'xres': [1000, 3], 'yres': [96, 1]}, [3100, 514]], [{'w': 2577, 'h': 2775, 'tag': 6, 'xres': [0, 1]}, None], [{'w': 2042, 'h': 678, 'unit': 3, 'xres': [150, 2]}, [2723, 904]], [{'w': 3491, 'h': 649, 'xres': [40, 1], 'yres': [1000, 3]}, [22168, 495]], [{'w': 1972, 'h': 432, 'unit': 3, 'xres': [72, 1], 'yres': [40, 1]}, [2739, 1080]], [{'w': 742, 'h': 2190, 'tag': 8, 'xres': [1000, 3], 'yres': [600, 1]}, [927, 565]]], [[{'w': 3266, 'h': 1419, 'tag': 5, 'unit': 3, 'xres': [300, 1], 'yres': [1000, 3]}, [426, 1089]], [{'w': 1140, 'h': 2204, 'tag': 5, 'unit': 3, 'xres': [40, 1], 'yres': [96, 1]}, [2296, 2850]], [{'w': 3677, 'h': 3545, 'tag': 6, 'unit': 3, 'xres': [1000, 3], 'yres': [40, 1]}, [8863, 1103]], [{'w': 553, 'h': 1044, 'tag': 8, 'unit': 2, 'xres': [200, 3], 'yres': [-300, 1]}, None], [{'w': 2636, 'h': 3063, 'unit': 3, 'xres': [200, 3], 'yres': [300, 1]}, [3954, 1021]], [{'w': 3188, 'h': 3159, 'tag': 1, 'unit': 2, 'xres': [1000, 3]}, [2429, 2407]], [{'w': 2562, 'h': 960, 'tag': 3, 'unit': 3, 'xres': [-72, 1], 'yres': [40, 1]}, None], [{'w': 278, 'h': 1200, 'tag': 8, 'unit': 2, 'xres': [96, 1], 'yres': [1000, 3]}, [914, 736]]], [[{'w': 2032, 'h': 1531, 'tag': 8, 'xres': [72, 1], 'yres': [300, 1]}, [1296, 7168]], [{'w': 2151, 'h': 2111, 'tag': 5, 'unit': 2, 'xres': [7200, 100]}, [7447, 7588]], [{'w': 2302, 'h': 1042, 'tag': 7, 'unit': 2, 'xres': [40, 1], 'yres': [1000, 3]}, [794, 14618]], [{'w': 2, 'h': 3716, 'unit': 2, 'xres': [300, 1]}, [2, 3146]], [{'w': 724, 'h': 1179, 'unit': 2, 'xres': [40, 1], 'yres': [-300, 1]}, None], [{'w': 3374, 'h': 1982, 'tag': 3, 'xres': [300, 1], 'yres': [1000, 3]}, [2857, 1510]], [{'w': 3633, 'h': 2881, 'xres': [-72, 1]}, None], [{'w': 3823, 'h': 2286, 'tag': 8, 'unit': 3, 'xres': [150, 2], 'yres': [1000, 3]}, [686, 5097]]], [[{'w': 1216, 'h': 3589, 'tag': 7, 'unit': 2, 'xres': [1000, 3], 'yres': [72, 1]}, [12661, 927]], [{'w': 47, 'h': 947, 'tag': 8, 'unit': 2, 'xres': [7200, 100]}, [3341, 166]], [{'w': 2131, 'h': 799, 'tag': 7, 'unit': 2, 'xres': [200, 3], 'yres': [40, 1]}, [5074, 8119]], [{'w': 1, 'h': 3356, 'tag': 7, 'xres': [-72, 1], 'yres': [96, 1]}, None], [{'w': 776, 'h': 1736, 'tag': 1, 'unit': 3, 'xres': [96, 1]}, [808, 1808]], [{'w': 2768, 'h': 3254, 'tag': 1, 'unit': 3, 'xres': [300, 1]}, [923, 1085]], [{'w': 3043, 'h': 1338, 'tag': 8, 'unit': 1, 'xres': [200, 3], 'yres': [72, 1]}, None], [{'w': 3732, 'h': 1632, 'tag': 6, 'unit': 3, 'xres': [150, 2], 'yres': [72, 1]}, [2267, 4976]]], [[{'w': 3766, 'h': 3194, 'tag': 8, 'xres': [150, 2], 'yres': [300, 1]}, [2704, 12754]], [{'w': 901, 'h': 2455, 'tag': 6, 'unit': 3, 'xres': [7200, 100], 'yres': [600, 1]}, [409, 1251]], [{'w': 1526, 'h': 1428, 'tag': 5, 'unit': 3, 'xres': [72, 1], 'yres': [600, 1]}, [238, 2119]], [{'w': 2271, 'h': 2906, 'tag': 1, 'unit': 3, 'xres': [72, 1]}, [3154, 4036]], [{'w': 3285, 'h': 917, 'tag': 3, 'unit': 3, 'xres': [300, 1], 'yres': [72, 1]}, [1095, 1274]], [{'w': 690, 'h': 269, 'xres': [40, 1], 'yres': [72, 1]}, [4382, 949]], [{'w': 2070, 'h': 2469, 'tag': 1, 'unit': 3, 'xres': [0, 1], 'yres': [-300, 1]}, None], [{'w': 3018, 'h': 798, 'tag': 8, 'xres': [7200, 100], 'yres': [600, 1]}, [338, 10647]]]]
labels = ["regression: resolution swap with dimensions", "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: resolution swap with dimensions 0 | [605, 446] | [302, 892] | Failed |
| repair trap 1 | [3150, 1905] | [3150, 1905] | Passed |
| combined fault 2 | [893, 1783] | [3100, 514] | Failed |
| control 3 | None | None | Passed |
| control 4 | [2723, 904] | [2723, 904] | Passed |
| boundary 5 | [22168, 495] | [22168, 495] | Passed |
| boundary 6 | [2739, 1080] | [2739, 1080] | Passed |
| control 7 | [1669, 314] | [927, 565] | Failed |
SHA-256 / 82e960434964dcbf15e9661bf8c3ceb77fe09fa8646792a49c0c6554e15366ad
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(d):
w, h = d['w'], d['h']
tag = d.get('tag', 1)
unit = d.get('unit', 2)
per = {2: Fraction(254, 10), 3: Fraction(10)}.get(unit)
if per is None:
return None
xn, xd = d['xres']
yn, yd = d.get('yres', d['xres'])
if xn <= 0 or xd <= 0 or yn <= 0 or yd <= 0:
return None
xr = Fraction(xn, xd)
yr = Fraction(yn, yd)
if tag in (5, 6, 7, 8):
xr, yr = yr, xr
def tenths(px, r):
return math.floor(px * per * 10 / r + Fraction(1, 2))
return [tenths(w, xr), tenths(h, yr)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[{'w': 2676, 'h': 1814, 'tag': 6, 'unit': 3, 'xres': [300, 1], 'yres': [600, 1]}, [302, 892]], [{'w': 2250, 'h': 3721, 'tag': 6, 'xres': [300, 1]}, [3150, 1905]], [{'w': 1712, 'h': 2976, 'tag': 8, 'unit': 3, 'xres': [1000, 3], 'yres': [96, 1]}, [3100, 514]], [{'w': 2577, 'h': 2775, 'tag': 6, 'xres': [0, 1]}, None], [{'w': 2042, 'h': 678, 'unit': 3, 'xres': [150, 2]}, [2723, 904]], [{'w': 3491, 'h': 649, 'xres': [40, 1], 'yres': [1000, 3]}, [22168, 495]], [{'w': 1972, 'h': 432, 'unit': 3, 'xres': [72, 1], 'yres': [40, 1]}, [2739, 1080]], [{'w': 742, 'h': 2190, 'tag': 8, 'xres': [1000, 3], 'yres': [600, 1]}, [927, 565]]], [[{'w': 3266, 'h': 1419, 'tag': 5, 'unit': 3, 'xres': [300, 1], 'yres': [1000, 3]}, [426, 1089]], [{'w': 1140, 'h': 2204, 'tag': 5, 'unit': 3, 'xres': [40, 1], 'yres': [96, 1]}, [2296, 2850]], [{'w': 3677, 'h': 3545, 'tag': 6, 'unit': 3, 'xres': [1000, 3], 'yres': [40, 1]}, [8863, 1103]], [{'w': 553, 'h': 1044, 'tag': 8, 'unit': 2, 'xres': [200, 3], 'yres': [-300, 1]}, None], [{'w': 2636, 'h': 3063, 'unit': 3, 'xres': [200, 3], 'yres': [300, 1]}, [3954, 1021]], [{'w': 3188, 'h': 3159, 'tag': 1, 'unit': 2, 'xres': [1000, 3]}, [2429, 2407]], [{'w': 2562, 'h': 960, 'tag': 3, 'unit': 3, 'xres': [-72, 1], 'yres': [40, 1]}, None], [{'w': 278, 'h': 1200, 'tag': 8, 'unit': 2, 'xres': [96, 1], 'yres': [1000, 3]}, [914, 736]]], [[{'w': 2032, 'h': 1531, 'tag': 8, 'xres': [72, 1], 'yres': [300, 1]}, [1296, 7168]], [{'w': 2151, 'h': 2111, 'tag': 5, 'unit': 2, 'xres': [7200, 100]}, [7447, 7588]], [{'w': 2302, 'h': 1042, 'tag': 7, 'unit': 2, 'xres': [40, 1], 'yres': [1000, 3]}, [794, 14618]], [{'w': 2, 'h': 3716, 'unit': 2, 'xres': [300, 1]}, [2, 3146]], [{'w': 724, 'h': 1179, 'unit': 2, 'xres': [40, 1], 'yres': [-300, 1]}, None], [{'w': 3374, 'h': 1982, 'tag': 3, 'xres': [300, 1], 'yres': [1000, 3]}, [2857, 1510]], [{'w': 3633, 'h': 2881, 'xres': [-72, 1]}, None], [{'w': 3823, 'h': 2286, 'tag': 8, 'unit': 3, 'xres': [150, 2], 'yres': [1000, 3]}, [686, 5097]]], [[{'w': 1216, 'h': 3589, 'tag': 7, 'unit': 2, 'xres': [1000, 3], 'yres': [72, 1]}, [12661, 927]], [{'w': 47, 'h': 947, 'tag': 8, 'unit': 2, 'xres': [7200, 100]}, [3341, 166]], [{'w': 2131, 'h': 799, 'tag': 7, 'unit': 2, 'xres': [200, 3], 'yres': [40, 1]}, [5074, 8119]], [{'w': 1, 'h': 3356, 'tag': 7, 'xres': [-72, 1], 'yres': [96, 1]}, None], [{'w': 776, 'h': 1736, 'tag': 1, 'unit': 3, 'xres': [96, 1]}, [808, 1808]], [{'w': 2768, 'h': 3254, 'tag': 1, 'unit': 3, 'xres': [300, 1]}, [923, 1085]], [{'w': 3043, 'h': 1338, 'tag': 8, 'unit': 1, 'xres': [200, 3], 'yres': [72, 1]}, None], [{'w': 3732, 'h': 1632, 'tag': 6, 'unit': 3, 'xres': [150, 2], 'yres': [72, 1]}, [2267, 4976]]], [[{'w': 3766, 'h': 3194, 'tag': 8, 'xres': [150, 2], 'yres': [300, 1]}, [2704, 12754]], [{'w': 901, 'h': 2455, 'tag': 6, 'unit': 3, 'xres': [7200, 100], 'yres': [600, 1]}, [409, 1251]], [{'w': 1526, 'h': 1428, 'tag': 5, 'unit': 3, 'xres': [72, 1], 'yres': [600, 1]}, [238, 2119]], [{'w': 2271, 'h': 2906, 'tag': 1, 'unit': 3, 'xres': [72, 1]}, [3154, 4036]], [{'w': 3285, 'h': 917, 'tag': 3, 'unit': 3, 'xres': [300, 1], 'yres': [72, 1]}, [1095, 1274]], [{'w': 690, 'h': 269, 'xres': [40, 1], 'yres': [72, 1]}, [4382, 949]], [{'w': 2070, 'h': 2469, 'tag': 1, 'unit': 3, 'xres': [0, 1], 'yres': [-300, 1]}, None], [{'w': 3018, 'h': 798, 'tag': 8, 'xres': [7200, 100], 'yres': [600, 1]}, [338, 10647]]]]
labels = ["regression: resolution swap with dimensions", "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: resolution swap with dimensions 0 | [446, 605] | [302, 892] | Failed |
| repair trap 1 | [1905, 3150] | [3150, 1905] | Failed |
| combined fault 2 | [1783, 893] | [3100, 514] | Failed |
| control 3 | None | None | Passed |
| control 4 | [2723, 904] | [2723, 904] | Passed |
| boundary 5 | [22168, 495] | [22168, 495] | Passed |
| boundary 6 | [2739, 1080] | [2739, 1080] | Passed |
| control 7 | [314, 1669] | [927, 565] | Failed |
SHA-256 / 230725ec0ceb65586f1e2b76661cff9fabb27f6b7a31505856fe146002a49f99
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(d):
w, h = d['w'], d['h']
tag = d.get('tag', 1)
unit = d.get('unit', 2)
per = {2: Fraction(254, 10), 3: Fraction(10)}.get(unit)
if per is None:
return None
xn, xd = d['xres']
yn, yd = d.get('yres', d['xres'])
if xn <= 0 or xd <= 0 or yn <= 0 or yd <= 0:
return None
xr = Fraction(xn, xd)
yr = Fraction(yn, yd)
if tag in (5, 6, 7, 8):
w, h, xr, yr = h, w, yr, xr
def tenths(px, r):
return math.floor(px * per * 10 / r + Fraction(1, 2))
return [tenths(w, xr), tenths(h, yr)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[{'w': 2676, 'h': 1814, 'tag': 6, 'unit': 3, 'xres': [300, 1], 'yres': [600, 1]}, [302, 892]], [{'w': 2250, 'h': 3721, 'tag': 6, 'xres': [300, 1]}, [3150, 1905]], [{'w': 1712, 'h': 2976, 'tag': 8, 'unit': 3, 'xres': [1000, 3], 'yres': [96, 1]}, [3100, 514]], [{'w': 2577, 'h': 2775, 'tag': 6, 'xres': [0, 1]}, None], [{'w': 2042, 'h': 678, 'unit': 3, 'xres': [150, 2]}, [2723, 904]], [{'w': 3491, 'h': 649, 'xres': [40, 1], 'yres': [1000, 3]}, [22168, 495]], [{'w': 1972, 'h': 432, 'unit': 3, 'xres': [72, 1], 'yres': [40, 1]}, [2739, 1080]], [{'w': 742, 'h': 2190, 'tag': 8, 'xres': [1000, 3], 'yres': [600, 1]}, [927, 565]]], [[{'w': 3266, 'h': 1419, 'tag': 5, 'unit': 3, 'xres': [300, 1], 'yres': [1000, 3]}, [426, 1089]], [{'w': 1140, 'h': 2204, 'tag': 5, 'unit': 3, 'xres': [40, 1], 'yres': [96, 1]}, [2296, 2850]], [{'w': 3677, 'h': 3545, 'tag': 6, 'unit': 3, 'xres': [1000, 3], 'yres': [40, 1]}, [8863, 1103]], [{'w': 553, 'h': 1044, 'tag': 8, 'unit': 2, 'xres': [200, 3], 'yres': [-300, 1]}, None], [{'w': 2636, 'h': 3063, 'unit': 3, 'xres': [200, 3], 'yres': [300, 1]}, [3954, 1021]], [{'w': 3188, 'h': 3159, 'tag': 1, 'unit': 2, 'xres': [1000, 3]}, [2429, 2407]], [{'w': 2562, 'h': 960, 'tag': 3, 'unit': 3, 'xres': [-72, 1], 'yres': [40, 1]}, None], [{'w': 278, 'h': 1200, 'tag': 8, 'unit': 2, 'xres': [96, 1], 'yres': [1000, 3]}, [914, 736]]], [[{'w': 2032, 'h': 1531, 'tag': 8, 'xres': [72, 1], 'yres': [300, 1]}, [1296, 7168]], [{'w': 2151, 'h': 2111, 'tag': 5, 'unit': 2, 'xres': [7200, 100]}, [7447, 7588]], [{'w': 2302, 'h': 1042, 'tag': 7, 'unit': 2, 'xres': [40, 1], 'yres': [1000, 3]}, [794, 14618]], [{'w': 2, 'h': 3716, 'unit': 2, 'xres': [300, 1]}, [2, 3146]], [{'w': 724, 'h': 1179, 'unit': 2, 'xres': [40, 1], 'yres': [-300, 1]}, None], [{'w': 3374, 'h': 1982, 'tag': 3, 'xres': [300, 1], 'yres': [1000, 3]}, [2857, 1510]], [{'w': 3633, 'h': 2881, 'xres': [-72, 1]}, None], [{'w': 3823, 'h': 2286, 'tag': 8, 'unit': 3, 'xres': [150, 2], 'yres': [1000, 3]}, [686, 5097]]], [[{'w': 1216, 'h': 3589, 'tag': 7, 'unit': 2, 'xres': [1000, 3], 'yres': [72, 1]}, [12661, 927]], [{'w': 47, 'h': 947, 'tag': 8, 'unit': 2, 'xres': [7200, 100]}, [3341, 166]], [{'w': 2131, 'h': 799, 'tag': 7, 'unit': 2, 'xres': [200, 3], 'yres': [40, 1]}, [5074, 8119]], [{'w': 1, 'h': 3356, 'tag': 7, 'xres': [-72, 1], 'yres': [96, 1]}, None], [{'w': 776, 'h': 1736, 'tag': 1, 'unit': 3, 'xres': [96, 1]}, [808, 1808]], [{'w': 2768, 'h': 3254, 'tag': 1, 'unit': 3, 'xres': [300, 1]}, [923, 1085]], [{'w': 3043, 'h': 1338, 'tag': 8, 'unit': 1, 'xres': [200, 3], 'yres': [72, 1]}, None], [{'w': 3732, 'h': 1632, 'tag': 6, 'unit': 3, 'xres': [150, 2], 'yres': [72, 1]}, [2267, 4976]]], [[{'w': 3766, 'h': 3194, 'tag': 8, 'xres': [150, 2], 'yres': [300, 1]}, [2704, 12754]], [{'w': 901, 'h': 2455, 'tag': 6, 'unit': 3, 'xres': [7200, 100], 'yres': [600, 1]}, [409, 1251]], [{'w': 1526, 'h': 1428, 'tag': 5, 'unit': 3, 'xres': [72, 1], 'yres': [600, 1]}, [238, 2119]], [{'w': 2271, 'h': 2906, 'tag': 1, 'unit': 3, 'xres': [72, 1]}, [3154, 4036]], [{'w': 3285, 'h': 917, 'tag': 3, 'unit': 3, 'xres': [300, 1], 'yres': [72, 1]}, [1095, 1274]], [{'w': 690, 'h': 269, 'xres': [40, 1], 'yres': [72, 1]}, [4382, 949]], [{'w': 2070, 'h': 2469, 'tag': 1, 'unit': 3, 'xres': [0, 1], 'yres': [-300, 1]}, None], [{'w': 3018, 'h': 798, 'tag': 8, 'xres': [7200, 100], 'yres': [600, 1]}, [338, 10647]]]]
labels = ["regression: resolution swap with dimensions", "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: resolution swap with dimensions 0 | [302, 892] | [302, 892] | Passed |
| repair trap 1 | [3150, 1905] | [3150, 1905] | Passed |
| combined fault 2 | [3100, 514] | [3100, 514] | Passed |
| control 3 | None | None | Passed |
| control 4 | [2723, 904] | [2723, 904] | Passed |
| boundary 5 | [22168, 495] | [22168, 495] | Passed |
| boundary 6 | [2739, 1080] | [2739, 1080] | Passed |
| control 7 | [927, 565] | [927, 565] | Passed |
SHA-256 / 783c4714a02d667f2c0e7f2918a577bb188d8290eff04de4471f4ddc3f6556db
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:42.414540+00:00.
Case digest / 3a21f074797187d7a27c052d4ec0eadbb3f7b19fbd04cf2d3d3e6f278bb586b6