base/
subword.rs

1//! Various convenience utilities for splitting 36-bit TX-2 words
2//! into smaller components and for joining them together.
3use std::ops::Shl;
4
5use super::onescomplement::unsigned::{Unsigned9Bit, Unsigned18Bit, Unsigned36Bit};
6
7/// Split a 36-bit word into two 18-bit values.
8#[must_use]
9pub fn split_halves(w: Unsigned36Bit) -> (Unsigned18Bit, Unsigned18Bit) {
10    (left_half(w), right_half(w))
11}
12
13/// Join two 18-bit values into a 36-bit word.
14#[must_use]
15pub fn join_halves(left: Unsigned18Bit, right: Unsigned18Bit) -> Unsigned36Bit {
16    Unsigned36Bit::from(left).shl(18) | Unsigned36Bit::from(right)
17}
18
19/// Join two quarters into a halfword.
20#[must_use]
21pub fn join_quarters(left: Unsigned9Bit, right: Unsigned9Bit) -> Unsigned18Bit {
22    Unsigned18Bit::from(left).shl(9) | Unsigned18Bit::from(right)
23}
24
25/// Extract the right (more-significant) halfword from a full word.
26#[must_use]
27pub fn right_half(word: Unsigned36Bit) -> Unsigned18Bit {
28    let bits: u64 = u64::from(word);
29    Unsigned18Bit::try_from(bits & 0o777_777).unwrap()
30}
31
32/// Extract the right (less-significant) halfword from a full word.
33#[must_use]
34pub fn left_half(word: Unsigned36Bit) -> Unsigned18Bit {
35    let bits: u64 = u64::from(word) >> 18;
36    Unsigned18Bit::try_from(bits & 0o777_777).unwrap()
37}
38
39/// Split a halfword into left (more significant) and right (less
40/// significant) 9-bit quarters (in the sense that they are quarters
41/// of the original 26-bit full word).
42#[must_use]
43pub fn split_halfword(halfword: Unsigned18Bit) -> (Unsigned9Bit, Unsigned9Bit) {
44    let bits: u32 = u32::from(halfword);
45    (
46        Unsigned9Bit::try_from((bits >> 9) & 0o777).unwrap(),
47        Unsigned9Bit::try_from((bits) & 0o777).unwrap(),
48    )
49}
50
51/// Split a 36-bit word into four 9-bit quarters.  The result is a
52/// tuple which contains the quarters ordered from most-significant
53/// (Q4) to least-significant (Q1).
54#[must_use]
55pub fn quarters(word: Unsigned36Bit) -> [Unsigned9Bit; 4] {
56    let (left, right) = split_halves(word);
57    let (q4, q3) = split_halfword(left);
58    let (q2, q1) = split_halfword(right);
59    [q4, q3, q2, q1]
60}
61
62#[cfg(test)]
63mod tests {
64    use test_strategy::proptest;
65
66    use super::super::onescomplement::unsigned::{Unsigned9Bit, Unsigned18Bit, Unsigned36Bit};
67    use super::*;
68
69    macro_rules! assert_octal_eq {
70        ($left:expr_2021, $right:expr_2021 $(,)?) => {{
71            match (&$left, &$right) {
72                (left_val, right_val) => {
73                    if !(*left_val == *right_val) {
74                        panic!(
75                            "Assertion failed: {:>#012o} != {:>#012o}",
76                            left_val, right_val
77                        );
78                    }
79                }
80            }
81        }};
82    }
83
84    #[test]
85    fn test_join_halves() {
86        assert_octal_eq!(
87            join_halves(
88                Unsigned18Bit::try_from(0o123_456_u32).unwrap(),
89                Unsigned18Bit::try_from(0o525_252_u32).unwrap()
90            ),
91            Unsigned36Bit::try_from(0o123_456_525_252_u64).unwrap()
92        );
93    }
94
95    #[test]
96    fn test_join_quarters() {
97        assert_octal_eq!(
98            join_quarters(
99                Unsigned9Bit::try_from(0o123_u16).unwrap(),
100                Unsigned9Bit::try_from(0o456_u16).unwrap()
101            ),
102            Unsigned18Bit::try_from(0o123_456_u32).unwrap()
103        );
104    }
105
106    #[test]
107    fn test_split_halfword() {
108        let h = Unsigned18Bit::try_from(0o123_456_u32).expect("valid test data");
109        assert_eq!(
110            split_halfword(h),
111            (
112                Unsigned9Bit::try_from(0o123).unwrap(),
113                Unsigned9Bit::try_from(0o456).unwrap()
114            )
115        );
116    }
117
118    #[test]
119    fn test_quarters() {
120        fn q(n: u16) -> Unsigned9Bit {
121            Unsigned9Bit::try_from(n).expect("valid test data")
122        }
123        assert_eq!(
124            quarters(Unsigned36Bit::try_from(0o123_456_525_252_u64).unwrap()),
125            [q(0o123), q(0o456), q(0o525), q(0o252)]
126        );
127    }
128
129    #[proptest]
130    fn test_word_split_and_join_are_inverses(value: Unsigned36Bit) {
131        let left = left_half(value);
132        let right = right_half(value);
133        let joined = join_halves(left, right);
134        assert_eq!(value, joined);
135    }
136
137    #[proptest]
138    fn test_halfword_split_and_join_are_inverses(value: Unsigned18Bit) {
139        let (left, right) = split_halfword(value);
140        let joined = join_quarters(left, right);
141        assert_eq!(joined, value);
142    }
143
144    #[proptest]
145    fn test_word_quartering_and_joining_are_inverses(value: Unsigned36Bit) {
146        let quarters: [Unsigned9Bit; 4] = quarters(value);
147        let left = join_quarters(quarters[0], quarters[1]);
148        let right = join_quarters(quarters[2], quarters[3]);
149        let joined = join_halves(left, right);
150        assert_eq!(joined, value);
151    }
152}