use alloc::vec;
use alloc::vec::Vec;
use super::bp::check_crc14;
use super::params::{Ldpc174_91Params, LdpcParams};
use super::{LDPC_K, LDPC_N};
pub(super) const GEN_PARITY: [[u8; 91]; 83] = [
[
1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0,
0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1,
0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0,
],
[
0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1,
1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1,
0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0,
1,
],
[
1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0,
1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0,
0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1,
0,
],
[
0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0,
0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1,
0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0,
1,
],
[
0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1,
1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1,
1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0,
1,
],
[
0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0,
0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0,
1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0,
1,
],
[
0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1,
1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1,
0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0,
1,
],
[
0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1,
0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1,
0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1,
1,
],
[
1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1,
0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0,
0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0,
0,
],
[
0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0,
0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1,
1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
0,
],
[
1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1,
0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1,
0,
],
[
0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0,
1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1,
1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0,
1,
],
[
0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1,
1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0,
0,
],
[
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1,
1,
],
[
0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1,
0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0,
0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0,
0,
],
[
1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0,
0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0,
0,
],
[
0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1,
0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1,
0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0,
0,
],
[
0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0,
0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1,
1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1,
1,
],
[
0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1,
1,
],
[
0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1,
1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0,
0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1,
0,
],
[
1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0,
0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1,
1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0,
0,
],
[
0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0,
0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0,
0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0,
0,
],
[
0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1,
1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0,
0,
],
[
0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0,
1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1,
0,
],
[
1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1,
1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1,
0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1,
1,
],
[
1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1,
0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1,
0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0,
1,
],
[
0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0,
1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0,
1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0,
0,
],
[
1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0,
1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1,
0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0,
0,
],
[
1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0,
1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1,
1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1,
0,
],
[
0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1,
1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0,
1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1,
1,
],
[
0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0,
0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0,
0,
],
[
0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0,
0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1,
0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0,
1,
],
[
1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0,
0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1,
1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1,
0,
],
[
1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1,
0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1,
1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1,
0,
],
[
0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1,
0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0,
0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0,
0,
],
[
0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1,
1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0,
1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0,
0,
],
[
0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1,
0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0,
0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
1,
],
[
1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1,
1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1,
1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1,
1,
],
[
1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0,
1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1,
1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0,
1,
],
[
1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0,
1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1,
0,
],
[
0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0,
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1,
0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
1,
],
[
0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1,
0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0,
0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1,
0,
],
[
1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1,
1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1,
1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0,
0,
],
[
0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1,
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1,
1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0,
0,
],
[
0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0,
1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1,
1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,
1,
],
[
0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0,
1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0,
0,
],
[
0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1,
0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0,
1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1,
],
[
0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1,
1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0,
0,
],
[
0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0,
1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0,
0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0,
0,
],
[
0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0,
0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1,
0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1,
1,
],
[
1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1,
0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1,
0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1,
0,
],
[
0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1,
1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0,
1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
0,
],
[
1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0,
0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0,
0,
],
[
0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1,
0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1,
0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
],
[
0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0,
1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0,
1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1,
1,
],
[
1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1,
0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0,
0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
1,
],
[
0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0,
0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
0,
],
[
1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1,
1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1,
0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1,
1,
],
[
1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0,
1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1,
1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0,
1,
],
[
1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0,
1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1,
1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1,
0,
],
[
1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0,
1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1,
1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1,
1,
],
[
1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1,
0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0,
1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1,
0,
],
[
1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1,
1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1,
0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
1,
],
[
0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0,
1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0,
1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0,
1,
],
[
1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0,
0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1,
1,
],
[
0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1,
0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0,
0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0,
0,
],
[
0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1,
1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0,
0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
0,
],
[
0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1,
0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1,
0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0,
1,
],
[
0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1,
0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0,
0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1,
1,
],
[
1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0,
0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1,
1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0,
1,
],
[
1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1,
0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0,
1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1,
0,
],
[
1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1,
0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0,
1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1,
1,
],
[
0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1,
0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0,
0,
],
[
1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1,
0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1,
1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1,
],
[
0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0,
1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0,
1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0,
0,
],
[
1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0,
0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0,
1,
],
[
0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1,
0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1,
1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0,
1,
],
[
1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1,
1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1,
0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1,
0,
],
[
1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1,
1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1,
0,
],
[
1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1,
1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0,
0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0,
0,
],
[
0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1,
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0,
0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0,
1,
],
[
0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1,
0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0,
0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1,
0,
],
[
0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1,
1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1,
0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0,
],
];
pub fn ldpc_encode_generic<P: LdpcParams>(info: &[u8], cw: &mut [u8]) {
debug_assert_eq!(info.len(), P::K, "info length must equal P::K");
debug_assert_eq!(cw.len(), P::N, "cw length must equal P::N");
cw[..P::K].copy_from_slice(info);
for j in 0..P::M {
let mut p = 0u8;
for k in 0..P::K {
p ^= P::gen_parity(j, k) & info[k];
}
cw[P::K + j] = p;
}
}
pub fn ldpc_encode(info: &[u8; LDPC_K]) -> [u8; LDPC_N] {
let mut cw = [0u8; LDPC_N];
ldpc_encode_generic::<Ldpc174_91Params>(info.as_slice(), cw.as_mut_slice());
cw
}
pub struct OsdResult {
pub message77: [u8; 77],
pub info: Vec<u8>,
pub codeword: Vec<u8>,
pub hard_errors: u32,
}
pub fn osd_decode_deep(
llr: &[f32; LDPC_N],
ndeep: u8,
verify: Option<fn(&[u8]) -> bool>,
) -> Option<OsdResult> {
osd_decode_generic::<Ldpc174_91Params>(llr.as_slice(), ndeep, LDPC_K, verify, false)
}
pub fn osd_decode_deep4(
llr: &[f32; LDPC_N],
k4_limit: usize,
verify: Option<fn(&[u8]) -> bool>,
) -> Option<OsdResult> {
osd_decode_generic::<Ldpc174_91Params>(llr.as_slice(), 4, k4_limit, verify, false)
}
pub fn osd_decode(llr: &[f32; LDPC_N]) -> Option<OsdResult> {
osd_decode_generic::<Ldpc174_91Params>(llr.as_slice(), 2, LDPC_K, Some(check_crc14), false)
}
const NPRE1_PARITY_WINDOW: usize = 40;
const NPRE1_GATE_NDEEP2: u32 = 10;
const NPRE1_GATE_NDEEP3: u32 = 12;
const NPRE2_NTAU: usize = 14;
struct OsdSetup {
perm: Vec<usize>,
g: Vec<u8>,
hdec_perm: Vec<u8>,
absrx_perm: Vec<f32>,
c_perm: Vec<u8>,
}
struct OsdBest {
state: Option<(Vec<u8>, Vec<u8>)>,
dd: f32,
}
impl OsdBest {
fn new() -> Self {
Self {
state: None,
dd: f32::INFINITY,
}
}
fn maybe_update(&mut self, decoded: Vec<u8>, cw: Vec<u8>, dd: f32) {
if dd < self.dd {
self.dd = dd;
self.state = Some((decoded, cw));
}
}
}
fn osd_setup_ldpc174_91(llr: &[f32; LDPC_N]) -> Option<OsdSetup> {
let n = LDPC_N;
let k = LDPC_K;
let mut perm: Vec<usize> = (0..n).collect();
perm.sort_unstable_by(|&a, &b| {
llr[b]
.abs()
.partial_cmp(&llr[a].abs())
.unwrap_or(core::cmp::Ordering::Equal)
});
const WORDS: usize = LDPC_N.div_ceil(64); type PackedRow = [u64; WORDS];
let mut g_packed: Vec<PackedRow> = vec![[0u64; WORDS]; k];
for row in 0..k {
for col in 0..n {
let j = perm[col];
let bit = if j < k {
(row == j) as u64
} else {
<Ldpc174_91Params as LdpcParams>::gen_parity(j - k, row) as u64
};
g_packed[row][col / 64] |= bit << (col % 64);
}
}
let mut pivot_col: Vec<usize> = vec![0; k];
let mut pivot_row = 0usize;
for col in 0..n {
if pivot_row >= k {
break;
}
let word = col / 64;
let bit = col % 64;
let mut found: Option<usize> = None;
for r in pivot_row..k {
if (g_packed[r][word] >> bit) & 1 != 0 {
found = Some(r);
break;
}
}
if let Some(r) = found {
if r != pivot_row {
g_packed.swap(r, pivot_row);
}
let pivot = g_packed[pivot_row];
for r2 in 0..k {
if r2 != pivot_row && (g_packed[r2][word] >> bit) & 1 != 0 {
for c in 0..WORDS {
g_packed[r2][c] ^= pivot[c];
}
}
}
pivot_col[pivot_row] = col;
pivot_row += 1;
}
}
if pivot_row < k {
return None;
}
let mut g: Vec<u8> = vec![0u8; k * n];
for row in 0..k {
for col in 0..n {
g[row * n + col] = ((g_packed[row][col / 64] >> (col % 64)) & 1) as u8;
}
}
let mut m0: Vec<u8> = vec![0u8; k];
for r in 0..k {
let orig = perm[pivot_col[r]];
m0[r] = if llr[orig] > 0.0 { 1 } else { 0 };
}
let mut hdec_perm: Vec<u8> = vec![0u8; n];
let mut absrx_perm: Vec<f32> = vec![0.0f32; n];
for col in 0..n {
hdec_perm[col] = if llr[perm[col]] > 0.0 { 1u8 } else { 0u8 };
absrx_perm[col] = llr[perm[col]].abs();
}
let mut c_perm: Vec<u8> = vec![0u8; n];
for r in 0..k {
if m0[r] == 1 {
for col in 0..n {
c_perm[col] ^= g[r * n + col];
}
}
}
Some(OsdSetup {
perm,
g,
hdec_perm,
absrx_perm,
c_perm,
})
}
#[cfg(test)]
fn osd_setup_ldpc174_91_fortran_pivot(llr: &[f32; LDPC_N]) -> Option<OsdSetup> {
let n = LDPC_N;
let k = LDPC_K;
let mut perm: Vec<usize> = (0..n).collect();
perm.sort_unstable_by(|&a, &b| {
llr[b]
.abs()
.partial_cmp(&llr[a].abs())
.unwrap_or(core::cmp::Ordering::Equal)
});
let mut g: Vec<u8> = vec![0u8; k * n];
for row in 0..k {
for col in 0..n {
let j = perm[col];
g[row * n + col] = if j < k {
(row == j) as u8
} else {
<Ldpc174_91Params as LdpcParams>::gen_parity(j - k, row)
};
}
}
const PIVOT_WINDOW_SLACK: usize = 20;
for id in 0..k {
let upper = (k + PIVOT_WINDOW_SLACK).min(n);
let mut found: Option<usize> = None;
for icol in id..upper {
if g[id * n + icol] == 1 {
found = Some(icol);
break;
}
}
let Some(icol) = found else { continue };
if icol != id {
for r in 0..k {
g.swap(r * n + id, r * n + icol);
}
perm.swap(id, icol);
}
for ii in 0..k {
if ii != id && g[ii * n + id] == 1 {
for c in 0..n {
g[ii * n + c] ^= g[id * n + c];
}
}
}
}
let mut m0: Vec<u8> = vec![0u8; k];
for r in 0..k {
m0[r] = if llr[perm[r]] > 0.0 { 1 } else { 0 };
}
let mut hdec_perm: Vec<u8> = vec![0u8; n];
let mut absrx_perm: Vec<f32> = vec![0.0f32; n];
for col in 0..n {
hdec_perm[col] = if llr[perm[col]] > 0.0 { 1u8 } else { 0u8 };
absrx_perm[col] = llr[perm[col]].abs();
}
let mut c_perm: Vec<u8> = vec![0u8; n];
for r in 0..k {
if m0[r] == 1 {
for col in 0..n {
c_perm[col] ^= g[r * n + col];
}
}
}
Some(OsdSetup {
perm,
g,
hdec_perm,
absrx_perm,
c_perm,
})
}
#[cfg(test)]
pub(crate) fn osd_decode_npre1_fortran_pivot(llr: &[f32; LDPC_N]) -> Option<OsdResult> {
let setup = osd_setup_ldpc174_91_fortran_pivot(llr)?;
let mut best = OsdBest::new();
osd_npre1_pass(&setup, &mut best, NPRE1_GATE_NDEEP2);
osd_result_from_best(llr, best)
}
#[cfg(test)]
pub(crate) fn osd_debug_basis_sets(llr: &[f32; LDPC_N]) -> (Vec<usize>, Vec<usize>) {
let current = osd_setup_ldpc174_91(llr)
.map(|s| s.perm[..LDPC_K].to_vec())
.unwrap_or_default();
let fortran_pivot = osd_setup_ldpc174_91_fortran_pivot(llr)
.map(|s| s.perm[..LDPC_K].to_vec())
.unwrap_or_default();
(current, fortran_pivot)
}
fn try_candidate_ldpc174_91(
perm: &[usize],
cp: &[u8],
c_unperm: &mut [u8; LDPC_N],
) -> Option<(Vec<u8>, Vec<u8>)> {
for col in 0..LDPC_N {
c_unperm[perm[col]] = cp[col];
}
if !check_crc14(&c_unperm[..LDPC_K]) {
return None;
}
Some((c_unperm[..LDPC_K].to_vec(), c_unperm.to_vec()))
}
fn osd_npre1_pass(setup: &OsdSetup, best: &mut OsdBest, ntheta: u32) {
let n = LDPC_N;
let k = LDPC_K;
let nt = NPRE1_PARITY_WINDOW.min(n - k);
let mut ce_iflag = [0u8; LDPC_N];
let mut e2sub = [0u8; LDPC_N - LDPC_K];
let mut e2 = [0u8; LDPC_N - LDPC_K];
let mut ce_pair = [0u8; LDPC_N];
let mut c_unperm = [0u8; LDPC_N];
{
let mut wd = 0.0f32;
for col in k..n {
if setup.c_perm[col] != setup.hdec_perm[col] {
wd += setup.absrx_perm[col];
}
}
if wd < best.dd
&& let Some((d, cw)) =
try_candidate_ldpc174_91(&setup.perm, &setup.c_perm, &mut c_unperm)
{
best.maybe_update(d, cw, wd);
}
}
for iflag in (0..k).rev() {
ce_iflag.copy_from_slice(&setup.c_perm);
for col in 0..n {
ce_iflag[col] ^= setup.g[iflag * n + col];
}
for j in 0..(n - k) {
e2sub[j] = ce_iflag[k + j] ^ setup.hdec_perm[k + j];
}
let mut parity_err: u32 = 0;
for j in 0..nt {
parity_err += e2sub[j] as u32;
}
let d1 = setup.absrx_perm[iflag];
if parity_err < ntheta {
let mut parity_wd = 0.0f32;
for j in 0..(n - k) {
if e2sub[j] == 1 {
parity_wd += setup.absrx_perm[k + j];
}
}
let dd = d1 + parity_wd;
if dd < best.dd
&& let Some((d, cw)) =
try_candidate_ldpc174_91(&setup.perm, &ce_iflag, &mut c_unperm)
{
best.maybe_update(d, cw, dd);
}
}
for n1 in (0..iflag).rev() {
for j in 0..(n - k) {
e2[j] = e2sub[j] ^ setup.g[n1 * n + (k + j)];
}
let mut parity_err_pair: u32 = 0;
for j in 0..nt {
parity_err_pair += e2[j] as u32;
}
if parity_err_pair + 2 > ntheta {
continue;
}
ce_pair.copy_from_slice(&ce_iflag);
for col in 0..n {
ce_pair[col] ^= setup.g[n1 * n + col];
}
let mut parity_wd_pair = 0.0f32;
for j in 0..(n - k) {
if e2[j] == 1 {
parity_wd_pair += setup.absrx_perm[k + j];
}
}
let dd = d1 + setup.absrx_perm[n1] + parity_wd_pair;
if dd < best.dd
&& let Some((d, cw)) =
try_candidate_ldpc174_91(&setup.perm, &ce_pair, &mut c_unperm)
{
best.maybe_update(d, cw, dd);
}
}
}
}
struct OsdNpre2Table {
fp: Vec<i32>,
pairs: Vec<(u8, u8)>,
next: Vec<i32>,
}
impl OsdNpre2Table {
fn new(ntau: usize) -> Self {
let buckets = 1usize << ntau;
Self {
fp: vec![-1; buckets],
pairs: Vec::new(),
next: Vec::new(),
}
}
fn insert(&mut self, key: usize, i1: u8, i2: u8) {
let idx = self.pairs.len() as i32;
self.pairs.push((i1, i2));
self.next.push(self.fp[key]);
self.fp[key] = idx;
}
fn iter_pairs(&self, key: usize) -> impl Iterator<Item = (u8, u8)> + '_ {
let mut cur = self.fp[key];
core::iter::from_fn(move || {
if cur < 0 {
None
} else {
let entry = self.pairs[cur as usize];
cur = self.next[cur as usize];
Some(entry)
}
})
}
}
fn build_npre2_table(setup: &OsdSetup, ntau: usize) -> OsdNpre2Table {
let n = LDPC_N;
let k = LDPC_K;
let mut table = OsdNpre2Table::new(ntau);
let mut row_keys = [0u16; LDPC_K];
for i in 0..k {
let mut rk: u16 = 0;
let row_base = i * n;
for j in 0..ntau {
if setup.g[row_base + k + j] != 0 {
rk |= 1u16 << (ntau - 1 - j);
}
}
row_keys[i] = rk;
}
for i1 in 0..k {
for i2 in 0..i1 {
let key = (row_keys[i1] ^ row_keys[i2]) as usize;
table.insert(key, i1 as u8, i2 as u8);
}
}
table
}
fn osd_npre2_pass(setup: &OsdSetup, best: &mut OsdBest, ntau: usize) {
let n = LDPC_N;
let k = LDPC_K;
let table = build_npre2_table(setup, ntau);
let mut ce_misub = [0u8; LDPC_N];
let mut e2sub = [0u8; LDPC_N - LDPC_K];
let mut ce_test = [0u8; LDPC_N];
let mut c_unperm = [0u8; LDPC_N];
for iflag in (0..k).rev() {
ce_misub.copy_from_slice(&setup.c_perm);
for col in 0..n {
ce_misub[col] ^= setup.g[iflag * n + col];
}
for j in 0..(n - k) {
e2sub[j] = ce_misub[k + j] ^ setup.hdec_perm[k + j];
}
let mut base_key: usize = 0;
for j in 0..ntau {
if e2sub[j] != 0 {
base_key |= 1 << (ntau - 1 - j);
}
}
for i2 in 0..=ntau {
let key = if i2 == 0 {
base_key
} else {
base_key ^ (1 << (ntau - i2))
};
for (in1, in2) in table.iter_pairs(key) {
let in1_u = in1 as usize;
let in2_u = in2 as usize;
if iflag == in1_u || iflag == in2_u || in1_u == in2_u {
continue;
}
ce_test.copy_from_slice(&ce_misub);
for col in 0..n {
ce_test[col] ^= setup.g[in1_u * n + col] ^ setup.g[in2_u * n + col];
}
let mut dd = 0.0f32;
for col in 0..n {
if ce_test[col] != setup.hdec_perm[col] {
dd += setup.absrx_perm[col];
}
}
if dd < best.dd
&& let Some((d, cw)) =
try_candidate_ldpc174_91(&setup.perm, &ce_test, &mut c_unperm)
{
best.maybe_update(d, cw, dd);
}
}
}
}
}
fn osd_result_from_best(llr: &[f32; LDPC_N], best: OsdBest) -> Option<OsdResult> {
let (decoded, codeword) = best.state?;
let mut hard_errors = 0u32;
for i in 0..LDPC_N {
if (codeword[i] == 1) != (llr[i] > 0.0) {
hard_errors += 1;
}
}
let mut message77 = [0u8; 77];
message77.copy_from_slice(&decoded[..77]);
Some(OsdResult {
message77,
info: decoded,
codeword,
hard_errors,
})
}
pub fn osd_decode_npre1(llr: &[f32; LDPC_N]) -> Option<OsdResult> {
let setup = osd_setup_ldpc174_91(llr)?;
let mut best = OsdBest::new();
osd_npre1_pass(&setup, &mut best, NPRE1_GATE_NDEEP2);
osd_result_from_best(llr, best)
}
pub fn osd_decode_npre1_npre2(llr: &[f32; LDPC_N]) -> Option<OsdResult> {
let setup = osd_setup_ldpc174_91(llr)?;
let mut best = OsdBest::new();
osd_npre1_pass(&setup, &mut best, NPRE1_GATE_NDEEP3);
osd_npre2_pass(&setup, &mut best, NPRE2_NTAU);
osd_result_from_best(llr, best)
}
fn osd_setup_generic_packed<P: LdpcParams>(
llr: &[f32],
) -> Option<(Vec<usize>, Vec<u8>, Vec<usize>)> {
let n = P::N;
let k = P::K;
let words = n.div_ceil(64);
let mut perm: Vec<usize> = (0..n).collect();
perm.sort_unstable_by(|&a, &b| {
llr[b]
.abs()
.partial_cmp(&llr[a].abs())
.unwrap_or(core::cmp::Ordering::Equal)
});
let mut g_packed: Vec<Vec<u64>> = vec![vec![0u64; words]; k];
for row in 0..k {
for col in 0..n {
let j = perm[col];
let bit = if j < k {
(row == j) as u64
} else {
P::gen_parity(j - k, row) as u64
};
if bit == 1 {
g_packed[row][col / 64] |= 1 << (col % 64);
}
}
}
let mut pivot_col: Vec<usize> = vec![0; k];
let mut pivot_row = 0usize;
for col in 0..n {
if pivot_row >= k {
break;
}
let word = col / 64;
let bit = col % 64;
let mut found: Option<usize> = None;
for r in pivot_row..k {
if (g_packed[r][word] >> bit) & 1 != 0 {
found = Some(r);
break;
}
}
let Some(r) = found else { continue };
if r != pivot_row {
g_packed.swap(r, pivot_row);
}
let pivot = g_packed[pivot_row].clone();
for r2 in 0..k {
if r2 != pivot_row && (g_packed[r2][word] >> bit) & 1 != 0 {
for c in 0..words {
g_packed[r2][c] ^= pivot[c];
}
}
}
pivot_col[pivot_row] = col;
pivot_row += 1;
}
if pivot_row < k {
return None; }
let mut g: Vec<u8> = vec![0u8; k * n];
for row in 0..k {
for col in 0..n {
g[row * n + col] = ((g_packed[row][col / 64] >> (col % 64)) & 1) as u8;
}
}
Some((perm, g, pivot_col))
}
#[cfg(feature = "internal-testing")]
pub fn npre1_pattern_counts<P: LdpcParams>(llr: &[f32]) -> Option<(u32, u32)> {
const NT: usize = 40; const NTHETA: u32 = 12;
let n = P::N;
let k = P::K;
let (perm, g, pivot_col) = osd_setup_generic_packed::<P>(llr)?;
let mut hdec = alloc::vec![0u8; n];
for (col, h) in hdec.iter_mut().enumerate() {
*h = if llr[perm[col]] > 0.0 { 1 } else { 0 };
}
let mut mrb = alloc::vec![0u8; k];
for (r, m) in mrb.iter_mut().enumerate() {
let orig = perm[pivot_col[r]];
*m = if llr[orig] > 0.0 { 1 } else { 0 };
}
let mut c0 = alloc::vec![0u8; n];
for (r, &mr) in mrb.iter().enumerate() {
if mr == 1 {
for (col, c) in c0.iter_mut().enumerate() {
*c ^= g[r * n + col];
}
}
}
let window = NT.min(n - k);
let mut ntotal = 0u32;
let mut npostgate = 0u32;
let mut e2sub = alloc::vec![0u8; n - k];
for p in 0..k {
for i in 0..(n - k) {
e2sub[i] = (c0[k + i] ^ g[p * n + k + i]) ^ hdec[k + i];
}
let mismatches: u32 = e2sub[..window].iter().map(|&b| b as u32).sum();
ntotal += 1;
if mismatches < NTHETA {
npostgate += 1;
}
for n1 in (0..p).rev() {
let mismatches: u32 = (0..window)
.map(|i| (e2sub[i] ^ g[n1 * n + k + i]) as u32)
.sum();
ntotal += 1;
if mismatches + 2 <= NTHETA {
npostgate += 1;
}
}
}
Some((ntotal, npostgate))
}
pub fn osd_decode_generic<P: LdpcParams>(
llr: &[f32],
ndeep: u8,
k4_limit: usize,
verify: Option<fn(&[u8]) -> bool>,
k4_tail: bool,
) -> Option<OsdResult> {
debug_assert_eq!(llr.len(), P::N, "llr length must equal P::N");
let n = P::N;
let k = P::K;
let Some((perm, g, pivot_col)) = osd_setup_generic_packed::<P>(llr) else {
return None; };
const OSD_MAX_N: usize = 256; const OSD_WORDS: usize = OSD_MAX_N.div_ceil(64);
let mut mrb = [0u8; OSD_MAX_N];
let mrb = &mut mrb[..k];
for r in 0..k {
let orig = perm[pivot_col[r]];
mrb[r] = if llr[orig] > 0.0 { 1 } else { 0 };
}
let mut g_packed = [[0u64; OSD_WORDS]; OSD_MAX_N];
let g_packed = &mut g_packed[..k];
for (row, row_words) in g_packed.iter_mut().enumerate() {
for col in 0..n {
if g[row * n + col] != 0 {
row_words[col / 64] |= 1 << (col % 64);
}
}
}
let mut c_perm_packed = [0u64; OSD_WORDS];
for r in 0..k {
if mrb[r] == 1 {
for w in 0..OSD_WORDS {
c_perm_packed[w] ^= g_packed[r][w];
}
}
}
let mut hdec_perm = [0u8; OSD_MAX_N];
let hdec_perm = &mut hdec_perm[..n];
let mut absrx_perm = [0.0f32; OSD_MAX_N];
let absrx_perm = &mut absrx_perm[..n];
for col in 0..n {
hdec_perm[col] = if llr[perm[col]] > 0.0 { 1u8 } else { 0u8 };
absrx_perm[col] = llr[perm[col]].abs();
}
let mut inv_perm = [0usize; OSD_MAX_N];
let inv_perm = &mut inv_perm[..n];
for (col, &p) in perm.iter().enumerate() {
inv_perm[p] = col;
}
let mut best_wd: Option<f32> = None;
let mut best_codeword = [0u8; OSD_MAX_N];
let mut best_decoded = [0u8; OSD_MAX_N];
let mut scratch_c = [0u8; OSD_MAX_N];
let mut scratch_decoded = [0u8; OSD_MAX_N];
let mut try_and_update = |cp: &[u64]| {
let decoded = &mut scratch_decoded[..k];
for (i, d) in decoded.iter_mut().enumerate() {
let idx = inv_perm[i];
*d = ((cp[idx / 64] >> (idx % 64)) & 1) as u8;
}
if let Some(f) = verify
&& !f(decoded)
{
return;
}
let c = &mut scratch_c[..n];
let mut wd = 0.0f32;
for col in 0..n {
let bit = ((cp[col / 64] >> (col % 64)) & 1) as u8;
c[perm[col]] = bit;
if bit != hdec_perm[col] {
wd += absrx_perm[col];
}
}
if best_wd.is_none_or(|bd| wd < bd) {
best_wd = Some(wd);
best_codeword[..n].copy_from_slice(&scratch_c[..n]);
best_decoded[..k].copy_from_slice(&scratch_decoded[..k]);
}
};
try_and_update(&c_perm_packed);
let mut c1_packed = [0u64; OSD_WORDS];
let mut c2_packed = [0u64; OSD_WORDS];
let mut c3_packed = [0u64; OSD_WORDS];
let mut c4_packed = [0u64; OSD_WORDS];
for k1 in 0..k {
for w in 0..OSD_WORDS {
c1_packed[w] = c_perm_packed[w] ^ g_packed[k1][w];
}
try_and_update(&c1_packed);
if ndeep < 2 {
continue;
}
for k2 in (k1 + 1)..k {
for w in 0..OSD_WORDS {
c2_packed[w] = c1_packed[w] ^ g_packed[k2][w];
}
try_and_update(&c2_packed);
if ndeep < 3 {
continue;
}
for k3 in (k2 + 1)..k {
for w in 0..OSD_WORDS {
c3_packed[w] = c2_packed[w] ^ g_packed[k3][w];
}
try_and_update(&c3_packed);
if ndeep >= 4 {
let (gate, k4_start, k4_end) = if k4_tail {
(k1 >= k.saturating_sub(k4_limit), k3 + 1, k)
} else {
(k3 + 1 < k4_limit, k3 + 1, k4_limit.min(k))
};
if gate {
for k4 in k4_start..k4_end {
for w in 0..OSD_WORDS {
c4_packed[w] = c3_packed[w] ^ g_packed[k4][w];
}
try_and_update(&c4_packed);
}
}
}
}
}
}
best_wd?;
let codeword = &best_codeword[..n];
let decoded = &best_decoded[..k];
let mut hard_errors = 0u32;
for i in 0..n {
if (codeword[i] == 1) != (llr[i] > 0.0) {
hard_errors += 1;
}
}
let mut message77 = [0u8; 77];
message77.copy_from_slice(&decoded[..77]);
Some(OsdResult {
message77,
info: decoded.to_vec(),
codeword: codeword.to_vec(),
hard_errors,
})
}
const OSD_NPRE_MAX_N: usize = 256; const OSD_NPRE_WORDS: usize = OSD_NPRE_MAX_N.div_ceil(64);
pub fn osd_decode_npre_generic<P: LdpcParams>(
llr: &[f32],
ntheta: u32,
ntau: usize,
run_npre2: bool,
verify: Option<fn(&[u8]) -> bool>,
) -> Option<OsdResult> {
debug_assert_eq!(llr.len(), P::N, "llr length must equal P::N");
let n = P::N;
let k = P::K;
let Some((perm, g, pivot_col)) = osd_setup_generic_packed::<P>(llr) else {
return None; };
let mut mrb = [0u8; OSD_NPRE_MAX_N];
let mrb = &mut mrb[..k];
for r in 0..k {
let orig = perm[pivot_col[r]];
mrb[r] = if llr[orig] > 0.0 { 1 } else { 0 };
}
let mut g_packed = [[0u64; OSD_NPRE_WORDS]; OSD_NPRE_MAX_N];
let g_packed = &mut g_packed[..k];
for (row, row_words) in g_packed.iter_mut().enumerate() {
for col in 0..n {
if g[row * n + col] != 0 {
row_words[col / 64] |= 1 << (col % 64);
}
}
}
let mut c_perm_packed = [0u64; OSD_NPRE_WORDS];
for r in 0..k {
if mrb[r] == 1 {
for w in 0..OSD_NPRE_WORDS {
c_perm_packed[w] ^= g_packed[r][w];
}
}
}
let mut hdec_perm = [0u8; OSD_NPRE_MAX_N];
let hdec_perm = &mut hdec_perm[..n];
let mut absrx_perm = [0.0f32; OSD_NPRE_MAX_N];
let absrx_perm = &mut absrx_perm[..n];
for col in 0..n {
hdec_perm[col] = if llr[perm[col]] > 0.0 { 1u8 } else { 0u8 };
absrx_perm[col] = llr[perm[col]].abs();
}
let mut inv_perm = [0usize; OSD_NPRE_MAX_N];
let inv_perm = &mut inv_perm[..n];
for (col, &p) in perm.iter().enumerate() {
inv_perm[p] = col;
}
let mut best_wd: Option<f32> = None;
let mut best_codeword = [0u8; OSD_NPRE_MAX_N];
let mut best_decoded = [0u8; OSD_NPRE_MAX_N];
let mut scratch_c = [0u8; OSD_NPRE_MAX_N];
let mut scratch_decoded = [0u8; OSD_NPRE_MAX_N];
let mut try_and_update = |cp: &[u64]| {
let decoded = &mut scratch_decoded[..k];
for (i, d) in decoded.iter_mut().enumerate() {
let idx = inv_perm[i];
*d = ((cp[idx / 64] >> (idx % 64)) & 1) as u8;
}
if let Some(f) = verify
&& !f(decoded)
{
return;
}
let c = &mut scratch_c[..n];
let mut wd = 0.0f32;
for col in 0..n {
let bit = ((cp[col / 64] >> (col % 64)) & 1) as u8;
c[perm[col]] = bit;
if bit != hdec_perm[col] {
wd += absrx_perm[col];
}
}
if best_wd.is_none_or(|bd| wd < bd) {
best_wd = Some(wd);
best_codeword[..n].copy_from_slice(&scratch_c[..n]);
best_decoded[..k].copy_from_slice(&scratch_decoded[..k]);
}
};
try_and_update(&c_perm_packed);
let nt = NPRE1_PARITY_WINDOW.min(n - k);
let mut e2sub = [0u8; OSD_NPRE_MAX_N];
let e2sub = &mut e2sub[..(n - k)];
let mut e2 = [0u8; OSD_NPRE_MAX_N];
let e2 = &mut e2[..(n - k)];
let mut ce_iflag_packed = [0u64; OSD_NPRE_WORDS];
let mut ce_pair_packed = [0u64; OSD_NPRE_WORDS];
for iflag in (0..k).rev() {
for w in 0..OSD_NPRE_WORDS {
ce_iflag_packed[w] = c_perm_packed[w] ^ g_packed[iflag][w];
}
for (j, e) in e2sub.iter_mut().enumerate() {
let col = k + j;
let bit = ((ce_iflag_packed[col / 64] >> (col % 64)) & 1) as u8;
*e = bit ^ hdec_perm[col];
}
let parity_err: u32 = e2sub[..nt].iter().map(|&b| b as u32).sum();
if parity_err < ntheta {
try_and_update(&ce_iflag_packed);
}
for n1 in (0..iflag).rev() {
for j in 0..(n - k) {
let col = k + j;
let g_bit = ((g_packed[n1][col / 64] >> (col % 64)) & 1) as u8;
e2[j] = e2sub[j] ^ g_bit;
}
let parity_err_pair: u32 = e2[..nt].iter().map(|&b| b as u32).sum();
if parity_err_pair + 2 > ntheta {
continue;
}
for w in 0..OSD_NPRE_WORDS {
ce_pair_packed[w] = ce_iflag_packed[w] ^ g_packed[n1][w];
}
try_and_update(&ce_pair_packed);
}
}
if run_npre2 {
let mut table = OsdNpre2Table::new(ntau);
let mut row_keys = [0u16; OSD_NPRE_MAX_N];
let row_keys = &mut row_keys[..k];
for (i, rk) in row_keys.iter_mut().enumerate() {
let mut key: u16 = 0;
for j in 0..ntau {
let col = k + j;
let bit = ((g_packed[i][col / 64] >> (col % 64)) & 1) as u8;
if bit != 0 {
key |= 1u16 << (ntau - 1 - j);
}
}
*rk = key;
}
for i1 in 0..k {
for i2 in 0..i1 {
let key = (row_keys[i1] ^ row_keys[i2]) as usize;
table.insert(key, i1 as u8, i2 as u8);
}
}
let mut ce_misub_packed = [0u64; OSD_NPRE_WORDS];
let mut ce_test_packed = [0u64; OSD_NPRE_WORDS];
for iflag in (0..k).rev() {
for w in 0..OSD_NPRE_WORDS {
ce_misub_packed[w] = c_perm_packed[w] ^ g_packed[iflag][w];
}
for (j, e) in e2sub.iter_mut().enumerate() {
let col = k + j;
let bit = ((ce_misub_packed[col / 64] >> (col % 64)) & 1) as u8;
*e = bit ^ hdec_perm[col];
}
let mut base_key: usize = 0;
for j in 0..ntau {
if e2sub[j] != 0 {
base_key |= 1 << (ntau - 1 - j);
}
}
for i2 in 0..=ntau {
let key = if i2 == 0 {
base_key
} else {
base_key ^ (1 << (ntau - i2))
};
for (in1, in2) in table.iter_pairs(key) {
let in1_u = in1 as usize;
let in2_u = in2 as usize;
if iflag == in1_u || iflag == in2_u || in1_u == in2_u {
continue;
}
for w in 0..OSD_NPRE_WORDS {
ce_test_packed[w] =
ce_misub_packed[w] ^ g_packed[in1_u][w] ^ g_packed[in2_u][w];
}
try_and_update(&ce_test_packed);
}
}
}
}
best_wd?;
let codeword = &best_codeword[..n];
let decoded = &best_decoded[..k];
let mut hard_errors = 0u32;
for i in 0..n {
if (codeword[i] == 1) != (llr[i] > 0.0) {
hard_errors += 1;
}
}
let mut message77 = [0u8; 77];
message77.copy_from_slice(&decoded[..77]);
Some(OsdResult {
message77,
info: decoded.to_vec(),
codeword: codeword.to_vec(),
hard_errors,
})
}
#[cfg(test)]
mod tests {
use super::*;
fn osd_setup_ldpc174_91_byte_reference(llr: &[f32; LDPC_N]) -> Option<OsdSetup> {
let n = LDPC_N;
let k = LDPC_K;
let mut perm: Vec<usize> = (0..n).collect();
perm.sort_unstable_by(|&a, &b| {
llr[b]
.abs()
.partial_cmp(&llr[a].abs())
.unwrap_or(core::cmp::Ordering::Equal)
});
let mut g: Vec<u8> = vec![0u8; k * n];
for row in 0..k {
for col in 0..n {
let j = perm[col];
g[row * n + col] = if j < k {
(row == j) as u8
} else {
<Ldpc174_91Params as LdpcParams>::gen_parity(j - k, row)
};
}
}
let mut pivot_col: Vec<usize> = vec![0; k];
let mut pivot_row = 0usize;
for col in 0..n {
if pivot_row >= k {
break;
}
let mut found: Option<usize> = None;
for r in pivot_row..k {
if g[r * n + col] != 0 {
found = Some(r);
break;
}
}
if let Some(r) = found {
if r != pivot_row {
for c in 0..n {
g.swap(r * n + c, pivot_row * n + c);
}
}
for r2 in 0..k {
if r2 != pivot_row && g[r2 * n + col] != 0 {
for c in 0..n {
g[r2 * n + c] ^= g[pivot_row * n + c];
}
}
}
pivot_col[pivot_row] = col;
pivot_row += 1;
}
}
if pivot_row < k {
return None;
}
let mut m0: Vec<u8> = vec![0u8; k];
for r in 0..k {
let orig = perm[pivot_col[r]];
m0[r] = if llr[orig] > 0.0 { 1 } else { 0 };
}
let mut hdec_perm: Vec<u8> = vec![0u8; n];
let mut absrx_perm: Vec<f32> = vec![0.0f32; n];
for col in 0..n {
hdec_perm[col] = if llr[perm[col]] > 0.0 { 1u8 } else { 0u8 };
absrx_perm[col] = llr[perm[col]].abs();
}
let mut c_perm: Vec<u8> = vec![0u8; n];
for r in 0..k {
if m0[r] == 1 {
for col in 0..n {
c_perm[col] ^= g[r * n + col];
}
}
}
Some(OsdSetup {
perm,
g,
hdec_perm,
absrx_perm,
c_perm,
})
}
#[test]
fn packed_elimination_matches_byte_reference() {
fn synth_llr(seed: u32) -> [f32; LDPC_N] {
let mut llr = [0f32; LDPC_N];
let mut s = seed;
for x in llr.iter_mut() {
s = s.wrapping_mul(1_103_515_245).wrapping_add(12345);
let u = (s >> 8) as f32 / (1u32 << 24) as f32; *x = (u - 0.5) * 6.0;
}
llr
}
for seed in [0x1234_5678u32, 1, 42, 0xdead_beef, 0xc0ffee] {
let llr = synth_llr(seed);
let expected = osd_setup_ldpc174_91_byte_reference(&llr);
let actual = osd_setup_ldpc174_91(&llr);
match (expected, actual) {
(Some(e), Some(a)) => {
assert_eq!(e.perm, a.perm, "seed={seed}: perm mismatch");
assert_eq!(e.g, a.g, "seed={seed}: g mismatch");
assert_eq!(e.hdec_perm, a.hdec_perm, "seed={seed}: hdec_perm mismatch");
assert_eq!(
e.absrx_perm, a.absrx_perm,
"seed={seed}: absrx_perm mismatch"
);
assert_eq!(e.c_perm, a.c_perm, "seed={seed}: c_perm mismatch");
}
(None, None) => {}
_ => panic!("seed={seed}: Some/None mismatch between reference and packed"),
}
}
let all_zero = [0f32; LDPC_N];
let mut alternating = [0f32; LDPC_N];
for (i, x) in alternating.iter_mut().enumerate() {
*x = if i % 2 == 0 { 1.0 } else { -1.0 };
}
for (label, llr) in [("all_zero", all_zero), ("alternating", alternating)] {
let expected = osd_setup_ldpc174_91_byte_reference(&llr);
let actual = osd_setup_ldpc174_91(&llr);
match (expected, actual) {
(Some(e), Some(a)) => {
assert_eq!(e.perm, a.perm, "{label}: perm mismatch");
assert_eq!(e.g, a.g, "{label}: g mismatch");
assert_eq!(e.hdec_perm, a.hdec_perm, "{label}: hdec_perm mismatch");
assert_eq!(e.absrx_perm, a.absrx_perm, "{label}: absrx_perm mismatch");
assert_eq!(e.c_perm, a.c_perm, "{label}: c_perm mismatch");
}
(None, None) => {}
_ => panic!("{label}: Some/None mismatch between reference and packed"),
}
}
}
#[test]
#[ignore = "manual diagnostic — issue #182 setup-vs-search cost split"]
fn issue_182_setup_vs_search_cost() {
let mut llr = [0f32; LDPC_N];
let mut seed = 0x1234_5678u32;
for x in llr.iter_mut() {
seed = seed.wrapping_mul(1_103_515_245).wrapping_add(12345);
let u = (seed >> 8) as f32 / (1u32 << 24) as f32; *x = (u - 0.5) * 6.0; }
const REPS: usize = 2000;
let t0 = std::time::Instant::now();
for _ in 0..REPS {
let _ = osd_setup_ldpc174_91(&llr);
}
let setup_only = t0.elapsed();
let t1 = std::time::Instant::now();
for _ in 0..REPS {
if let Some(setup) = osd_setup_ldpc174_91(&llr) {
let mut best = OsdBest::new();
osd_npre1_pass(&setup, &mut best, NPRE1_GATE_NDEEP2);
}
}
let setup_plus_search = t1.elapsed();
println!(
"setup_only: {:?}/call ({:?} total for {REPS} reps)",
setup_only / REPS as u32,
setup_only
);
println!(
"setup+npre1_pass: {:?}/call ({:?} total for {REPS} reps)",
setup_plus_search / REPS as u32,
setup_plus_search
);
println!(
"npre1_pass alone (by subtraction): {:?}/call",
(setup_plus_search.saturating_sub(setup_only)) / REPS as u32
);
}
#[test]
fn zero_llr_no_spurious_decode() {
let llr = [0.0f32; LDPC_N];
let _ = osd_decode(&llr);
}
#[test]
fn npre1_zero_llr_no_panic() {
let llr = [0.0f32; LDPC_N];
let _ = osd_decode_npre1(&llr);
}
#[test]
fn npre1_npre2_zero_llr_no_panic() {
let llr = [0.0f32; LDPC_N];
let _ = osd_decode_npre1_npre2(&llr);
}
#[test]
fn npre1_npre2_decodes_clean_codeword() {
use super::super::bp::crc14;
let mut info_with_crc = [0u8; LDPC_K];
for i in 0..77 {
info_with_crc[i] = ((i * 13 + 7) & 1) as u8;
}
let mut bytes = [0u8; 12];
for (i, &bit) in info_with_crc[..77].iter().enumerate() {
bytes[i / 8] |= (bit & 1) << (7 - (i % 8));
}
let crc = crc14(&bytes);
for j in 0..14 {
info_with_crc[77 + j] = ((crc >> (13 - j)) & 1) as u8;
}
let cw = ldpc_encode(&info_with_crc);
let mut llr = [0.0f32; LDPC_N];
for i in 0..LDPC_N {
llr[i] = if cw[i] == 1 { 4.0 } else { -4.0 };
}
let osd = osd_decode_npre1_npre2(&llr).expect("clean LLR must decode");
assert_eq!(&osd.info[..91], &info_with_crc[..]);
assert_eq!(osd.hard_errors, 0);
}
#[test]
fn npre1_decodes_clean_codeword() {
use super::super::bp::crc14;
let mut info_with_crc = [0u8; LDPC_K];
for i in 0..77 {
info_with_crc[i] = ((i * 13 + 7) & 1) as u8;
}
let mut bytes = [0u8; 12];
for (i, &bit) in info_with_crc[..77].iter().enumerate() {
bytes[i / 8] |= (bit & 1) << (7 - (i % 8));
}
let crc = crc14(&bytes);
for j in 0..14 {
info_with_crc[77 + j] = ((crc >> (13 - j)) & 1) as u8;
}
assert!(
check_crc14(&info_with_crc),
"test CRC packing must round-trip"
);
let cw = ldpc_encode(&info_with_crc);
let mut llr = [0.0f32; LDPC_N];
for i in 0..LDPC_N {
llr[i] = if cw[i] == 1 { 4.0 } else { -4.0 };
}
let osd = osd_decode_npre1(&llr).expect("clean LLR must decode");
assert_eq!(
&osd.info[..91],
&info_with_crc[..],
"decoded info bits must match the encoded input"
);
assert_eq!(osd.hard_errors, 0, "clean LLR has zero hard errors");
}
#[test]
fn verify_ldpc_encode_parity() {
use super::super::LDPC_M;
use super::super::tables::{NM, NRW};
let test_infos: &[[u8; LDPC_K]] = &[
[1u8; LDPC_K], {
let mut v = [0u8; LDPC_K];
for i in (0..LDPC_K).step_by(2) {
v[i] = 1;
}
v
}, {
let mut v = [0u8; LDPC_K];
for i in 0..77 {
v[i] = 1;
}
v
},
];
for (t, info) in test_infos.iter().enumerate() {
let cw = ldpc_encode(info);
for j in 0..LDPC_M {
let n = NRW[j] as usize;
let check: u8 = NM[j][..n]
.iter()
.map(|&i| cw[i as usize])
.fold(0u8, |acc, b| acc ^ b);
assert_eq!(
check, 0,
"GEN_PARITY inconsistency: parity check row {} failed for test info #{} — encoder bug confirmed",
j, t
);
}
}
}
}
#[cfg(test)]
mod packed_setup_differential {
use super::*;
use crate::fec::ldpc::params::{Ldpc128_90Params, Ldpc240_101Params};
pub(super) fn osd_setup_generic_reference<P: LdpcParams>(
llr: &[f32],
) -> Option<(Vec<usize>, Vec<u8>, Vec<usize>)> {
let n = P::N;
let k = P::K;
let mut perm: Vec<usize> = (0..n).collect();
perm.sort_unstable_by(|&a, &b| {
llr[b]
.abs()
.partial_cmp(&llr[a].abs())
.unwrap_or(core::cmp::Ordering::Equal)
});
let mut g: Vec<u8> = vec![0u8; k * n];
for row in 0..k {
for col in 0..n {
let j = perm[col];
g[row * n + col] = if j < k {
(row == j) as u8
} else {
P::gen_parity(j - k, row)
};
}
}
let mut pivot_col: Vec<usize> = vec![0; k];
let mut pivot_row = 0usize;
for col in 0..n {
if pivot_row >= k {
break;
}
let mut found: Option<usize> = None;
for r in pivot_row..k {
if g[r * n + col] != 0 {
found = Some(r);
break;
}
}
if let Some(r) = found {
if r != pivot_row {
for c in 0..n {
g.swap(r * n + c, pivot_row * n + c);
}
}
for r2 in 0..k {
if r2 != pivot_row && g[r2 * n + col] != 0 {
for c in 0..n {
g[r2 * n + c] ^= g[pivot_row * n + c];
}
}
}
pivot_col[pivot_row] = col;
pivot_row += 1;
}
}
if pivot_row < k {
return None;
}
Some((perm, g, pivot_col))
}
struct Xorshift32(u32);
impl Xorshift32 {
fn next_f32(&mut self) -> f32 {
self.0 ^= self.0 << 13;
self.0 ^= self.0 >> 17;
self.0 ^= self.0 << 5;
(self.0 % 4000) as f32 / 100.0 - 20.0 }
}
fn check_matches<P: LdpcParams>(seed: u32) {
const REPS: usize = 200;
let mut rng = Xorshift32(seed);
let mut mismatches = 0usize;
for t in 0..REPS {
let mut info = alloc::vec![0u8; P::K];
for b in info.iter_mut() {
rng.0 ^= rng.0 << 13;
rng.0 ^= rng.0 >> 17;
rng.0 ^= rng.0 << 5;
*b = (rng.0 & 1) as u8;
}
let mut cw = alloc::vec![0u8; P::N];
ldpc_encode_generic::<P>(&info, &mut cw);
let llr: alloc::vec::Vec<f32> = cw
.iter()
.map(|&b| {
let noise = rng.next_f32();
(if b == 1 { 10.0 } else { -10.0 }) + noise
})
.collect();
let reference = osd_setup_generic_reference::<P>(&llr);
let packed = osd_setup_generic_packed::<P>(&llr);
if reference != packed {
mismatches += 1;
eprintln!("trial {t}: reference={reference:?}\npacked={packed:?}");
}
}
assert_eq!(mismatches, 0, "{mismatches}/{REPS} trials diverged");
}
#[test]
fn matches_reference_ldpc128_90() {
check_matches::<Ldpc128_90Params>(0xC0FF_EE01);
}
#[test]
fn matches_reference_ldpc240_101() {
check_matches::<Ldpc240_101Params>(0xC0FF_EE02);
}
#[test]
fn matches_reference_ldpc174_91() {
check_matches::<Ldpc174_91Params>(0xC0FF_EE03);
}
#[test]
fn osd_decode_generic_recovers_clean_codeword() {
let info: alloc::vec::Vec<u8> = (0..Ldpc128_90Params::K)
.map(|i| (i % 3 == 0) as u8)
.collect();
let mut cw = alloc::vec![0u8; Ldpc128_90Params::N];
ldpc_encode_generic::<Ldpc128_90Params>(&info, &mut cw);
let llr: alloc::vec::Vec<f32> = cw
.iter()
.map(|&b| if b == 1 { 10.0 } else { -10.0 })
.collect();
let result =
osd_decode_generic::<Ldpc128_90Params>(&llr, 2, Ldpc128_90Params::K, None, false)
.expect("clean codeword must decode");
assert_eq!(result.info, info);
}
}
#[cfg(test)]
mod packed_setup_timing {
use super::packed_setup_differential::*;
use super::*;
use crate::fec::ldpc::params::Ldpc128_90Params;
use std::time::Instant;
#[test]
#[ignore = "manual diagnostic — osd setup host timing, reference vs packed"]
fn setup_timing_ldpc128_90() {
let llr: alloc::vec::Vec<f32> = (0..Ldpc128_90Params::N)
.map(|i| ((i as f32 * 37.0) % 41.0) - 20.0)
.collect();
let _ = osd_setup_generic_reference::<Ldpc128_90Params>(&llr);
const REPS: usize = 5000;
let t0 = Instant::now();
for _ in 0..REPS {
let _ = std::hint::black_box(osd_setup_generic_reference::<Ldpc128_90Params>(
std::hint::black_box(&llr),
));
}
let reference = t0.elapsed();
let _ = osd_setup_generic_packed::<Ldpc128_90Params>(&llr);
let t0 = Instant::now();
for _ in 0..REPS {
let _ = std::hint::black_box(osd_setup_generic_packed::<Ldpc128_90Params>(
std::hint::black_box(&llr),
));
}
let packed = t0.elapsed();
eprintln!(
"reference: {:.2} us/call | packed: {:.2} us/call | {:.2}x",
reference.as_secs_f64() * 1e6 / REPS as f64,
packed.as_secs_f64() * 1e6 / REPS as f64,
reference.as_secs_f64() / packed.as_secs_f64(),
);
}
}