/ src / gmpy2_mpz_pack.c
gmpy2_mpz_pack.c
  1  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2   * gmpy2_mpz_pack.c                                                        *
  3   * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  4   * Python interface to the GMP or MPIR, MPFR, and MPC multiple precision   *
  5   * libraries.                                                              *
  6   *                                                                         *
  7   * Copyright 2000 - 2009 Alex Martelli                                     *
  8   *                                                                         *
  9   * Copyright 2008 - 2021 Case Van Horsen                                   *
 10   *                                                                         *
 11   * This file is part of GMPY2.                                             *
 12   *                                                                         *
 13   * GMPY2 is free software: you can redistribute it and/or modify it under  *
 14   * the terms of the GNU Lesser General Public License as published by the  *
 15   * Free Software Foundation, either version 3 of the License, or (at your  *
 16   * option) any later version.                                              *
 17   *                                                                         *
 18   * GMPY2 is distributed in the hope that it will be useful, but WITHOUT    *
 19   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or   *
 20   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public    *
 21   * License for more details.                                               *
 22   *                                                                         *
 23   * You should have received a copy of the GNU Lesser General Public        *
 24   * License along with GMPY2; if not, see <http://www.gnu.org/licenses/>    *
 25   * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 26  
 27  /*
 28   **************************************************************************
 29   * pack and unpack methods
 30   *
 31   * Both pack and unpack use a devious trick when stuffing values into the
 32   * internal structure of an mpz_t. By setting a bit beyond the range of
 33   * interest, we are guaranteed that memory will remain available. When
 34   * the bit is cleared, it also triggers normalization of the value by
 35   * accounting for leading bits that are zero.
 36   **************************************************************************
 37   */
 38  
 39  PyDoc_STRVAR(doc_pack,
 40  "pack(lst, n) -> mpz\n\n"
 41  "Pack a list of integers 'lst' into a single 'mpz' by concatenating\n"
 42  "each integer element of 'lst' after padding to length n bits. Raises\n"
 43  "an error if any integer is negative or greater than n bits in\n"
 44  "length.");
 45  
 46  static PyObject *
 47  GMPy_MPZ_pack(PyObject *self, PyObject *args)
 48  {
 49      mp_bitcnt_t nbits, total_bits, tempx_bits;
 50      Py_ssize_t index, lst_count, i, temp_bits, limb_count;
 51      PyObject *lst;
 52      mpz_t temp, temp1;
 53      MPZ_Object *result, *tempx = 0;
 54      CTXT_Object *context = NULL;
 55  
 56      if (PyTuple_GET_SIZE(args) != 2) {
 57          TYPE_ERROR("pack() requires 'list','int' arguments");
 58          return NULL;
 59      }
 60  
 61      nbits = mp_bitcnt_t_From_Integer(PyTuple_GET_ITEM(args, 1));
 62      if (nbits == (mp_bitcnt_t)(-1) && PyErr_Occurred()) {
 63          return NULL;
 64      }
 65  
 66      if (!PyList_Check(PyTuple_GET_ITEM(args, 0))) {
 67          TYPE_ERROR("pack() requires 'list','int' arguments");
 68          return NULL;
 69      }
 70  
 71      if (!(result = GMPy_MPZ_New(context)))
 72          return NULL;
 73  
 74      lst = PyTuple_GET_ITEM(args, 0);
 75      lst_count = PyList_GET_SIZE(lst);
 76      total_bits = nbits * lst_count;
 77  
 78      if ((total_bits / lst_count) != nbits) {
 79          VALUE_ERROR("result too large to store in an 'mpz'");
 80          return NULL;
 81      }
 82  
 83      mpz_set_ui(result->z, 0);
 84      mpz_setbit(result->z, total_bits + (mp_bits_per_limb * 2));
 85  
 86      mpz_init(temp);
 87      mpz_init(temp1);
 88      mpz_set_ui(temp, 0);
 89      limb_count = 0;
 90      tempx_bits = 0;
 91  
 92      for (index = 0; index < lst_count; index++) {
 93          if (!(tempx = GMPy_MPZ_From_Integer(PyList_GetItem(lst, index), context))
 94              || (mpz_sgn(tempx->z) < 0)
 95              || (mpz_sizeinbase(tempx->z,2) > (size_t)nbits)) {
 96              TYPE_ERROR("pack() requires list elements be positive integers < 2^n bits");
 97              mpz_clear(temp);
 98              Py_XDECREF((PyObject*)tempx);
 99              Py_DECREF((PyObject*)result);
100              return NULL;
101          }
102          mpz_mul_2exp(temp1, tempx->z, tempx_bits);
103          mpz_add(temp, temp, temp1);
104          tempx_bits += nbits;
105          i = 0;
106          temp_bits = mpz_sizeinbase(temp, 2) * mpz_sgn(temp);
107          while (tempx_bits >= (mp_bitcnt_t)mp_bits_per_limb) {
108              if (temp_bits > 0) {
109                  result->z->_mp_d[limb_count] = mpz_getlimbn(temp, i);
110              }
111              i += 1;
112              tempx_bits -= mp_bits_per_limb;
113              limb_count += 1;
114              temp_bits -= mp_bits_per_limb;
115          }
116          if (temp_bits > 0) {
117              mpz_tdiv_q_2exp(temp, temp, mp_bits_per_limb * i);
118          }
119          else {
120              mpz_set_ui(temp, 0);
121          }
122          Py_DECREF((PyObject*)tempx);
123      }
124      result->z->_mp_d[limb_count] = mpz_getlimbn(temp, 0);
125      mpz_clrbit(result->z, total_bits + (mp_bits_per_limb * 2));
126      mpz_clear(temp);
127      mpz_clear(temp1);
128      return (PyObject*)result;
129  }
130  
131  PyDoc_STRVAR(doc_unpack,
132  "unpack(x, n) -> list\n\n"
133  "Unpack an integer 'x' into a list of n-bit values. Equivalent to\n"
134  "repeated division by 2**n. Raises error if 'x' is negative.");
135  
136  static PyObject *
137  GMPy_MPZ_unpack(PyObject *self, PyObject *args)
138  {
139      mp_bitcnt_t nbits, total_bits, guard_bit, extra_bits, temp_bits;
140      Py_ssize_t index = 0, lst_count, i, lst_ptr = 0;
141      PyObject *result;
142      mpz_t temp;
143      mp_limb_t extra = 0;
144      MPZ_Object *item, *tempx = NULL;
145      CTXT_Object *context = NULL;
146  
147      if (PyTuple_GET_SIZE(args) != 2) {
148          TYPE_ERROR("unpack() requires 'int','int' arguments");
149          return NULL;
150      }
151  
152      nbits = mp_bitcnt_t_From_Integer(PyTuple_GET_ITEM(args, 1));
153      if (nbits == (mp_bitcnt_t)(-1) && PyErr_Occurred()) {
154          return NULL;
155      }
156  
157      if (!(tempx = GMPy_MPZ_From_Integer(PyTuple_GET_ITEM(args, 0), context))) {
158          TYPE_ERROR("unpack() requires 'int','int' arguments");
159          return NULL;
160      }
161  
162      if (mpz_sgn(tempx->z) < 0) {
163          VALUE_ERROR("unpack() requires x >= 0");
164          return NULL;
165      }
166  
167      if (mpz_sgn(tempx->z) == 0) {
168          total_bits = 0;
169      }
170      else {
171          total_bits = mpz_sizeinbase(tempx->z, 2);
172      }
173  
174      lst_count = total_bits / nbits;
175      if ((total_bits % nbits) || !lst_count) {
176          lst_count += 1;
177      }
178  
179      if (!(result = PyList_New(lst_count))) {
180          Py_DECREF((PyObject*)tempx);
181          return NULL;
182      }
183  
184      if (mpz_sgn(tempx->z) == 0) {
185          if (!(item = GMPy_MPZ_New(context))) {
186              Py_DECREF((PyObject*)tempx);
187              Py_DECREF(result);
188              return NULL;
189          }
190          mpz_set_ui(item->z, 0);
191          PyList_SET_ITEM(result, 0, (PyObject*)item);
192          Py_DECREF((PyObject*)tempx);
193          return result;
194      }
195  
196      mpz_init(temp);
197      guard_bit = nbits + (2 * mp_bits_per_limb);
198      extra_bits = 0;
199      index = 0;
200  
201      while (lst_ptr < lst_count) {
202          i = 0;
203          temp_bits = 0;
204          mpz_set_ui(temp, 0);
205          mpz_setbit(temp, guard_bit);
206          while (temp_bits + extra_bits < nbits) {
207              temp->_mp_d[i++] = mpz_getlimbn(tempx->z, index++);
208              temp_bits += mp_bits_per_limb;
209          }
210          mpz_clrbit(temp, guard_bit);
211          mpz_mul_2exp(temp, temp, extra_bits);
212          if (mpz_sgn(temp) == 0 && extra != 0) {
213              mpz_set_ui(temp, 1);
214              temp->_mp_d[0] = extra;
215          }
216          else {
217             mpn_add_1(temp->_mp_d, temp->_mp_d, mpz_size(temp), extra);
218          }
219          temp_bits += extra_bits;
220  
221          while ((lst_ptr < lst_count) && (temp_bits >= nbits)) {
222              if(!(item = GMPy_MPZ_New(context))) {
223                  mpz_clear(temp);
224                  Py_DECREF((PyObject*)tempx);
225                  Py_DECREF(result);
226                  return NULL;
227              }
228              mpz_tdiv_r_2exp(item->z, temp, nbits);
229              PyList_SET_ITEM(result, lst_ptr++, (PyObject*)item);
230              mpz_tdiv_q_2exp(temp, temp, nbits);
231              temp_bits -= nbits;
232          }
233          extra = mpz_getlimbn(temp, 0);
234          extra_bits = temp_bits;
235      }
236      Py_DECREF((PyObject*)tempx);
237      mpz_clear(temp);
238      return result;
239  }
240  
241