#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mpi-internal.h"
#include "longlong.h"
#include "g10lib.h"
#define MPN_MUL_N_RECURSE(prodp, up, vp, size, tspace) \
do { \
if( (size) < KARATSUBA_THRESHOLD ) \
mul_n_basecase (prodp, up, vp, size); \
else \
mul_n (prodp, up, vp, size, tspace); \
} while (0);
#define MPN_SQR_N_RECURSE(prodp, up, size, tspace) \
do { \
if ((size) < KARATSUBA_THRESHOLD) \
_gcry_mpih_sqr_n_basecase (prodp, up, size); \
else \
_gcry_mpih_sqr_n (prodp, up, size, tspace); \
} while (0);
static mpi_limb_t
mul_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up,
mpi_ptr_t vp, mpi_size_t size)
{
mpi_size_t i;
mpi_limb_t cy;
mpi_limb_t v_limb;
v_limb = vp[0];
if( v_limb <= 1 ) {
if( v_limb == 1 )
MPN_COPY( prodp, up, size );
else
MPN_ZERO( prodp, size );
cy = 0;
}
else
cy = _gcry_mpih_mul_1( prodp, up, size, v_limb );
prodp[size] = cy;
prodp++;
for( i = 1; i < size; i++ ) {
v_limb = vp[i];
if( v_limb <= 1 ) {
cy = 0;
if( v_limb == 1 )
cy = _gcry_mpih_add_n(prodp, prodp, up, size);
}
else
cy = _gcry_mpih_addmul_1(prodp, up, size, v_limb);
prodp[size] = cy;
prodp++;
}
return cy;
}
static void
mul_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_ptr_t vp,
mpi_size_t size, mpi_ptr_t tspace )
{
if( size & 1 ) {
mpi_size_t esize = size - 1;
mpi_limb_t cy_limb;
MPN_MUL_N_RECURSE( prodp, up, vp, esize, tspace );
cy_limb = _gcry_mpih_addmul_1( prodp + esize, up, esize, vp[esize] );
prodp[esize + esize] = cy_limb;
cy_limb = _gcry_mpih_addmul_1( prodp + esize, vp, size, up[esize] );
prodp[esize + size] = cy_limb;
}
else {
mpi_size_t hsize = size >> 1;
mpi_limb_t cy;
int negflg;
MPN_MUL_N_RECURSE(prodp + size, up + hsize, vp + hsize, hsize, tspace);
if( _gcry_mpih_cmp(up + hsize, up, hsize) >= 0 ) {
_gcry_mpih_sub_n(prodp, up + hsize, up, hsize);
negflg = 0;
}
else {
_gcry_mpih_sub_n(prodp, up, up + hsize, hsize);
negflg = 1;
}
if( _gcry_mpih_cmp(vp + hsize, vp, hsize) >= 0 ) {
_gcry_mpih_sub_n(prodp + hsize, vp + hsize, vp, hsize);
negflg ^= 1;
}
else {
_gcry_mpih_sub_n(prodp + hsize, vp, vp + hsize, hsize);
}
MPN_MUL_N_RECURSE(tspace, prodp, prodp + hsize, hsize, tspace + size);
MPN_COPY (prodp + hsize, prodp + size, hsize);
cy = _gcry_mpih_add_n( prodp + size, prodp + size,
prodp + size + hsize, hsize);
if(negflg)
cy -= _gcry_mpih_sub_n(prodp + hsize, prodp + hsize, tspace, size);
else
cy += _gcry_mpih_add_n(prodp + hsize, prodp + hsize, tspace, size);
MPN_MUL_N_RECURSE(tspace, up, vp, hsize, tspace + size);
cy += _gcry_mpih_add_n(prodp + hsize, prodp + hsize, tspace, size);
if( cy )
_gcry_mpih_add_1(prodp + hsize + size, prodp + hsize + size, hsize, cy);
MPN_COPY(prodp, tspace, hsize);
cy = _gcry_mpih_add_n(prodp + hsize, prodp + hsize, tspace + hsize, hsize);
if( cy )
_gcry_mpih_add_1(prodp + size, prodp + size, size, 1);
}
}
void
_gcry_mpih_sqr_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size )
{
mpi_size_t i;
mpi_limb_t cy_limb;
mpi_limb_t v_limb;
v_limb = up[0];
if( v_limb <= 1 ) {
if( v_limb == 1 )
MPN_COPY( prodp, up, size );
else
MPN_ZERO(prodp, size);
cy_limb = 0;
}
else
cy_limb = _gcry_mpih_mul_1( prodp, up, size, v_limb );
prodp[size] = cy_limb;
prodp++;
for( i=1; i < size; i++) {
v_limb = up[i];
if( v_limb <= 1 ) {
cy_limb = 0;
if( v_limb == 1 )
cy_limb = _gcry_mpih_add_n(prodp, prodp, up, size);
}
else
cy_limb = _gcry_mpih_addmul_1(prodp, up, size, v_limb);
prodp[size] = cy_limb;
prodp++;
}
}
void
_gcry_mpih_sqr_n( mpi_ptr_t prodp,
mpi_ptr_t up, mpi_size_t size, mpi_ptr_t tspace)
{
if( size & 1 ) {
mpi_size_t esize = size - 1;
mpi_limb_t cy_limb;
MPN_SQR_N_RECURSE( prodp, up, esize, tspace );
cy_limb = _gcry_mpih_addmul_1( prodp + esize, up, esize, up[esize] );
prodp[esize + esize] = cy_limb;
cy_limb = _gcry_mpih_addmul_1( prodp + esize, up, size, up[esize] );
prodp[esize + size] = cy_limb;
}
else {
mpi_size_t hsize = size >> 1;
mpi_limb_t cy;
MPN_SQR_N_RECURSE(prodp + size, up + hsize, hsize, tspace);
if( _gcry_mpih_cmp( up + hsize, up, hsize) >= 0 )
_gcry_mpih_sub_n( prodp, up + hsize, up, hsize);
else
_gcry_mpih_sub_n (prodp, up, up + hsize, hsize);
MPN_SQR_N_RECURSE(tspace, prodp, hsize, tspace + size);
MPN_COPY(prodp + hsize, prodp + size, hsize);
cy = _gcry_mpih_add_n(prodp + size, prodp + size,
prodp + size + hsize, hsize);
cy -= _gcry_mpih_sub_n (prodp + hsize, prodp + hsize, tspace, size);
MPN_SQR_N_RECURSE (tspace, up, hsize, tspace + size);
cy += _gcry_mpih_add_n (prodp + hsize, prodp + hsize, tspace, size);
if( cy )
_gcry_mpih_add_1(prodp + hsize + size, prodp + hsize + size,
hsize, cy);
MPN_COPY(prodp, tspace, hsize);
cy = _gcry_mpih_add_n (prodp + hsize, prodp + hsize, tspace + hsize, hsize);
if( cy )
_gcry_mpih_add_1 (prodp + size, prodp + size, size, 1);
}
}
void
_gcry_mpih_mul_n( mpi_ptr_t prodp,
mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t size)
{
int secure;
if( up == vp ) {
if( size < KARATSUBA_THRESHOLD )
_gcry_mpih_sqr_n_basecase( prodp, up, size );
else {
mpi_ptr_t tspace;
secure = _gcry_is_secure( up );
tspace = mpi_alloc_limb_space( 2 * size, secure );
_gcry_mpih_sqr_n( prodp, up, size, tspace );
_gcry_mpi_free_limb_space (tspace, 2 * size );
}
}
else {
if( size < KARATSUBA_THRESHOLD )
mul_n_basecase( prodp, up, vp, size );
else {
mpi_ptr_t tspace;
secure = _gcry_is_secure( up ) || _gcry_is_secure( vp );
tspace = mpi_alloc_limb_space( 2 * size, secure );
mul_n (prodp, up, vp, size, tspace);
_gcry_mpi_free_limb_space (tspace, 2 * size );
}
}
}
void
_gcry_mpih_mul_karatsuba_case( mpi_ptr_t prodp,
mpi_ptr_t up, mpi_size_t usize,
mpi_ptr_t vp, mpi_size_t vsize,
struct karatsuba_ctx *ctx )
{
mpi_limb_t cy;
if( !ctx->tspace || ctx->tspace_size < vsize ) {
if( ctx->tspace )
_gcry_mpi_free_limb_space( ctx->tspace, ctx->tspace_nlimbs );
ctx->tspace_nlimbs = 2 * vsize;
ctx->tspace = mpi_alloc_limb_space (2 * vsize,
(_gcry_is_secure (up)
|| _gcry_is_secure (vp)));
ctx->tspace_size = vsize;
}
MPN_MUL_N_RECURSE( prodp, up, vp, vsize, ctx->tspace );
prodp += vsize;
up += vsize;
usize -= vsize;
if( usize >= vsize ) {
if( !ctx->tp || ctx->tp_size < vsize ) {
if( ctx->tp )
_gcry_mpi_free_limb_space( ctx->tp, ctx->tp_nlimbs );
ctx->tp_nlimbs = 2 * vsize;
ctx->tp = mpi_alloc_limb_space (2 * vsize,
(_gcry_is_secure (up)
|| _gcry_is_secure (vp)));
ctx->tp_size = vsize;
}
do {
MPN_MUL_N_RECURSE( ctx->tp, up, vp, vsize, ctx->tspace );
cy = _gcry_mpih_add_n( prodp, prodp, ctx->tp, vsize );
_gcry_mpih_add_1( prodp + vsize, ctx->tp + vsize, vsize, cy );
prodp += vsize;
up += vsize;
usize -= vsize;
} while( usize >= vsize );
}
if( usize ) {
if( usize < KARATSUBA_THRESHOLD ) {
_gcry_mpih_mul( ctx->tspace, vp, vsize, up, usize );
}
else {
if( !ctx->next ) {
ctx->next = xcalloc( 1, sizeof *ctx );
}
_gcry_mpih_mul_karatsuba_case( ctx->tspace,
vp, vsize,
up, usize,
ctx->next );
}
cy = _gcry_mpih_add_n( prodp, prodp, ctx->tspace, vsize);
_gcry_mpih_add_1( prodp + vsize, ctx->tspace + vsize, usize, cy );
}
}
void
_gcry_mpih_release_karatsuba_ctx( struct karatsuba_ctx *ctx )
{
struct karatsuba_ctx *ctx2;
if( ctx->tp )
_gcry_mpi_free_limb_space( ctx->tp, ctx->tp_nlimbs );
if( ctx->tspace )
_gcry_mpi_free_limb_space( ctx->tspace, ctx->tspace_nlimbs );
for( ctx=ctx->next; ctx; ctx = ctx2 ) {
ctx2 = ctx->next;
if( ctx->tp )
_gcry_mpi_free_limb_space( ctx->tp, ctx->tp_nlimbs );
if( ctx->tspace )
_gcry_mpi_free_limb_space( ctx->tspace, ctx->tspace_nlimbs );
xfree( ctx );
}
}
mpi_limb_t
_gcry_mpih_mul( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t usize,
mpi_ptr_t vp, mpi_size_t vsize)
{
mpi_ptr_t prod_endp = prodp + usize + vsize - 1;
mpi_limb_t cy;
struct karatsuba_ctx ctx;
if( vsize < KARATSUBA_THRESHOLD ) {
mpi_size_t i;
mpi_limb_t v_limb;
if( !vsize )
return 0;
v_limb = vp[0];
if( v_limb <= 1 ) {
if( v_limb == 1 )
MPN_COPY( prodp, up, usize );
else
MPN_ZERO( prodp, usize );
cy = 0;
}
else
cy = _gcry_mpih_mul_1( prodp, up, usize, v_limb );
prodp[usize] = cy;
prodp++;
for( i = 1; i < vsize; i++ ) {
v_limb = vp[i];
if( v_limb <= 1 ) {
cy = 0;
if( v_limb == 1 )
cy = _gcry_mpih_add_n(prodp, prodp, up, usize);
}
else
cy = _gcry_mpih_addmul_1(prodp, up, usize, v_limb);
prodp[usize] = cy;
prodp++;
}
return cy;
}
memset( &ctx, 0, sizeof ctx );
_gcry_mpih_mul_karatsuba_case( prodp, up, usize, vp, vsize, &ctx );
_gcry_mpih_release_karatsuba_ctx( &ctx );
return *prod_endp;
}