use std::sync::OnceLock;
pub const WIDTH: usize = 30;
pub const HEIGHT: usize = 33;
#[rustfmt::skip]
pub const MAXI_GRID: [[u16; WIDTH]; HEIGHT] = [
[122,121,128,127,134,133,140,139,146,145,152,151,158,157,164,163,170,169,176,175,182,181,188,187,194,193,200,199, 0, 0],
[124,123,130,129,136,135,142,141,148,147,154,153,160,159,166,165,172,171,178,177,184,183,190,189,196,195,202,201,817, 0],
[126,125,132,131,138,137,144,143,150,149,156,155,162,161,168,167,174,173,180,179,186,185,192,191,198,197,204,203,819,818],
[284,283,278,277,272,271,266,265,260,259,254,253,248,247,242,241,236,235,230,229,224,223,218,217,212,211,206,205,820, 0],
[286,285,280,279,274,273,268,267,262,261,256,255,250,249,244,243,238,237,232,231,226,225,220,219,214,213,208,207,822,821],
[288,287,282,281,276,275,270,269,264,263,258,257,252,251,246,245,240,239,234,233,228,227,222,221,216,215,210,209,823, 0],
[290,289,296,295,302,301,308,307,314,313,320,319,326,325,332,331,338,337,344,343,350,349,356,355,362,361,368,367,825,824],
[292,291,298,297,304,303,310,309,316,315,322,321,328,327,334,333,340,339,346,345,352,351,358,357,364,363,370,369,826, 0],
[294,293,300,299,306,305,312,311,318,317,324,323,330,329,336,335,342,341,348,347,354,353,360,359,366,365,372,371,828,827],
[410,409,404,403,398,397,392,391, 80, 79, 0, 0, 14, 13, 38, 37, 3, 0, 45, 44,110,109,386,385,380,379,374,373,829, 0],
[412,411,406,405,400,399,394,393, 82, 81, 41, 0, 16, 15, 40, 39, 4, 0, 0, 46,112,111,388,387,382,381,376,375,831,830],
[414,413,408,407,402,401,396,395, 84, 83, 42, 0, 0, 0, 0, 0, 6, 5, 48, 47,114,113,390,389,384,383,378,377,832, 0],
[416,415,422,421,428,427,104,103, 56, 55, 17, 0, 0, 0, 0, 0, 0, 0, 21, 20, 86, 85,434,433,440,439,446,445,834,833],
[418,417,424,423,430,429,106,105, 58, 57, 0, 0, 0, 0, 0, 0, 0, 0, 23, 22, 88, 87,436,435,442,441,448,447,835, 0],
[420,419,426,425,432,431,108,107, 60, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 90, 89,438,437,444,443,450,449,837,836],
[482,481,476,475,470,469, 49, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 54, 53,464,463,458,457,452,451,838, 0],
[484,483,478,477,472,471, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,466,465,460,459,454,453,840,839],
[486,485,480,479,474,473, 52, 51, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 43,468,467,462,461,456,455,841, 0],
[488,487,494,493,500,499, 98, 97, 62, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 92, 91,506,505,512,511,518,517,843,842],
[490,489,496,495,502,501,100, 99, 64, 63, 0, 0, 0, 0, 0, 0, 0, 0, 29, 28, 94, 93,508,507,514,513,520,519,844, 0],
[492,491,498,497,504,503,102,101, 66, 65, 18, 0, 0, 0, 0, 0, 0, 0, 19, 30, 96, 95,510,509,516,515,522,521,846,845],
[560,559,554,553,548,547,542,541, 74, 73, 33, 0, 0, 0, 0, 0, 0, 11, 68, 67,116,115,536,535,530,529,524,523,847, 0],
[562,561,556,555,550,549,544,543, 76, 75, 0, 0, 8, 7, 36, 35, 12, 0, 70, 69,118,117,538,537,532,531,526,525,849,848],
[564,563,558,557,552,551,546,545, 78, 77, 0, 34, 10, 9, 26, 25, 0, 0, 72, 71,120,119,540,539,534,533,528,527,850, 0],
[566,565,572,571,578,577,584,583,590,589,596,595,602,601,608,607,614,613,620,619,626,625,632,631,638,637,644,643,852,851],
[568,567,574,573,580,579,586,585,592,591,598,597,604,603,610,609,616,615,622,621,628,627,634,633,640,639,646,645,853, 0],
[570,569,576,575,582,581,588,587,594,593,600,599,606,605,612,611,618,617,624,623,630,629,636,635,642,641,648,647,855,854],
[728,727,722,721,716,715,710,709,704,703,698,697,692,691,686,685,680,679,674,673,668,667,662,661,656,655,650,649,856, 0],
[730,729,724,723,718,717,712,711,706,705,700,699,694,693,688,687,682,681,676,675,670,669,664,663,658,657,652,651,858,857],
[732,731,726,725,720,719,714,713,708,707,702,701,696,695,690,689,684,683,678,677,672,671,666,665,660,659,654,653,859, 0],
[734,733,740,739,746,745,752,751,758,757,764,763,770,769,776,775,782,781,788,787,794,793,800,799,806,805,812,811,861,860],
[736,735,742,741,748,747,754,753,760,759,766,765,772,771,778,777,784,783,790,789,796,795,802,801,808,807,814,813,862, 0],
[738,737,744,743,750,749,756,755,762,761,768,767,774,773,780,779,786,785,792,791,798,797,804,803,810,809,816,815,864,863],
];
pub const ORIENTATION: [(usize, usize); 13] = [
(0, 28),
(0, 29),
(9, 10),
(9, 11),
(10, 11),
(15, 7),
(16, 8),
(16, 20),
(17, 20),
(22, 10),
(23, 10),
(22, 17),
(23, 17),
];
pub fn cell_target(v: u16) -> Option<(usize, u8)> {
if v == 0 {
None
} else {
let n = (v - 1) as usize;
Some((n / 6, 5 - (n % 6) as u8))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Cw {
Byte(u8),
LatchA,
LatchB,
ShiftA,
ShiftB,
ShiftC,
ShiftD,
ShiftE,
TwoShiftA,
ThreeShiftA,
Lock,
Eci,
Ns,
Pad,
}
const SRC_A: &str = "\rABCDEFGHIJKLMNOPQRSTUVWXYZ\u{FFFA}\u{1C}\u{1D}\u{1E}\u{FFFB} \u{FFFC}\"#$%&'()*+,-./0123456789:\u{FFF1}\u{FFF2}\u{FFF3}\u{FFF4}\u{FFF8}";
const SRC_B: &str = "`abcdefghijklmnopqrstuvwxyz\u{FFFA}\u{1C}\u{1D}\u{1E}\u{FFFB}{\u{FFFC}}~\u{7F};<=>?[\\]^_ ,./:@!|\u{FFFC}\u{FFF5}\u{FFF6}\u{FFFC}\u{FFF0}\u{FFF2}\u{FFF3}\u{FFF4}\u{FFF7}";
const SRC_C: &str = "\u{C0}\u{C1}\u{C2}\u{C3}\u{C4}\u{C5}\u{C6}\u{C7}\u{C8}\u{C9}\u{CA}\u{CB}\u{CC}\u{CD}\u{CE}\u{CF}\u{D0}\u{D1}\u{D2}\u{D3}\u{D4}\u{D5}\u{D6}\u{D7}\u{D8}\u{D9}\u{DA}\u{FFFA}\u{1C}\u{1D}\u{1E}\u{FFFB}\u{DB}\u{DC}\u{DD}\u{DE}\u{DF}\u{AA}\u{AC}\u{B1}\u{B2}\u{B3}\u{B5}\u{B9}\u{BA}\u{BC}\u{BD}\u{BE}\u{80}\u{81}\u{82}\u{83}\u{84}\u{85}\u{86}\u{87}\u{88}\u{89}\u{FFF7} \u{FFF9}\u{FFF3}\u{FFF4}\u{FFF8}";
const SRC_D: &str = "\u{E0}\u{E1}\u{E2}\u{E3}\u{E4}\u{E5}\u{E6}\u{E7}\u{E8}\u{E9}\u{EA}\u{EB}\u{EC}\u{ED}\u{EE}\u{EF}\u{F0}\u{F1}\u{F2}\u{F3}\u{F4}\u{F5}\u{F6}\u{F7}\u{F8}\u{F9}\u{FA}\u{FFFA}\u{1C}\u{1D}\u{1E}\u{FFFB}\u{FB}\u{FC}\u{FD}\u{FE}\u{FF}\u{A1}\u{A8}\u{AB}\u{AF}\u{B0}\u{B4}\u{B7}\u{B8}\u{BB}\u{BF}\u{8A}\u{8B}\u{8C}\u{8D}\u{8E}\u{8F}\u{90}\u{91}\u{92}\u{93}\u{94}\u{FFF7} \u{FFF2}\u{FFF9}\u{FFF4}\u{FFF8}";
const SRC_E: &str = "\u{0}\u{1}\u{2}\u{3}\u{4}\u{5}\u{6}\u{7}\u{8}\u{9}\n\u{B}\u{C}\r\u{E}\u{F}\u{10}\u{11}\u{12}\u{13}\u{14}\u{15}\u{16}\u{17}\u{18}\u{19}\u{1A}\u{FFFA}\u{FFFC}\u{FFFC}\u{1B}\u{FFFB}\u{1C}\u{1D}\u{1E}\u{1F}\u{9F}\u{A0}\u{A2}\u{A3}\u{A4}\u{A5}\u{A6}\u{A7}\u{A9}\u{AD}\u{AE}\u{B6}\u{95}\u{96}\u{97}\u{98}\u{99}\u{9A}\u{9B}\u{9C}\u{9D}\u{9E}\u{FFF7} \u{FFF2}\u{FFF3}\u{FFF9}\u{FFF8}";
fn parse_set(src: &str) -> [Cw; 64] {
let mut out = [Cw::Pad; 64];
let mut n = 0;
for ch in src.chars() {
let cp = ch as u32;
out[n] = match cp {
0xFFF0 => Cw::ShiftA,
0xFFF1 => Cw::ShiftB,
0xFFF2 => Cw::ShiftC,
0xFFF3 => Cw::ShiftD,
0xFFF4 => Cw::ShiftE,
0xFFF5 => Cw::TwoShiftA,
0xFFF6 => Cw::ThreeShiftA,
0xFFF7 => Cw::LatchA,
0xFFF8 => Cw::LatchB,
0xFFF9 => Cw::Lock,
0xFFFA => Cw::Eci,
0xFFFB => Cw::Ns,
0xFFFC => Cw::Pad,
b if b <= 0xFF => Cw::Byte(b as u8),
other => panic!("invalid MaxiCode set character U+{other:04X}"),
};
n += 1;
}
assert_eq!(n, 64, "MaxiCode code set must have 64 entries");
out
}
pub fn sets() -> &'static [[Cw; 64]; 5] {
static SETS: OnceLock<[[Cw; 64]; 5]> = OnceLock::new();
SETS.get_or_init(|| {
[
parse_set(SRC_A),
parse_set(SRC_B),
parse_set(SRC_C),
parse_set(SRC_D),
parse_set(SRC_E),
]
})
}
pub fn value_in_set(set: usize, byte: u8) -> Option<u8> {
sets()[set]
.iter()
.position(|&c| c == Cw::Byte(byte))
.map(|p| p as u8)
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
#[test]
fn grid_covers_every_data_module_once() {
let mut seen = HashSet::new();
for row in &MAXI_GRID {
for &v in row {
if v != 0 {
assert!(v <= 864, "module {v} out of range");
assert!(seen.insert(v), "module {v} appears twice");
}
}
}
assert_eq!(seen.len(), 864);
}
#[test]
fn code_sets_parse_to_64() {
let s = sets();
assert_eq!(s[0][1], Cw::Byte(b'A'));
assert_eq!(s[0][32], Cw::Byte(b' '));
assert_eq!(s[0][58], Cw::Byte(b':'));
assert_eq!(s[0][0], Cw::Byte(b'\r'));
assert_eq!(s[1][0], Cw::Byte(b'`'));
assert_eq!(s[1][63], Cw::LatchA);
assert_eq!(s[2][58], Cw::LatchA);
assert_eq!(s[2][60], Cw::Lock);
assert_eq!(s[4][0], Cw::Byte(0));
assert_eq!(value_in_set(0, b'7'), Some(55));
assert_eq!(value_in_set(1, b'7'), None);
}
}