Blame stdlib/mul_n.c

Packit Service 82fcde
/* mpn_mul_n -- Multiply two natural numbers of length n.
Packit Service 82fcde
Packit Service 82fcde
Copyright (C) 1991-2018 Free Software Foundation, Inc.
Packit Service 82fcde
Packit Service 82fcde
This file is part of the GNU MP Library.
Packit Service 82fcde
Packit Service 82fcde
The GNU MP Library is free software; you can redistribute it and/or modify
Packit Service 82fcde
it under the terms of the GNU Lesser General Public License as published by
Packit Service 82fcde
the Free Software Foundation; either version 2.1 of the License, or (at your
Packit Service 82fcde
option) any later version.
Packit Service 82fcde
Packit Service 82fcde
The GNU MP Library is distributed in the hope that it will be useful, but
Packit Service 82fcde
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit Service 82fcde
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
Packit Service 82fcde
License for more details.
Packit Service 82fcde
Packit Service 82fcde
You should have received a copy of the GNU Lesser General Public License
Packit Service 82fcde
along with the GNU MP Library; see the file COPYING.LIB.  If not, see
Packit Service 82fcde
<http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <gmp.h>
Packit Service 82fcde
#include "gmp-impl.h"
Packit Service 82fcde
Packit Service 82fcde
/* Multiply the natural numbers u (pointed to by UP) and v (pointed to by VP),
Packit Service 82fcde
   both with SIZE limbs, and store the result at PRODP.  2 * SIZE limbs are
Packit Service 82fcde
   always stored.  Return the most significant limb.
Packit Service 82fcde
Packit Service 82fcde
   Argument constraints:
Packit Service 82fcde
   1. PRODP != UP and PRODP != VP, i.e. the destination
Packit Service 82fcde
      must be distinct from the multiplier and the multiplicand.  */
Packit Service 82fcde
Packit Service 82fcde
/* If KARATSUBA_THRESHOLD is not already defined, define it to a
Packit Service 82fcde
   value which is good on most machines.  */
Packit Service 82fcde
#ifndef KARATSUBA_THRESHOLD
Packit Service 82fcde
#define KARATSUBA_THRESHOLD 32
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
/* The code can't handle KARATSUBA_THRESHOLD smaller than 2.  */
Packit Service 82fcde
#if KARATSUBA_THRESHOLD < 2
Packit Service 82fcde
#undef KARATSUBA_THRESHOLD
Packit Service 82fcde
#define KARATSUBA_THRESHOLD 2
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
/* Handle simple cases with traditional multiplication.
Packit Service 82fcde
Packit Service 82fcde
   This is the most critical code of multiplication.  All multiplies rely
Packit Service 82fcde
   on this, both small and huge.  Small ones arrive here immediately.  Huge
Packit Service 82fcde
   ones arrive here as this is the base case for Karatsuba's recursive
Packit Service 82fcde
   algorithm below.  */
