| /* hgcd_matrix.c. |
| |
| THE FUNCTIONS IN THIS FILE ARE INTERNAL WITH MUTABLE INTERFACES. IT IS ONLY |
| SAFE TO REACH THEM THROUGH DOCUMENTED INTERFACES. IN FACT, IT IS ALMOST |
| GUARANTEED THAT THEY'LL CHANGE OR DISAPPEAR IN A FUTURE GNU MP RELEASE. |
| |
| Copyright 2003-2005, 2008, 2012 Free Software Foundation, Inc. |
| |
| This file is part of the GNU MP Library. |
| |
| The GNU MP Library is free software; you can redistribute it and/or modify |
| it under the terms of either: |
| |
| * the GNU Lesser General Public License as published by the Free |
| Software Foundation; either version 3 of the License, or (at your |
| option) any later version. |
| |
| or |
| |
| * the GNU General Public License as published by the Free Software |
| Foundation; either version 2 of the License, or (at your option) any |
| later version. |
| |
| or both in parallel, as here. |
| |
| The GNU MP Library is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received copies of the GNU General Public License and the |
| GNU Lesser General Public License along with the GNU MP Library. If not, |
| see https://www.gnu.org/licenses/. */ |
| |
| #include "gmp-impl.h" |
| #include "longlong.h" |
| |
| /* For input of size n, matrix elements are of size at most ceil(n/2) |
| - 1, but we need two limbs extra. */ |
| void |
| mpn_hgcd_matrix_init (struct hgcd_matrix *M, mp_size_t n, mp_ptr p) |
| { |
| mp_size_t s = (n+1)/2 + 1; |
| M->alloc = s; |
| M->n = 1; |
| MPN_ZERO (p, 4 * s); |
| M->p[0][0] = p; |
| M->p[0][1] = p + s; |
| M->p[1][0] = p + 2 * s; |
| M->p[1][1] = p + 3 * s; |
| |
| M->p[0][0][0] = M->p[1][1][0] = 1; |
| } |
| |
| /* Update column COL, adding in Q * column (1-COL). Temporary storage: |
| * qn + n <= M->alloc, where n is the size of the largest element in |
| * column 1 - COL. */ |
| void |
| mpn_hgcd_matrix_update_q (struct hgcd_matrix *M, mp_srcptr qp, mp_size_t qn, |
| unsigned col, mp_ptr tp) |
| { |
| ASSERT (col < 2); |
| |
| if (qn == 1) |
| { |
| mp_limb_t q = qp[0]; |
| mp_limb_t c0, c1; |
| |
| c0 = mpn_addmul_1 (M->p[0][col], M->p[0][1-col], M->n, q); |
| c1 = mpn_addmul_1 (M->p[1][col], M->p[1][1-col], M->n, q); |
| |
| M->p[0][col][M->n] = c0; |
| M->p[1][col][M->n] = c1; |
| |
| M->n += (c0 | c1) != 0; |
| } |
| else |
| { |
| unsigned row; |
| |
| /* Carries for the unlikely case that we get both high words |
| from the multiplication and carries from the addition. */ |
| mp_limb_t c[2]; |
| mp_size_t n; |
| |
| /* The matrix will not necessarily grow in size by qn, so we |
| need normalization in order not to overflow M. */ |
| |
| for (n = M->n; n + qn > M->n; n--) |
| { |
| ASSERT (n > 0); |
| if (M->p[0][1-col][n-1] > 0 || M->p[1][1-col][n-1] > 0) |
| break; |
| } |
| |
| ASSERT (qn + n <= M->alloc); |
| |
| for (row = 0; row < 2; row++) |
| { |
| if (qn <= n) |
| mpn_mul (tp, M->p[row][1-col], n, qp, qn); |
| else |
| mpn_mul (tp, qp, qn, M->p[row][1-col], n); |
| |
| ASSERT (n + qn >= M->n); |
| c[row] = mpn_add (M->p[row][col], tp, n + qn, M->p[row][col], M->n); |
| } |
| |
| n += qn; |
| |
| if (c[0] | c[1]) |
| { |
| M->p[0][col][n] = c[0]; |
| M->p[1][col][n] = c[1]; |
| n++; |
| } |
| else |
| { |
| n -= (M->p[0][col][n-1] | M->p[1][col][n-1]) == 0; |
| ASSERT (n >= M->n); |
| } |
| M->n = n; |
| } |
| |
| ASSERT (M->n < M->alloc); |
| } |
| |
| /* Multiply M by M1 from the right. Since the M1 elements fit in |
| GMP_NUMB_BITS - 1 bits, M grows by at most one limb. Needs |
| temporary space M->n */ |
| void |
| mpn_hgcd_matrix_mul_1 (struct hgcd_matrix *M, const struct hgcd_matrix1 *M1, |
| mp_ptr tp) |
| { |
| mp_size_t n0, n1; |
| |
| /* Could avoid copy by some swapping of pointers. */ |
| MPN_COPY (tp, M->p[0][0], M->n); |
| n0 = mpn_hgcd_mul_matrix1_vector (M1, M->p[0][0], tp, M->p[0][1], M->n); |
| MPN_COPY (tp, M->p[1][0], M->n); |
| n1 = mpn_hgcd_mul_matrix1_vector (M1, M->p[1][0], tp, M->p[1][1], M->n); |
| |
| /* Depends on zero initialization */ |
| M->n = MAX(n0, n1); |
| ASSERT (M->n < M->alloc); |
| } |
| |
| /* Multiply M by M1 from the right. Needs 3*(M->n + M1->n) + 5 limbs |
| of temporary storage (see mpn_matrix22_mul_itch). */ |
| void |
| mpn_hgcd_matrix_mul (struct hgcd_matrix *M, const struct hgcd_matrix *M1, |
| mp_ptr tp) |
| { |
| mp_size_t n; |
| |
| /* About the new size of M:s elements. Since M1's diagonal elements |
| are > 0, no element can decrease. The new elements are of size |
| M->n + M1->n, one limb more or less. The computation of the |
| matrix product produces elements of size M->n + M1->n + 1. But |
| the true size, after normalization, may be three limbs smaller. |
| |
| The reason that the product has normalized size >= M->n + M1->n - |
| 2 is subtle. It depends on the fact that M and M1 can be factored |
| as products of (1,1; 0,1) and (1,0; 1,1), and that we can't have |
| M ending with a large power and M1 starting with a large power of |
| the same matrix. */ |
| |
| /* FIXME: Strassen multiplication gives only a small speedup. In FFT |
| multiplication range, this function could be sped up quite a lot |
| using invariance. */ |
| ASSERT (M->n + M1->n < M->alloc); |
| |
| ASSERT ((M->p[0][0][M->n-1] | M->p[0][1][M->n-1] |
| | M->p[1][0][M->n-1] | M->p[1][1][M->n-1]) > 0); |
| |
| ASSERT ((M1->p[0][0][M1->n-1] | M1->p[0][1][M1->n-1] |
| | M1->p[1][0][M1->n-1] | M1->p[1][1][M1->n-1]) > 0); |
| |
| mpn_matrix22_mul (M->p[0][0], M->p[0][1], |
| M->p[1][0], M->p[1][1], M->n, |
| M1->p[0][0], M1->p[0][1], |
| M1->p[1][0], M1->p[1][1], M1->n, tp); |
| |
| /* Index of last potentially non-zero limb, size is one greater. */ |
| n = M->n + M1->n; |
| |
| n -= ((M->p[0][0][n] | M->p[0][1][n] | M->p[1][0][n] | M->p[1][1][n]) == 0); |
| n -= ((M->p[0][0][n] | M->p[0][1][n] | M->p[1][0][n] | M->p[1][1][n]) == 0); |
| n -= ((M->p[0][0][n] | M->p[0][1][n] | M->p[1][0][n] | M->p[1][1][n]) == 0); |
| |
| ASSERT ((M->p[0][0][n] | M->p[0][1][n] | M->p[1][0][n] | M->p[1][1][n]) > 0); |
| |
| M->n = n + 1; |
| } |
| |
| /* Multiplies the least significant p limbs of (a;b) by M^-1. |
| Temporary space needed: 2 * (p + M->n)*/ |
| mp_size_t |
| mpn_hgcd_matrix_adjust (const struct hgcd_matrix *M, |
| mp_size_t n, mp_ptr ap, mp_ptr bp, |
| mp_size_t p, mp_ptr tp) |
| { |
| /* M^-1 (a;b) = (r11, -r01; -r10, r00) (a ; b) |
| = (r11 a - r01 b; - r10 a + r00 b */ |
| |
| mp_ptr t0 = tp; |
| mp_ptr t1 = tp + p + M->n; |
| mp_limb_t ah, bh; |
| mp_limb_t cy; |
| |
| ASSERT (p + M->n < n); |
| |
| /* First compute the two values depending on a, before overwriting a */ |
| |
| if (M->n >= p) |
| { |
| mpn_mul (t0, M->p[1][1], M->n, ap, p); |
| mpn_mul (t1, M->p[1][0], M->n, ap, p); |
| } |
| else |
| { |
| mpn_mul (t0, ap, p, M->p[1][1], M->n); |
| mpn_mul (t1, ap, p, M->p[1][0], M->n); |
| } |
| |
| /* Update a */ |
| MPN_COPY (ap, t0, p); |
| ah = mpn_add (ap + p, ap + p, n - p, t0 + p, M->n); |
| |
| if (M->n >= p) |
| mpn_mul (t0, M->p[0][1], M->n, bp, p); |
| else |
| mpn_mul (t0, bp, p, M->p[0][1], M->n); |
| |
| cy = mpn_sub (ap, ap, n, t0, p + M->n); |
| ASSERT (cy <= ah); |
| ah -= cy; |
| |
| /* Update b */ |
| if (M->n >= p) |
| mpn_mul (t0, M->p[0][0], M->n, bp, p); |
| else |
| mpn_mul (t0, bp, p, M->p[0][0], M->n); |
| |
| MPN_COPY (bp, t0, p); |
| bh = mpn_add (bp + p, bp + p, n - p, t0 + p, M->n); |
| cy = mpn_sub (bp, bp, n, t1, p + M->n); |
| ASSERT (cy <= bh); |
| bh -= cy; |
| |
| if (ah > 0 || bh > 0) |
| { |
| ap[n] = ah; |
| bp[n] = bh; |
| n++; |
| } |
| else |
| { |
| /* The subtraction can reduce the size by at most one limb. */ |
| if (ap[n-1] == 0 && bp[n-1] == 0) |
| n--; |
| } |
| ASSERT (ap[n-1] > 0 || bp[n-1] > 0); |
| return n; |
| } |