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}