1use std::ops::Shl;
14
15#[cfg(test)]
16use test_strategy::proptest;
17
18use super::onescomplement::unsigned::{Unsigned6Bit, Unsigned36Bit};
19#[cfg(test)]
20use super::u36;
21
22#[must_use]
30pub fn cycle_and_splay(target: Unsigned36Bit, bits: Unsigned6Bit) -> Unsigned36Bit {
31 let src = u64::from(bits);
39 let lowest_bits = Unsigned36Bit::from(0o010_101_010_101_u32);
40 let newbits: u64 = (src & 1)
41 | ((src & 0o2) << 5)
42 | ((src & 0o4) << 10)
43 | ((src & 0o10) << 15)
44 | ((src & 0o20) << 20)
45 | ((src & 0o40) << 25);
46 (target.shl(1) & !lowest_bits) | newbits
47}
48
49#[test]
50fn test_cycle_and_splay() {
51 assert_eq!(cycle_and_splay(Unsigned36Bit::ZERO, Unsigned6Bit::ZERO), 0);
52
53 let ex: u64 = 1u64 << 0 | 1u64 << 6 | 1u64 << 12 | 1u64 << 18 | 1u64 << 24 | 1u64 << 30;
54 let expected: Unsigned36Bit = Unsigned36Bit::try_from(ex).expect("test data should be valid");
55 assert_eq!(
56 cycle_and_splay(Unsigned36Bit::ZERO, Unsigned6Bit::MAX),
57 expected
58 );
59
60 for bitpos in 0u8..=5u8 {
61 let input_bit = Unsigned6Bit::try_from(1u8 << bitpos).expect("valid test data");
62 let current: Unsigned36Bit = 2_u8.into();
63 let expected_output = Unsigned36Bit::try_from(1u64 << (bitpos * 6))
64 .expect("valid test data")
65 | (current << Unsigned36Bit::ONE);
66 assert_eq!(cycle_and_splay(current, input_bit), expected_output);
67 }
68}
69
70#[must_use]
73pub fn unsplay(source: Unsigned36Bit) -> [Unsigned6Bit; 6] {
74 fn bits(
75 b0: Unsigned36Bit,
76 b1: Unsigned36Bit,
77 b2: Unsigned36Bit,
78 b3: Unsigned36Bit,
79 b4: Unsigned36Bit,
80 b5: Unsigned36Bit,
81 ) -> Unsigned6Bit {
82 #[allow(clippy::bool_to_int_with_if)]
85 let result: u8 = (if b0 == 0 { 0 } else { 0o01 })
86 | (if b1 == 0 { 0 } else { 0o02 })
87 | (if b2 == 0 { 0 } else { 0o04 })
88 | (if b3 == 0 { 0 } else { 0o10 })
89 | (if b4 == 0 { 0 } else { 0o20 })
90 | (if b5 == 0 { 0 } else { 0o40 });
91 Unsigned6Bit::try_from(result).unwrap()
92 }
93
94 let getbit = |n: u32| -> Unsigned36Bit { source & (1 << n) };
95 [
96 bits(
97 getbit(5),
98 getbit(11),
99 getbit(17),
100 getbit(23),
101 getbit(29),
102 getbit(35),
103 ),
104 bits(
105 getbit(4),
106 getbit(10),
107 getbit(16),
108 getbit(22),
109 getbit(28),
110 getbit(34),
111 ),
112 bits(
113 getbit(3),
114 getbit(9),
115 getbit(15),
116 getbit(21),
117 getbit(27),
118 getbit(33),
119 ),
120 bits(
121 getbit(2),
122 getbit(8),
123 getbit(14),
124 getbit(20),
125 getbit(26),
126 getbit(32),
127 ),
128 bits(
129 getbit(1),
130 getbit(7),
131 getbit(13),
132 getbit(19),
133 getbit(25),
134 getbit(31),
135 ),
136 bits(
137 getbit(0),
138 getbit(6),
139 getbit(12),
140 getbit(18),
141 getbit(24),
142 getbit(30),
143 ),
144 ]
145}
146
147#[test]
148fn test_unsplay() {
149 const ZERO: Unsigned6Bit = Unsigned6Bit::ZERO;
150 const MAX: Unsigned6Bit = Unsigned6Bit::MAX;
151 assert_eq!(
152 unsplay(Unsigned36Bit::ZERO),
153 [ZERO, ZERO, ZERO, ZERO, ZERO, ZERO]
154 );
155 assert_eq!(
156 unsplay(u36!(0o777_777_777_777)),
157 [MAX, MAX, MAX, MAX, MAX, MAX]
158 );
159}
160
161#[cfg(test)]
162fn round_trip(input: Unsigned36Bit) -> Result<(), String> {
163 let unsplayed = unsplay(input);
164 dbg!(&input);
165 dbg!(&unsplayed);
166 let mut output = Unsigned36Bit::ZERO;
167 for (i, component) in unsplayed.into_iter().enumerate() {
168 let next = cycle_and_splay(output, component);
169 println!(
170 "round_trip: splay iteration {i}: value {component:03o} changed word from {output:012o} to {next:012o}",
171 );
172 output = next;
173 }
174 dbg!(&output);
175 if input == output {
176 Ok(())
177 } else {
178 Err(format!(
179 "mismatch: input word {input:012o} unsplayed to {unsplayed:?}, which splayed to {output:012o}, which doesn't match",
180 ))
181 }
182}
183
184#[test]
186fn test_round_trip_selected() {
187 for value in [
188 Unsigned36Bit::ZERO,
189 Unsigned36Bit::ONE,
190 Unsigned36Bit::from(0o2_u32),
191 Unsigned36Bit::from(0o4_u32),
192 Unsigned36Bit::from(0o3_u32),
193 Unsigned36Bit::from(0o7_u32),
194 Unsigned36Bit::from(0o77_u32),
195 Unsigned36Bit::from(0o7700_u32),
196 Unsigned36Bit::MAX,
197 Unsigned36Bit::from(0o23_574_373_u32),
198 ] {
199 if let Err(e) = round_trip(value) {
200 panic!("round_trip failed for {value:012o}: {e}");
201 }
202 }
203}
204
205#[cfg(test)]
208#[proptest]
209fn test_unsplay_splay_round_trip(input: Unsigned36Bit) {
210 if let Err(e) = round_trip(input) {
211 panic!("round trip failed for {input:012o}: {e}");
212 }
213}
214
215#[test]
216fn test_round_trip_bitcycle() {
217 for bitpos in 0..36 {
218 let input: Unsigned36Bit = Unsigned36Bit::ONE.shl(bitpos);
219 if let Err(e) = round_trip(input) {
220 panic!("round_trip failed for {input:012o}: {e}");
221 }
222 }
223}