/* Copyright (c) (2010,2011,2012,2013,2014,2015,2016,2017,2018,2019) Apple Inc. All rights reserved.
 *
 * corecrypto is licensed under Apple Inc.’s Internal Use License Agreement (which
 * is contained in the License.txt file distributed with corecrypto) and only to 
 * people who accept that license. IMPORTANT:  Any license rights granted to you by 
 * Apple Inc. (if any) are limited to internal use within your organization only on 
 * devices and computers you own or control, for the sole purpose of verifying the 
 * security characteristics and correct functioning of the Apple Software.  You may 
 * not, directly or indirectly, redistribute the Apple Software or any portions thereof.
 */

#import "CCNValidation.h"
#import "ccn_unit.h"

#define GI_MODG_ENABLE  1

#include <corecrypto/ccrng.h>
#include <corecrypto/ccrng_system.h>
#include "cczp_internal.h"
#include "ccn_internal.h"

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <pthread.h>

#define LOOPS_DEF		3
#define MAX_SIZE_DEF	3 * 4096 / 8
#define LOOP_NOTIFY		100

/* tweak these to focus on a specific test */
#define DVT_BASIC_ENABLE		1		/* comp, mulg, addg, subg */
#define DVT_SHIFT_ENABLE		0		/* shift l/r */
#define DVT_MOD_ENABLE			1		/* mod */
#define DVT_GCD_ENABLE			0		/* gcd */
#define DVT_PRIME_ENABLE		0		/* prime test */
#define DVT_INVMOD_ENABLE		0		/* ginverseMod */
#define DVT_DIVMOD_ENABLE		0		/* divmodg_via_recip */

@implementation CCNValidation

/*
 * ...min <= return <= max
 */

static uint32_t genRand(uint32_t min, uint32_t max)
{
	uint32_t r;
	uint8_t c[4];

    if (max == min) {			/* avoid % 1 ! */
		return max;
	}

    ccrng_generate(ccrng(NULL), 4, c);

	r = (((uint32_t)c[0] << 24) |
         ((uint32_t)c[1] << 16) |
         ((uint32_t)c[2] << 8) |
         c[3]);
	return min + (r % (max - min + 1));
}

/*
 * Fill buffer with random data, random size from 1 to maxByteSize.
 * Returns size of random data generated.
 */
#define MIN_SIZE		1

static cc_size fillData(cc_size maxByteSize, uint8_t *data)
{
	cc_size		size;

	size = maxByteSize; //genRand(MIN_SIZE, maxByteSize);
    ccrng_generate(ccrng(NULL), size, data);
	return size;
}

/*
 * create a giant with random size and data. *Buf is mallocd and
 * uninitialized and will change here.
 * Giants with values of zero  or one will never be returned - too
 * many corner cases involving those numbers.
 */
static cc_size genGiant(cc_size count, cc_unit *r, size_t maxBytes, uint8_t *buf)
{
    assert(maxBytes <= ccn_sizeof_n(count));
	for(;;) {
		cc_size size = fillData(maxBytes, buf);

        if (ccn_read_uint(count, r, size, buf)) {
			/* should never happen */
			printf("***genGiant overflow\n");
			exit(1);
		}
        cc_unit one[count];
        ccn_seti(count, one, 1);
        /* If r is less or equal to 1 we don't return it. */
        if (ccn_cmp(count, r, one) <= 0)
            continue;

		return count;
	}
}

/*
 * Individual tests. API is identical for all tests.
 *
 * g1, g2 : giants with random data, size, and sign. Tests do not modify
 *           these.
 * scr1, scr2 : scratch giants, big enough for all conceivable ops. Can
 *           be modified at will.
 * Return : 0 for sucess, 1 on error.
 */
#if DVT_BASIC_ENABLE

-(void) compTst:(cc_size) count : (const cc_unit *) g1 : (cc_unit *) scr1 : (cc_unit *) scr2
{
    ccn_set(count, scr1, g1);
    ccn_set(count, scr2, scr1);
    XCAssertCCNEquals(count, g1, scr2,
                      @"scr1 = g1; scr2 = scr1; g1 == scr2");
}

