1pub const MIN_LEN: usize = 8;
18pub const MAX_LEVELS: usize = 6;
20pub const MAX_LEVELS_1D: usize = 13;
22
23const ALPHA: f64 = -1.586_134_342_059_924;
24const BETA: f64 = -0.052_980_118_572_961;
25const GAMMA: f64 = 0.882_911_075_530_934;
26const DELTA: f64 = 0.443_506_852_043_971;
27const KAPPA: f64 = 1.149_604_398_860_241;
28
29#[derive(Clone, Debug, PartialEq, Eq)]
32pub struct Band {
33 pub level: usize,
35 pub mask: u8,
37 pub start: [usize; 3],
39 pub end: [usize; 3],
41}
42
43impl Band {
44 pub fn len(&self) -> usize {
46 (0..3).map(|a| self.end[a] - self.start[a]).product()
47 }
48 pub fn is_empty(&self) -> bool {
50 self.len() == 0
51 }
52}
53
54#[derive(Clone, Debug)]
56pub struct Pyramid {
57 pub dims: [usize; 3],
59 pub lens: Vec<[usize; 3]>,
61 pub split: Vec<[bool; 3]>,
63 pub bands: Vec<Band>,
65}
66
67pub fn as3(dims: &[usize]) -> [usize; 3] {
69 assert!(!dims.is_empty() && dims.len() <= 3, "1D–3D arrays only");
70 let mut d = [1; 3];
71 d[3 - dims.len()..].copy_from_slice(dims);
72 d
73}
74
75impl Pyramid {
76 pub fn new(dims: [usize; 3], max_levels: usize) -> Self {
78 let mut lens = vec![dims];
79 let mut split = Vec::new();
80 while split.len() < max_levels {
81 let cur = *lens.last().unwrap();
82 let s = [0, 1, 2].map(|a| cur[a] >= MIN_LEN);
83 if !s.iter().any(|&b| b) {
84 break;
85 }
86 split.push(s);
87 lens.push([0, 1, 2].map(|a| if s[a] { cur[a].div_ceil(2) } else { cur[a] }));
88 }
89 let nlev = split.len();
90 let mut bands = vec![Band {
91 level: nlev,
92 mask: 0,
93 start: [0; 3],
94 end: lens[nlev],
95 }];
96 for l in (0..nlev).rev() {
97 for mask in 1u8..8 {
98 if (0..3).any(|a| mask >> a & 1 == 1 && !split[l][a]) {
99 continue;
100 }
101 let mut start = [0; 3];
102 let mut end = [0; 3];
103 for a in 0..3 {
104 if mask >> a & 1 == 1 {
105 start[a] = lens[l + 1][a];
106 end[a] = lens[l][a];
107 } else {
108 end[a] = lens[l + 1][a];
109 }
110 }
111 let b = Band {
112 level: l,
113 mask,
114 start,
115 end,
116 };
117 if !b.is_empty() {
118 bands.push(b);
119 }
120 }
121 }
122 Self {
123 dims,
124 lens,
125 split,
126 bands,
127 }
128 }
129
130 pub fn nlev(&self) -> usize {
132 self.split.len()
133 }
134
135 pub fn len(&self) -> usize {
137 self.dims.iter().product()
138 }
139
140 pub fn is_empty(&self) -> bool {
142 self.len() == 0
143 }
144
145 fn splits_before(&self, a: usize, l: usize) -> usize {
147 self.split[..l].iter().filter(|s| s[a]).count()
148 }
149
150 pub fn band_weight(&self, band: &Band) -> f64 {
154 let (lo, hi) = norms_97();
155 (0..3)
156 .map(|a| {
157 let l = band.level;
158 if l < self.nlev() && self.split[l][a] {
159 let k = self.splits_before(a, l) + 1;
160 if band.mask >> a & 1 == 1 {
161 hi[k]
162 } else {
163 lo[k]
164 }
165 } else {
166 lo[self.splits_before(a, l.min(self.nlev()))]
167 }
168 })
169 .product()
170 }
171
172 pub fn parent_band(&self, bi: usize) -> Option<usize> {
176 let b = &self.bands[bi];
177 if b.mask == 0 || b.level + 1 >= self.nlev() {
178 return None;
179 }
180 self.bands
181 .iter()
182 .position(|c| c.level == b.level + 1 && c.mask == b.mask)
183 }
184
185 #[inline]
187 pub fn parent_pos(&self, b: &Band, pb: &Band, p: [usize; 3]) -> [usize; 3] {
188 let mut q = [0; 3];
189 for a in 0..3 {
190 let rel = p[a] - b.start[a];
191 let r = if self.split[pb.level][a] {
192 rel / 2
193 } else {
194 rel
195 };
196 q[a] = (pb.start[a] + r).min(pb.end[a] - 1);
197 }
198 q
199 }
200}
201
202fn lines(dims: [usize; 3], ext: [usize; 3], axis: usize, mut f: impl FnMut(usize, usize)) {
203 let stride = [dims[1] * dims[2], dims[2], 1];
205 let others: Vec<usize> = (0..3).filter(|&a| a != axis).collect();
206 for i in 0..ext[others[0]] {
207 for j in 0..ext[others[1]] {
208 f(i * stride[others[0]] + j * stride[others[1]], stride[axis]);
209 }
210 }
211}
212
213#[inline]
216fn lift_d(s: &[f64], d: &mut [f64], c: f64) {
217 let ns = s.len();
219 for i in 0..d.len() {
220 let r = if i + 1 < ns { s[i + 1] } else { s[ns - 1] };
221 d[i] += c * (s[i] + r);
222 }
223}
224
225#[inline]
226fn lift_s(s: &mut [f64], d: &[f64], c: f64) {
227 let nd = d.len();
229 for i in 0..s.len() {
230 let l = if i == 0 { d[0] } else { d[i - 1] };
231 let r = if i < nd { d[i] } else { d[nd - 1] };
232 s[i] += c * (l + r);
233 }
234}
235
236pub fn fwd97(x: &mut [f64], tmp: &mut Vec<f64>) {
238 let n = x.len();
239 if n < 2 {
240 return;
241 }
242 let ns = n.div_ceil(2);
243 tmp.clear();
244 tmp.extend(x.iter().step_by(2));
245 tmp.extend(x.iter().skip(1).step_by(2));
246 let (s, d) = tmp.split_at_mut(ns);
247 lift_d(s, d, ALPHA);
248 lift_s(s, d, BETA);
249 lift_d(s, d, GAMMA);
250 lift_s(s, d, DELTA);
251 for v in s.iter_mut() {
252 *v *= KAPPA;
253 }
254 for v in d.iter_mut() {
255 *v /= KAPPA;
256 }
257 x.copy_from_slice(tmp);
258}
259
260pub fn inv97(x: &mut [f64], tmp: &mut Vec<f64>) {
262 let n = x.len();
263 if n < 2 {
264 return;
265 }
266 let ns = n.div_ceil(2);
267 tmp.clear();
268 tmp.extend_from_slice(x);
269 let (s, d) = tmp.split_at_mut(ns);
270 for v in s.iter_mut() {
271 *v /= KAPPA;
272 }
273 for v in d.iter_mut() {
274 *v *= KAPPA;
275 }
276 lift_s(s, d, -DELTA);
277 lift_d(s, d, -GAMMA);
278 lift_s(s, d, -BETA);
279 lift_d(s, d, -ALPHA);
280 for i in 0..ns {
281 x[2 * i] = s[i];
282 }
283 for i in 0..n / 2 {
284 x[2 * i + 1] = d[i];
285 }
286}
287
288pub fn norms_97() -> (&'static [f64], &'static [f64]) {
290 use std::sync::OnceLock;
291 static N: OnceLock<(Vec<f64>, Vec<f64>)> = OnceLock::new();
292 let n = N.get_or_init(|| {
293 let len = 1 << 16;
294 let mut lo = vec![1.0];
295 let mut hi = vec![0.0];
296 let mut tmp = Vec::new();
297 for k in 1..=MAX_LEVELS_1D {
298 let mut ls: Vec<usize> = vec![len];
300 for _ in 0..k {
301 ls.push(ls.last().unwrap().div_ceil(2usize));
302 }
303 let norm = |pos: usize, tmp: &mut Vec<f64>| {
304 let mut x = vec![0.0; len];
305 x[pos] = 1.0;
306 for j in (0..k).rev() {
307 inv97(&mut x[..ls[j]], tmp);
308 }
309 x.iter().map(|v| v * v).sum::<f64>().sqrt()
310 };
311 lo.push(norm(ls[k] / 2, &mut tmp));
312 hi.push(norm(ls[k] + (ls[k - 1] - ls[k]) / 2, &mut tmp));
313 }
314 (lo, hi)
315 });
316 (&n.0, &n.1)
317}
318
319fn apply3<T: Copy + Default>(
320 p: &Pyramid,
321 x: &mut [T],
322 inverse: bool,
323 mut f: impl FnMut(&mut [T], &mut Vec<T>, bool),
324) {
325 let mut line = Vec::new();
326 let mut tmp = Vec::new();
327 let levels: Vec<usize> = if inverse {
328 (0..p.nlev()).rev().collect()
329 } else {
330 (0..p.nlev()).collect()
331 };
332 for l in levels {
333 let axes: Vec<usize> = if inverse {
334 vec![2, 1, 0]
335 } else {
336 vec![0, 1, 2]
337 };
338 for a in axes {
339 if !p.split[l][a] {
340 continue;
341 }
342 let ext = p.lens[l];
343 let n = ext[a];
344 lines(p.dims, ext, a, |off, st| {
345 line.clear();
346 line.extend((0..n).map(|i| x[off + i * st]));
347 f(&mut line, &mut tmp, inverse);
348 for i in 0..n {
349 x[off + i * st] = line[i];
350 }
351 });
352 }
353 }
354}
355
356pub fn forward_97(p: &Pyramid, x: &mut [f64]) {
358 apply3(p, x, false, |l, t, _| fwd97(l, t));
359}
360
361pub fn inverse_97(p: &Pyramid, x: &mut [f64]) {
363 apply3(p, x, true, |l, t, _| inv97(l, t));
364}
365
366pub fn fwd53(x: &mut [i64], tmp: &mut Vec<i64>) {
370 let n = x.len();
371 if n < 2 {
372 return;
373 }
374 let ns = n.div_ceil(2);
375 tmp.clear();
376 tmp.extend(x.iter().step_by(2));
377 tmp.extend(x.iter().skip(1).step_by(2));
378 let (s, d) = tmp.split_at_mut(ns);
379 for i in 0..d.len() {
380 let r = if i + 1 < ns { s[i + 1] } else { s[ns - 1] };
381 d[i] -= (s[i] + r).div_euclid(2);
382 }
383 let nd = d.len();
384 for i in 0..ns {
385 let l = if i == 0 { d[0] } else { d[i - 1] };
386 let r = if i < nd { d[i] } else { d[nd - 1] };
387 s[i] += (l + r + 2).div_euclid(4);
388 }
389 x.copy_from_slice(tmp);
390}
391
392pub fn inv53(x: &mut [i64], tmp: &mut Vec<i64>) {
394 let n = x.len();
395 if n < 2 {
396 return;
397 }
398 let ns = n.div_ceil(2);
399 tmp.clear();
400 tmp.extend_from_slice(x);
401 let (s, d) = tmp.split_at_mut(ns);
402 let nd = d.len();
403 for i in 0..ns {
404 let l = if i == 0 { d[0] } else { d[i - 1] };
405 let r = if i < nd { d[i] } else { d[nd - 1] };
406 s[i] -= (l + r + 2).div_euclid(4);
407 }
408 for i in 0..nd {
409 let r = if i + 1 < ns { s[i + 1] } else { s[ns - 1] };
410 d[i] += (s[i] + r).div_euclid(2);
411 }
412 for i in 0..ns {
413 x[2 * i] = s[i];
414 }
415 for i in 0..nd {
416 x[2 * i + 1] = d[i];
417 }
418}
419
420pub fn forward_53(p: &Pyramid, x: &mut [i64]) {
422 apply3(p, x, false, |l, t, _| fwd53(l, t));
423}
424
425pub fn inverse_53(p: &Pyramid, x: &mut [i64]) {
427 apply3(p, x, true, |l, t, _| inv53(l, t));
428}
429
430#[cfg(test)]
431mod tests {
432 use super::*;
433
434 fn field(d: [usize; 3]) -> Vec<f64> {
435 let mut v = Vec::new();
436 for i in 0..d[0] {
437 for j in 0..d[1] {
438 for k in 0..d[2] {
439 let (x, y, z) = (i as f64, j as f64, k as f64);
440 v.push(
441 (0.3 * x).sin() + (0.11 * y * z).cos() + 0.01 * x * y
442 - (k % 7) as f64 * 0.1,
443 );
444 }
445 }
446 }
447 v
448 }
449
450 #[test]
451 fn perfect_reconstruction_97_any_size() {
452 for d in [
453 [1, 1, 1],
454 [1, 1, 2],
455 [1, 1, 37],
456 [3, 17, 9],
457 [13, 8, 31],
458 [16, 16, 16],
459 [1, 50, 23],
460 ] {
461 let p = Pyramid::new(d, MAX_LEVELS);
462 let x = field(d);
463 let mut y = x.clone();
464 forward_97(&p, &mut y);
465 inverse_97(&p, &mut y);
466 let e = x
467 .iter()
468 .zip(&y)
469 .map(|(a, b)| (a - b).abs())
470 .fold(0.0, f64::max);
471 assert!(e < 1e-11, "{d:?}: {e}");
472 let cells: usize = p.bands.iter().map(|b| b.len()).sum();
473 assert_eq!(cells, p.len(), "bands tile the array {d:?}");
474 }
475 }
476
477 #[test]
478 fn lossless_53_any_size() {
479 for d in [[1, 1, 5], [7, 9, 11], [16, 32, 8], [2, 1, 64]] {
480 let p = Pyramid::new(d, MAX_LEVELS);
481 let x: Vec<i64> = field(d)
482 .iter()
483 .map(|v| (v * 1000.0) as i64 ^ 0x5a)
484 .collect();
485 let mut y = x.clone();
486 forward_53(&p, &mut y);
487 if p.nlev() > 0 {
488 assert_ne!(x, y);
489 }
490 inverse_53(&p, &mut y);
491 assert_eq!(x, y);
492 }
493 }
494
495 #[test]
496 fn weights_near_orthonormal() {
497 let (lo, hi) = norms_97();
498 for k in 1..=MAX_LEVELS {
500 assert!(
501 (lo[k] - 1.0).abs() < 0.1 && (hi[k] - 1.0).abs() < 0.15,
502 "{k}: {} {}",
503 lo[k],
504 hi[k]
505 );
506 }
507 }
508
509 #[test]
510 fn parent_positions_in_band() {
511 let p = Pyramid::new([64, 128, 128], MAX_LEVELS);
512 assert_eq!(p.nlev(), 5);
513 for (bi, b) in p.bands.iter().enumerate() {
514 if let Some(pi) = p.parent_band(bi) {
515 let pb = &p.bands[pi];
516 let q = p.parent_pos(b, pb, [b.end[0] - 1, b.end[1] - 1, b.end[2] - 1]);
517 for a in 0..3 {
518 assert!(q[a] >= pb.start[a] && q[a] < pb.end[a]);
519 }
520 }
521 }
522 }
523}