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