1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct ClassicMqState {
8 pub qe: u32,
10 pub nmps: u8,
12 pub nlps: u8,
14 pub switch: bool,
16}
17
18macro_rules! mq_states {
19 ($($qe:expr, $nmps:expr, $nlps:expr, $switch:expr),+ $(,)?) => {
20 [$(ClassicMqState { qe: $qe, nmps: $nmps, nlps: $nlps, switch: $switch }),+]
21 };
22}
23
24#[rustfmt::skip]
26pub const MQ_STATES: [ClassicMqState; 47] = mq_states!(
27 0x5601, 1, 1, true,
28 0x3401, 2, 6, false,
29 0x1801, 3, 9, false,
30 0x0AC1, 4, 12, false,
31 0x0521, 5, 29, false,
32 0x0221, 38, 33, false,
33 0x5601, 7, 6, true,
34 0x5401, 8, 14, false,
35 0x4801, 9, 14, false,
36 0x3801, 10, 14, false,
37 0x3001, 11, 17, false,
38 0x2401, 12, 18, false,
39 0x1C01, 13, 20, false,
40 0x1601, 29, 21, false,
41 0x5601, 15, 14, true,
42 0x5401, 16, 14, false,
43 0x5101, 17, 15, false,
44 0x4801, 18, 16, false,
45 0x3801, 19, 17, false,
46 0x3401, 20, 18, false,
47 0x3001, 21, 19, false,
48 0x2801, 22, 19, false,
49 0x2401, 23, 20, false,
50 0x2201, 24, 21, false,
51 0x1C01, 25, 22, false,
52 0x1801, 26, 23, false,
53 0x1601, 27, 24, false,
54 0x1401, 28, 25, false,
55 0x1201, 29, 26, false,
56 0x1101, 30, 27, false,
57 0x0AC1, 31, 28, false,
58 0x09C1, 32, 29, false,
59 0x08A1, 33, 30, false,
60 0x0521, 34, 31, false,
61 0x0441, 35, 32, false,
62 0x02A1, 36, 33, false,
63 0x0221, 37, 34, false,
64 0x0141, 38, 35, false,
65 0x0111, 39, 36, false,
66 0x0085, 40, 37, false,
67 0x0049, 41, 38, false,
68 0x0025, 42, 39, false,
69 0x0015, 43, 40, false,
70 0x0009, 44, 41, false,
71 0x0005, 45, 42, false,
72 0x0001, 45, 43, false,
73 0x5601, 46, 46, false,
74);
75
76const fn mq_qe_values() -> [u32; 47] {
77 let mut values = [0; 47];
78 let mut index = 0;
79 while index < values.len() {
80 values[index] = MQ_STATES[index].qe;
81 index += 1;
82 }
83 values
84}
85
86const fn packed_mq_transitions() -> [u32; 47] {
87 let mut values = [0; 47];
88 let mut index = 0;
89 while index < values.len() {
90 let state = MQ_STATES[index];
91 values[index] =
92 state.nmps as u32 | ((state.nlps as u32) << 8) | ((state.switch as u32) << 16);
93 index += 1;
94 }
95 values
96}
97
98pub const MQ_QE_VALUES: [u32; 47] = mq_qe_values();
100pub const PACKED_MQ_TRANSITION_VALUES: [u32; 47] = packed_mq_transitions();
102
103pub const fn irreversible_midpoint_bit(
109 magnitude: u64,
110 decoded_bitplanes: u32,
111 number_of_coding_passes: u32,
112) -> Option<u32> {
113 if magnitude == 0 || decoded_bitplanes == 0 || number_of_coding_passes == 0 {
114 return None;
115 }
116
117 let final_pass = number_of_coding_passes - 1;
118 let decoded_plane = final_pass.div_ceil(3);
119 let Some(mut lowest_decoded_bit) = decoded_bitplanes.checked_sub(decoded_plane + 1) else {
120 return None;
121 };
122 if lowest_decoded_bit >= u64::BITS {
123 return None;
124 }
125
126 if final_pass % 3 == 1 && magnitude & (1_u64 << lowest_decoded_bit) == 0 {
130 lowest_decoded_bit += 1;
131 }
132
133 if lowest_decoded_bit < u64::BITS {
134 Some(lowest_decoded_bit)
135 } else {
136 None
137 }
138}
139
140#[rustfmt::skip]
142pub const SIGN_CONTEXT_LOOKUP: [(u8, u8); 256] = [
143 (9,0), (10,0), (10,1), (0,0), (12,0), (13,0), (11,0), (0,0), (12,1), (11,1),
144 (13,1), (0,0), (0,0), (0,0), (0,0), (0,0), (12,0), (13,0), (11,0), (0,0),
145 (12,0), (13,0), (11,0), (0,0), (9,0), (10,0), (10,1), (0,0), (0,0), (0,0),
146 (0,0), (0,0), (12,1), (11,1), (13,1), (0,0), (9,0), (10,0), (10,1), (0,0),
147 (12,1), (11,1), (13,1), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
148 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
149 (0,0), (0,0), (0,0), (10,0), (10,0), (9,0), (0,0), (13,0), (13,0), (12,0),
150 (0,0), (11,1), (11,1), (12,1), (0,0), (0,0), (0,0), (0,0), (0,0), (13,0),
151 (13,0), (12,0), (0,0), (13,0), (13,0), (12,0), (0,0), (10,0), (10,0), (9,0),
152 (0,0), (0,0), (0,0), (0,0), (0,0), (11,1), (11,1), (12,1), (0,0), (10,0),
153 (10,0), (9,0), (0,0), (11,1), (11,1), (12,1), (0,0), (0,0), (0,0), (0,0),
154 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
155 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (10,1), (9,0), (10,1), (0,0),
156 (11,0), (12,0), (11,0), (0,0), (13,1), (12,1), (13,1), (0,0), (0,0), (0,0),
157 (0,0), (0,0), (11,0), (12,0), (11,0), (0,0), (11,0), (12,0), (11,0), (0,0),
158 (10,1), (9,0), (10,1), (0,0), (0,0), (0,0), (0,0), (0,0), (13,1), (12,1),
159 (13,1), (0,0), (10,1), (9,0), (10,1), (0,0), (13,1), (12,1), (13,1), (0,0),
160 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
161 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
162 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
163 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
164 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
165 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
166 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
167 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
168];
169
170const fn packed_sign_contexts() -> [u16; 256] {
171 let mut values = [0; 256];
172 let mut index = 0;
173 while index < values.len() {
174 let (context, xor) = SIGN_CONTEXT_LOOKUP[index];
175 values[index] = context as u16 | ((xor as u16) << 8);
176 index += 1;
177 }
178 values
179}
180
181pub const PACKED_SIGN_CONTEXT_LOOKUP: [u16; 256] = packed_sign_contexts();
183
184#[rustfmt::skip]
186pub const ZERO_CTX_LL_LH_LOOKUP: [u8; 256] = [
187 0, 3, 1, 3, 5, 7, 6, 7, 1, 3, 2, 3, 6, 7, 6, 7, 5, 7, 6, 7, 8, 8, 8, 8, 6,
188 7, 6, 7, 8, 8, 8, 8, 1, 3, 2, 3, 6, 7, 6, 7, 2, 3, 2, 3, 6, 7, 6, 7, 6, 7,
189 6, 7, 8, 8, 8, 8, 6, 7, 6, 7, 8, 8, 8, 8, 3, 4, 3, 4, 7, 7, 7, 7, 3, 4, 3,
190 4, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, 7, 8, 8, 8, 8, 3, 4, 3, 4,
191 7, 7, 7, 7, 3, 4, 3, 4, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, 7, 8,
192 8, 8, 8, 1, 3, 2, 3, 6, 7, 6, 7, 2, 3, 2, 3, 6, 7, 6, 7, 6, 7, 6, 7, 8, 8,
193 8, 8, 6, 7, 6, 7, 8, 8, 8, 8, 2, 3, 2, 3, 6, 7, 6, 7, 2, 3, 2, 3, 6, 7, 6,
194 7, 6, 7, 6, 7, 8, 8, 8, 8, 6, 7, 6, 7, 8, 8, 8, 8, 3, 4, 3, 4, 7, 7, 7, 7,
195 3, 4, 3, 4, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, 7, 8, 8, 8, 8, 3,
196 4, 3, 4, 7, 7, 7, 7, 3, 4, 3, 4, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 7, 7,
197 7, 7, 8, 8, 8, 8,
198];
199
200#[rustfmt::skip]
202pub const ZERO_CTX_HL_LOOKUP: [u8; 256] = [
203 0, 5, 1, 6, 3, 7, 3, 7, 1, 6, 2, 6, 3, 7, 3, 7, 3, 7, 3, 7, 4, 7, 4, 7, 3,
204 7, 3, 7, 4, 7, 4, 7, 1, 6, 2, 6, 3, 7, 3, 7, 2, 6, 2, 6, 3, 7, 3, 7, 3, 7,
205 3, 7, 4, 7, 4, 7, 3, 7, 3, 7, 4, 7, 4, 7, 5, 8, 6, 8, 7, 8, 7, 8, 6, 8, 6,
206 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 6, 8, 6, 8,
207 7, 8, 7, 8, 6, 8, 6, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7,
208 8, 7, 8, 1, 6, 2, 6, 3, 7, 3, 7, 2, 6, 2, 6, 3, 7, 3, 7, 3, 7, 3, 7, 4, 7,
209 4, 7, 3, 7, 3, 7, 4, 7, 4, 7, 2, 6, 2, 6, 3, 7, 3, 7, 2, 6, 2, 6, 3, 7, 3,
210 7, 3, 7, 3, 7, 4, 7, 4, 7, 3, 7, 3, 7, 4, 7, 4, 7, 6, 8, 6, 8, 7, 8, 7, 8,
211 6, 8, 6, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 6,
212 8, 6, 8, 7, 8, 7, 8, 6, 8, 6, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8,
213 7, 8, 7, 8, 7, 8,
214];
215
216#[rustfmt::skip]
218pub const ZERO_CTX_HH_LOOKUP: [u8; 256] = [
219 0, 1, 3, 4, 1, 2, 4, 5, 3, 4, 6, 7, 4, 5, 7, 7, 1, 2, 4, 5, 2, 2, 5, 5, 4,
220 5, 7, 7, 5, 5, 7, 7, 3, 4, 6, 7, 4, 5, 7, 7, 6, 7, 8, 8, 7, 7, 8, 8, 4, 5,
221 7, 7, 5, 5, 7, 7, 7, 7, 8, 8, 7, 7, 8, 8, 1, 2, 4, 5, 2, 2, 5, 5, 4, 5, 7,
222 7, 5, 5, 7, 7, 2, 2, 5, 5, 2, 2, 5, 5, 5, 5, 7, 7, 5, 5, 7, 7, 4, 5, 7, 7,
223 5, 5, 7, 7, 7, 7, 8, 8, 7, 7, 8, 8, 5, 5, 7, 7, 5, 5, 7, 7, 7, 7, 8, 8, 7,
224 7, 8, 8, 3, 4, 6, 7, 4, 5, 7, 7, 6, 7, 8, 8, 7, 7, 8, 8, 4, 5, 7, 7, 5, 5,
225 7, 7, 7, 7, 8, 8, 7, 7, 8, 8, 6, 7, 8, 8, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8,
226 8, 7, 7, 8, 8, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 5, 7, 7, 5, 5, 7, 7,
227 7, 7, 8, 8, 7, 7, 8, 8, 5, 5, 7, 7, 5, 5, 7, 7, 7, 7, 8, 8, 7, 7, 8, 8, 7,
228 7, 8, 8, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 8, 8, 7, 7, 8, 8, 8, 8,
229 8, 8, 8, 8, 8, 8,
230];
231
232#[cfg(test)]
233mod tests {
234 use core::hint::black_box;
235
236 use super::*;
237
238 #[test]
239 fn irreversible_midpoint_bit_tracks_the_last_decoded_pass() {
240 assert_eq!(irreversible_midpoint_bit(4, 3, 1), Some(2));
241 assert_eq!(irreversible_midpoint_bit(2, 3, 2), Some(1));
242 assert_eq!(irreversible_midpoint_bit(4, 3, 2), Some(2));
243 assert_eq!(irreversible_midpoint_bit(4, 3, 3), Some(1));
244 }
245
246 #[test]
247 fn irreversible_midpoint_bit_rejects_empty_or_inconsistent_state() {
248 assert_eq!(irreversible_midpoint_bit(0, 3, 1), None);
249 assert_eq!(irreversible_midpoint_bit(4, 0, 1), None);
250 assert_eq!(irreversible_midpoint_bit(4, 3, 0), None);
251 assert_eq!(irreversible_midpoint_bit(1, 1, 5), None);
252 }
253
254 #[test]
255 fn device_tables_match_their_structured_sources_at_runtime() {
256 let build_qe: fn() -> [u32; 47] = mq_qe_values;
257 let build_transitions: fn() -> [u32; 47] = packed_mq_transitions;
258 let build_sign_contexts: fn() -> [u16; 256] = packed_sign_contexts;
259
260 let qe = black_box(build_qe)();
261 let transitions = black_box(build_transitions)();
262 let sign_contexts = black_box(build_sign_contexts)();
263
264 for (index, state) in MQ_STATES.iter().copied().enumerate() {
265 assert_eq!(qe[index], state.qe);
266 assert_eq!(
267 transitions[index],
268 u32::from(state.nmps)
269 | (u32::from(state.nlps) << 8)
270 | (u32::from(state.switch) << 16)
271 );
272 }
273 for (index, (context, xor)) in SIGN_CONTEXT_LOOKUP.iter().copied().enumerate() {
274 assert_eq!(
275 sign_contexts[index],
276 u16::from(context) | (u16::from(xor) << 8)
277 );
278 }
279 }
280}