-(void) addSubTst:(cc_size) count : (const cc_unit *) g1 : (const cc_unit *) g2 : (cc_unit *) scr1
{
    ccn_add(count, scr1, g1, g2);     // scr1 := g1 + g2
    ccn_sub(count, scr1, scr1, g1);   // scr1 := g1 + g2 - g1 =? g2
    XCAssertCCNEquals(count, g2, scr1, @"g2 == g1+g2-g1\ng1 = %@", ccn_string(count, g1));
}

/* Largest random uint32_t integer to use a multiplier */
#define LARGEST_MUL 0xffff

-(void) mulTst:(cc_size) count : (const cc_unit *) g1 : (cc_unit *) scr1 : (cc_unit *) scr2
{
	uint32_t randInt = genRand(1, LARGEST_MUL);
	uint32_t i;

    ccn_seti(2 * count, scr1, randInt);    // scr1 := randInt
    ccn_mul(count, scr2, g1, scr1); // scr2 := g1 * randInt

	/* now do the same thing with multiple adds */
    ccn_seti(2 * count, scr1, 0);       // scr1 := 0
    cc_unit carry = 0;
	for(i = 0; i<randInt; i++) {
		carry += ccn_add(count, scr1, scr1, g1);
	}
    scr1[count] = carry;
    XCAssertCCNEquals(2 * count, scr2, scr1,
                      @"g1*rand (via mul) == g1*rand (via add)\ng1 = %@\nrand = %lu",
                      ccn_string(count, g1), randInt);
}

-(void) squareTst:(cc_size) count : (const cc_unit *) g1 : (cc_unit *) scr1 : (cc_unit *) scr2
{
    ccn_mul(count, scr1, g1, g1);
    ccn_sqr(count, scr2, g1);
    XCAssertCCNEquals(count, scr1, scr2, @"g1*g1 == g1^2\ng1 = %@",
                      ccn_string(count, g1));
}

-(void) mulOnesTst:(cc_size) count : (const cc_unit *) g1 : (cc_unit *) scr1 : (cc_unit *) scr2
{
    cc_unit gOnes[count];

	/* set up gOnes with all ones data */
	for (cc_size i = 0; i < count; ++i) {
		gOnes[i] = (cc_unit)(-1);
	}

    ccn_mul(count, scr1, gOnes, g1);
    ccn_mul(count, scr2, g1, gOnes);
    XCAssertCCNEquals(count, scr1, scr2, @"g1*allones == allones*g1\ng1 = %@",
                      ccn_string(count, g1));
}

#endif	/* DVT_BASIC_ENABLE */

#if		DVT_SHIFT_ENABLE

#define MIN_SHIFT	1

-(void) lshiftTst:(cc_size) count : (const cc_unit *) g1 : (const cc_unit *) g2 : (cc_unit *) scr1 : (cc_unit *) scr2
{
	/* can only shift the size of an lgiant, which could fill up a bgiant */
	int maxShift = GI_MAX_PRIME_SIZE_BITS;
	int shiftCnt = genRand(MIN_SHIFT, maxShift);
	bgiant scr3;
	int rtn = 0;

	localBigGiantAlloc(&scr3);
	gtog(g1, scr1);						// scr1 := g1
	gshiftleft(shiftCnt, scr1);			// scr1 := (g1 << shiftCnt)

	gtog(g1, scr2);						// scr2 := g1
	if(shiftCnt <= 14) {
	    int multInt = (1 << shiftCnt);
	    int_to_giant(multInt, &scr3.g);	// scr3 := (1 << shiftCnt)
	}
	else {
	    int_to_giant(1, &scr3.g);		// scr3 := 1;
	    gshiftleft(shiftCnt, &scr3.g);	// scr3 := (1 << shiftCnt)
	}
	mulg(&scr3.g, scr2);				// scr2 := g1 * (1 << shiftCnt);
	if(gcompg(scr1, scr2)) {
		printf("shiftCnt %d 0x%x\n", shiftCnt, shiftCnt);
		printf("g1   : "); printGiantHex(g1);
		printf("scr1 : "); printGiantHex(scr1);
		printf("scr2 : "); printGiantHex(scr2);
		printf("gshiftleft error\n");
		rtn = testError();
	}
	return rtn;
}

