Skip to main content

malachite_nz/integer_polynomial/arithmetic/
bit_pack.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// Uses code adopted from the FLINT Library.
4//
5//      Copyright © 2010 William Hart
6//
7// This file is part of Malachite.
8//
9// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
10// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
11// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
12
13use crate::integer::Integer;
14use crate::integer_polynomial::IntegerPolynomial;
15use crate::integer_polynomial::arithmetic::coefficient::PolynomialCoefficient;
16use crate::integer_polynomial::arithmetic::vec::small_value;
17use crate::natural::Natural;
18use crate::natural::arithmetic::add::limbs_slice_add_limb_in_place;
19use crate::natural::arithmetic::shl::{limbs_shl_to_out, limbs_slice_shl_in_place};
20use crate::natural::arithmetic::sub::limbs_sub_limb_in_place;
21use crate::natural::logic::not::limbs_not_to_out;
22use crate::natural_polynomial::arithmetic::bit_pack::limbs_bit_pack;
23use crate::platform::Limb;
24use malachite_base::num::arithmetic::traits::{ModPowerOf2Assign, PowerOf2, WrappingAddAssign};
25use malachite_base::num::basic::integers::PrimitiveInt;
26use malachite_base::num::conversion::traits::{ExactFrom, WrappingFrom};
27use malachite_base::num::logic::traits::LowMask;
28use malachite_base::polynomial::BitPack;
29
30// The limb in which field `i` of consecutive `bits`-bit fields starts, and the bit within that
31// limb.
32pub(crate) fn field_start(i: usize, bits: u64) -> (usize, u64) {
33    let start = u64::exact_from(i) * bits;
34    (
35        usize::exact_from(start >> Limb::LOG_WIDTH),
36        start & Limb::WIDTH_MASK,
37    )
38}
39
40// Packs `x` into the `bits`-bit field of `arr` that starts at bit `shift` of `arr[0]`, as a two's
41// complement number from which `borrow` is subtracted, and returns whether the field is negative,
42// that is, whether the field above must borrow from its own. If `negate`, `-x` is packed instead.
43// The low `shift` bits of `arr[0]` hold the field below and are preserved; the bits above them are
44// assumed to be zero. `arr` must extend to the limb holding the field's last bit, and for a
45// negative `x` sometimes one limb further.
46//
47// # Worst-case complexity
48// $T(n) = O(n)$
49//
50// $M(n) = O(1)$
51//
52// where $T$ is time, $M$ is additional memory, and $n$ is `bits`.
53//
54// This is equivalent to `fmpz_bit_pack` from `fmpz/bit_pack.c`, FLINT 3.6.0.
55crate_test_fn! {limbs_pack_field<C: PolynomialCoefficient>(
56    arr: &mut [Limb],
57    shift: u64,
58    bits: u64,
59    x: &C,
60    negate: bool,
61    borrow: bool,
62) -> bool {
63    let save = arr[0];
64    let limbs = usize::exact_from((shift + bits) >> Limb::LOG_WIDTH);
65    let rem_bits = (shift + bits) & Limb::WIDTH_MASK;
66    if x.is_zero() {
67        // Special case: store -borrow.
68        if borrow {
69            // Store -1 shifted, and add save back in.
70            arr[0] = (Limb::MAX << shift).wrapping_add(save);
71            // Complement the remaining limbs.
72            if limbs > 1 {
73                arr[1..limbs].fill(Limb::MAX);
74            }
75            if limbs != 0 {
76                // Complement the remaining bits.
77                if rem_bits != 0 {
78                    arr[limbs] = Limb::low_mask(rem_bits);
79                }
80            } else {
81                // Mask off the final limb.
82                arr[limbs].mod_power_of_2_assign(rem_bits);
83            }
84        }
85        return borrow;
86    }
87    // Let |x| = b. If x is negative and negate is false, or x is positive and negate is true, we
88    // want -b - borrow; otherwise, we want b - borrow.
89    if x.is_negative() ^ negate {
90        // -b - borrow = !b + 1 - borrow
91        let size = if let Some(c) = small_value(x) {
92            let c_bits = Limb::wrapping_from(c);
93            // d = -b - borrow
94            let d = if c < 0 {
95                c_bits.wrapping_sub(Limb::from(borrow))
96            } else {
97                c_bits.wrapping_neg().wrapping_sub(Limb::from(borrow))
98            };
99            // Store d << shift, and add save back into place.
100            arr[0] = (d << shift).wrapping_add(save);
101            // Store the carry from d << shift, and complement the remaining bits of the second
102            // limb.
103            if limbs != 0 {
104                arr[1] = if shift != 0 {
105                    (d >> (Limb::WIDTH - shift)).wrapping_add(Limb::MAX << shift)
106                } else {
107                    Limb::MAX
108                };
109            }
110            2
111        } else {
112            let xs = x.unsigned_abs_ref().as_limbs_asc();
113            let mut s = xs.len();
114            // Complement the coefficient into arr.
115            limbs_not_to_out(&mut arr[..s], xs);
116            // Deal with + 1 - borrow; there cannot be a carry, or else we complemented 0.
117            if !borrow {
118                limbs_slice_add_limb_in_place(&mut arr[..s], 1);
119            }
120            // Shift into place.
121            if shift != 0 {
122                let carry = limbs_slice_shl_in_place(&mut arr[..s], shift);
123                if limbs + usize::from(rem_bits != 0) > s {
124                    arr[s] = (Limb::MAX << shift).wrapping_add(carry);
125                    s += 1;
126                }
127            }
128            // Add back in the saved bits from the start of the field.
129            arr[0].wrapping_add_assign(save);
130            s
131        };
132        if limbs >= size {
133            // Complement any additional limbs.
134            if limbs > size {
135                arr[size..limbs].fill(Limb::MAX);
136            }
137            // Complement the remaining bits.
138            if rem_bits != 0 {
139                arr[limbs] = Limb::low_mask(rem_bits);
140            }
141        } else {
142            // Mask off the final limb.
143            arr[limbs].mod_power_of_2_assign(rem_bits);
144        }
145        true
146    } else {
147        // b - borrow
148        if let Some(c) = small_value(x) {
149            // d = b - borrow
150            let d = Limb::wrapping_from(c.unsigned_abs()) - Limb::from(borrow);
151            // Store d << shift, and add save back into place.
152            arr[0] = (d << shift).wrapping_add(save);
153            // Store the carry from d << shift.
154            if limbs + usize::from(rem_bits != 0) > 1 && shift != 0 {
155                arr[1] = d >> (Limb::WIDTH - shift);
156            }
157        } else {
158            let xs = x.unsigned_abs_ref().as_limbs_asc();
159            let mut s = xs.len();
160            // Shift into place.
161            if shift != 0 {
162                let carry = limbs_shl_to_out(&mut arr[..s], xs, shift);
163                if carry != 0 {
164                    arr[s] = carry;
165                    s += 1;
166                }
167            } else {
168                arr[..s].copy_from_slice(xs);
169            }
170            // Deal with - borrow.
171            if borrow {
172                limbs_sub_limb_in_place(&mut arr[..s], Limb::power_of_2(shift));
173            }
174            // Add back in the saved bits from the start of the field.
175            arr[0].wrapping_add_assign(save);
176        }
177        false
178    }
179}}
180
181// Packs `xs` into consecutive `bits`-bit fields of `out`, starting at bit 0, as two's complement
182// numbers, each negative one borrowing from the field above; if `negate`, the negations of `xs` are
183// packed instead. `out` must be zeroed, and long enough for every field; if the last element packs
184// as a negative number, and its field ends at a limb boundary, one limb more. (FLINT's callers
185// choose `negate` so that the leading coefficient packs as nonnegative, which avoids the extra
186// limb.)
187//
188// # Worst-case complexity
189// $T(n) = O(n)$
190//
191// $M(n) = O(1)$
192//
193// where $T$ is time, $M$ is additional memory, and $n$ is `xs.len() * bits`.
194//
195// This is equivalent to `_fmpz_poly_bit_pack` from `fmpz_poly/bit_pack.c`, FLINT 3.6.0, where
196// `negate` being true corresponds to a `negate` of -1.
197crate_test_fn! {limbs_pack_coefficients<C: PolynomialCoefficient>(
198    out: &mut [Limb],
199    xs: &[C],
200    bits: u64,
201    negate: bool,
202) {
203    let mut borrow = false;
204    for (i, x) in xs.iter().enumerate() {
205        let (limbs, shift) = field_start(i, bits);
206        borrow = limbs_pack_field(&mut out[limbs..], shift, bits, x, negate, borrow);
207    }
208}}
209
210// Packs the magnitudes of the positive and of the negative coefficients separately, each at its own
211// index, and subtracts the second total from the first.
212fn bit_pack_ref(p: &IntegerPolynomial, bits: u64) -> Integer {
213    let positive = limbs_bit_pack(
214        p.coefficients
215            .iter()
216            .enumerate()
217            .filter(|(_, c)| **c > 0u32)
218            .map(|(i, c)| (i, c.unsigned_abs_ref())),
219        bits,
220    );
221    let negative = limbs_bit_pack(
222        p.coefficients
223            .iter()
224            .enumerate()
225            .filter(|(_, c)| **c < 0u32)
226            .map(|(i, c)| (i, c.unsigned_abs_ref())),
227        bits,
228    );
229    let positive = Integer::from(Natural::from_owned_limbs_asc(positive));
230    if negative.is_empty() {
231        positive
232    } else {
233        positive - Integer::from(Natural::from_owned_limbs_asc(negative))
234    }
235}
236
237impl BitPack for IntegerPolynomial {
238    type Output = Integer;
239
240    /// Packs the coefficients of an [`IntegerPolynomial`] into an [`Integer`], placing the
241    /// coefficient of $x^i$ at bit $ib$, taking it by value. The result is the value of the
242    /// polynomial at $2^b$.
243    ///
244    /// $$
245    /// f(p, b) = p(2^b) = \sum_i a_i2^{ib}.
246    /// $$
247    ///
248    /// When every coefficient's absolute value is less than $2^b$, each occupies its own $b$-bit
249    /// field, and the sign of the result is the sign of the leading coefficient. Wider coefficients
250    /// overlap the fields above them, and the result is still $p(2^b)$.
251    ///
252    /// # Worst-case complexity
253    /// $T(n, m) = O(n + m)$
254    ///
255    /// $M(n, m) = O(n + m)$
256    ///
257    /// where $T$ is time, $M$ is additional memory, $n$ is the total number of bits of the
258    /// coefficients, and $m$ is `self.len()` times `bits`.
259    ///
260    /// # Examples
261    /// ```
262    /// use core::str::FromStr;
263    /// use malachite_base::polynomial::BitPack;
264    /// use malachite_nz::integer_polynomial::IntegerPolynomial;
265    ///
266    /// // 3 * 2^16 + 2 * 2^8 + 1
267    /// let p = IntegerPolynomial::from_str("3*x^2+2*x+1").unwrap();
268    /// assert_eq!(p.clone().bit_pack(8).to_string(), "197121");
269    /// // A negative coefficient borrows from the field above it: 3 * 2^16 - 2 * 2^8 + 1.
270    /// let p = IntegerPolynomial::from_str("3*x^2-2*x+1").unwrap();
271    /// assert_eq!(p.clone().bit_pack(8).to_string(), "196097");
272    /// // The sign of the result is the sign of the leading coefficient.
273    /// let p = IntegerPolynomial::from_str("-x^2+255*x+255").unwrap();
274    /// assert_eq!(p.clone().bit_pack(8).to_string(), "-1");
275    /// // Coefficients wider than the fields overlap, and the result is still p(2^b).
276    /// let p = IntegerPolynomial::from_str("1000*x+1000").unwrap();
277    /// assert_eq!(p.bit_pack(8).to_string(), "257000");
278    /// ```
279    ///
280    /// This is equivalent to `fmpz_poly_bit_pack` from `fmpz_poly/bit_pack.c`, FLINT 3.6.0, when
281    /// `bits` is positive and every coefficient's absolute value is less than $2^b$; FLINT gives 0
282    /// when `bits` is 0 rather than $p(1)$, and truncates wider coefficients.
283    #[inline]
284    fn bit_pack(self, bits: u64) -> Integer {
285        bit_pack_ref(&self, bits)
286    }
287}
288
289impl BitPack for &IntegerPolynomial {
290    type Output = Integer;
291
292    /// Packs the coefficients of an [`IntegerPolynomial`] into an [`Integer`], placing the
293    /// coefficient of $x^i$ at bit $ib$, taking it by reference. The result is the value of the
294    /// polynomial at $2^b$.
295    ///
296    /// $$
297    /// f(p, b) = p(2^b) = \sum_i a_i2^{ib}.
298    /// $$
299    ///
300    /// When every coefficient's absolute value is less than $2^b$, each occupies its own $b$-bit
301    /// field, and the sign of the result is the sign of the leading coefficient. Wider coefficients
302    /// overlap the fields above them, and the result is still $p(2^b)$.
303    ///
304    /// # Worst-case complexity
305    /// $T(n, m) = O(n + m)$
306    ///
307    /// $M(n, m) = O(n + m)$
308    ///
309    /// where $T$ is time, $M$ is additional memory, $n$ is the total number of bits of the
310    /// coefficients, and $m$ is `self.len()` times `bits`.
311    ///
312    /// # Examples
313    /// ```
314    /// use core::str::FromStr;
315    /// use malachite_base::polynomial::BitPack;
316    /// use malachite_nz::integer_polynomial::IntegerPolynomial;
317    ///
318    /// // 3 * 2^16 + 2 * 2^8 + 1
319    /// let p = IntegerPolynomial::from_str("3*x^2+2*x+1").unwrap();
320    /// assert_eq!((&p).bit_pack(8).to_string(), "197121");
321    /// // A negative coefficient borrows from the field above it: 3 * 2^16 - 2 * 2^8 + 1.
322    /// let p = IntegerPolynomial::from_str("3*x^2-2*x+1").unwrap();
323    /// assert_eq!((&p).bit_pack(8).to_string(), "196097");
324    /// // The sign of the result is the sign of the leading coefficient.
325    /// let p = IntegerPolynomial::from_str("-x^2+255*x+255").unwrap();
326    /// assert_eq!((&p).bit_pack(8).to_string(), "-1");
327    /// // Coefficients wider than the fields overlap, and the result is still p(2^b).
328    /// let p = IntegerPolynomial::from_str("1000*x+1000").unwrap();
329    /// assert_eq!((&p).bit_pack(8).to_string(), "257000");
330    /// ```
331    ///
332    /// This is equivalent to `fmpz_poly_bit_pack` from `fmpz_poly/bit_pack.c`, FLINT 3.6.0, when
333    /// `bits` is positive and every coefficient's absolute value is less than $2^b$; FLINT gives 0
334    /// when `bits` is 0 rather than $p(1)$, and truncates wider coefficients.
335    #[inline]
336    fn bit_pack(self, bits: u64) -> Integer {
337        bit_pack_ref(self, bits)
338    }
339}