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#[inline]
155pub const fn reversible_midpoint_bit(
156 magnitude: u64,
157 decoded_bitplanes: u32,
158 number_of_coding_passes: u32,
159) -> Option<u32> {
160 match irreversible_midpoint_bit(magnitude, decoded_bitplanes, number_of_coding_passes) {
161 Some(lowest_decoded_bit) if lowest_decoded_bit > 0 => Some(lowest_decoded_bit - 1),
162 _ => None,
163 }
164}
165
166#[rustfmt::skip]
168pub const SIGN_CONTEXT_LOOKUP: [(u8, u8); 256] = [
169 (9,0), (10,0), (10,1), (0,0), (12,0), (13,0), (11,0), (0,0), (12,1), (11,1),
170 (13,1), (0,0), (0,0), (0,0), (0,0), (0,0), (12,0), (13,0), (11,0), (0,0),
171 (12,0), (13,0), (11,0), (0,0), (9,0), (10,0), (10,1), (0,0), (0,0), (0,0),
172 (0,0), (0,0), (12,1), (11,1), (13,1), (0,0), (9,0), (10,0), (10,1), (0,0),
173 (12,1), (11,1), (13,1), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
174 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
175 (0,0), (0,0), (0,0), (10,0), (10,0), (9,0), (0,0), (13,0), (13,0), (12,0),
176 (0,0), (11,1), (11,1), (12,1), (0,0), (0,0), (0,0), (0,0), (0,0), (13,0),
177 (13,0), (12,0), (0,0), (13,0), (13,0), (12,0), (0,0), (10,0), (10,0), (9,0),
178 (0,0), (0,0), (0,0), (0,0), (0,0), (11,1), (11,1), (12,1), (0,0), (10,0),
179 (10,0), (9,0), (0,0), (11,1), (11,1), (12,1), (0,0), (0,0), (0,0), (0,0),
180 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
181 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (10,1), (9,0), (10,1), (0,0),
182 (11,0), (12,0), (11,0), (0,0), (13,1), (12,1), (13,1), (0,0), (0,0), (0,0),
183 (0,0), (0,0), (11,0), (12,0), (11,0), (0,0), (11,0), (12,0), (11,0), (0,0),
184 (10,1), (9,0), (10,1), (0,0), (0,0), (0,0), (0,0), (0,0), (13,1), (12,1),
185 (13,1), (0,0), (10,1), (9,0), (10,1), (0,0), (13,1), (12,1), (13,1), (0,0),
186 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
187 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
188 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
189 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
190 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
191 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
192 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
193 (0,0), (0,0), (0,0), (0,0), (0,0), (0,0), (0,0),
194];
195
196const fn packed_sign_contexts() -> [u16; 256] {
197 let mut values = [0; 256];
198 let mut index = 0;
199 while index < values.len() {
200 let (context, xor) = SIGN_CONTEXT_LOOKUP[index];
201 values[index] = context as u16 | ((xor as u16) << 8);
202 index += 1;
203 }
204 values
205}
206
207pub const PACKED_SIGN_CONTEXT_LOOKUP: [u16; 256] = packed_sign_contexts();
209
210#[rustfmt::skip]
212pub const ZERO_CTX_LL_LH_LOOKUP: [u8; 256] = [
213 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,
214 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,
215 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,
216 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,
217 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,
218 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,
219 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,
220 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,
221 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,
222 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,
223 7, 7, 8, 8, 8, 8,
224];
225
226#[rustfmt::skip]
228pub const ZERO_CTX_HL_LOOKUP: [u8; 256] = [
229 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,
230 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,
231 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,
232 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,
233 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,
234 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,
235 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,
236 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,
237 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,
238 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,
239 7, 8, 7, 8, 7, 8,
240];
241
242#[rustfmt::skip]
244pub const ZERO_CTX_HH_LOOKUP: [u8; 256] = [
245 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,
246 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,
247 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,
248 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,
249 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,
250 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,
251 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,
252 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,
253 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,
254 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,
255 8, 8, 8, 8, 8, 8,
256];
257
258#[cfg(test)]
259mod tests {
260 use core::hint::black_box;
261
262 use super::*;
263
264 #[test]
265 fn irreversible_midpoint_bit_tracks_the_last_decoded_pass() {
266 assert_eq!(irreversible_midpoint_bit(4, 3, 1), Some(2));
267 assert_eq!(irreversible_midpoint_bit(2, 3, 2), Some(1));
268 assert_eq!(irreversible_midpoint_bit(4, 3, 2), Some(2));
269 assert_eq!(irreversible_midpoint_bit(4, 3, 3), Some(1));
270 }
271
272 #[test]
273 fn irreversible_midpoint_bit_rejects_empty_or_inconsistent_state() {
274 assert_eq!(irreversible_midpoint_bit(0, 3, 1), None);
275 assert_eq!(irreversible_midpoint_bit(4, 0, 1), None);
276 assert_eq!(irreversible_midpoint_bit(4, 3, 0), None);
277 assert_eq!(irreversible_midpoint_bit(1, 1, 5), None);
278 }
279
280 #[test]
281 fn reversible_midpoint_bit_sits_below_the_lowest_decoded_bit() {
282 assert_eq!(reversible_midpoint_bit(5, 3, 7), None);
284 assert_eq!(reversible_midpoint_bit(4, 3, 6), None);
286 assert_eq!(reversible_midpoint_bit(4, 3, 5), Some(0));
288 assert_eq!(reversible_midpoint_bit(1, 3, 5), None);
289 assert_eq!(reversible_midpoint_bit(4, 3, 4), Some(0));
291 assert_eq!(reversible_midpoint_bit(4, 3, 1), Some(1));
293 assert_eq!(reversible_midpoint_bit(0, 3, 1), None);
294 assert_eq!(reversible_midpoint_bit(1, 1, 5), None);
295 }
296
297 #[test]
298 fn device_tables_match_their_structured_sources_at_runtime() {
299 let build_qe: fn() -> [u32; 47] = mq_qe_values;
300 let build_transitions: fn() -> [u32; 47] = packed_mq_transitions;
301 let build_sign_contexts: fn() -> [u16; 256] = packed_sign_contexts;
302
303 let qe = black_box(build_qe)();
304 let transitions = black_box(build_transitions)();
305 let sign_contexts = black_box(build_sign_contexts)();
306
307 for (index, state) in MQ_STATES.iter().copied().enumerate() {
308 assert_eq!(qe[index], state.qe);
309 assert_eq!(
310 transitions[index],
311 u32::from(state.nmps)
312 | (u32::from(state.nlps) << 8)
313 | (u32::from(state.switch) << 16)
314 );
315 }
316 for (index, (context, xor)) in SIGN_CONTEXT_LOOKUP.iter().copied().enumerate() {
317 assert_eq!(
318 sign_contexts[index],
319 u16::from(context) | (u16::from(xor) << 8)
320 );
321 }
322 }
323}