Karatsuba Implementation Issues

204 views Asked by At

Couple things:

  • I am a university student
  • This is not a homework problem (I'm curious about how bignum libraries work)
  • All the code snippets here can be compiled with gcc -lgmp

Here is my implementation which keeps returning incorrect answers. I've tried going through with gdb & checking individual ops with GMP and marking which operations are definitely failing in the code, but I haven't managed to find specifically what's going wrong.

Beyond that, I can't identify where the issue is.

uint64_t *multiply(u256 c, u256 a, uint64_t alen, u256 b, uint64_t blen) {
    if (alen == 1 || blen == 1) {
        u2_set(c, 0);
        c[0] = a[0] * b[0];
        c[1] = c[0] >> 32LU;
        c[0] &= MASK;
        return c;
    } else {
        uint64_t al, bl;
        uint64_t M = min(alen, blen);
        al = ceil(alen, 2);
        bl = ceil(blen, 2);
        uint64_t m = M / 2;
        u256 low1, low2, high1, high2, z0, z1, z2;
        u2_set(low1, 0);
        u2_set(low2, 0);
        u2_set(high1, 0);
        u2_set(high2, 0);
        memcpy(low1, a, sizeof(uint64_t) * al);
        memcpy(low2, b, sizeof(uint64_t) * bl);
        memcpy(high1, &(a[al]), sizeof(uint64_t) * (alen - al));
        memcpy(high2, &(b[bl]), sizeof(uint64_t) * (blen - bl));

        /* Starts ok, goes bad immediately. Mostly ok. */
        multiply(z0, low1, al, low2, bl);

        u2_add(low1, low1, high1); /* CHECKED */
        u2_add(low2, low2, high2); /* CHECKED */

        multiply(z1, low1, al, low2, bl); /* Starts ok, goes bad immediately. */

        /* Bad but starts & usually is ok */
        multiply(z2, high1, (alen - al), high2, (blen - bl));

        /* ALL GOOD */
        u256 o;
        u2_sub(z1, z1, z2);
        u2_sub(z1, z1, z0);
        u2_shl(z1, z1, m);
        u2_shl(z2, z2, m * 2);
        u2_add(o, z2, u2_add(z1, z1, z0));

        u2_copy(c, o);
        return c;
    }
}

To be honest, the worst part is not having test vectors for the intermediate values. Beyond the ones in the wikipedia page example (which I seem to pass), I can't find anything.

u256 is an array of 8 uint64_t's. I'm only using the lower 32 bits of those integers to make masking and carries a bit easier. The idea is to do the Karatsuba multiplication base 232.

The rest of the code can be found here.

Any help would be appreciated.

EDIT:

Example of what's wrong:

a = 89637787616193205010487395076171907549998432494691382753495063501471509582488
b = 62570744880332518477645686292054860709644349224438728618405612469614908709348

Multiplying the 2 should return

16439926136035242788212517705012640494860769275971090459163708006828967815008

Instead it returns

38696864898835518778416

That specific test can be run by compiling this code: here

with gcc and -lgmp

ceil is defined as so:

#define ceil(a, b) (((a) + (b) - 1LU) / (b))

using this test code, which checks stuff a bit more in-depth, I believe I narrowed the problem down some.

It occurs on the m=4 multiply of z0 = low1 * low2. The two values multiplied are 100000000000 and 100000000000, and the expected result is 10000000000000000000000 while it ends up being 1478053504202008644. Running the above test code will reproduce this. I assume this means my karatsuba implementation is simply broken, as the components aren't throwing any errors and it's specifically failing on the m=2 multiplications. I'm checking the m=1 multiplications and they seem fine.

0

There are 0 answers