1use crate::block::{BlockCode, container_traits, expect_len};
61use crate::crc::Crc;
62use comnum_core::{Result, ensure};
63use comnum_math::{db2lin, ln_1p_exp};
64
65fn phi(x: f64) -> f64 {
67 if x <= 0.0 {
68 1.0
69 } else if x < 10.0 {
70 (-0.4527 * x.powf(0.86) + 0.0218).exp()
71 } else {
72 (std::f64::consts::PI / x).sqrt() * (-x / 4.0).exp() * (1.0 - 10.0 / (7.0 * x))
73 }
74}
75
76fn phi_inv(y: f64) -> f64 {
78 if y >= 1.0 {
79 return 0.0;
80 }
81 let (lo, mut hi) = (0.0f64, 1.0f64);
82 while phi(hi) > y {
83 hi *= 2.0;
84 if hi > 1e6 {
85 return hi;
86 }
87 }
88 comnum_math::bisect(|x| phi(x) > y, lo, hi, 100)
89}
90
91fn reliabilities(n: usize, mu: f64) -> Vec<f64> {
101 let mut v = vec![mu];
102 while v.len() < n {
103 v = v.iter().flat_map(|&m| [phi_inv(1.0 - (1.0 - phi(m)).powi(2)), 2.0 * m]).collect();
105 }
106 v
107}
108
109fn transform(u: &mut [u8]) {
111 let n = u.len();
112 let mut s = 1;
113 while s < n {
114 for i in 0..n {
115 if i & s == 0 {
116 u[i] ^= u[i + s];
117 }
118 }
119 s <<= 1;
120 }
121}
122
123#[inline]
125fn f(a: f64, b: f64) -> f64 {
126 a.signum() * b.signum() * a.abs().min(b.abs())
127}
128
129#[inline]
131fn g(a: f64, b: f64, u: u8) -> f64 {
132 b + if u == 0 { a } else { -a }
133}
134
135struct Pool<T> {
138 len: usize,
140 data: Vec<T>,
142 of: Vec<usize>,
144 refs: Vec<usize>,
146 free: Vec<usize>,
148}
149
150impl<T: Copy + Default> Pool<T> {
151 fn new(len: usize, arrays: usize, paths: usize) -> Pool<T> {
152 Pool {
153 len,
154 data: vec![T::default(); len * arrays],
155 of: vec![0; paths],
156 refs: vec![0; arrays],
157 free: Vec::new(),
158 }
159 }
160
161 fn reset(&mut self) {
163 self.refs.fill(0);
164 self.refs[0] = 1;
165 self.of[0] = 0;
166 self.free.clear();
167 self.free.extend((1..self.refs.len()).rev());
168 }
169
170 fn share(&mut self, from: usize, to: usize) {
172 let a = self.of[from];
173 self.of[to] = a;
174 self.refs[a] += 1;
175 }
176
177 fn release(&mut self, path: usize) {
179 let a = self.of[path];
180 self.refs[a] -= 1;
181 if self.refs[a] == 0 {
182 self.free.push(a);
183 }
184 }
185
186 fn writable(&mut self, path: usize, keep: usize) -> usize {
189 let a = self.of[path];
190 if self.refs[a] == 1 {
191 return a;
192 }
193 self.refs[a] -= 1;
194 let b = self.free.pop().unwrap_or_else(|| unreachable!("at most one array per path"));
195 self.refs[b] = 1;
196 self.of[path] = b;
197 self.data.copy_within(a * self.len..a * self.len + keep, b * self.len);
198 b
199 }
200
201 fn array(&self, path: usize) -> &[T] {
202 let a = self.of[path];
203 &self.data[a * self.len..(a + 1) * self.len]
204 }
205
206 fn array_mut(&mut self, a: usize) -> &mut [T] {
207 &mut self.data[a * self.len..(a + 1) * self.len]
208 }
209}
210
211struct ListDecoder {
220 m: usize,
221 list: usize,
222 llr: Vec<Pool<f64>>,
224 bits: Vec<Pool<u8>>,
227 order: Vec<usize>,
230 free: Vec<usize>,
232 pm: Vec<f64>,
234 reg: Vec<[u8; 5]>,
236 cand: Vec<(f64, usize, u8)>,
238 lam: Vec<f64>,
240 claimed: Vec<bool>,
242 next: Vec<usize>,
244 hist: Vec<(u32, u8)>,
246 start: Vec<usize>,
247}
248
249impl ListDecoder {
250 fn new(n: usize, list: usize) -> ListDecoder {
251 let m = n.trailing_zeros() as usize;
252 let llr = (0..=m).map(|l| Pool::new(n >> l, if l == 0 { 1 } else { list }, list)).collect();
253 let bits = (0..m).map(|l| Pool::new(n >> l, list, list)).collect();
254 ListDecoder {
255 m,
256 list,
257 llr,
258 bits,
259 order: Vec::with_capacity(list),
260 free: Vec::with_capacity(list),
261 pm: vec![0.0; list],
262 reg: vec![[0; 5]; list],
263 cand: Vec::with_capacity(2 * list),
264 lam: Vec::with_capacity(list),
265 claimed: Vec::with_capacity(list),
266 next: Vec::with_capacity(list),
267 hist: Vec::with_capacity(n * list),
268 start: Vec::with_capacity(n + 1),
269 }
270 }
271
272 fn leaf_llr(&mut self, path: usize, phi: usize) -> f64 {
274 let m = self.m;
275 let first = if phi == 0 { 1 } else { m - phi.trailing_zeros() as usize };
276 for layer in first..=m {
277 let half = 1usize << (m - layer);
278 let (above, below) = self.llr.split_at_mut(layer);
279 let (a, b) = above[layer - 1].array(path).split_at(half);
280 let pool = &mut below[0];
281 let dst = pool.writable(path, 0);
282 let child = pool.array_mut(dst);
283 if (phi >> (m - layer)) & 1 == 0 {
284 for ((c, x), y) in child.iter_mut().zip(a).zip(b) {
285 *c = f(*x, *y);
286 }
287 } else {
288 let partial = &self.bits[layer - 1].array(path)[..half];
289 for (((c, x), y), &s) in child.iter_mut().zip(a).zip(b).zip(partial) {
290 *c = g(*x, *y, s);
291 }
292 }
293 }
294 self.llr[m].array(path)[0]
295 }
296
297 fn set_bit(&mut self, path: usize, phi: usize, bit: u8) {
299 let m = self.m;
300 let mut side = phi & 1;
301 let pool = &mut self.bits[m - 1];
302 let a = pool.writable(path, side);
303 pool.array_mut(a)[side] = bit;
304 let (mut layer, mut len) = (m - 1, 1);
306 while side == 1 && layer > 0 {
307 let (above, below) = self.bits.split_at_mut(layer);
308 let pool = &mut below[0];
309 let a = pool.of[path];
310 let node = pool.array_mut(a);
311 let (l, r) = node.split_at_mut(len);
312 for (x, y) in l.iter_mut().zip(r.iter()) {
313 *x ^= y;
314 }
315 side = (phi >> (m - layer)) & 1;
316 let parent = &mut above[layer - 1];
317 let b = parent.writable(path, side * 2 * len);
318 parent.array_mut(b)[side * 2 * len..(side + 1) * 2 * len].copy_from_slice(node);
319 layer -= 1;
320 len *= 2;
321 }
322 }
323
324 fn decode(&mut self, llr: &[f64], info: &[bool], pc: &[bool]) -> Vec<(Vec<u8>, f64)> {
326 let n = llr.len();
327 self.reset(llr);
328 for phi in 0..n {
329 self.candidates(phi, info, pc);
330 self.release_unclaimed();
331 self.assign_slots();
332 self.commit(phi, n, info[phi] && !pc[phi]);
333 }
334 self.start.push(self.hist.len());
335 self.trace_back(n)
336 }
337
338 fn reset(&mut self, llr: &[f64]) {
340 for p in &mut self.llr {
341 p.reset();
342 }
343 for p in &mut self.bits {
344 p.reset();
345 }
346 self.llr[0].data.copy_from_slice(llr);
347 self.order.clear();
348 self.order.push(0);
349 self.free.clear();
350 self.free.extend((1..self.list).rev());
351 self.pm[0] = 0.0;
352 self.reg[0] = [0; 5];
353 self.hist.clear();
354 self.start.clear();
355 }
356
357 fn candidates(&mut self, phi: usize, info: &[bool], pc: &[bool]) {
361 self.lam.clear();
362 for r in 0..self.order.len() {
363 let lam = self.leaf_llr(self.order[r], phi);
364 self.lam.push(lam);
365 }
366 self.cand.clear();
367 for (r, (&slot, &lam)) in self.order.iter().zip(&self.lam).enumerate() {
368 let pm = self.pm[slot];
369 let cost = |bit: u8| {
370 let s = if bit == 0 { lam } else { -lam };
371 pm + ln_1p_exp(-s)
372 };
373 if pc[phi] {
374 let bit = self.reg[slot][1];
376 self.cand.push((cost(bit), r, bit));
377 } else if info[phi] {
378 self.cand.push((cost(0), r, 0));
379 self.cand.push((cost(1), r, 1));
380 } else {
381 self.cand.push((cost(0), r, 0));
382 }
383 }
384 self.cand.sort_unstable_by(|x, y| x.0.total_cmp(&y.0).then((x.1, x.2).cmp(&(y.1, y.2))));
386 self.cand.truncate(self.list);
387 }
388
389 fn release_unclaimed(&mut self) {
391 self.claimed.clear();
392 self.claimed.resize(self.order.len(), false);
393 for c in &self.cand {
394 self.claimed[c.1] = true;
395 }
396 for r in 0..self.order.len() {
397 if !self.claimed[r] {
398 let slot = self.order[r];
399 for p in &mut self.llr {
400 p.release(slot);
401 }
402 for p in &mut self.bits {
403 p.release(slot);
404 }
405 self.free.push(slot);
406 }
407 }
408 }
409
410 fn assign_slots(&mut self) {
413 self.claimed.fill(false);
414 self.next.clear();
415 for k in 0..self.cand.len() {
416 let r = self.cand[k].1;
417 let parent = self.order[r];
418 let slot = if self.claimed[r] {
419 let slot = self.free.pop().unwrap_or_else(|| unreachable!("at most L paths"));
420 for p in &mut self.llr {
421 p.share(parent, slot);
422 }
423 for p in &mut self.bits {
424 p.share(parent, slot);
425 }
426 self.reg[slot] = self.reg[parent];
427 slot
428 } else {
429 self.claimed[r] = true;
430 parent
431 };
432 self.next.push(slot);
433 }
434 }
435
436 fn commit(&mut self, phi: usize, n: usize, data: bool) {
440 self.start.push(self.hist.len());
441 for k in 0..self.cand.len() {
442 let (metric, r, bit) = self.cand[k];
443 let slot = self.next[k];
444 self.pm[slot] = metric;
445 let reg = &mut self.reg[slot];
446 reg.rotate_left(1);
447 if data {
448 reg[0] ^= bit;
449 }
450 self.hist.push((r as u32, bit));
451 if phi + 1 < n {
452 self.set_bit(slot, phi, bit);
453 }
454 }
455 std::mem::swap(&mut self.order, &mut self.next);
456 }
457
458 fn trace_back(&self, n: usize) -> Vec<(Vec<u8>, f64)> {
460 (0..self.order.len())
461 .map(|rank| {
462 let mut u = vec![0u8; n];
463 let mut r = rank;
464 for phi in (0..n).rev() {
465 let (parent, bit) = self.hist[self.start[phi] + r];
466 u[phi] = bit;
467 r = parent as usize;
468 }
469 (u, self.pm[self.order[rank]])
470 })
471 .collect()
472 }
473}
474
475#[derive(Clone, Debug, PartialEq)]
477pub struct PolarCode {
478 n: usize,
479 k: usize,
480 info_positions: Vec<usize>,
482 design_ebn0_db: Option<f64>,
485 crc: Option<Crc>,
487 list: usize,
489 pc_positions: Vec<usize>,
491}
492
493fn pc_register(u: &[u8], info: &[bool], pc: &[bool], upto: usize) -> u8 {
497 let mut y = [0u8; 5];
498 for i in 0..upto {
499 y.rotate_left(1);
500 if info[i] && !pc[i] {
501 y[0] ^= u[i];
502 }
503 }
504 y.rotate_left(1);
505 y[0]
506}
507
508impl PolarCode {
509 pub fn new(n: usize, k: usize, design_ebn0_db: f64) -> Result<PolarCode> {
516 ensure(n.is_power_of_two() && n >= 2, || format!("the length must be a power of two, got {n}"))?;
517 ensure(k >= 1 && k <= n, || format!("0 < K ≤ N, got K = {k}"))?;
518 ensure(design_ebn0_db.is_finite(), || format!("the design Eb/N0 must be finite, got {design_ebn0_db}"))?;
519 let mut code = PolarCode {
520 n,
521 k,
522 info_positions: Vec::new(),
523 design_ebn0_db: Some(design_ebn0_db),
524 crc: None,
525 list: 1,
526 pc_positions: Vec::new(),
527 };
528 code.construct(design_ebn0_db)?;
529 Ok(code)
530 }
531
532 fn construct(&mut self, design_ebn0_db: f64) -> Result<()> {
533 let extra = self.crc.as_ref().map_or(0, |c| c.width() as usize);
534 let a = self.k + extra;
535 ensure(a <= self.n, || format!("{} information and CRC bits exceed N = {}", a, self.n))?;
536 let rate = self.k as f64 / self.n as f64;
538 let mu = 4.0 * rate * db2lin(design_ebn0_db);
539 let mut order: Vec<(f64, usize)> = reliabilities(self.n, mu).into_iter().zip(0..self.n).collect();
540 order.sort_by(|x, y| y.0.total_cmp(&x.0).then(y.1.cmp(&x.1)));
541 let mut pos: Vec<usize> = order.iter().take(a).map(|o| o.1).collect();
542 pos.sort_unstable();
543 self.info_positions = pos;
544 Ok(())
545 }
546
547 pub fn with_crc(mut self, crc: Crc) -> Result<PolarCode> {
557 let Some(design_ebn0_db) = self.design_ebn0_db else {
558 return Err(comnum_core::Error::Config(
559 "with_crc: the information positions of a code built by with_positions are fixed; \
560 check the CRC outside the code (decode_list)"
561 .into(),
562 ));
563 };
564 self.crc = Some(crc);
565 self.construct(design_ebn0_db)?;
566 Ok(self)
567 }
568
569 pub fn with_list(mut self, list: usize) -> Result<PolarCode> {
575 ensure(list >= 1, || "the list decoder needs at least one path".into())?;
576 self.list = list;
577 Ok(self)
578 }
579
580 pub fn crc(&self) -> Option<&Crc> {
582 self.crc.as_ref()
583 }
584
585 pub fn list(&self) -> usize {
587 self.list
588 }
589
590 pub fn with_positions(n: usize, info_positions: &[usize], pc_positions: &[usize]) -> Result<PolarCode> {
601 ensure(n.is_power_of_two() && n >= 2, || format!("the length must be a power of two, got {n}"))?;
602 let mut seen = vec![false; n];
603 for &p in info_positions.iter().chain(pc_positions) {
604 ensure(p < n && !seen[p], || format!("position {p} repeated or beyond N = {n}"))?;
605 seen[p] = true;
606 }
607 let mut info = info_positions.to_vec();
608 info.sort_unstable();
609 let mut pc = pc_positions.to_vec();
610 pc.sort_unstable();
611 ensure(!info.is_empty(), || "the code has no information positions".to_string())?;
612 Ok(PolarCode {
613 n,
614 k: info.len(),
615 info_positions: info,
616 design_ebn0_db: None,
617 crc: None,
618 list: 1,
619 pc_positions: pc,
620 })
621 }
622
623 pub fn info_positions(&self) -> &[usize] {
625 &self.info_positions
626 }
627
628 pub fn pc_positions(&self) -> &[usize] {
630 &self.pc_positions
631 }
632
633 pub fn decode_list(&self, llr: &[f64]) -> Result<Vec<Vec<u8>>> {
639 expect_len(llr.len(), self.n, "LLRs")?;
640 Ok(self.decode_paths(llr).iter().map(|(u, _)| self.info_positions.iter().map(|&p| u[p]).collect()).collect())
641 }
642
643 fn masks(&self) -> (Vec<bool>, Vec<bool>) {
644 let mut info = vec![false; self.n];
645 let mut pc = vec![false; self.n];
646 for &p in &self.info_positions {
647 info[p] = true;
648 }
649 for &p in &self.pc_positions {
650 info[p] = true;
651 pc[p] = true;
652 }
653 (info, pc)
654 }
655
656 fn decode_paths(&self, llr: &[f64]) -> Vec<(Vec<u8>, f64)> {
659 let (info, pc) = self.masks();
660 ListDecoder::new(self.n, self.list).decode(llr, &info, &pc)
663 }
664}
665
666impl BlockCode for PolarCode {
667 fn k(&self) -> usize {
668 self.k
669 }
670
671 fn n(&self) -> usize {
672 self.n
673 }
674
675 fn encode_block(&self, info: &[u8]) -> Result<Vec<u8>> {
676 expect_len(info.len(), self.k, "bits")?;
677 let payload = match &self.crc {
678 Some(c) => c.attach(info),
679 None => info.to_vec(),
680 };
681 let mut u = vec![0u8; self.n];
682 for (&p, &b) in self.info_positions.iter().zip(&payload) {
683 u[p] = b & 1;
684 }
685 if !self.pc_positions.is_empty() {
686 let (info, pc) = self.masks();
687 for &p in &self.pc_positions {
688 u[p] = pc_register(&u, &info, &pc, p);
689 }
690 }
691 transform(&mut u);
692 Ok(u)
693 }
694
695 fn decode_block(&self, llr: &[f64]) -> Result<Vec<u8>> {
696 expect_len(llr.len(), self.n, "LLRs")?;
697 let paths = self.decode_paths(llr);
698 let payload = |u: &[u8]| -> Vec<u8> { self.info_positions.iter().map(|&p| u[p]).collect() };
699 let chosen = match &self.crc {
700 Some(c) => {
701 paths.iter().map(|(u, _)| payload(u)).find(|w| c.check(w)).unwrap_or_else(|| payload(&paths[0].0))
702 }
703 None => payload(&paths[0].0),
704 };
705 Ok(chosen[..self.k].to_vec())
706 }
707}
708
709container_traits!(PolarCode);
710
711#[cfg(test)]
712mod tests {
713 use super::*;
714 use crate::block::testing::awgn_llr;
715 use comnum_core::rng::Rng;
716
717 #[test]
718 fn the_transform_is_an_involution_and_sc_decodes_noiseless_words() {
719 let mut r = Rng::new(1);
720 let mut u: Vec<u8> = (0..64).map(|_| r.bit()).collect();
721 let u0 = u.clone();
722 transform(&mut u);
723 transform(&mut u);
724 assert_eq!(u, u0);
725 let code = PolarCode::new(64, 32, 1.0).unwrap();
726 let info: Vec<u8> = (0..32).map(|_| r.bit()).collect();
727 let c = code.encode_block(&info).unwrap();
728 let y: Vec<f64> = c.iter().map(|b| 4.0 * (1.0 - 2.0 * f64::from(*b))).collect();
729 assert_eq!(code.decode_block(&y).unwrap(), info);
730 }
731
732 #[test]
733 fn the_construction_freezes_the_worst_channels() {
734 let code = PolarCode::new(8, 4, 2.0).unwrap();
736 assert_eq!(code.info_positions(), &[3, 5, 6, 7]);
737 }
738
739 #[test]
740 fn the_list_and_the_crc_lower_the_block_error_rate() {
741 let base = PolarCode::new(128, 64, 2.0).unwrap();
742 let scl = base.clone().with_crc(Crc::crc6()).unwrap().with_list(8).unwrap();
744 let mut r = Rng::new(2);
745 let (mut e_sc, mut e_scl) = (0, 0);
746 let s2 = 1.0 / 10f64.powf(0.2); for _ in 0..300 {
748 let info: Vec<u8> = (0..64).map(|_| r.bit()).collect();
749 for (code, e) in [(&base, &mut e_sc), (&scl, &mut e_scl)] {
750 let c = code.encode_block(&info).unwrap();
751 let y = awgn_llr(&c, s2, &mut r);
752 if code.decode_block(&y).unwrap() != info {
753 *e += 1;
754 }
755 }
756 }
757 assert!(e_scl * 2 < e_sc, "SC {e_sc} vs CA-SCL {e_scl} block errors");
758 }
759
760 #[test]
761 fn with_crc_rejects_the_fixed_positions_of_a_table() {
762 let table = PolarCode::with_positions(16, &[7, 11, 13, 14, 15], &[12]).unwrap();
763 let err = table.clone().with_crc(Crc::crc6()).unwrap_err();
764 assert!(matches!(err, comnum_core::Error::Config(_)), "{err}");
765 assert!(table.crc().is_none());
767 assert!(table.info_positions().iter().all(|p| !table.pc_positions().contains(p)));
768 }
769
770 #[test]
771 fn with_crc_twice_keeps_k_and_the_last_crc() {
772 let once = PolarCode::new(128, 64, 2.0).unwrap().with_crc(Crc::crc6()).unwrap();
773 let twice =
774 PolarCode::new(128, 64, 2.0).unwrap().with_crc(Crc::crc11()).unwrap().with_crc(Crc::crc6()).unwrap();
775 assert_eq!(once, twice);
776 assert_eq!(BlockCode::k(&twice), 64);
777 assert_eq!(twice.info_positions().len(), 64 + 6);
778 assert_eq!(twice.crc().map(Crc::width), Some(6));
779 }
780
781 fn reference_paths(code: &PolarCode, llr: &[f64]) -> Vec<(Vec<u8>, f64)> {
784 fn leaf_llr(i: usize, ch: &[f64], u: &[u8]) -> f64 {
785 let n = ch.len();
786 if n == 1 {
787 return ch[0];
788 }
789 let h = n / 2;
790 let (a, b) = ch.split_at(h);
791 if i < h {
792 let v: Vec<f64> = a.iter().zip(b).map(|(x, y)| f(*x, *y)).collect();
793 leaf_llr(i, &v, u)
794 } else {
795 let mut x = u[..h].to_vec();
796 transform(&mut x);
797 let v: Vec<f64> =
798 a.iter().zip(b).zip(&x).map(|((p, q), &xb)| q + if xb == 0 { *p } else { -p }).collect();
799 leaf_llr(i - h, &v, &u[h..])
800 }
801 }
802 let (info, pc) = code.masks();
803 let mut paths: Vec<(Vec<u8>, f64)> = vec![(Vec::with_capacity(code.n), 0.0)];
804 for i in 0..code.n {
805 let mut next = Vec::with_capacity(paths.len() * 2);
806 for (u, pm) in &paths {
807 let mut full = u.clone();
808 full.resize(code.n, 0);
809 let lam = leaf_llr(i, llr, &full);
810 let cost = |bit: u8| {
811 let s = if bit == 0 { lam } else { -lam };
812 pm + ln_1p_exp(-s)
813 };
814 let bits: &[u8] = if pc[i] {
815 &[pc_register(&full, &info, &pc, i)]
816 } else if info[i] {
817 &[0, 1]
818 } else {
819 &[0]
820 };
821 for &bit in bits {
822 let mut v = u.clone();
823 v.push(bit);
824 next.push((v, cost(bit)));
825 }
826 }
827 next.sort_by(|a, b| a.1.total_cmp(&b.1));
828 next.truncate(code.list);
829 paths = next;
830 }
831 paths
832 }
833
834 #[test]
835 fn the_list_decoder_matches_the_leaf_by_leaf_reference() {
836 let mut r = Rng::new(2015);
837 let mut cases = 0;
838 for m in 3..=10 {
839 let n = 1usize << m;
840 for list in [1, 2, 8, 32] {
841 let frames = 3;
842 for frame in 0..frames {
843 let k = 1 + r.below(n - 1);
844 let code = match frame % 3 {
845 0 => PolarCode::new(n, k, -1.0 + 4.0 * r.uniform()).unwrap(),
846 1 if k + 6 <= n => PolarCode::new(n, k, 2.0).unwrap().with_crc(Crc::crc6()).unwrap(),
847 _ => {
848 let mut pos: Vec<usize> = (0..n).collect();
850 r.shuffle(&mut pos);
851 let n_pc = (n / 8).min(n - k);
852 PolarCode::with_positions(n, &pos[..k], &pos[k..k + n_pc]).unwrap()
853 }
854 }
855 .with_list(list)
856 .unwrap();
857 let info: Vec<u8> = (0..code.k).map(|_| r.bit()).collect();
858 let c = code.encode_block(&info).unwrap();
859 let s2 = 0.3 + 1.5 * r.uniform();
860 let mut llr = awgn_llr(&c, s2, &mut r);
861 if frame == 1 {
862 for l in &mut llr {
864 *l = (*l / 2.0).round();
865 }
866 }
867 let got = code.decode_paths(&llr);
868 let want = reference_paths(&code, &llr);
869 assert_eq!(got.len(), want.len(), "n {n}, L {list}");
870 for ((gu, gm), (wu, wm)) in got.iter().zip(&want) {
871 assert_eq!(gu, wu, "n {n}, K {k}, L {list}, frame {frame}");
872 assert_eq!(gm.to_bits(), wm.to_bits(), "n {n}, K {k}, L {list}, frame {frame}");
873 }
874 cases += 1;
875 }
876 }
877 }
878 assert!(cases > 40);
879 }
880
881 #[test]
882 fn a_non_finite_design_ebn0_is_rejected() {
883 assert!(PolarCode::new(64, 32, f64::NAN).is_err());
884 assert!(PolarCode::new(64, 32, f64::INFINITY).is_err());
885 assert!(PolarCode::new(64, 32, 1.0).unwrap().with_list(0).is_err());
886 }
887
888 fn former_reliability(i: usize, n: usize, mu: f64) -> f64 {
891 if n == 1 {
892 return mu;
893 }
894 let m = if i < n / 2 { phi_inv(1.0 - (1.0 - phi(mu)).powi(2)) } else { 2.0 * mu };
895 former_reliability(i % (n / 2), n / 2, m)
896 }
897
898 #[test]
899 fn the_shared_reliabilities_are_those_of_the_recursion() {
900 for n in [2usize, 8, 64, 1024] {
901 for mu in [0.05, 1.3, 7.0, 40.0] {
902 let r = reliabilities(n, mu);
903 assert_eq!(r.len(), n);
904 for (i, v) in r.iter().enumerate() {
905 assert_eq!(v.to_bits(), former_reliability(i, n, mu).to_bits(), "n {n}, mu {mu}, i {i}");
906 }
907 }
908 }
909 }
910}