Packit Service 82fcde
Packit Service 82fcde
void
Packit Service 82fcde
impn_mul_n_basecase (mp_ptr prodp, mp_srcptr up, mp_srcptr vp, mp_size_t size)
Packit Service 82fcde
{
Packit Service 82fcde
  mp_size_t i;
Packit Service 82fcde
  mp_limb_t cy_limb;
Packit Service 82fcde
  mp_limb_t v_limb;
Packit Service 82fcde
Packit Service 82fcde
  /* Multiply by the first limb in V separately, as the result can be
Packit Service 82fcde
     stored (not added) to PROD.  We also avoid a loop for zeroing.  */
Packit Service 82fcde
  v_limb = vp[0];
Packit Service 82fcde
  if (v_limb <= 1)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (v_limb == 1)
Packit Service 82fcde
	MPN_COPY (prodp, up, size);
Packit Service 82fcde
      else
Packit Service 82fcde
	MPN_ZERO (prodp, size);
Packit Service 82fcde
      cy_limb = 0;
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    cy_limb = mpn_mul_1 (prodp, up, size, v_limb);
Packit Service 82fcde
Packit Service 82fcde
  prodp[size] = cy_limb;
Packit Service 82fcde
  prodp++;
Packit Service 82fcde
Packit Service 82fcde
  /* For each iteration in the outer loop, multiply one limb from
Packit Service 82fcde
     U with one limb from V, and add it to PROD.  */
Packit Service 82fcde
  for (i = 1; i < size; i++)
Packit Service 82fcde
    {
Packit Service 82fcde
      v_limb = vp[i];
Packit Service 82fcde
      if (v_limb <= 1)
Packit Service 82fcde
	{
Packit Service 82fcde
	  cy_limb = 0;
Packit Service 82fcde
	  if (v_limb == 1)
Packit Service 82fcde
	    cy_limb = mpn_add_n (prodp, prodp, up, size);
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	cy_limb = mpn_addmul_1 (prodp, up, size, v_limb);
Packit Service 82fcde
Packit Service 82fcde
      prodp[size] = cy_limb;
Packit Service 82fcde
      prodp++;
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
void
Packit Service 82fcde
impn_mul_n (mp_ptr prodp,
Packit Service 82fcde
	     mp_srcptr up, mp_srcptr vp, mp_size_t size, mp_ptr tspace)
Packit Service 82fcde
{
Packit Service 82fcde
  if ((size & 1) != 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* The size is odd, the code code below doesn't handle that.
Packit Service 82fcde
	 Multiply the least significant (size - 1) limbs with a recursive
Packit Service 82fcde
	 call, and handle the most significant limb of S1 and S2
Packit Service 82fcde
	 separately.  */
Packit Service 82fcde
      /* A slightly faster way to do this would be to make the Karatsuba
Packit Service 82fcde
	 code below behave as if the size were even, and let it check for
Packit Service 82fcde
	 odd size in the end.  I.e., in essence move this code to the end.
Packit Service 82fcde
	 Doing so would save us a recursive call, and potentially make the
Packit Service 82fcde
	 stack grow a lot less.  */
Packit Service 82fcde
Packit Service 82fcde
      mp_size_t esize = size - 1;	/* even size */
Packit Service 82fcde
      mp_limb_t cy_limb;
Packit Service 82fcde
Packit Service 82fcde
      MPN_MUL_N_RECURSE (prodp, up, vp, esize, tspace);
Packit Service 82fcde
      cy_limb = mpn_addmul_1 (prodp + esize, up, esize, vp[esize]);
Packit Service 82fcde
      prodp[esize + esize] = cy_limb;
Packit Service 82fcde
      cy_limb = mpn_addmul_1 (prodp + esize, vp, size, up[esize]);
Packit Service 82fcde
Packit Service 82fcde
      prodp[esize + size] = cy_limb;
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Anatolij Alekseevich Karatsuba's divide-and-conquer algorithm.
Packit Service 82fcde
Packit Service 82fcde
	 Split U in two pieces, U1 and U0, such that
Packit Service 82fcde
	 U = U0 + U1*(B**n),
Packit Service 82fcde
	 and V in V1 and V0, such that
Packit Service 82fcde
	 V = V0 + V1*(B**n).
Packit Service 82fcde
Packit Service 82fcde
	 UV is then computed recursively using the identity
Packit Service 82fcde
Packit Service 82fcde
		2n   n          n                     n
Packit Service 82fcde
	 UV = (B  + B )U V  +  B (U -U )(V -V )  +  (B + 1)U V
Packit Service 82fcde
			1 1        1  0   0  1              0 0
Packit Service 82fcde
Packit Service 82fcde
	 Where B = 2**BITS_PER_MP_LIMB.  */
Packit Service 82fcde
Packit Service 82fcde
      mp_size_t hsize = size >> 1;
Packit Service 82fcde
      mp_limb_t cy;
Packit Service 82fcde
      int negflg;
Packit Service 82fcde
Packit Service 82fcde
      /*** Product H.	 ________________  ________________
Packit Service 82fcde
			|_____U1 x V1____||____U0 x V0_____|  */
Packit Service 82fcde
      /* Put result in upper part of PROD and pass low part of TSPACE
Packit Service 82fcde
	 as new TSPACE.  */
Packit Service 82fcde
      MPN_MUL_N_RECURSE (prodp + size, up + hsize, vp + hsize, hsize, tspace);
Packit Service 82fcde
Packit Service 82fcde
      /*** Product M.	 ________________
Packit Service 82fcde
			|_(U1-U0)(V0-V1)_|  */
Packit Service 82fcde
      if (mpn_cmp (up + hsize, up, hsize) >= 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  mpn_sub_n (prodp, up + hsize, up, hsize);
Packit Service 82fcde
	  negflg = 0;
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  mpn_sub_n (prodp, up, up + hsize, hsize);
Packit Service 82fcde
	  negflg = 1;
Packit Service 82fcde
	}
Packit Service 82fcde
      if (mpn_cmp (vp + hsize, vp, hsize) >= 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  mpn_sub_n (prodp + hsize, vp + hsize, vp, hsize);
Packit Service 82fcde
	  negflg ^= 1;
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  mpn_sub_n (prodp + hsize, vp, vp + hsize, hsize);
Packit Service 82fcde
	  /* No change of NEGFLG.  */
Packit Service 82fcde
	}
Packit Service 82fcde
      /* Read temporary operands from low part of PROD.
Packit Service 82fcde
	 Put result in low part of TSPACE using upper part of TSPACE
Packit Service 82fcde
	 as new TSPACE.  */
Packit Service 82fcde
      MPN_MUL_N_RECURSE (tspace, prodp, prodp + hsize, hsize, tspace + size);
Packit Service 82fcde
Packit Service 82fcde
      /*** Add/copy product H.  */
Packit Service 82fcde
      MPN_COPY (prodp + hsize, prodp + size, hsize);
Packit Service 82fcde
      cy = mpn_add_n (prodp + size, prodp + size, prodp + size + hsize, hsize);
Packit Service 82fcde
Packit Service 82fcde
      /*** Add product M (if NEGFLG M is a negative number).  */
Packit Service 82fcde
      if (negflg)
Packit Service 82fcde
	cy -= mpn_sub_n (prodp + hsize, prodp + hsize, tspace, size);
Packit Service 82fcde
      else
Packit Service 82fcde
	cy += mpn_add_n (prodp + hsize, prodp + hsize, tspace, size);
Packit Service 82fcde
Packit Service 82fcde
      /*** Product L.	 ________________  ________________
Packit Service 82fcde
			|________________||____U0 x V0_____|  */
Packit Service 82fcde
      /* Read temporary operands from low part of PROD.
Packit Service 82fcde
	 Put result in low part of TSPACE using upper part of TSPACE
Packit Service 82fcde
	 as new TSPACE.  */
Packit Service 82fcde
      MPN_MUL_N_RECURSE (tspace, up, vp, hsize, tspace + size);
Packit Service 82fcde
Packit Service 82fcde
      /*** Add/copy Product L (twice).  */
Packit Service 82fcde
Packit Service 82fcde
      cy += mpn_add_n (prodp + hsize, prodp + hsize, tspace, size);
Packit Service 82fcde
      if (cy)
Packit Service 82fcde
	mpn_add_1 (prodp + hsize + size, prodp + hsize + size, hsize, cy);
Packit Service 82fcde
Packit Service 82fcde
      MPN_COPY (prodp, tspace, hsize);
Packit Service 82fcde
      cy = mpn_add_n (prodp + hsize, prodp + hsize, tspace + hsize, hsize);
Packit Service 82fcde
      if (cy)
Packit Service 82fcde
	mpn_add_1 (prodp + size, prodp + size, size, 1);
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
void
Packit Service 82fcde
impn_sqr_n_basecase (mp_ptr prodp, mp_srcptr up, mp_size_t size)
Packit Service 82fcde
{
Packit Service 82fcde
  mp_size_t i;
Packit Service 82fcde
  mp_limb_t cy_limb;
Packit Service 82fcde
  mp_limb_t v_limb;
Packit Service 82fcde
Packit Service 82fcde
  /* Multiply by the first limb in V separately, as the result can be
Packit Service 82fcde
     stored (not added) to PROD.  We also avoid a loop for zeroing.  */
Packit Service 82fcde
  v_limb = up[0];
Packit Service 82fcde
  if (v_limb <= 1)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (v_limb == 1)
Packit Service 82fcde
	MPN_COPY (prodp, up, size);
Packit Service 82fcde
      else
Packit Service 82fcde
	MPN_ZERO (prodp, size);
Packit Service 82fcde
      cy_limb = 0;
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    cy_limb = mpn_mul_1 (prodp, up, size, v_limb);
Packit Service 82fcde
Packit Service 82fcde
  prodp[size] = cy_limb;
Packit Service 82fcde
  prodp++;
Packit Service 82fcde
Packit Service 82fcde
  /* For each iteration in the outer loop, multiply one limb from
Packit Service 82fcde
     U with one limb from V, and add it to PROD.  */
Packit Service 82fcde
  for (i = 1; i < size; i++)
Packit Service 82fcde
    {
Packit Service 82fcde
      v_limb = up[i];
Packit Service 82fcde
      if (v_limb <= 1)
Packit Service 82fcde
	{
Packit Service 82fcde
	  cy_limb = 0;
Packit Service 82fcde
	  if (v_limb == 1)
Packit Service 82fcde
	    cy_limb = mpn_add_n (prodp, prodp, up, size);
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	cy_limb = mpn_addmul_1 (prodp, up, size, v_limb);
Packit Service 82fcde
Packit Service 82fcde
      prodp[size] = cy_limb;
Packit Service 82fcde
      prodp++;
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
void
Packit Service 82fcde
impn_sqr_n (mp_ptr prodp,
Packit Service 82fcde
	     mp_srcptr up, mp_size_t size, mp_ptr tspace)
Packit Service 82fcde
{
Packit Service 82fcde
  if ((size & 1) != 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* The size is odd, the code code below doesn't handle that.
Packit Service 82fcde
	 Multiply the least significant (size - 1) limbs with a recursive
Packit Service 82fcde
	 call, and handle the most significant limb of S1 and S2
Packit Service 82fcde
	 separately.  */
Packit Service 82fcde
      /* A slightly faster way to do this would be to make the Karatsuba
Packit Service 82fcde
	 code below behave as if the size were even, and let it check for
Packit Service 82fcde
	 odd size in the end.  I.e., in essence move this code to the end.
Packit Service 82fcde
	 Doing so would save us a recursive call, and potentially make the
Packit Service 82fcde
	 stack grow a lot less.  */
Packit Service 82fcde
Packit Service 82fcde
      mp_size_t esize = size - 1;	/* even size */
Packit Service 82fcde
      mp_limb_t cy_limb;
Packit Service 82fcde
Packit Service 82fcde
      MPN_SQR_N_RECURSE (prodp, up, esize, tspace);
Packit Service 82fcde
      cy_limb = mpn_addmul_1 (prodp + esize, up, esize, up[esize]);
Packit Service 82fcde
      prodp[esize + esize] = cy_limb;
Packit Service 82fcde
      cy_limb = mpn_addmul_1 (prodp + esize, up, size, up[esize]);
Packit Service 82fcde
Packit Service 82fcde
      prodp[esize + size] = cy_limb;
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      mp_size_t hsize = size >> 1;
Packit Service 82fcde
      mp_limb_t cy;
Packit Service 82fcde
Packit Service 82fcde
      /*** Product H.	 ________________  ________________
Packit Service 82fcde
			|_____U1 x U1____||____U0 x U0_____|  */
Packit Service 82fcde
      /* Put result in upper part of PROD and pass low part of TSPACE
Packit Service 82fcde
	 as new TSPACE.  */
Packit Service 82fcde
      MPN_SQR_N_RECURSE (prodp + size, up + hsize, hsize, tspace);
Packit Service 82fcde
Packit Service 82fcde
      /*** Product M.	 ________________
Packit Service 82fcde
			|_(U1-U0)(U0-U1)_|  */
Packit Service 82fcde
      if (mpn_cmp (up + hsize, up, hsize) >= 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  mpn_sub_n (prodp, up + hsize, up, hsize);
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  mpn_sub_n (prodp, up, up + hsize, hsize);
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      /* Read temporary operands from low part of PROD.
Packit Service 82fcde
	 Put result in low part of TSPACE using upper part of TSPACE
Packit Service 82fcde
	 as new TSPACE.  */
Packit Service 82fcde
      MPN_SQR_N_RECURSE (tspace, prodp, hsize, tspace + size);
Packit Service 82fcde
Packit Service 82fcde
      /*** Add/copy product H.  */
Packit Service 82fcde
      MPN_COPY (prodp + hsize, prodp + size, hsize);
Packit Service 82fcde
      cy = mpn_add_n (prodp + size, prodp + size, prodp + size + hsize, hsize);
Packit Service 82fcde
Packit Service 82fcde
      /*** Add product M (if NEGFLG M is a negative number).  */
Packit Service 82fcde
      cy -= mpn_sub_n (prodp + hsize, prodp + hsize, tspace, size);
Packit Service 82fcde
Packit Service 82fcde
      /*** Product L.	 ________________  ________________
Packit Service 82fcde
			|________________||____U0 x U0_____|  */
Packit Service 82fcde
      /* Read temporary operands from low part of PROD.
Packit Service 82fcde
	 Put result in low part of TSPACE using upper part of TSPACE
Packit Service 82fcde
	 as new TSPACE.  */
Packit Service 82fcde
      MPN_SQR_N_RECURSE (tspace, up, hsize, tspace + size);
Packit Service 82fcde
Packit Service 82fcde
      /*** Add/copy Product L (twice).  */
Packit Service 82fcde
Packit Service 82fcde
      cy += mpn_add_n (prodp + hsize, prodp + hsize, tspace, size);
Packit Service 82fcde
      if (cy)
Packit Service 82fcde
	mpn_add_1 (prodp + hsize + size, prodp + hsize + size, hsize, cy);
Packit Service 82fcde
Packit Service 82fcde
      MPN_COPY (prodp, tspace, hsize);
Packit Service 82fcde
      cy = mpn_add_n (prodp + hsize, prodp + hsize, tspace + hsize, hsize);
Packit Service 82fcde
      if (cy)
Packit Service 82fcde
	mpn_add_1 (prodp + size, prodp + size, size, 1);
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* This should be made into an inline function in gmp.h.  */
Packit Service 82fcde
void
Packit Service 82fcde
mpn_mul_n (mp_ptr prodp, mp_srcptr up, mp_srcptr vp, mp_size_t size)
Packit Service 82fcde
{
Packit Service 82fcde
  TMP_DECL (marker);
Packit Service 82fcde
  TMP_MARK (marker);
Packit Service 82fcde
  if (up == vp)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (size < KARATSUBA_THRESHOLD)
Packit Service 82fcde
	{
Packit Service 82fcde
	  impn_sqr_n_basecase (prodp, up, size);
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  mp_ptr tspace;
Packit Service 82fcde
	  tspace = (mp_ptr) TMP_ALLOC (2 * size * BYTES_PER_MP_LIMB);
Packit Service 82fcde
	  impn_sqr_n (prodp, up, size, tspace);
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      if (size < KARATSUBA_THRESHOLD)
Packit Service 82fcde
	{
Packit Service 82fcde
	  impn_mul_n_basecase (prodp, up, vp, size);
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  mp_ptr tspace;
Packit Service 82fcde
	  tspace = (mp_ptr) TMP_ALLOC (2 * size * BYTES_PER_MP_LIMB);
Packit Service 82fcde
	  impn_mul_n (prodp, up, vp, size, tspace);
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
  TMP_FREE (marker);
Packit Service 82fcde
}