base/onescomplement/
unsigned.rs

1//! This is an implementation of unsigned types of various bit widths
2//! which accompanies the signed one's-complement types.  Since these
3//! types are unsigned, there is no sign bit and they aren't really
4//! one's-complement types.  But the interface to these is mostly
5//! similar to that of the signed (real one's-complement) types.
6
7use std::cmp::Ordering;
8use std::fmt::{self, Debug, Display, Formatter, Octal};
9use std::hash::{Hash, Hasher};
10
11use serde::Serialize;
12
13#[cfg(test)]
14use test_strategy::Arbitrary;
15
16use super::super::subword::{right_half, split_halfword, split_halves};
17use super::error::ConversionFailed;
18use super::signed::{Signed5Bit, Signed6Bit, Signed9Bit, Signed12Bit, Signed18Bit, Signed36Bit};
19use super::{Signum, WordCommon};
20
21#[cfg(test)]
22mod tests;
23
24/// This macro implements conversions from native types to Unsigned*Bit
25/// which are always possible (e.g. `From<u8> for Unsigned9Bit`).
26macro_rules! from_native_type_to_self {
27    ($SelfT:ty, $($from:ty)*) => {
28        $(
29            impl From<$from> for $SelfT {
30                fn from(n: $from) -> Self {
31                    Self {
32                        bits: n.into(),
33                    }
34                }
35            }
36        )*
37    }
38}
39
40/// This macro implements conversions from Unsigned*Bit to native
41/// types which are always possible (e.g. `From<Unsigned9Bit> for i16`).
42macro_rules! from_self_to_native_type {
43    ($SelfT:ty, $($to:ty)*) => {
44        $(
45            impl From<$SelfT> for $to {
46                fn from(n: $SelfT) -> $to {
47                    // Note that $InnerT for Unsigned9Bit is u16, and
48                    // from_self_to_native_type is called with $to =
49                    // i16, because the limits of Unsigned9Bit are
50                    // wholly inside the limits of i16.  However,
51                    // since some u16 values call outside the range of
52                    // i16, we cannot use .into() here.  That is, we
53                    // know more about the range of values n.bits can
54                    // take than te compiler knows).
55                    n.bits as $to
56                }
57            }
58        )*
59    }
60}
61
62/// This macro implements conversions from Unsigned*Bit to native
63/// types where the conversion may not always fit.  For example
64/// `TryFrom<Unsigned18Bit> for u8`.
65macro_rules! try_from_self_to_native_type {
66    ($SelfT:ty, $($to:ty)*) => {
67        $(
68            impl TryFrom<$SelfT> for $to {
69                type Error = ConversionFailed;
70                fn try_from(n: $SelfT) -> Result<$to, ConversionFailed> {
71                    <$to>::try_from(n.bits).map_err(|_| ConversionFailed::TooLarge)
72                }
73            }
74        )*
75    }
76}
77
78/// This macro implements a conversions from native types to
79/// Unsigned*Bit where the conversion may not always fit.  For example
80/// `TryFrom<u64> for Unsigned36Bit`.
81macro_rules! try_from_native_type_to_self {
82    ($SelfT:ty, $InnerT:ty, $($from:ty)*) => {
83        $(
84            impl TryFrom<$from> for $SelfT {
85                type Error = ConversionFailed;
86                fn try_from(n: $from) -> Result<Self, ConversionFailed> {
87                    let bits: $InnerT = match n.try_into() {
88                        Err(_) => {
89                            // Because $InnerT is unsigned, we know
90                            // that n < 0 is always an error case.
91                            // Since this macro also gets used for
92                            // conversions from unsigned types,
93                            // sometimes this conditional is useless
94                            // (and we expect it to be optimized
95                            // away).
96                            #[allow(unused_comparisons)]
97                            if n < 0 {
98                                return Err(ConversionFailed::TooSmall);
99                            } else {
100                                return Err(ConversionFailed::TooLarge);
101                            }
102                        }
103                        Ok(value) if value > Self::VALUE_BITS => {
104                            return Err(ConversionFailed::TooLarge);
105                        }
106                        Ok(value) => value,
107                    };
108                    Ok(
109                        Self {
110                            bits,
111                        }
112                    )
113                }
114            }
115        )*
116    }
117}
118
119/// This macro implements the base functionality of the unsigned
120/// types.  The `SelfT` argument is the name of the (unsigned in this
121/// case) type we are defining.  `BITS` is the bit width of the type
122/// we are defining.  `InnerT` is the name of the native type which
123/// will store those bits.  `SignedPeerT` is the name of the
124/// equivalent signed type having the same width (e.g. for
125/// `Unsigned18Bit` this should be `Signed18Bit`).
126macro_rules! unsigned_ones_complement_impl {
127    ($SelfT:ty, $BITS:expr_2021, $InnerT:ty, $SignedPeerT:ty) => {
128        impl $SelfT {
129            const MODULUS: $InnerT = (1 << $BITS);
130            const VALUE_BITS: $InnerT = Self::MODULUS - 1;
131
132            pub const MAX: Self = Self {
133                bits: Self::MODULUS - 1,
134            };
135
136            pub const ZERO: Self = Self { bits: 0 };
137            pub const ONE: Self = Self { bits: 1 };
138            pub const MIN: Self = Self::ZERO;
139
140            // This will always fail at compile time, so no need to
141            // hide it.  It's pub so that it can be used in u36!() and
142            // similar.
143            pub const fn new<const N: $InnerT>() -> $SelfT {
144                type Word = $SelfT;
145                struct Helper<const M: $InnerT>;
146                impl<const M: $InnerT> Helper<M> {
147                    const U: Word = {
148                        if M > Word::MAX.bits {
149                            panic!("input value is out of range")
150                        } else {
151                            Word {
152                                bits: Word::MAX.bits & M,
153                            }
154                        }
155                    };
156                }
157                Helper::<N>::U
158            }
159
160            pub const fn is_zero(&self) -> bool {
161                self.bits == 0
162            }
163
164            pub const fn is_negative(&self) -> bool {
165                false
166            }
167
168            pub const fn is_positive(&self) -> bool {
169                true
170            }
171
172            pub const fn reinterpret_as_signed(&self) -> $SignedPeerT {
173                type T = $SignedPeerT;
174                T { bits: self.bits }
175            }
176
177            #[must_use]
178            pub fn wrapping_add(self, rhs: $SelfT) -> $SelfT {
179                let left = <$InnerT>::from(self);
180                let right = <$InnerT>::from(rhs);
181                let in_range_value: $InnerT = left.wrapping_add(right) & Self::VALUE_BITS;
182                Self::try_from(in_range_value).unwrap()
183            }
184
185            #[must_use]
186            pub fn wrapping_sub(self, rhs: $SelfT) -> $SelfT {
187                let left = <$InnerT>::from(self);
188                let right = <$InnerT>::from(rhs);
189                let in_range_value = (left + Self::MODULUS).wrapping_sub(right) & Self::VALUE_BITS;
190                Self::try_from(in_range_value).unwrap()
191            }
192
193            pub fn checked_add(self, rhs: $SelfT) -> Option<$SelfT> {
194                let left = <$InnerT>::from(self);
195                let right = <$InnerT>::from(rhs);
196                match left.checked_add(right) {
197                    Some(result) => Self::try_from(result).ok(),
198                    None => None,
199                }
200            }
201
202            pub fn checked_sub(self, rhs: $SelfT) -> Option<$SelfT> {
203                let left = <$InnerT>::from(self);
204                let right = <$InnerT>::from(rhs);
205                match left.checked_sub(right) {
206                    Some(result) => Self::try_from(result).ok(),
207                    None => None,
208                }
209            }
210
211            #[must_use]
212            pub fn wrapping_mul(self, rhs: $SelfT) -> $SelfT {
213                let left = <$InnerT>::from(self);
214                let right = <$InnerT>::from(rhs);
215                let in_range_value = left.wrapping_mul(right) & Self::VALUE_BITS;
216                Self::try_from(in_range_value).unwrap()
217            }
218
219            pub fn checked_mul(self, rhs: $SelfT) -> Option<$SelfT> {
220                let left = <$InnerT>::from(self);
221                let right = <$InnerT>::from(rhs);
222                match left.checked_mul(right) {
223                    Some(result) => Self::try_from(result).ok(),
224                    None => None,
225                }
226            }
227
228            pub fn checked_div(self, rhs: $SelfT) -> Option<$SelfT> {
229                let left = <$InnerT>::from(self);
230                let right = <$InnerT>::from(rhs);
231                match left.checked_div(right) {
232                    Some(result) => Self::try_from(result).ok(),
233                    None => None,
234                }
235            }
236
237            #[must_use]
238            pub const fn abs(self) -> Self {
239                self
240            }
241
242            pub const fn overflowing_abs(self) -> (Self, bool) {
243                (self, false)
244            }
245
246            // We cannot call std::ops::And in a const because trait
247            // methods cannot be const.  So we have this work-alike in
248            // impl, since it can be called in a const context.
249            #[must_use]
250            pub const fn and(self, mask: $InnerT) -> Self {
251                Self {
252                    bits: self.bits & mask,
253                }
254            }
255
256            // We cannot call std::ops::BitOr in a const because trait
257            // methods cannot be const.  So we have this work-alike in
258            // impl, since it can be called in a const context.
259            #[must_use]
260            pub const fn bitor(self, mask: $InnerT) -> Self {
261                Self {
262                    bits: self.bits | mask,
263                }
264            }
265        }
266
267        impl WordCommon for $SelfT {
268            fn signum(&self) -> Signum {
269                if self.is_zero() {
270                    Signum::Zero
271                } else if self.is_negative() {
272                    Signum::Negative
273                } else {
274                    Signum::Positive
275                }
276            }
277        }
278
279        impl Default for $SelfT {
280            fn default() -> Self {
281                Self { bits: 0 }
282            }
283        }
284
285        impl Display for $SelfT {
286            fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
287                Octal::fmt(&self.bits, f)
288            }
289        }
290
291        impl Octal for $SelfT {
292            fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
293                Octal::fmt(&self.bits, f)
294            }
295        }
296
297        impl Debug for $SelfT {
298            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
299                write!(f, concat!(stringify!($SelfT), "{{bits: {:#o}}}"), self.bits)
300            }
301        }
302
303        impl Hash for $SelfT {
304            fn hash<H>(&self, state: &mut H)
305            where
306                H: Hasher,
307            {
308                self.bits.hash(state)
309            }
310        }
311
312        impl<T> PartialEq<T> for $SelfT
313        where
314            T: TryInto<$SelfT> + Copy,
315        {
316            fn eq(&self, other: &T) -> bool {
317                let converted: Result<$SelfT, _> = (*other).try_into();
318                if let Ok(rhs) = converted {
319                    match self.cmp(&rhs) {
320                        Ordering::Equal => true,
321                        _ => false,
322                    }
323                } else {
324                    false
325                }
326            }
327        }
328
329        impl Eq for $SelfT {}
330
331        impl PartialOrd<$SelfT> for $SelfT {
332            fn partial_cmp(&self, other: &$SelfT) -> Option<Ordering> {
333                Some(self.cmp(other))
334            }
335        }
336
337        impl PartialOrd<u32> for $SelfT {
338            fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
339                match <$SelfT>::try_from(*other) {
340                    Ok(value) => Some(self.cmp(&value)),
341                    // The error case tells us that `other` doesn't fit
342                    // into $SelfT, so `other` must be greater.
343                    Err(_) => Some(Ordering::Less),
344                }
345            }
346        }
347
348        impl PartialOrd<u64> for $SelfT {
349            fn partial_cmp(&self, other: &u64) -> Option<Ordering> {
350                match <$SelfT>::try_from(*other) {
351                    Ok(value) => Some(self.cmp(&value)),
352                    // The error case tells us that `other` doesn't fit
353                    // into $SelfT, so `other` must be greater.
354                    Err(_) => Some(Ordering::Less),
355                }
356            }
357        }
358
359        impl Ord for $SelfT {
360            fn cmp(&self, other: &$SelfT) -> Ordering {
361                // We perform conversion here so that -0 == +0.
362                let lhs = <$InnerT>::from(*self);
363                let rhs = <$InnerT>::from(*other);
364                lhs.cmp(&rhs)
365            }
366        }
367
368        impl std::ops::Not for $SelfT {
369            type Output = Self;
370            fn not(self) -> Self {
371                Self {
372                    bits: (!self.bits) & Self::VALUE_BITS,
373                }
374            }
375        }
376
377        impl std::ops::BitAnd<$InnerT> for $SelfT {
378            type Output = Self;
379            fn bitand(self, mask: $InnerT) -> Self {
380                Self {
381                    bits: self.bits & mask,
382                }
383            }
384        }
385
386        impl std::ops::BitAnd<$SelfT> for $SelfT {
387            type Output = Self;
388            fn bitand(self, rhs: Self) -> Self {
389                self.bitand(rhs.bits)
390            }
391        }
392
393        impl std::ops::BitOr<$InnerT> for $SelfT {
394            type Output = Self;
395            fn bitor(self, mask: $InnerT) -> Self {
396                Self {
397                    bits: self.bits | mask,
398                }
399            }
400        }
401
402        impl std::ops::BitOr for $SelfT {
403            type Output = Self;
404            fn bitor(self, rhs: Self) -> Self {
405                self.bitor(rhs.bits)
406            }
407        }
408
409        impl std::ops::BitXor<$InnerT> for $SelfT {
410            type Output = Self;
411            fn bitxor(self, mask: $InnerT) -> Self {
412                Self {
413                    bits: self.bits ^ mask,
414                }
415            }
416        }
417
418        impl std::ops::BitXor for $SelfT {
419            type Output = Self;
420            fn bitxor(self, rhs: Self) -> Self {
421                self.bitxor(rhs.bits)
422            }
423        }
424
425        impl std::ops::Shr<$SelfT> for $SelfT {
426            type Output = $SelfT;
427            fn shr(self, rhs: Self) -> Self {
428                let shift_by = rhs.bits % $BITS;
429                // Compute a mask which has a 1 in the positions which
430                // we're about to shift off the right of the word.
431                let goners_mask: $InnerT = (1 << shift_by) - 1;
432
433                // Preserve the bits we're about to shift off the
434                // right-hand side.  Although shr() will shift them
435                // back onto the most-significant end of the value,
436                // these are in the wrong position in the word (as
437                // self.bits, being wider than the value we are
438                // representing, has bits which are ignored).
439                let saved = (self.bits & goners_mask) << ($BITS - shift_by);
440
441                let bits = (self.bits.shr(shift_by) | saved) & Self::VALUE_BITS;
442                Self { bits }
443            }
444        }
445
446        impl std::ops::Shr<u32> for $SelfT {
447            type Output = $SelfT;
448            fn shr(self, shift_by: u32) -> Self {
449                // Compute a mask which has a 1 in the positions which
450                // we're about to shift off the right of the word.
451                let goners_mask: $InnerT = (1 << shift_by) - 1;
452
453                // Preserve the bits we're about to shift off the
454                // right-hand side.  Although shr() will shift them
455                // back onto the most-significant end of the value,
456                // these are in the wrong position in the word (as
457                // self.bits, being wider than the value we are
458                // representing, has bits which are ignored).
459                let saved = (self.bits & goners_mask) << ($BITS - shift_by);
460
461                let bits = (self.bits.shr(shift_by) | saved) & Self::VALUE_BITS;
462                Self { bits }
463            }
464        }
465
466        impl std::iter::Sum for $SelfT {
467            fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
468                iter.fold(Self::ZERO, |acc, n| {
469                    acc.checked_add(n).expect("sum should not overflow")
470                })
471            }
472        }
473
474        impl std::ops::Shl<$SelfT> for $SelfT {
475            type Output = $SelfT;
476            fn shl(self, shift_by: $SelfT) -> Self {
477                // Clippy is suspicious of the use of the modulus
478                // operation, but this lint check is really intended
479                // to detect things like subtractions inside an add
480                // operation.
481                #[allow(clippy::suspicious_arithmetic_impl)]
482                let shift: u32 = (shift_by.bits % $BITS) as u32;
483                self.shl(shift)
484            }
485        }
486
487        impl std::ops::Shl<u32> for $SelfT {
488            type Output = $SelfT;
489            fn shl(self, rhs: u32) -> Self {
490                let shift_by = rhs % $BITS;
491                // `goners_mask` has a 1 in the positions which we're
492                // about to shift off the left of the word.
493                let mask: $InnerT = (1 << shift_by) - 1; // correct size, wrong position
494                let goners_mask: $InnerT = mask << ($BITS - shift_by); // correct size+pos
495
496                // Preserve the bits we're about to shift off the
497                // left-hand side.  Although shl() will shift bits
498                // back onto the least-significant end of the value,
499                // the bits shifted there are the previous top bits of
500                // self.bits, but the top bits of self.bits aren't
501                // part of the value of `self`, since `self.bits` has
502                // a width greater than $BITS.
503                let saved = (self.bits & goners_mask) >> ($BITS - shift_by);
504                let bits = ((self.bits << shift_by) | saved) & Self::VALUE_BITS;
505                Self { bits }
506            }
507        }
508    };
509}
510
511/// `Unsigned5Bit` is used as a system configuration value; that is,
512/// an index into F-memory.
513#[cfg_attr(test, derive(Arbitrary))]
514#[derive(Clone, Copy, Serialize)]
515pub struct Unsigned5Bit {
516    #[cfg_attr(test, strategy(0..32u8))]
517    pub(crate) bits: u8,
518}
519
520/// `Unsigned6Bit` is used as an X-register address. That is, the `j`
521/// in `Xj`.
522#[cfg_attr(test, derive(Arbitrary))]
523#[derive(Clone, Copy, Serialize)]
524pub struct Unsigned6Bit {
525    #[cfg_attr(test, strategy(0..64u8))]
526    pub(crate) bits: u8,
527}
528
529/// `Unsigned9Bit` is the value of a "quarter" of the 36-bit TX-2
530/// machine word.  A number of instructions - and in particular the
531/// Exchange Unit - work on the quarters of a word.
532#[cfg_attr(test, derive(Arbitrary))]
533#[derive(Clone, Copy, Serialize)]
534pub struct Unsigned9Bit {
535    #[cfg_attr(test, strategy(0..512u16))]
536    pub(crate) bits: u16,
537}
538
539/// `Unsigned12Bit` is used as the _mode_ of a connected I/O device.
540#[cfg_attr(test, derive(Arbitrary))]
541#[derive(Clone, Copy, Serialize)]
542pub struct Unsigned12Bit {
543    #[cfg_attr(test, strategy(0..4095u16))]
544    pub(crate) bits: u16,
545}
546
547/// `Unsigned18Bit` is the value of a "half" of the 36-bit TX-2
548/// machine word.  We use this type to hold STUV-memory addresses,
549/// among other things.  Physical memory addresses though are only 17
550/// bits wide.  The remaining bit can be used to "mark" an address
551/// either for tracing (when it's an instruction address) or for
552/// deferred addressing (when it's an operand address).
553#[cfg_attr(test, derive(Arbitrary))]
554#[derive(Clone, Copy, Serialize)]
555pub struct Unsigned18Bit {
556    #[cfg_attr(test, strategy(0..262_143u32))]
557    pub(crate) bits: u32,
558}
559
560/// `Unsigned36Bit` is the basic machine word of the TX-2.  This is
561/// the width of the registers in the Arithmetic Unit, and it is the
562/// unit on which the Exchange Unit operates when performing memory
563/// fetches or stores.  This is also the width of all instructions.
564#[cfg_attr(test, derive(Arbitrary))]
565#[derive(Clone, Copy, Serialize)]
566pub struct Unsigned36Bit {
567    #[cfg_attr(test, strategy(0..68_719_476_735u64))]
568    pub(crate) bits: u64,
569}
570
571unsigned_ones_complement_impl!(Unsigned5Bit, 5, u8, Signed5Bit);
572unsigned_ones_complement_impl!(Unsigned6Bit, 6, u8, Signed6Bit);
573unsigned_ones_complement_impl!(Unsigned9Bit, 9, u16, Signed9Bit);
574unsigned_ones_complement_impl!(Unsigned12Bit, 12, u16, Signed12Bit);
575unsigned_ones_complement_impl!(Unsigned18Bit, 18, u32, Signed18Bit);
576unsigned_ones_complement_impl!(Unsigned36Bit, 36, u64, Signed36Bit);
577
578////////////////////////////////////////////////////////////////////////
579// Unsigned5Bit
580////////////////////////////////////////////////////////////////////////
581
582// all the things that always fit into Unsigned5Bit
583// (there are none)
584// all the things that Unsigned5Bit always fits into
585from_self_to_native_type!(Unsigned5Bit, u8 i8 u16 i16 u32 i32 u64 i64 usize isize);
586// all the things that Unsigned5Bit may not fit into
587// (there are none)
588// all the things that may not fit into Unsigned5Bit
589try_from_native_type_to_self!(Unsigned5Bit, u8, i8 u8 u16 i16 u32 i32 u64 i64 usize isize);
590
591////////////////////////////////////////////////////////////////////////
592// Unsigned6Bit
593////////////////////////////////////////////////////////////////////////
594
595// all the things that always fit into Unsigned6Bit
596// (there are none)
597// all the things that Unsigned5Bit always fits into
598from_self_to_native_type!(Unsigned6Bit, u8 i8 u16 i16 u32 i32 u64 i64 usize isize);
599// all the things that Unsigned6Bit may not fit into
600// (there are none)
601// all the things that may not fit into Unsigned6Bit
602try_from_native_type_to_self!(Unsigned6Bit, u8, i8 u8 u16 i16 u32 i32 u64 i64 usize isize);
603
604impl From<Unsigned5Bit> for Unsigned6Bit {
605    fn from(n: Unsigned5Bit) -> Self {
606        Self { bits: n.bits }
607    }
608}
609
610impl TryFrom<Unsigned18Bit> for Unsigned6Bit {
611    type Error = ConversionFailed;
612    fn try_from(n: Unsigned18Bit) -> Result<Self, ConversionFailed> {
613        match u8::try_from(n.bits) {
614            Ok(n) => {
615                if n > Self::VALUE_BITS {
616                    Err(ConversionFailed::TooLarge)
617                } else {
618                    Ok(Self { bits: n })
619                }
620            }
621            Err(_) => Err(ConversionFailed::TooLarge),
622        }
623    }
624}
625
626////////////////////////////////////////////////////////////////////////
627// Unsigned9Bit
628////////////////////////////////////////////////////////////////////////
629
630// all the things that always fit into Unsigned9Bit
631from_native_type_to_self!(Unsigned9Bit, u8);
632// all the things that Unsigned9Bit always fits into
633from_self_to_native_type!(Unsigned9Bit, u16 i16 u32 i32 u64 i64 usize isize);
634// all the things that Unsigned9Bit may not fit into
635try_from_self_to_native_type!(Unsigned9Bit, u8 i8);
636// all the things that may not fit into Unsigned9Bit
637try_from_native_type_to_self!(Unsigned9Bit, u16, i8 u16 i16 u32 i32 u64 i64 usize isize);
638
639impl From<Unsigned5Bit> for Unsigned9Bit {
640    fn from(n: Unsigned5Bit) -> Self {
641        Self {
642            bits: n.bits.into(),
643        }
644    }
645}
646
647impl From<Unsigned6Bit> for Unsigned9Bit {
648    fn from(n: Unsigned6Bit) -> Self {
649        Self {
650            bits: n.bits.into(),
651        }
652    }
653}
654
655////////////////////////////////////////////////////////////////////////
656// Unsigned12Bit
657////////////////////////////////////////////////////////////////////////
658
659// all the things that always fit into Unsigned12Bit
660from_native_type_to_self!(Unsigned12Bit, u8);
661// all the things that Unsigned12Bit always fits into
662from_self_to_native_type!(Unsigned12Bit, u16 i16 u32 i32 u64 i64 usize isize);
663// all the things that Unsigned12Bit may not fit into
664try_from_self_to_native_type!(Unsigned12Bit, u8 i8);
665// all the things that may not fit into Unsigned12Bit
666try_from_native_type_to_self!(Unsigned12Bit, u16, i8 u16 i16 u32 i32 u64 i64);
667
668impl From<Unsigned5Bit> for Unsigned12Bit {
669    fn from(n: Unsigned5Bit) -> Self {
670        Self {
671            bits: n.bits.into(),
672        }
673    }
674}
675
676impl From<Unsigned6Bit> for Unsigned12Bit {
677    fn from(n: Unsigned6Bit) -> Self {
678        Self {
679            bits: n.bits.into(),
680        }
681    }
682}
683
684impl From<Unsigned9Bit> for Unsigned12Bit {
685    fn from(n: Unsigned9Bit) -> Self {
686        Self { bits: n.bits }
687    }
688}
689
690impl TryFrom<Unsigned18Bit> for Unsigned12Bit {
691    type Error = ConversionFailed;
692    fn try_from(n: Unsigned18Bit) -> Result<Self, ConversionFailed> {
693        match u16::try_from(n.bits) {
694            Ok(n) => {
695                if n > Self::VALUE_BITS {
696                    Err(ConversionFailed::TooLarge)
697                } else {
698                    Ok(Self { bits: n })
699                }
700            }
701            Err(_) => Err(ConversionFailed::TooLarge),
702        }
703    }
704}
705
706////////////////////////////////////////////////////////////////////////
707// Unsigned18Bit
708////////////////////////////////////////////////////////////////////////
709
710// all the things that always fit into Unsigned18Bit
711from_native_type_to_self!(Unsigned18Bit, u8 u16);
712// all the things that Unsigned18Bit always fits into
713from_self_to_native_type!(Unsigned18Bit, u32 i32 u64 i64 usize isize);
714// all the things Unsigned18Bit may not fit into
715try_from_self_to_native_type!(Unsigned18Bit, u8 i8 u16 i16);
716// all the things that may not fit into Unsigned18Bit
717try_from_native_type_to_self!(Unsigned18Bit, u32, i8 i16 u32 i32 u64 i64);
718
719impl From<Unsigned5Bit> for Unsigned18Bit {
720    fn from(n: Unsigned5Bit) -> Self {
721        Self {
722            bits: n.bits.into(),
723        }
724    }
725}
726
727impl From<Unsigned9Bit> for Unsigned18Bit {
728    fn from(n: Unsigned9Bit) -> Self {
729        Self {
730            bits: n.bits.into(),
731        }
732    }
733}
734
735impl TryFrom<Unsigned36Bit> for Unsigned18Bit {
736    type Error = ConversionFailed;
737    fn try_from(n: Unsigned36Bit) -> Result<Self, ConversionFailed> {
738        let (high, low) = split_halves(n);
739        if high == Unsigned18Bit::ZERO {
740            Ok(low)
741        } else {
742            Err(ConversionFailed::TooLarge)
743        }
744    }
745}
746
747impl TryFrom<usize> for Unsigned18Bit {
748    type Error = ConversionFailed;
749    fn try_from(n: usize) -> Result<Self, ConversionFailed> {
750        match n.try_into() {
751            Ok(bits) => {
752                if bits > Unsigned18Bit::MAX.bits {
753                    Err(ConversionFailed::TooLarge)
754                } else {
755                    Ok(Unsigned18Bit { bits })
756                }
757            }
758            Err(_) => Err(ConversionFailed::TooLarge),
759        }
760    }
761}
762
763////////////////////////////////////////////////////////////////////////
764// Unsigned36Bit
765////////////////////////////////////////////////////////////////////////
766
767// all the things that always fit into Unsigned36Bit
768from_native_type_to_self!(Unsigned36Bit, u8 u16 u32);
769// all the things that Unsigned36Bit always fits into
770from_self_to_native_type!(Unsigned36Bit, u64 i64);
771// all the things Unsigned36Bit may not fit into
772try_from_self_to_native_type!(Unsigned36Bit, u8 i8 u16 i16 u32 i32);
773// all the things that may not fit into Unsigned36Bit
774try_from_native_type_to_self!(Unsigned36Bit, u64, i8 i16 i32 u64 i64);
775
776impl TryFrom<Unsigned36Bit> for Unsigned6Bit {
777    type Error = ConversionFailed;
778    fn try_from(n: Unsigned36Bit) -> Result<Self, ConversionFailed> {
779        match u8::try_from(n.bits) {
780            Ok(n) => {
781                if n > Self::VALUE_BITS {
782                    Err(ConversionFailed::TooLarge)
783                } else {
784                    Ok(Self { bits: n })
785                }
786            }
787            Err(_) => Err(ConversionFailed::TooLarge),
788        }
789    }
790}
791
792impl From<Unsigned5Bit> for Unsigned36Bit {
793    fn from(n: Unsigned5Bit) -> Self {
794        Self {
795            bits: n.bits.into(),
796        }
797    }
798}
799
800impl From<Unsigned6Bit> for Unsigned36Bit {
801    fn from(n: Unsigned6Bit) -> Self {
802        Self {
803            bits: n.bits.into(),
804        }
805    }
806}
807
808impl From<Unsigned9Bit> for Unsigned36Bit {
809    fn from(n: Unsigned9Bit) -> Self {
810        Self {
811            bits: n.bits.into(),
812        }
813    }
814}
815
816impl From<Unsigned12Bit> for Unsigned36Bit {
817    fn from(n: Unsigned12Bit) -> Self {
818        Self {
819            bits: n.bits.into(),
820        }
821    }
822}
823
824impl From<Unsigned18Bit> for Unsigned36Bit {
825    fn from(n: Unsigned18Bit) -> Self {
826        Self {
827            bits: n.bits.into(),
828        }
829    }
830}
831
832impl std::ops::BitAnd<Unsigned18Bit> for Unsigned36Bit {
833    type Output = Unsigned18Bit;
834    fn bitand(self, rhs: Unsigned18Bit) -> Unsigned18Bit {
835        right_half(self).bitand(rhs)
836    }
837}
838
839impl std::ops::BitAnd<Unsigned9Bit> for Unsigned36Bit {
840    type Output = Unsigned9Bit;
841    fn bitand(self, rhs: Unsigned9Bit) -> Unsigned9Bit {
842        let (_q2, q1) = split_halfword(right_half(self));
843        q1.bitand(rhs)
844    }
845}
846
847impl std::ops::BitAnd<Unsigned6Bit> for Unsigned36Bit {
848    type Output = Unsigned6Bit;
849    fn bitand(self, rhs: Unsigned6Bit) -> Unsigned6Bit {
850        Unsigned6Bit {
851            bits: u8::try_from(self.bits & 0o77).expect("a six-bit quantity should be in-range")
852                & rhs.bits,
853        }
854    }
855}