/* clear a bit in a giant */
static void clearBit(cc_size count,
                     giant g,
                     unsigned bitPos)
{
    gi_uint16 nDex = bitPos >> CCN_LOG2_BITS_PER_UNIT;
    giantDigit bitMask = 1 << (bitPos & (GIANT_BITS_PER_DIGIT - 1));

	if(nDex > (g->sign - 1)) {
		printf("***clearBit screwup, nDex %u sign %u\n", nDex, g->sign);
		return;
	}
	g->n[nDex] &= ~bitMask;
}

-(void) rshiftTst:(cc_size) count : (const cc_unit *) g1 : (const cc_unit *) g2 : (cc_unit *) scr1 : (cc_unit *) scr2
{

	unsigned maxShift = bitlen(g1) - 1;
	unsigned shiftCnt;
	lgiant scr3;
	int rtn = 0;
	unsigned dex;

	localGiantAlloc(&scr3);

	/* special case, can't have g1 = 1 */
	if(maxShift == 0) {
#if	GIANTS_DEBUG
		printf("...rshiftTst: tweaking g1 = 1\n");
#endif
		g1->n[0] = 2;
		shiftCnt = 1;
	}
	else {
		shiftCnt = genRand(1, maxShift);
	}

	/* first do the shift right */
	gtog(g1, scr1);						// scr1 := g1
	gshiftright(shiftCnt, scr1);		// scr1 := (g1 >> shiftCnt)

	/* now shift left the result by the same amount */
	gtog(scr1, scr2);					// scr2 := (g1 >> shiftCnt)
	gshiftleft(shiftCnt, scr2);			// scr2 := ((g1 >> shiftCnt) << shiftCnt

	/* get a copy of original giant a mask off the bits which should be full of
	 * zeroes now */
	gtog(g1, &scr3.g);
	for(dex=0; dex<shiftCnt; dex++) {
		clearBit(count, &scr3.g, dex);
	}

	if(gcompg(scr2, &scr3.g)) {
		printf("shiftCnt %u 0x%x\n", shiftCnt, shiftCnt);
		printf("g1   : "); printGiantHex(g1);
		printf("scr1 : "); printGiantHex(scr1);
		printf("scr2 : "); printGiantHex(scr2);
		printf("scr3 : "); printGiantHex(&scr3.g);
		printf("gshiftright error\n");
		rtn = testError();
	}
	return rtn;
}

-(void) rshiftwordTst:(cc_size) count : (const cc_unit *) g1 : (const cc_unit *) g2 : (cc_unit *) scr1 : (cc_unit *) scr2
{

	unsigned maxShiftWords = g1->sign;
	unsigned shiftCntWords;
	unsigned shiftCntBits;

	int rtn = 0;

	shiftCntWords = genRand(1, maxShiftWords);
	shiftCntBits = shiftCntWords * GIANT_BITS_PER_DIGIT;

	/* shift right by words --> scr1 */
	gtog(g1, scr1);
	gshifltwordsright(shiftCntWords, scr1);

	/* shift right by bits --> scr2 */
	gtog(g1, scr2);
	gshiftright(shiftCntBits, scr2);

	/*
	 * this assumes of course that we have a reliable gshiftright(), which
	 * was in fact tested in rshiftTest().
	 */
	if(gcompg(scr1, scr2)) {
		printf("shiftCntWords %u 0x%x\n", shiftCntWords, shiftCntWords);
		printf("g1   : "); printGiantHex(g1);
		printf("scr1 : "); printGiantHex(scr1);
		printf("scr2 : "); printGiantHex(scr2);
		printf("gshifltwordsright error\n");
		rtn = testError();
	}
	return rtn;
}

#endif	/* DVT_SHIFT_ENABLE */

#if		GI_MODG_ENABLE && DVT_MOD_ENABLE

#define ccn_modulo(count, n, a, d) ([self ccn_mod: count : n : a : d])

-(void) ccn_mod:(cc_size) count : (cc_unit *) n : (const cc_unit *) a : (const cc_unit *) d
{
    struct cczp *zp = alloca(cczp_size(ccn_sizeof_n(count)));

    CCZP_N(zp) = count;
    ccn_set(count, CCZP_PRIME(zp), d);
    cczp_init(zp);

    CC_DECL_WORKSPACE_STACK(ws,CCZP_MOD_WORKSPACE_N(count));
    cczp_mod_ws(ws, zp, n, a);
    CC_FREE_WORKSPACE_STACK(ws);
}

#define ccn_modn(count, n, count_a, a, d) ([self ccn_modn: count : n : count_a : a : d])

