1use crate::block::{BlockCode, SoftDecode, container_traits, expect_len};
88use crate::conv::Trellis;
89use crate::turbo::{TurboCode, TurboDecoder};
90use comnum_core::{Error, Result, ensure, ensure_shape};
91
92#[derive(Clone, Debug, PartialEq, Eq)]
107pub struct BitSelection {
108 positions: Vec<usize>,
109 full_len: usize,
110}
111
112impl BitSelection {
113 pub fn new(positions: Vec<usize>, full_len: usize) -> Result<BitSelection> {
118 ensure(positions.iter().all(|&p| p < full_len), || {
119 format!("a selected bit lies beyond the word of {full_len} bits")
120 })?;
121 Ok(BitSelection { positions, full_len })
122 }
123
124 pub fn len(&self) -> usize {
126 self.positions.len()
127 }
128
129 pub fn is_empty(&self) -> bool {
131 self.positions.is_empty()
132 }
133
134 pub fn full_len(&self) -> usize {
136 self.full_len
137 }
138
139 pub fn positions(&self) -> &[usize] {
141 &self.positions
142 }
143
144 pub fn select<T: Copy>(&self, word: &[T]) -> Result<Vec<T>> {
149 expect_len(word.len(), self.full_len, "bits of the word")?;
150 Ok(self.positions.iter().map(|&p| word[p]).collect())
151 }
152
153 pub fn accumulate_into(&self, llr: &[f64], out: &mut [f64]) -> Result<()> {
160 expect_len(llr.len(), self.positions.len(), "LLRs")?;
161 expect_len(out.len(), self.full_len, "LLRs of the word")?;
162 out.fill(0.0);
163 for (&p, &l) in self.positions.iter().zip(llr) {
164 out[p] += l;
165 }
166 Ok(())
167 }
168
169 pub fn word_llr(&self, llr: &[f64]) -> Result<Vec<f64>> {
174 let mut out = vec![0.0; self.full_len];
175 self.accumulate_into(llr, &mut out)?;
176 Ok(out)
177 }
178}
179
180#[derive(Clone, Debug, PartialEq, Eq)]
182pub struct Puncturer {
183 n_out: usize,
184 period: usize,
185 keep: Vec<bool>,
187}
188
189impl Puncturer {
190 pub fn new(pattern: &[&[u8]]) -> Result<Puncturer> {
197 let n_out = pattern.len();
198 ensure(n_out >= 1, || "puncturing: the pattern needs at least one row".into())?;
199 let period = pattern[0].len();
200 ensure(period >= 1, || "puncturing: the pattern needs at least one column".into())?;
201 ensure(pattern.iter().all(|r| r.len() == period), || "puncturing: the rows must have the same length".into())?;
202 ensure(pattern.iter().flat_map(|r| r.iter()).all(|&b| b <= 1), || {
203 "puncturing: the pattern entries must be 0 or 1".into()
204 })?;
205 let keep: Vec<bool> = (0..period).flat_map(|p| pattern.iter().map(move |r| r[p] == 1)).collect();
206 ensure(keep.iter().any(|&k| k), || "puncturing: the pattern keeps no bit".into())?;
207 Ok(Puncturer { n_out, period, keep })
208 }
209
210 pub fn identity(n_out: usize) -> Result<Puncturer> {
215 let row = [1u8];
216 let rows: Vec<&[u8]> = vec![&row; n_out];
217 Puncturer::new(&rows)
218 }
219
220 fn of(pattern: &[&[u8]]) -> Puncturer {
221 Puncturer::new(pattern).unwrap_or_else(|_| unreachable!("a valid pattern"))
222 }
223
224 pub fn ieee80211_2_3() -> Puncturer {
226 Puncturer::of(&[&[1, 1], &[1, 0]])
227 }
228
229 pub fn ieee80211_3_4() -> Puncturer {
231 Puncturer::of(&[&[1, 1, 0], &[1, 0, 1]])
232 }
233
234 pub fn ieee80211_5_6() -> Puncturer {
237 Puncturer::of(&[&[1, 1, 0, 1, 0], &[1, 0, 1, 0, 1]])
238 }
239
240 pub fn dvbs_2_3() -> Puncturer {
244 Puncturer::ieee80211_2_3()
245 }
246
247 pub fn dvbs_3_4() -> Puncturer {
250 Puncturer::ieee80211_3_4()
251 }
252
253 pub fn dvbs_5_6() -> Puncturer {
256 Puncturer::ieee80211_5_6()
257 }
258
259 pub fn dvbs_7_8() -> Puncturer {
261 Puncturer::of(&[&[1, 1, 1, 1, 0, 1, 0], &[1, 0, 0, 0, 1, 0, 1]])
262 }
263
264 pub fn turbo_1_2() -> Puncturer {
268 Puncturer::of(&[&[1, 1], &[1, 0], &[0, 1]])
269 }
270
271 pub fn from_name(name: &str) -> Result<Puncturer> {
287 Ok(match name.to_ascii_lowercase().as_str() {
288 "ieee80211_2_3" => Puncturer::ieee80211_2_3(),
289 "ieee80211_3_4" => Puncturer::ieee80211_3_4(),
290 "ieee80211_5_6" => Puncturer::ieee80211_5_6(),
291 "dvbs_2_3" => Puncturer::dvbs_2_3(),
292 "dvbs_3_4" => Puncturer::dvbs_3_4(),
293 "dvbs_5_6" => Puncturer::dvbs_5_6(),
294 "dvbs_7_8" => Puncturer::dvbs_7_8(),
295 "turbo_1_2" => Puncturer::turbo_1_2(),
296 other => {
297 return Err(Error::Config(format!(
298 "unknown puncturing pattern {other:?} (ieee80211_2_3, ieee80211_3_4, ieee80211_5_6, \
299 dvbs_2_3, dvbs_3_4, dvbs_5_6, dvbs_7_8, turbo_1_2)"
300 )));
301 }
302 })
303 }
304
305 pub fn n_out(&self) -> usize {
307 self.n_out
308 }
309
310 pub fn period(&self) -> usize {
312 self.period
313 }
314
315 pub fn kept_per_period(&self) -> usize {
317 self.keep.iter().filter(|&&k| k).count()
318 }
319
320 pub fn keeps(&self, step: usize, i: usize) -> bool {
322 self.keep[(step % self.period) * self.n_out + i]
323 }
324
325 pub fn punctured_len(&self, n_coded: usize) -> Result<usize> {
330 self.check_coded(n_coded)?;
331 Ok((0..n_coded).filter(|&j| self.keep[j % (self.period * self.n_out)]).count())
332 }
333
334 pub fn selection(&self, n_coded: usize) -> Result<BitSelection> {
340 self.check_coded(n_coded)?;
341 let cycle = self.period * self.n_out;
342 Ok(BitSelection { positions: (0..n_coded).filter(|&j| self.keep[j % cycle]).collect(), full_len: n_coded })
343 }
344
345 fn check_coded(&self, n_coded: usize) -> Result<()> {
346 if n_coded % self.n_out == 0 {
347 Ok(())
348 } else {
349 Err(Error::Shape(format!("{n_coded} coded bits is not a multiple of the {} outputs", self.n_out)))
350 }
351 }
352
353 pub fn puncture(&self, bits: &[u8]) -> Result<Vec<u8>> {
359 self.selection(bits.len())?.select(bits)
360 }
361
362 pub fn depuncture(&self, llr: &[f64], n_coded: usize) -> Result<Vec<f64>> {
369 self.selection(n_coded)?.word_llr(llr)
370 }
371}
372
373#[derive(Clone, Debug, PartialEq, Eq)]
376pub struct Punctured<C> {
377 code: C,
379 puncturer: Puncturer,
381 selection: BitSelection,
383}
384
385impl<C: BlockCode> Punctured<C> {
386 pub fn new(code: C, puncturer: Puncturer) -> Result<Punctured<C>> {
392 let selection = puncturer.selection(code.n())?;
393 Ok(Punctured { code, puncturer, selection })
394 }
395
396 pub fn code(&self) -> &C {
398 &self.code
399 }
400
401 pub fn puncturer(&self) -> &Puncturer {
403 &self.puncturer
404 }
405
406 pub fn selection(&self) -> &BitSelection {
408 &self.selection
409 }
410}
411
412impl<C: BlockCode> BlockCode for Punctured<C> {
413 fn k(&self) -> usize {
414 self.code.k()
415 }
416
417 fn n(&self) -> usize {
418 self.selection.len()
419 }
420
421 fn encode_block(&self, info: &[u8]) -> Result<Vec<u8>> {
422 self.selection.select(&self.code.encode_block(info)?)
423 }
424
425 fn decode_block(&self, llr: &[f64]) -> Result<Vec<u8>> {
426 self.code.decode_block(&self.selection.word_llr(llr)?)
427 }
428}
429
430impl<C: SoftDecode> SoftDecode for Punctured<C> {
431 fn app_into(&self, llr: &[f64], apriori: Option<&[f64]>, out: &mut [f64]) -> Result<usize> {
433 self.code.app_into(&self.selection.word_llr(llr)?, apriori, out)
434 }
435}
436
437#[derive(Clone, Debug, PartialEq)]
441pub struct PuncturedTurbo {
442 code: TurboCode,
444 puncturer: Puncturer,
446 selection: BitSelection,
449}
450
451impl PuncturedTurbo {
452 pub fn new(code: TurboCode, puncturer: Puncturer) -> Result<PuncturedTurbo> {
457 ensure(puncturer.n_out() == 3, || {
458 format!("puncturing a turbo code needs a pattern of 3 rows, got {}", puncturer.n_out())
459 })?;
460 let k = code.k();
461 let mut positions: Vec<usize> =
464 puncturer.selection(3 * k)?.positions().iter().map(|&j| (j % 3) * k + j / 3).collect();
465 positions.extend(3 * k..code.n());
466 let selection = BitSelection { positions, full_len: code.n() };
467 Ok(PuncturedTurbo { code, puncturer, selection })
468 }
469
470 pub fn code(&self) -> &TurboCode {
472 &self.code
473 }
474
475 pub fn puncturer(&self) -> &Puncturer {
477 &self.puncturer
478 }
479
480 pub fn decoder_mut(&mut self) -> &mut TurboDecoder {
483 &mut self.code.decoder
484 }
485
486 pub fn selection(&self) -> &BitSelection {
488 &self.selection
489 }
490}
491
492impl BlockCode for PuncturedTurbo {
493 fn k(&self) -> usize {
494 self.code.k()
495 }
496
497 fn n(&self) -> usize {
498 self.selection.len()
499 }
500
501 fn encode_block(&self, info: &[u8]) -> Result<Vec<u8>> {
502 self.selection.select(&self.code.encode_block(info)?)
503 }
504
505 fn decode_block(&self, llr: &[f64]) -> Result<Vec<u8>> {
506 self.code.decode_block(&self.selection.word_llr(llr)?)
507 }
508}
509
510impl SoftDecode for PuncturedTurbo {
511 fn app_into(&self, llr: &[f64], apriori: Option<&[f64]>, out: &mut [f64]) -> Result<usize> {
513 self.code.app_into(&self.selection.word_llr(llr)?, apriori, out)
514 }
515}
516
517container_traits!(Punctured<C>, C: BlockCode);
518container_traits!(PuncturedTurbo);
519
520pub fn free_distance(trellis: &Trellis, puncturer: &Puncturer) -> Result<u32> {
529 let n = trellis.n_out;
530 ensure_shape(puncturer.n_out() == n, || {
531 format!("the pattern has {} rows for a code of {n} outputs", puncturer.n_out())
532 })?;
533 let (ns, p) = (trellis.n_states(), puncturer.period());
534 let weight = |o: u32, phase: usize| {
535 (0..n).filter(|&i| puncturer.keeps(phase, i) && (o >> (n - 1 - i)) & 1 == 1).count() as u32
536 };
537 let mut best = u32::MAX;
538 for start in 0..p {
539 let mut dist = vec![u32::MAX; ns * p];
541 let s1 = trellis.next[0][1];
542 let w1 = weight(trellis.out[0][1], start);
543 let ph1 = (start + 1) % p;
544 dist[s1 * p + ph1] = dist[s1 * p + ph1].min(w1);
545 if s1 == 0 {
546 best = best.min(w1);
547 }
548 for _ in 0..ns * p {
550 let mut changed = false;
551 for s in 0..ns {
552 for ph in 0..p {
553 let d = dist[s * p + ph];
554 if d == u32::MAX || s == 0 {
555 continue;
556 }
557 for u in 0..2 {
558 let s2 = trellis.next[s][u];
559 let d2 = d + weight(trellis.out[s][u], ph);
560 let ph2 = (ph + 1) % p;
561 if s2 == 0 {
562 best = best.min(d2);
563 } else if d2 < dist[s2 * p + ph2] {
564 dist[s2 * p + ph2] = d2;
565 changed = true;
566 }
567 }
568 }
569 }
570 if !changed {
571 break;
572 }
573 }
574 }
575 Ok(best)
576}
577
578#[cfg(test)]
579mod tests {
580 use super::*;
581 use crate::block::testing::awgn_llr;
582 use crate::bounds::bpsk_noise_variance;
583 use crate::conv::ConvolutionalCode;
584 use comnum_core::rng::Rng;
585
586 #[test]
587 fn puncture_and_depuncture_restore_the_positions() {
588 let p = Puncturer::ieee80211_3_4();
589 assert_eq!((p.n_out(), p.period(), p.kept_per_period()), (2, 3, 4));
590 let bits: Vec<u8> = (0..12).map(|i| (i % 3 == 0) as u8).collect();
591 let punctured = p.puncture(&bits).unwrap();
593 assert_eq!(punctured, [bits[0], bits[1], bits[2], bits[5], bits[6], bits[7], bits[8], bits[11]]);
594 let llr: Vec<f64> = punctured
595 .iter()
596 .enumerate()
597 .map(|(i, &b)| if b == 1 { -(i as f64 + 1.0) } else { i as f64 + 1.0 })
598 .collect();
599 let full = p.depuncture(&llr, 12).unwrap();
600 assert_eq!(full.len(), 12);
601 for (j, v) in full.iter().enumerate() {
602 if p.keeps(j / 2, j % 2) {
603 assert_eq!(u8::from(*v < 0.0), bits[j]);
604 } else {
605 assert_eq!(*v, 0.0, "a punctured position carries a zero LLR");
606 }
607 }
608 assert!(p.puncture(&bits[..5]).is_err());
609 assert!(p.depuncture(&llr[..7], 12).is_err());
610 assert!(Puncturer::new(&[&[1, 1], &[1]]).is_err());
611 assert!(Puncturer::new(&[&[0, 0], &[0, 0]]).is_err());
612 assert!(Puncturer::new(&[&[2]]).is_err());
613 assert!(Puncturer::new(&[]).is_err());
614 assert_eq!(p.punctured_len(8).unwrap(), 6);
616 }
617
618 fn former_streams_to_steps<T: Copy>(k: usize, v: &[T]) -> Vec<T> {
621 (0..k).flat_map(|i| [v[i], v[k + i], v[2 * k + i]]).collect()
622 }
623
624 fn former_depuncture(p: &Puncturer, llr: &[f64], n_coded: usize) -> Vec<f64> {
626 let cycle = p.period * p.n_out;
627 let mut src = llr.iter();
628 (0..n_coded).map(|j| if p.keep[j % cycle] { src.next().copied().unwrap_or(0.0) } else { 0.0 }).collect()
629 }
630
631 #[test]
632 fn the_selections_give_the_former_punctured_words_and_llrs() {
633 let mut rng = Rng::new(12);
634 for p in [Puncturer::ieee80211_3_4(), Puncturer::dvbs_7_8(), Puncturer::turbo_1_2()] {
635 for n_coded in [0, p.n_out(), 7 * p.n_out(), 120] {
636 if n_coded % p.n_out() != 0 {
637 continue;
638 }
639 let bits: Vec<u8> = (0..n_coded).map(|_| rng.bit()).collect();
640 let cycle = p.period * p.n_out;
641 let former: Vec<u8> =
642 bits.iter().enumerate().filter(|(j, _)| p.keep[j % cycle]).map(|(_, &b)| b).collect();
643 assert_eq!(p.puncture(&bits).unwrap(), former);
644 let llr: Vec<f64> = (0..former.len()).map(|_| rng.gaussian()).collect();
645 let (new, old) = (p.depuncture(&llr, n_coded).unwrap(), former_depuncture(&p, &llr, n_coded));
646 assert!(new.len() == old.len() && new.iter().zip(&old).all(|(a, b)| a.to_bits() == b.to_bits()));
647 }
648 }
649 let turbo = PuncturedTurbo::new(TurboCode::lte(40).unwrap(), Puncturer::turbo_1_2()).unwrap();
652 let llr: Vec<f64> = (0..turbo.n()).map(|_| rng.gaussian()).collect();
653 let body = former_depuncture(&Puncturer::turbo_1_2(), &llr[..80], 120);
654 let mut former: Vec<f64> =
655 (0..3).flat_map(|s| (0..40).map(move |i| (s, i))).map(|(s, i)| body[3 * i + s]).collect();
656 former.extend_from_slice(&llr[80..]);
657 let new = turbo.selection().word_llr(&llr).unwrap();
658 assert!(new.iter().zip(&former).all(|(a, b)| a.to_bits() == b.to_bits()));
659 assert!(BitSelection::new(vec![0, 4], 4).is_err());
660 }
661
662 #[test]
663 fn the_soft_output_passes_through_the_puncturing() {
664 let mut rng = Rng::new(13);
665 let code = Punctured::new(ConvolutionalCode::k7(50).unwrap(), Puncturer::ieee80211_2_3()).unwrap();
666 let llr: Vec<f64> = (0..code.n()).map(|_| rng.gaussian()).collect();
667 let app = code.app(&llr, None).unwrap();
668 let mother = code.code().app(&code.puncturer().depuncture(&llr, code.code().n()).unwrap(), None).unwrap();
669 assert!(app.iter().zip(&mother).all(|(a, b)| a.to_bits() == b.to_bits()));
670 let mut turbo = PuncturedTurbo::new(TurboCode::lte(40).unwrap(), Puncturer::turbo_1_2()).unwrap();
671 turbo.decoder_mut().iterations = 3;
672 let llr: Vec<f64> = (0..turbo.n()).map(|_| rng.gaussian()).collect();
673 assert_eq!(turbo.code().decoder.iterations, 3);
674 assert_eq!(
675 comnum_core::signal::hard_decisions(&turbo.app(&llr, None).unwrap()),
676 turbo.decode_block(&llr).unwrap()
677 );
678 }
679
680 #[test]
681 fn punctured_codes_decode_without_error_at_high_snr() {
682 let mut rng = Rng::new(7);
683 for puncturer in [
684 Puncturer::ieee80211_2_3(),
685 Puncturer::ieee80211_3_4(),
686 Puncturer::ieee80211_5_6(),
687 Puncturer::dvbs_2_3(),
688 Puncturer::dvbs_3_4(),
689 Puncturer::dvbs_5_6(),
690 Puncturer::dvbs_7_8(),
691 ] {
692 let code = Punctured::new(ConvolutionalCode::k7(300).unwrap(), puncturer).unwrap();
693 let info: Vec<u8> = (0..300).map(|_| rng.bit()).collect();
694 let c = code.encode_block(&info).unwrap();
695 assert_eq!(c.len(), code.n());
696 let y = awgn_llr(&c, bpsk_noise_variance(10.0, code.code_rate()), &mut rng);
698 assert_eq!(code.decode_block(&y).unwrap(), info, "{:?}", code.puncturer());
699 let mut hard = code.code().clone();
701 hard.hard_decision = true;
702 let hard_code = Punctured::new(hard, code.puncturer().clone()).unwrap();
703 assert_eq!(hard_code.decode_block(&y).unwrap(), info);
704 }
705 let turbo = PuncturedTurbo::new(TurboCode::lte(40).unwrap(), Puncturer::turbo_1_2()).unwrap();
706 assert_eq!(turbo.n(), 2 * 40 + 12);
707 assert!((turbo.code_rate() - 40.0 / 92.0).abs() < 1e-12);
708 let info: Vec<u8> = (0..40).map(|_| rng.bit()).collect();
709 let c = turbo.encode_block(&info).unwrap();
710 let mother = turbo.code().encode_block(&info).unwrap();
711 assert_eq!(
712 &c[..80],
713 &former_streams_to_steps(40, &mother[..120])
714 .iter()
715 .enumerate()
716 .filter(|(j, _)| Puncturer::turbo_1_2().keep[j % 6])
717 .map(|(_, &b)| b)
718 .collect::<Vec<u8>>()[..]
719 );
720 assert_eq!(c[80..], mother[120..]);
721 let y = awgn_llr(&c, bpsk_noise_variance(6.0, turbo.code_rate()), &mut rng);
722 assert_eq!(turbo.decode_block(&y).unwrap(), info);
723 assert!(PuncturedTurbo::new(TurboCode::lte(40).unwrap(), Puncturer::ieee80211_2_3()).is_err());
724 }
725
726 #[test]
727 fn the_rate_3_4_code_lies_between_rate_1_2_and_uncoded_bpsk() {
728 let ebn0 = 4.0;
732 let (k, blocks) = (2000, 25);
733 let half = ConvolutionalCode::k7(k).unwrap();
734 let three_quarters = Punctured::new(ConvolutionalCode::k7(k).unwrap(), Puncturer::ieee80211_3_4()).unwrap();
735 let mut rng = Rng::new(34);
736 let (mut e_half, mut e_34, mut e_uncoded) = (0usize, 0usize, 0usize);
737 for _ in 0..blocks {
738 let info: Vec<u8> = (0..k).map(|_| rng.bit()).collect();
739 let errors = |decoded: &[u8]| decoded.iter().zip(&info).filter(|(a, b)| a != b).count();
740 let c = half.encode_block(&info).unwrap();
741 let y = awgn_llr(&c, bpsk_noise_variance(ebn0, half.code_rate()), &mut rng);
742 e_half += errors(&half.decode_block(&y).unwrap());
743 let c = three_quarters.encode_block(&info).unwrap();
744 let y = awgn_llr(&c, bpsk_noise_variance(ebn0, three_quarters.code_rate()), &mut rng);
745 e_34 += errors(&three_quarters.decode_block(&y).unwrap());
746 let y = awgn_llr(&info, bpsk_noise_variance(ebn0, 1.0), &mut rng);
747 e_uncoded += errors(&comnum_core::signal::hard_decisions(&y));
748 }
749 assert!(e_half < e_34 && e_34 < e_uncoded, "rate 1/2: {e_half}, rate 3/4: {e_34}, uncoded: {e_uncoded}");
754 assert!(
755 e_34 * 2 < e_uncoded && e_half * 2 < e_34,
756 "rate 1/2: {e_half}, rate 3/4: {e_34}, uncoded: {e_uncoded}"
757 );
758 }
759}
760
761#[cfg(test)]
762mod literature {
763 use super::*;
764
765 #[test]
773 fn yasuda_1984_table_i_free_distances_of_the_k7_punctured_codes() {
774 let t = Trellis::feedforward(7, &[0o133, 0o171]).unwrap();
775 assert_eq!(free_distance(&t, &Puncturer::identity(2).unwrap()).unwrap(), 10);
776 assert_eq!(free_distance(&t, &Puncturer::dvbs_2_3()).unwrap(), 6);
777 assert_eq!(free_distance(&t, &Puncturer::dvbs_3_4()).unwrap(), 5);
778 assert_eq!(free_distance(&t, &Puncturer::dvbs_5_6()).unwrap(), 4);
779 assert_eq!(free_distance(&t, &Puncturer::dvbs_7_8()).unwrap(), 3);
780 assert_eq!(free_distance(&t, &Puncturer::ieee80211_2_3()).unwrap(), 6);
781 assert_eq!(free_distance(&t, &Puncturer::ieee80211_3_4()).unwrap(), 5);
782 assert_eq!(free_distance(&t, &Puncturer::ieee80211_5_6()).unwrap(), 4);
783 assert!(free_distance(&t, &Puncturer::turbo_1_2()).is_err());
784 assert_eq!(free_distance(&t, &Puncturer::new(&[&[1, 0], &[1, 1]]).unwrap()).unwrap(), 5);
785 }
786
787 #[test]
791 fn etsi_en_300_421_table_2_is_the_80211_pattern_at_the_shared_rates() {
792 let table: [(&[u8], &[u8], Puncturer); 3] = [
794 (&[1, 0], &[1, 1], Puncturer::dvbs_2_3()),
795 (&[1, 0, 1], &[1, 1, 0], Puncturer::dvbs_3_4()),
796 (&[1, 0, 1, 0, 1], &[1, 1, 0, 1, 0], Puncturer::dvbs_5_6()),
797 ];
798 for (x, y, alias) in table {
799 assert_eq!(Puncturer::new(&[y, x]).unwrap(), alias);
800 }
801 }
802
803 #[test]
809 fn yasuda_1984_tables_i_ii_every_punctured_code() {
810 let generators =
811 [(0o5, 0o7), (0o15, 0o17), (0o23, 0o35), (0o53, 0o75), (0o133, 0o171), (0o247, 0o371), (0o561, 0o753)];
812 let mother: [u32; 7] = [5, 6, 7, 8, 10, 10, 12];
813 let table: [[(&str, &str, u32); 7]; 12] = [
815 [
817 ("10", "11", 3),
818 ("11", "10", 4),
819 ("11", "10", 4),
820 ("10", "11", 6),
821 ("11", "10", 6),
822 ("10", "11", 7),
823 ("11", "10", 7),
824 ],
825 [
827 ("101", "110", 3),
828 ("110", "101", 4),
829 ("101", "110", 3),
830 ("100", "111", 4),
831 ("110", "101", 5),
832 ("110", "101", 6),
833 ("111", "100", 6),
834 ],
835 [
837 ("1011", "1100", 2),
838 ("1011", "1100", 3),
839 ("1010", "1101", 3),
840 ("1000", "1111", 4),
841 ("1111", "1000", 4),
842 ("1010", "1101", 5),
843 ("1101", "1010", 5),
844 ],
845 [
847 ("10111", "11000", 2),
848 ("10100", "11011", 3),
849 ("10111", "11000", 3),
850 ("10000", "11111", 4),
851 ("11010", "10101", 4),
852 ("11100", "10011", 4),
853 ("10110", "11001", 5),
854 ],
855 [
857 ("101111", "110000", 2),
858 ("100011", "111100", 2),
859 ("101010", "110101", 3),
860 ("110110", "101001", 3),
861 ("111010", "100101", 3),
862 ("101001", "110110", 4),
863 ("110110", "101001", 4),
864 ],
865 [
867 ("1011111", "1100000", 2),
868 ("1000010", "1111101", 2),
869 ("1010011", "1101100", 3),
870 ("1011101", "1100010", 3),
871 ("1111010", "1000101", 3),
872 ("1010100", "1101011", 4),
873 ("1101011", "1010100", 4),
874 ],
875 [
877 ("10111111", "11000000", 2),
878 ("10000011", "11111100", 2),
879 ("10100011", "11011100", 3),
880 ("11100010", "10011101", 3),
881 ("11110100", "10001011", 3),
882 ("10110110", "11001001", 3),
883 ("11100000", "10011111", 4),
884 ],
885 [
887 ("101111111", "110000000", 2),
888 ("101000000", "110111111", 2),
889 ("111110011", "100001100", 2),
890 ("100001111", "111110000", 3),
891 ("111101110", "100010001", 3),
892 ("101100110", "110011001", 4),
893 ("111000101", "100111010", 4),
894 ],
895 [
897 ("1011111111", "1100000000", 2),
898 ("1000000011", "1111111100", 2),
899 ("1000000101", "1111111010", 2),
900 ("1001110100", "1110001011", 3),
901 ("1110110111", "1001001000", 3),
902 ("1001000011", "1110111100", 3),
903 ("1000101100", "1111010011", 4),
904 ],
905 [
907 ("10111111111", "11000000000", 2),
908 ("10000000010", "11111111101", 2),
909 ("10101101101", "11010010010", 2),
910 ("10001110100", "11110001011", 3),
911 ("11110111110", "10001000001", 3),
912 ("10110000110", "11001111001", 3),
913 ("11000010001", "10111101110", 4),
914 ],
915 [
917 ("101111111111", "110000000000", 2),
918 ("100000000011", "111111111100", 2),
919 ("101101111011", "110010000100", 2),
920 ("110100110110", "101011001001", 3),
921 ("111111110101", "100000001010", 3),
922 ("100100001100", "111011110011", 3),
923 ("110000011010", "101111100101", 4),
924 ],
925 [
927 ("1011111111111", "1100000000000", 2),
928 ("1010000000000", "1101111111111", 2),
929 ("1110110110111", "1001001001000", 2),
930 ("1100011000100", "1011100111011", 3),
931 ("1101000001111", "1010111110000", 3),
932 ("1010100100000", "1101011011111", 3),
933 ("1100000100001", "1011111011110", 4),
934 ],
935 ];
936 let bits = |s: &str| s.bytes().map(|b| b - b'0').collect::<Vec<u8>>();
937 for (j, &(g1, g2)) in generators.iter().enumerate() {
938 let t = Trellis::feedforward(j as u32 + 3, &[g1, g2]).unwrap();
939 assert_eq!(free_distance(&t, &Puncturer::identity(2).unwrap()).unwrap(), mother[j], "nu = {}", j + 2);
940 for row in &table {
941 let (a, b, d) = row[j];
942 let (a, b) = (bits(a), bits(b));
943 let p = Puncturer::new(&[&a, &b]).unwrap();
944 assert_eq!(free_distance(&t, &p).unwrap(), d, "nu = {}, rate {}/{}", j + 2, a.len(), a.len() + 1);
945 }
946 }
947 }
948}