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}