-(void) ccn_modn:(cc_size) count : (cc_unit *) n : (cc_size) count_a :(const cc_unit *) a : (const cc_unit *) d
{
    struct cczp *zp = alloca(cczp_size(ccn_sizeof_n(count)));

    CCZP_N(zp) = count;
    ccn_set(count, CCZP_PRIME(zp), d);
    cczp_init(zp);

    cczp_modn(zp, n, count_a,a);
}

#define LARGEST_MOD_MUL	0x40

-(void) modTst:(cc_size) count : (const cc_unit *) g1 : (const cc_unit *) g2 : (cc_unit *) scr1 : (cc_unit *) scr2
{
    cc_unit *scr3 = alloca(ccn_sizeof_n(2 * count)),
        *scr4 = alloca(ccn_sizeof_n(2 * count));
    const cc_unit *modulus;
    const cc_unit *factor;
	//int randInt = genRand(1, LARGEST_MOD_MUL);
    //ccn_seti(count, scr1, randInt); // scr1 = rand
    uint8_t buf[ccn_sizeof_n(count)];
    genGiant(2*count, scr1, ccn_sizeof_n(count) - genRand(0, CCN_UNIT_SIZE - 1), buf);
    if (ccn_cmp(count,g2,g1)>=0)
    {
        modulus=g2;
        factor=g1;
    }
    else
    {
        modulus=g1;
        factor=g2;
    }

	/* current ccn_mod can't deal with g mod 1 */
    if (ccn_bitlen(count, modulus) == 1) {
		printf("..modTst: tweaking g1 = 1\n");
		scr2[0] = 0x54;
	}
	/* end ccn_mod workaround */

	/* this will only work if randInt < g1 */
    if (ccn_cmp(count, scr1, modulus) >= 0) {
	    ccn_modn(count, scr1, 2*count,scr1, modulus);			// scr1 = rand mod g1
        XCTAssertTrue(ccn_cmp(count, scr1, modulus) < 0,
                     @"\nscr1 = %@\nmod = %@", ccn_string(count, scr1),
                     ccn_string(count, modulus));
	}
    else
    {
        ccn_modulo(count, scr2, scr1, modulus);			// scr1 = rand mod g1
        XCTAssertTrue(ccn_cmp(count, scr1, scr2) == 0,
                     @"\nscr1 = %@\nmod = %@", ccn_string(count, scr1),
                     ccn_string(count, modulus));
        ccn_modn(count, scr2, 2*count,scr1, modulus);			// scr1 = rand mod g1
        XCTAssertTrue(ccn_cmp(count, scr1, scr2) == 0,
                     @"\nscr1 = %@\nmod = %@", ccn_string(count, scr1),
                     ccn_string(count, modulus));
    }

    ccn_mul(count, scr3, factor, modulus);              // scr3 = g2 * g1
    if (ccn_add(count, scr3, scr3, scr1))               // scr3 = g2 * g1 + rand
        ccn_add1(count, scr3 + count, scr3 + count, 1); // propagate carry in scr3

	ccn_modulo(count, scr4, scr3, modulus);                // scr4 := scr3 mod g1 =? rand
    XCAssertCCNEquals(count, scr1, scr4, @"rand != (g2 * g1 + rand) mod g1\ng1: %@\ng2: %@\nscr3: %@\nscr4:%@", ccn_string(count, g1), ccn_string(count, g2), ccn_string(2 * count, scr3), ccn_string(2 * count, scr4));

    ccn_modn(count, scr4, 2*count,scr3, modulus);                // scr3 := scr3 mod g1 =? rand
    XCAssertCCNEquals(count, scr1, scr4, @"rand != (g2 * g1 + rand) mod g1\ng1: %@\ng2: %@\nscr3: %@\nscr4:%@", ccn_string(count, g1), ccn_string(count, g2), ccn_string(2 * count, scr3), ccn_string(2 * count, scr4));
}

#endif	/* GI_MODG_ENABLE && DVT_MOD_ENABLE */

#if		GI_MODG_ENABLE && DVT_GCD_ENABLE

#define SHOW_GCDTEST	0

