prize18-5 Exercise 1: Canonical and Compatibility Equivalence ============================================================== Predictions: e-acute pair: both True (canonical implies compatibility). A-ring and angstrom sign: both True. The ffi ligature and "ffi": canonical False, compatibility True. Two mark orders that share a combining class (both class 230 here): the order is kept, so both False. Fullwidth A and A: canonical False, compatibility True. a and A: both False (normalization does not change case). ex1.py: import unicodedata as ud def canon_eq(a, b): return ud.normalize("NFD", a) == ud.normalize("NFD", b) def compat_eq(a, b): return ud.normalize("NFKD", a) == ud.normalize("NFKD", b) pairs = [ ("e acute", "\u00e9", "e\u0301"), ("angstrom", "\u00c5", "\u212b"), ("ffi ligature", "\ufb03", "ffi"), ("marks swapped", "e\u0302\u0301", "e\u0301\u0302"), ("fullwidth", "\uff21", "A"), ("case", "a", "A"), ] for label, a, b in pairs: print(f"{label:14} canonical={canon_eq(a, b)!s:5} compatibility={compat_eq(a, b)}") Real output (Python 3.14): e acute canonical=True compatibility=True angstrom canonical=True compatibility=True ffi ligature canonical=False compatibility=True marks swapped canonical=False compatibility=False fullwidth canonical=False compatibility=True case canonical=False compatibility=False All six match the predictions. The "marks swapped" row is the subtle one: the circumflex and acute both have combining class 230, so normalization must not reorder them.