void multiply(limb *factor1, limb *factor2, limb *result, int len, int *pResultLen) { int length = len; // Compute length of numbers for each recursion. if (length > KARATSUBA_CUTOFF) { int div = 1; while (length > KARATSUBA_CUTOFF) { div *= 2; length = (length + 1) / 2; } length *= div; } karatLength = length; memset(arr, 0, 2 * length*sizeof(limb)); memcpy(&arr[0], factor1, len*sizeof(limb)); memcpy(&arr[length], factor2, len*sizeof(limb)); Karatsuba(0, length, 2 * length); memcpy(result, &arr[2 * (karatLength - length)], 2 * length * sizeof(limb)); if (pResultLen != NULL) { memcpy(result, &arr[2 * (karatLength - length)], 2 * length * sizeof(limb)); if (karatLength > length && arr[2 * (karatLength - length)-1].x == 0) { *pResultLen = length * 2 - 1; } else { *pResultLen = length * 2; } } }
LongInt Algorithm::Karatsuba(LongInt a_1, LongInt b_1) { if(a_1.length()==1 || b_1.length()==1)//&& { return a_1*b_1; } int length; if(a_1.length()!=1 && b_1.length()!=1) { length=(a_1.length() >= b_1.length() ? b_1.length() : a_1.length() )/2; LongInt a_2; a_2.number=a_1.getVector_of_Number(a_1.length()-length); LongInt b_2; b_2.number=b_1.getVector_of_Number(b_1.length()-length); LongInt ab_1,ab_2; ab_1=Karatsuba(a_1,b_1); ab_2=Karatsuba(a_2,b_2); return ( (((Karatsuba(a_1+a_2,b_1+b_2)-ab_1-ab_2)<<length) + ab_2) + (ab_1<<(2*length)) ); } }
// Recursive Karatsuba function. static void Karatsuba(int idxFactor1, int nbrLen, int diffIndex) { int idxFactor2 = idxFactor1 + nbrLen; int i; unsigned int carry1First, carry1Second; unsigned int carry2Second; limb *ptrResult, *ptrHigh, tmp; int middle; int sign; int halfLength; if (nbrLen <= KARATSUBA_CUTOFF) { // Check if one of the factors is equal to zero. ptrResult = &arr[idxFactor1]; for (i = nbrLen; i > 0; i--) { if ((ptrResult++)->x != 0) { break; } } if (i > 0) { // First factor is not zero. Check second. ptrResult = &arr[idxFactor2]; for (i = nbrLen; i > 0; i--) { if ((ptrResult++)->x != 0) { break; } } } if (i==0) { // One of the factors is equal to zero. for (i = nbrLen - 1; i >= 0; i--) { arr[idxFactor1 + i].x = arr[idxFactor2 + i].x = 0; } return; } // Below cutoff: perform standard classical multiplcation. ClassicalMult(idxFactor1, idxFactor2, nbrLen); return; } // Length > KARATSUBA_CUTOFF: Use Karatsuba multiplication. // It uses three half-length multiplications instead of four. // x*y = (xH*b + xL)*(yH*b + yL) // x*y = (b + 1)*(xH*yH*b + xL*yL) + (xH - xL)*(yL - yH)*b // The length of b is stored in variable halfLength. // Since the absolute values of (xH - xL) and (yL - yH) fit in // a single limb, there will be no overflow. // At this moment the order is: xL, xH, yL, yH. // Exchange high part of first factor with low part of 2nd factor. halfLength = nbrLen >> 1; for (i = idxFactor1 + halfLength; i<idxFactor2; i++) { tmp.x = arr[i].x; arr[i].x = arr[i + halfLength].x; arr[i + halfLength].x = tmp.x; } // At this moment the order is: xL, yL, xH, yH. // Get absolute values of (xH-xL) and (yL-yH) and the signs. sign = absSubtract(idxFactor1, idxFactor2, diffIndex, halfLength); sign ^= absSubtract(idxFactor2 + halfLength, idxFactor1 + halfLength, diffIndex + halfLength, halfLength); middle = diffIndex; diffIndex += nbrLen; Karatsuba(idxFactor1, halfLength, diffIndex); // Multiply both low parts. Karatsuba(idxFactor2, halfLength, diffIndex); // Multiply both high parts. Karatsuba(middle, halfLength, diffIndex); // Multiply the differences. // Process all carries at the end. // Obtain (b+1)(xH*yH*b + xL*yL) = xH*yH*b^2 + (xL*yL+xH*yH)*b + xL*yL // The first and last terms are already in correct locations. ptrResult = &arr[idxFactor1+halfLength]; carry1First = carry1Second = carry2Second = 0; for (i = halfLength; i > 0; i--) { // The sum of three ints overflows an unsigned int variable, // so two adds are required. Also carries must be separated in // order to avoid overflow: // 00000001 + 7FFFFFFF + 7FFFFFFF = FFFFFFFF unsigned int accum1Lo = carry1First + ptrResult->x + (ptrResult + halfLength)->x; unsigned int accum2Lo; carry1First = accum1Lo >> BITS_PER_GROUP; accum2Lo = carry2Second + (accum1Lo & MAX_VALUE_LIMB) + (ptrResult - halfLength)->x; carry2Second = accum2Lo >> BITS_PER_GROUP; accum1Lo = carry1Second + (accum1Lo & MAX_VALUE_LIMB) + (ptrResult + nbrLen)->x; carry1Second = accum1Lo >> BITS_PER_GROUP; (ptrResult + halfLength)->x = accum1Lo & MAX_VALUE_LIMB; ptrResult->x = accum2Lo & MAX_VALUE_LIMB; ptrResult++; } (ptrResult + halfLength)->x += carry1First + carry1Second; ptrResult->x += carry1First + carry2Second; // Process carries. ptrResult = &arr[idxFactor1]; carry1First = 0; for (i = 2*nbrLen; i > 0; i--) { carry1First += ptrResult->x; (ptrResult++)->x = carry1First & MAX_VALUE_LIMB; carry1First >>= BITS_PER_GROUP; } // Compute final product. ptrHigh = &arr[middle]; ptrResult = &arr[idxFactor1 + halfLength]; if (sign != 0) { // (xH-xL) * (yL-yH) is negative. int borrow = 0; for (i = nbrLen; i > 0; i--) { borrow += ptrResult->x - (ptrHigh++)->x; (ptrResult++)->x = borrow & MAX_VALUE_LIMB; borrow >>= BITS_PER_GROUP; } for (i = halfLength; i > 0; i--) { borrow += ptrResult->x; (ptrResult++)->x = borrow & MAX_VALUE_LIMB; borrow >>= BITS_PER_GROUP; } }