-(void) gcdTst:(cc_size) count : (giant) g1 : (giant) g2 : (giant) scr1 : (giant) scr2
{
	/*
	 * This test's algorithm results in a gcd op on (g1*randInt, g2*randInt).
	 * This can overflow an lgiant, so we have to truncate g1 and g2 to ensure
	 * we're within bounds.
	 */
#define MAX_GCD_TRUNC_SIZE	(MAX_PRIME_SIZE_DIGITS - 3)
	lgiant g1Trunc;
	lgiant g2Trunc;
	bgiant existGcd;
	uint32_t randInt = genRand(2, 0x100000);
	bgiant expectGcd;
	bgiant g1Mul;
	bgiant g2Mul;
	bgiant gotGcd;
	int ourRtn = 0;

	localGiantAlloc(&g1Trunc);
	localGiantAlloc(&g2Trunc);
	gtog(g1, &g1Trunc.g);
	gtog(g2, &g2Trunc.g);
	if(g1Trunc.g.sign > MAX_GCD_TRUNC_SIZE) {
		g1Trunc.g.sign = MAX_GCD_TRUNC_SIZE;
		gtrimSign(&g1Trunc.g);
	}
	if(g2Trunc.g.sign > MAX_GCD_TRUNC_SIZE) {
		g2Trunc.g.sign = MAX_GCD_TRUNC_SIZE;
		gtrimSign(&g2Trunc.g);
	}

	localBigGiantAlloc(&existGcd);
	localBigGiantAlloc(&g1Mul);
	localBigGiantAlloc(&g2Mul);
	localBigGiantAlloc(&expectGcd);
	localBigGiantAlloc(&gotGcd);

	/* first see if g1 and g2 already have a gcd other than 1 */
	gtog(&g1Trunc.g, &existGcd.g);
	nbgcdg(&g2Trunc.g, &existGcd.g);
	if(isone(&existGcd.g)) {
		/*
		 * g1Trunc, g2Trunc are apparently relatively prime.
		 * Quick check: are they both even?
		 */
		if( ((g1Trunc.g.n[0] & 1) == 0) && ((g2Trunc.g.n[0] & 1) == 0) ) {
			printf("***HEY! WE've got two even numbers whose gcd is 1!\n");
			printf("g1        : "); printGiant(&g1Trunc.g);
			printf("g2        : "); printGiant(&g2Trunc.g);
			return testError();
		}
	}

	/* random multiplier */
	int_to_giant(randInt, scr1);

	/*
	 * Expected gcd after we multiply g1Trunc and g2Trunc by randInt is
	 * existGcd * randInt
	 */
	mulg_common(scr1, &existGcd.g, &expectGcd.g);

	/* multiply both reference giants by randInt */
	mulg_common(&g1Trunc.g, scr1, &g1Mul.g);
	mulg_common(&g2Trunc.g, scr1, &g2Mul.g);

	/* gcd of resulting giants should be expectedGcd */
	gtog(&g2Mul.g, &gotGcd.g);
	nbgcdg(&g1Mul.g, &gotGcd.g);
	if(gcompg(&gotGcd.g, &expectGcd.g)) {
		ourRtn = 1;
		printf("****gcompg failure\n");
	}
	if(ourRtn || SHOW_GCDTEST) {
		printf("----------- \n");
		printf("g1        : "); printGiant(&g1Trunc.g);
		printf("g2        : "); printGiant(&g2Trunc.g);
		printf("existGcd  : "); printGiant(&existGcd.g);
		printf("randInt   : "); printGiant(scr1);
		printf("g1Mul     : "); printGiant(&g1Mul.g);
		printf("g2Mul     : "); printGiant(&g2Mul.g);
		printf("expectGcd : "); printGiant(&expectGcd.g);
		printf("gotGcd    : "); printGiant(&gotGcd.g);
		printf("----------- \n");
	}
	if(ourRtn) {
		return testError();
	}
	return 0;
}

#endif	/* GI_MODG_ENABLE && DVT_GCD_ENABLE */

#if GI_PRIME_TEST_ENABLE && DVT_PRIME_ENABLE

/*
 * isGiantPrime() test - easy - just multiply g1 * g2 and make sure it's
 * not reported as prime. However we have to cut the sizes of g1 and
 * g2 in half - can't do isGiantPrime on a number bigger than
 * GIANT_LITTLE_DIGITS.
 */
#define PRIME_DEPTH_DEF		8

