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}