Skip to main content

malachite_nz/integer_polynomial/arithmetic/
bit_unpack.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::bit_pack::field_start;
16use crate::integer_polynomial::arithmetic::coefficient::PolynomialCoefficient;
17use crate::integer_polynomial::arithmetic::vec::SMALL_FMPZ_BITCOUNT_MAX;
18use crate::natural::Natural;
19use crate::natural::arithmetic::add::limbs_slice_add_limb_in_place;
20use crate::natural::arithmetic::shr::limbs_shr_to_out;
21use crate::natural::logic::not::limbs_not_in_place;
22use crate::platform::{Limb, SignedLimb};
23use alloc::vec;
24use alloc::vec::Vec;
25use malachite_base::num::arithmetic::traits::{
26    ModPowerOf2Assign, NegAssign, PowerOf2, WrappingAddAssign,
27};
28use malachite_base::num::basic::integers::PrimitiveInt;
29use malachite_base::num::basic::traits::One;
30use malachite_base::num::conversion::traits::{ExactFrom, PowerOf2Digits, WrappingFrom};
31use malachite_base::num::logic::traits::{BitAccess, LowMask};
32use malachite_base::polynomial::{BitUnpack, Polynomial};
33
34// The limbs of the `bits`-bit field of `arr` that starts at bit `shift` of `arr[0]`, as an unsigned
35// number with `ceil(bits / Limb::WIDTH)` limbs.
36fn limbs_extract_field(arr: &[Limb], shift: u64, bits: u64) -> Vec<Limb> {
37    let limbs = usize::exact_from((shift + bits) >> Limb::LOG_WIDTH);
38    let rem_bits = (shift + bits) & Limb::WIDTH_MASK;
39    // The number of limbs that hold the field, including b extra bits:
40    let l = usize::exact_from(bits.div_ceil(Limb::WIDTH));
41    let b = bits & Limb::WIDTH_MASK;
42    let mut p = vec![0; l];
43    // Shift in l limbs.
44    if shift != 0 {
45        limbs_shr_to_out(&mut p, &arr[..l], shift);
46    } else {
47        p.copy_from_slice(&arr[..l]);
48    }
49    // Shift in any remaining bits that weren't already shifted.
50    if limbs + usize::from(rem_bits != 0) > l {
51        p[l - 1].wrapping_add_assign(arr[limbs] << (Limb::WIDTH - shift));
52    }
53    // Mask off the last limb, if it isn't full.
54    if b != 0 {
55        p[l - 1].mod_power_of_2_assign(b);
56    }
57    p
58}
59
60// Unpacks the `bits`-bit field of `arr` that starts at bit `shift` of `arr[0]`, as a two's
61// complement number to which `borrow` is added, and returns it, negated if `negate`, together with
62// whether the field was negative, that is, whether the field above must borrow from its own.
63//
64// # Worst-case complexity
65// $T(n) = O(n)$
66//
67// $M(n) = O(n)$
68//
69// where $T$ is time, $M$ is additional memory, and $n$ is `bits`.
70//
71// This is equivalent to `fmpz_bit_unpack` from `fmpz/bit_unpack.c`, FLINT 3.6.0.
72crate_test_fn! {limbs_unpack_field<C: PolynomialCoefficient>(
73    arr: &[Limb],
74    shift: u64,
75    bits: u64,
76    negate: bool,
77    borrow: bool,
78) -> (C, bool) {
79    let limbs = usize::exact_from((shift + bits) >> Limb::LOG_WIDTH);
80    let rem_bits = (shift + bits) & Limb::WIDTH_MASK;
81    // Determine whether the field is positive or negative.
82    let sign = if rem_bits != 0 {
83        arr[limbs].get_bit(rem_bits - 1)
84    } else {
85        arr[limbs - 1].get_highest_bit()
86    };
87    // The value is built from its sign (`true` for non-negative) and absolute value.
88    let (value_sign, abs, negative) = if bits <= SMALL_FMPZ_BITCOUNT_MAX {
89        // The field fits in a small coefficient.
90        let mask = Limb::low_mask(bits);
91        let mut c = if limbs + usize::from(rem_bits != 0) > 1 {
92            // The field crosses a limb boundary.
93            ((arr[0] >> shift).wrapping_add(arr[1] << (Limb::WIDTH - shift))) & mask
94        } else {
95            // The field is in the first limb only; mask it.
96            (arr[0] >> shift) & mask
97        };
98        // Sign-extend.
99        if sign {
100            c.wrapping_add_assign(Limb::MAX << bits);
101        }
102        let c = SignedLimb::wrapping_from(c);
103        // Determine whether we need to return a borrow, and deal with the borrow; since the field
104        // has at most `SMALL_FMPZ_BITCOUNT_MAX` bits, adding it cannot overflow.
105        let value = c + SignedLimb::from(borrow);
106        (
107            value >= 0,
108            Natural::from(Limb::wrapping_from(value.unsigned_abs())),
109            c < 0,
110        )
111    } else {
112        // A large coefficient.
113        let mut p = limbs_extract_field(arr, shift, bits);
114        let l = p.len();
115        let b = bits & Limb::WIDTH_MASK;
116        if sign {
117            // Sign-extend.
118            if b != 0 {
119                p[l - 1].wrapping_add_assign(Limb::MAX << b);
120            }
121            // Negate.
122            limbs_not_in_place(&mut p);
123            if !borrow {
124                limbs_slice_add_limb_in_place(&mut p, 1);
125            }
126            (false, Natural::from_owned_limbs_asc(p), true)
127        } else {
128            // Deal with the borrow.
129            if borrow {
130                limbs_slice_add_limb_in_place(&mut p, 1);
131            }
132            (true, Natural::from_owned_limbs_asc(p), false)
133        }
134    };
135    // Negate if required.
136    (C::from_sign_and_abs(value_sign != negate, abs), negative)
137}}
138
139// Unpacks the `bits`-bit field of `arr` that starts at bit `shift` of `arr[0]`, as an unsigned
140// number.
141//
142// # Worst-case complexity
143// $T(n) = O(n)$
144//
145// $M(n) = O(n)$
146//
147// where $T$ is time, $M$ is additional memory, and $n$ is `bits`.
148//
149// This is equivalent to `fmpz_bit_unpack_unsigned` from `fmpz/bit_unpack.c`, FLINT 3.6.0.
150crate_test_fn! {limbs_unpack_field_unsigned<C: PolynomialCoefficient>(
151    arr: &[Limb],
152    shift: u64,
153    bits: u64,
154) -> C {
155    let limbs = usize::exact_from((shift + bits) >> Limb::LOG_WIDTH);
156    let rem_bits = (shift + bits) & Limb::WIDTH_MASK;
157    if bits <= SMALL_FMPZ_BITCOUNT_MAX {
158        // The field fits in a small coefficient.
159        let mask = Limb::low_mask(bits);
160        C::from_sign_and_abs(true, Natural::from(if limbs + usize::from(rem_bits != 0) > 1 {
161            // The field crosses a limb boundary.
162            ((arr[0] >> shift).wrapping_add(arr[1] << (Limb::WIDTH - shift))) & mask
163        } else {
164            // The field is in the first limb only; mask it.
165            (arr[0] >> shift) & mask
166        }))
167    } else {
168        // A large coefficient.
169        C::from_sign_and_abs(
170            true,
171            Natural::from_owned_limbs_asc(limbs_extract_field(arr, shift, bits)),
172        )
173    }
174}}
175
176// Unpacks the fields `nlo..nhi` of consecutive `bits`-bit fields of `xs`, as two's complement
177// numbers, each negative one borrowing from the field above, into `out`; if `negate`, their
178// negations are unpacked instead. Returns whether the last field unpacked was negative, in which
179// case a coefficient of 1, or -1 if `negate`, belongs at index `nhi`. If `nlo` is not zero, the
180// borrow into the first field unpacked is read from the top bit of the field below it.
181//
182// # Worst-case complexity
183// $T(n) = O(n)$
184//
185// $M(n) = O(n)$
186//
187// where $T$ is time, $M$ is additional memory, and $n$ is `(nhi - nlo) * bits`.
188//
189// This is equivalent to `_fmpz_poly_bit_unpack` from `fmpz_poly/bit_unpack.c`, FLINT 3.6.0, where
190// `negate` being true corresponds to a `negate` of -1.
191crate_test_fn! {limbs_unpack_coefficients<C: PolynomialCoefficient>(
192    out: &mut [C],
193    nlo: usize,
194    nhi: usize,
195    xs: &[Limb],
196    bits: u64,
197    negate: bool,
198) -> bool {
199    // The borrow into field nlo is the sign bit of the field below it.
200    let mut borrow = if nlo == 0 {
201        false
202    } else {
203        let top = u64::exact_from(nlo) * bits - 1;
204        xs[usize::exact_from(top >> Limb::LOG_WIDTH)].get_bit(top & Limb::WIDTH_MASK)
205    };
206    for (c, i) in out.iter_mut().zip(nlo..nhi) {
207        let (limbs, shift) = field_start(i, bits);
208        let next_borrow;
209        (*c, next_borrow) = limbs_unpack_field(&xs[limbs..], shift, bits, negate, borrow);
210        borrow = next_borrow;
211    }
212    borrow
213}}
214
215// Unpacks the fields `nlo..nhi` of consecutive `bits`-bit fields of `xs`, as unsigned numbers, into
216// `out`.
217//
218// # Worst-case complexity
219// $T(n) = O(n)$
220//
221// $M(n) = O(n)$
222//
223// where $T$ is time, $M$ is additional memory, and $n$ is `(nhi - nlo) * bits`.
224//
225// This is equivalent to `_fmpz_poly_bit_unpack_unsigned` from `fmpz_poly/bit_unpack.c`, FLINT
226// 3.6.0.
227crate_test_fn! {limbs_unpack_coefficients_unsigned<C: PolynomialCoefficient>(
228    out: &mut [C],
229    nlo: usize,
230    nhi: usize,
231    xs: &[Limb],
232    bits: u64,
233) {
234    for (c, i) in out.iter_mut().zip(nlo..nhi) {
235        let (limbs, shift) = field_start(i, bits);
236        *c = limbs_unpack_field_unsigned(&xs[limbs..], shift, bits);
237    }
238}}
239
240// Reads the base-2^bits digits of |n| as signed numbers, each negative one borrowing 1 from the
241// digit above, and negates the result if n is negative.
242//
243// This is equivalent to `fmpz_poly_bit_unpack` from `fmpz_poly/bit_unpack.c`, FLINT 3.6.0.
244fn bit_unpack_ref(n: &Integer, bits: u64) -> IntegerPolynomial {
245    assert_ne!(bits, 0, "Cannot unpack a polynomial from fields of 0 bits");
246    let digits: Vec<Natural> = n.unsigned_abs_ref().to_power_of_2_digits_asc(bits);
247    let field = Integer::power_of_2(bits);
248    let mut coefficients = Vec::with_capacity(digits.len() + 1);
249    let mut borrow = false;
250    for digit in digits {
251        let negative = digit.get_bit(bits - 1);
252        let mut c = Integer::from(digit);
253        if negative {
254            c -= &field;
255        }
256        if borrow {
257            c += Integer::ONE;
258        }
259        coefficients.push(c);
260        borrow = negative;
261    }
262    if borrow {
263        coefficients.push(Integer::ONE);
264    }
265    if *n < 0u32 {
266        for c in &mut coefficients {
267            c.neg_assign();
268        }
269    }
270    IntegerPolynomial::from_coefficients_asc(coefficients)
271}
272
273impl BitUnpack<Integer> for IntegerPolynomial {
274    /// Unpacks an [`IntegerPolynomial`] from the `bits`-bit fields of an [`Integer`], taking it by
275    /// value. The result $p$ satisfies $p(2^b) = n$.
276    ///
277    /// $$
278    /// f(n, b) = p, \quad \text{where} \quad p(2^b) = n.
279    /// $$
280    ///
281    /// The fields of $|n|$ are read as signed $b$-bit numbers, in two's complement, and each
282    /// negative one borrows 1 from the field above, so the coefficients lie in $[-2^{b-1},
283    /// 2^{b-1}]$; if $n$ is negative, every coefficient is then negated. This inverts
284    /// [`bit_pack`](malachite_base::polynomial::BitPack::bit_pack) on polynomials whose
285    /// coefficients' absolute values are less than $2^{b-1}$.
286    ///
287    /// # Worst-case complexity
288    /// $T(n) = O(n)$
289    ///
290    /// $M(n) = O(n)$
291    ///
292    /// where $T$ is time, $M$ is additional memory, and $n$ is `n.significant_bits()`.
293    ///
294    /// # Panics
295    /// Panics if `bits` is 0.
296    ///
297    /// # Examples
298    /// ```
299    /// use malachite_base::polynomial::BitUnpack;
300    /// use malachite_nz::integer::Integer;
301    /// use malachite_nz::integer_polynomial::IntegerPolynomial;
302    ///
303    /// assert_eq!(
304    ///     IntegerPolynomial::bit_unpack(Integer::from(197121), 8).to_string(),
305    ///     "3*x^2+2*x+1"
306    /// );
307    /// // A field whose top bit is set is negative, and borrows from the field above.
308    /// assert_eq!(
309    ///     IntegerPolynomial::bit_unpack(Integer::from(196097), 8).to_string(),
310    ///     "3*x^2-2*x+1"
311    /// );
312    /// assert_eq!(
313    ///     IntegerPolynomial::bit_unpack(Integer::from(128), 8).to_string(),
314    ///     "x-128"
315    /// );
316    /// assert_eq!(
317    ///     IntegerPolynomial::bit_unpack(Integer::from(-196097), 8).to_string(),
318    ///     "-3*x^2+2*x-1"
319    /// );
320    /// ```
321    ///
322    /// This is equivalent to `fmpz_poly_bit_unpack` from `fmpz_poly/bit_unpack.c`, FLINT 3.6.0,
323    /// except that it panics when `bits` is 0, where FLINT gives the zero polynomial.
324    #[inline]
325    fn bit_unpack(n: Integer, bits: u64) -> Self {
326        bit_unpack_ref(&n, bits)
327    }
328}
329
330impl BitUnpack<&Integer> for IntegerPolynomial {
331    /// Unpacks an [`IntegerPolynomial`] from the `bits`-bit fields of an [`Integer`], taking it by
332    /// reference. The result $p$ satisfies $p(2^b) = n$.
333    ///
334    /// $$
335    /// f(n, b) = p, \quad \text{where} \quad p(2^b) = n.
336    /// $$
337    ///
338    /// The fields of $|n|$ are read as signed $b$-bit numbers, in two's complement, and each
339    /// negative one borrows 1 from the field above, so the coefficients lie in $[-2^{b-1},
340    /// 2^{b-1}]$; if $n$ is negative, every coefficient is then negated. This inverts
341    /// [`bit_pack`](malachite_base::polynomial::BitPack::bit_pack) on polynomials whose
342    /// coefficients' absolute values are less than $2^{b-1}$.
343    ///
344    /// # Worst-case complexity
345    /// $T(n) = O(n)$
346    ///
347    /// $M(n) = O(n)$
348    ///
349    /// where $T$ is time, $M$ is additional memory, and $n$ is `n.significant_bits()`.
350    ///
351    /// # Panics
352    /// Panics if `bits` is 0.
353    ///
354    /// # Examples
355    /// ```
356    /// use malachite_base::polynomial::BitUnpack;
357    /// use malachite_nz::integer::Integer;
358    /// use malachite_nz::integer_polynomial::IntegerPolynomial;
359    ///
360    /// assert_eq!(
361    ///     IntegerPolynomial::bit_unpack(&Integer::from(197121), 8).to_string(),
362    ///     "3*x^2+2*x+1"
363    /// );
364    /// // A field whose top bit is set is negative, and borrows from the field above.
365    /// assert_eq!(
366    ///     IntegerPolynomial::bit_unpack(&Integer::from(196097), 8).to_string(),
367    ///     "3*x^2-2*x+1"
368    /// );
369    /// assert_eq!(
370    ///     IntegerPolynomial::bit_unpack(&Integer::from(128), 8).to_string(),
371    ///     "x-128"
372    /// );
373    /// assert_eq!(
374    ///     IntegerPolynomial::bit_unpack(&Integer::from(-196097), 8).to_string(),
375    ///     "-3*x^2+2*x-1"
376    /// );
377    /// ```
378    ///
379    /// This is equivalent to `fmpz_poly_bit_unpack` from `fmpz_poly/bit_unpack.c`, FLINT 3.6.0,
380    /// except that it panics when `bits` is 0, where FLINT gives the zero polynomial.
381    #[inline]
382    fn bit_unpack(n: &Integer, bits: u64) -> Self {
383        bit_unpack_ref(n, bits)
384    }
385}