-(void) primeTst:(cc_size) count : (giant) g1 : (giant) g2 : (giant) scr1 : (giant) scr2
{
	lgiant t1;
	lgiant t2;

	localGiantAlloc(&t1);
	localGiantAlloc(&t2);

	gtog(g1, &t1.g);
	gtog(g2, &t2.g);
	if(t1.g.sign > (GIANT_LITTLE_DIGITS / 2)) {
		t1.g.sign /= 2;
	}
	if(t2.g.sign > (GIANT_LITTLE_DIGITS / 2)) {
		t2.g.sign /= 2;
	}

	mulg_common(&t1.g, &t2.g, scr1);
	if(isGiantPrime(scr1, PRIME_DEPTH_DEF)) {
		printf("***isGiantPrime error: composite number reported as prime for "
               "depth %u\n", PRIME_DEPTH_DEF);
		printf("t1   : "); printGiant(&t1.g);
		printf("t2   : "); printGiant(&t2.g);
		printf("scr1 : "); printGiant(scr2);
		return testError();
	}
	return 0;
}

/*
 * Highest known prime in giantPrime.c - we have to keep this in sync if
 * SIZEOF_PRIME_TABLE changes.
 */
#define SIEVE_MAX	1619

-(void) primeSieveTst:(cc_size) count : (giant) g1 : (giant) g2 : (giant) scr1 : (giant) scr2
{
	/*
	 * multiply g1 by a random r, 2<=r<=SIEVE_MAX. Ensure that
	 * checkPrimeCandidate reports the result as SIEVE_COMPOSITE or
	 * SIEVE_CANDIDATE.
	 */
	unsigned r = genRand(2, SIEVE_MAX);
	int rtn;

	int_to_giant(r, scr1);
	mulg_common(g1, scr1, scr2);
	rtn = checkPrimeCandidate(scr2);
	switch(rtn) {
		case SIEVE_COMPOSITE:
		case SIEVE_CANDIDATE:
			return 0;
		default:
			break;
	}
	printf("***checkPrimeCandidate returned %d for known composite\n", rtn);
	printf("r      : %u\n", r);
	printf("g1     : "); printGiant(g1);
	printf("r * g1 : "); printGiant(scr2);
	return testError();
}

#endif	/* GI_PRIME_TEST_ENABLE && DVT_PRIME_ENABLE */

#if		DVT_INVMOD_ENABLE && GI_MODG_ENABLE && GI_INVERSE_MOD_ENABLE

/* cook up a global prime modulus for ginverseMod(), just once then it's const */

lgiant primeModulus;
lgiant primeModulusRecip;

static GIReturn genPrimeModulus(cc_size count,
                                unsigned giantBytes,
                                int quiet)
{
	localGiantAlloc(&primeModulus);
	localGiantAlloc(&primeModulusRecip);

	GIReturn grtn;

	if(!quiet) {
		printf("...generating %u bit prime for ginverseModTest...\n", giantBytes * 8);
	}
	grtn = genPrimeGiant(&primeModulus.g, giantBytes * 8, PRIME_DEPTH_DEFAULT);
	if(grtn) {
		printf("***Error (%d) generating primeModulus for ginverseMod() test\n",
               (int)grtn);
		return grtn;
	}

	make_recip(&primeModulus.g, &primeModulusRecip.g);
	return GR_Success;
}

-(void) ginverseModTst:(cc_size) count : (giant) g1 : (giant) g2 : (giant) scr1 : (giant) scr2
{
	lgiant workG1;		/* working, possibly modified g1 */

	localGiantAlloc(&workG1);
	gtog(g1, &workG1.g);

	/* first ensure g1 < primeModulus */
	modg_via_recip(&primeModulus.g, &primeModulusRecip.g, &workG1.g);

	/* scr1 := g1^(-1) mod primeModulus */
	gtog(&workG1.g, scr1);
	ginverseMod(&primeModulus.g, &primeModulusRecip.g, scr1);

	/* scr2 := g1 * scr1 mod primeModulus, should equal 1 (one) */
	gtog(&workG1.g, scr2);
	rmulg(scr1, scr2, &primeModulus.g, &primeModulusRecip.g);

	if(!isone(scr2)) {
		printf("***ginverseMod failure, g1 * (g1^(-1) mod m) mod m != 1 ***\n");
		printf("----------- \n");
		printf("g1                   : "); printGiant(&workG1.g);
		printf("modulus              : "); printGiant(&primeModulus.g);
		printf("modulusRecip         : "); printGiant(&primeModulusRecip.g);
		printf("g1^(-1) mod m        : "); printGiant(scr1);
		printf("g1 * (g1^(-1)) mod m : "); printGiant(scr2);
		printf("----------- \n");
		return testError();
	}
	return 0;
}

