1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use core::ptr;

use awint_internals::*;
use const_fn::const_fn;

use crate::Bits;

/// # Casting between `Bits` of arbitrary sizes
impl Bits {
    /// Resize-copy-assigns `rhs` to `self`. If `self.bw() >= rhs.bw()`, the
    /// copied value of `rhs` will be extended with bits set to `extension`. If
    /// `self.bw() < rhs.bw()`, the copied value of `rhs` will be truncated.
    #[const_fn(cfg(feature = "const_support"))]
    pub const fn resize_(&mut self, rhs: &Self, extension: bool) {
        // Safety: the exact number of digits needed are copied or set
        unsafe {
            if self.bw() <= rhs.bw() {
                // truncation
                ptr::copy_nonoverlapping(rhs.as_ptr(), self.as_mut_ptr(), self.total_digits());
                self.clear_unused_bits();
            } else {
                ptr::copy_nonoverlapping(rhs.as_ptr(), self.as_mut_ptr(), rhs.total_digits());
                if extension && (rhs.unused() != 0) {
                    *self.get_unchecked_mut(rhs.total_digits() - 1) |= MAX << rhs.extra();
                }
                self.digit_set(
                    extension,
                    rhs.total_digits()..self.total_digits(),
                    extension,
                )
            }
        }
    }

    /// Zero-resize-copy-assigns `rhs` to `self` and returns overflow. This is
    /// the same as `lhs.resize_(rhs, false)`, but returns `true` if the
    /// unsigned meaning of the integer is changed.
    #[const_fn(cfg(feature = "const_support"))]
    pub const fn zero_resize_(&mut self, rhs: &Self) -> bool {
        self.resize_(rhs, false);
        if self.bw() < rhs.bw() {
            // Safety: `self.total_digits() <= rhs.total_digits()` because of the above
            // check
            unsafe {
                // check if there are set bits that would be truncated
                if (self.extra() != 0)
                    && ((rhs.get_unchecked(self.total_digits() - 1) >> self.extra()) != 0)
                {
                    return true
                }
                const_for!(i in {self.total_digits()..rhs.total_digits()} {
                    if rhs.get_unchecked(i) != 0 {
                        return true
                    }
                });
            }
        }
        false
    }

    /// Sign-resize-copy-assigns `rhs` to `self` and returns overflow. This is
    /// the same as `lhs.resize_(rhs, rhs.msb())`, but returns `true` if
    /// the signed meaning of the integer is changed.
    #[const_fn(cfg(feature = "const_support"))]
    pub const fn sign_resize_(&mut self, rhs: &Self) -> bool {
        self.resize_(rhs, rhs.msb());
        // this function is far harder to implement than it would first seem
        if self.bw() < rhs.bw() {
            // Safety: `self.total_digits() <= rhs.total_digits()` because of the above
            // check
            unsafe {
                if rhs.msb() {
                    // check if the new most significant bit is unset (which would mean overflow
                    // from negative to positive)
                    if !self.msb() {
                        return true
                    }
                    // check if there are unset bits that would be truncated
                    if self.total_digits() == rhs.total_digits() {
                        // first and only digit
                        if rhs.extra() != 0 {
                            //  rhs extra mask and lhs cutoff mask
                            let expected = (MAX >> (BITS - rhs.extra())) & (MAX << self.extra());
                            if (rhs.last() & expected) != expected {
                                return true
                            }
                        } else {
                            let expected = MAX << self.extra();
                            if (rhs.last() & expected) != expected {
                                return true
                            }
                        }
                        // avoid the other tests if this is the only digit
                        return false
                    }
                    // first digit
                    if self.extra() != 0 {
                        let expected = MAX << self.extra();
                        if (rhs.get_unchecked(self.total_digits() - 1) & expected) != expected {
                            return true
                        }
                    }
                    // middle digits
                    const_for!(i in {self.total_digits()..(rhs.total_digits() - 1)} {
                        if rhs.get_unchecked(i) != MAX {
                            return true
                        }
                    });
                    // last digit
                    if rhs.extra() != 0 {
                        let expected = MAX >> (BITS - rhs.extra());
                        if (rhs.last() & expected) != expected {
                            return true
                        }
                    } else if rhs.last() != MAX {
                        return true
                    }
                } else {
                    // check if the new most significant bit is set (which would mean overflow from
                    // positive to negative)
                    if self.msb() {
                        return true
                    }
                    // check if there are set bits that would be truncated
                    if (self.extra() != 0)
                        && ((rhs.get_unchecked(self.total_digits() - 1) >> self.extra()) != 0)
                    {
                        return true
                    }
                    // Safety: `self.total_digits() <= rhs.total_digits()` because of the above
                    // check
                    const_for!(i in {self.total_digits()..rhs.total_digits()} {
                        if rhs.get_unchecked(i) != 0 {
                            return true
                        }
                    });
                }
            }
        }
        false
    }
}