#endif	/* DVT_INVMOD_ENABLE && GI_MODG_ENABLE && GI_INVERSE_MOD_ENABLE */

#if		DVT_DIVMOD_ENABLE && GI_MODG_ENABLE

/*
 * Test divmodg_via_recip().
 *
 * Cook up third random giant g3 such that g3 < g2;
 * giant product = (g1 * g2) + g3;
 * use divmodg_via_recip to get
 *    giant gmod = product mod g2;
 *    giant gdiv = product div g2;
 * Ensure that gmod==g3 and gdiv==g1;
 */
-(void) divModTst:(cc_size) count : (giant) g1 : (giant) g2 : (giant) scr1 : (giant) scr2
{
	lgiant g3;
	uint8_t buf[MAX_PRIME_SIZE_BYTES];
	tgiant product;
	giant gmod = scr1;
	giant gdiv = scr2;
	lgiant recip;
	int gotErr = 0;

	if(isone(g2)) {
		/* well shoot. This ain't gonna work. */
		printf("divModTst: g2==1; skipping this test.\n");
		return 0;
	}

	/* generate random g3 < g2 */
	localGiantAlloc(&g3);
	genGiant(count, &g3.g, g2->sign * GIANT_BYTES_PER_DIGIT, buf);
	if(gcompg(g2, &g3.g) <= 0) {
		lgiant oneG;

		/* g3 := g2 - 1 */
		localGiantAlloc(&oneG);
		int_to_giant(1, &oneG.g);
		gtog(g2, &g3.g);
		normal_subg(&oneG.g, &g3.g);
	}

	/* product := (g1 * g2) + g3 */
	localTriGiantAlloc(&product);
	mulg_common(g1, g2, &product.g);
	addg(&g3.g, &product.g);

	/*
	 * gmod = product mod g2;
	 * gdiv = product div g2;
	 */
	localGiantAlloc(&recip);
	make_recip(g2, &recip.g);
	gtog(&product.g, gmod);
	divmodg_via_recip(g2, &recip.g, gmod, gdiv);

	/* Ensure that gmod==g3 and gdiv==g1 */
	if(gcompg(&g3.g, gmod) != 0) {
		printf("***Error in mod portion of divmodg_via_recip\n");
		gotErr = 1;
	}
	if(gcompg(g1, gdiv) != 0) {
		printf("***Error in div portion of divmodg_via_recip\n");
		gotErr = 1;
	}
	if(gotErr) {
		printf("----------- \n");
		printf("g1                   : "); printGiant(g1);
		printf("g2                   : "); printGiant(g2);
		printf("g3                   : "); printGiant(&g3.g);
		printf("(g1 * g2) + g3       : "); printGiant(&product.g);
		printf("product mod g2       : "); printGiant(gmod);
		printf("product div g2       : "); printGiant(gdiv);
		printf("----------- \n");
		return testError();
	}
	return 0;
}

#endif	/* DVT_DIVMOD_ENABLE && GI_MODG_ENABLE */

static void testStartBanner(
                            char *testName,
                            int argc,
                            char **argv)
{
	int i;
	printf("Starting %s; args: ", testName);
	for(i=1; i<argc; i++) {
		printf("%s ", argv[i]);
	}
	printf("\n");
}

-(void)main: (int)argc argv: (char **)argv
{
	int				arg;
	char			*argp;
	uint8_t         *buf;
	int				loop;

	int				loops = LOOPS_DEF;
	unsigned		maxByteSize = MAX_SIZE_DEF;
    cc_size maxCount = ccn_nof_size(maxByteSize);
	int				verbose = 0;
	int				quiet = 1;
	int				doPrimeMod = 1;

	for(arg=1; arg<argc; arg++) {
		argp = argv[arg];
		switch(argp[0]) {
		    case 'x':
			maxByteSize = (unsigned)atoi(&argp[2]);
				break;
		    case 'l':
			loops = atoi(&argp[2]);
				break;
			case 'k':
				doPrimeMod = 0;
				break;
			case 'v':
				verbose = 1;
				break;
			case 'q':
				quiet = 1;
				break;
		    case 'h':
		    default:
			//usage(argv);
                break;
		}
	}
	buf = malloc(maxByteSize);

	/* Scratch giants, big enough for anything. */
    cc_unit g1[maxCount], g2[maxCount],
        scr1[2 * maxCount], scr2[2 * maxCount];

    if (argc > 1)
        testStartBanner("MPNValidation", argc, argv);

#if		DVT_INVMOD_ENABLE && GI_MODG_ENABLE && GI_INVERSE_MOD_ENABLE
	if (doPrimeMod) {
		/* one-time only init for this guy... */
		if (genPrimeModulus(maxCount, maxByteSize, quiet)) {
            free(buf);
			exit(1);
		}
	}
#endif

	for (loop=0; loop<loops; loop++) {

	    if (!quiet && ((loop % LOOP_NOTIFY) == 0)) {
		printf("..loop %d\n", loop);
	    }

        cc_size size = genRand(MIN_SIZE, maxByteSize);
        cc_size count = ccn_nof_size(size);
	    genGiant(count, g1, size, buf);
	    genGiant(count, g2, size, buf);

#if DVT_BASIC_ENABLE
		if (verbose) printf("...compTest\n");
	    [self compTst: count : g1 : scr1 : scr2];

		if (verbose) printf("...addSubTest\n");
	    [self addSubTst: count : g1 : g2 : scr1];

		if (verbose) printf("...mulTest\n");
	    [self mulTst: count : g1 : scr1 : scr2];

		if (verbose) printf("...squareTest\n");
	    [self squareTst: count : g1 : scr1 : scr2];

		if (verbose) printf("...mulOnesTest\n");
	    [self mulOnesTst: count : g1 : scr1 : scr2];
#endif	/* DVT_BASIC_ENABLE */

#if DVT_SHIFT_ENABLE
		if (verbose) printf("...lshiftTest\n");
	    [self lshiftTst: count : g1 : g2 : scr1 : scr2];

		if (verbose) printf("...rshiftTest\n");
	    [self rshiftTst: count : g1 : g2 : scr1 : scr2];

		if (verbose) printf("...rshiftwordTest\n");
	    [self rshiftwordTst: count : g1 : g2 : scr1 : scr2];
#endif /* DVT_SHIFT_ENABLE */

		if (doPrimeMod) {
#if GI_MODG_ENABLE && DVT_MOD_ENABLE
			if (verbose) printf("...modTest\n");
            [self modTst: count : g1 : g2 : scr1 : scr2];
#endif	/* DVT_MOD_ENABLE */

#if		GI_MODG_ENABLE && DVT_GCD_ENABLE
			if (verbose) printf("...gcdTest\n");
            [self gcdTst: count : &g1.g : &g2.g : &scr1.g : &scr2.g];
#endif	/* DVT_GCD_ENABLE */

#if GI_PRIME_TEST_ENABLE && DVT_PRIME_ENABLE
			if (verbose) printf("...primeTest\n");
            [self primeTst: count : &g1.g : &g2.g : &scr1.g : &scr2.g];

			if (verbose) printf("...primeSieveTest\n");
            [self primeSieveTst: count : &g1.g : &g2.g : &scr1.g : &scr2.g];
#endif	/* GI_PRIME_TEST_ENABLE && DVT_PRIME_ENABLE */

#if		DVT_INVMOD_ENABLE && GI_MODG_ENABLE && GI_INVERSE_MOD_ENABLE
			if (verbose) printf("...ginverseModTest\n");
            [self ginverseModTst: count : &g1.g : &g2.g : &scr1.g : &scr2.g];
#endif	/* DVT_INVMOD_ENABLE && GI_MODG_ENABLE */

#if		DVT_DIVMOD_ENABLE && GI_MODG_ENABLE
			if (verbose) printf("...divModTest\n");
            [self divModTst: count : &g1.g : &g2.g : &scr1.g : &scr2.g];
#endif	/* DVT_DIVMOD_ENABLE && GI_MODG_ENABLE */
		} /* doPrimeMod */
	}

	if (!quiet) printf("...giantDvt complete\n");

    free(buf);
}

- (void) testMod {
    [self main: 0 argv: NULL];
}

@end
