#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
static int debug = 0;
typedef struct t1 {
int digit,log,ordinal;
} t1;
const t1 table1[] = {
{ 0, 50, 9},
{ 1, 0, 0},
{ 2, 1, 1},
{ 3, 7, 4},
{ 4, 2, 2},
{ 5, 23, 7},
{ 6, 8, 5},
{ 7, 33, 8},
{ 8, 3, 3},
{ 9, 14, 6},
};
typedef struct t2 {
int a,b;
} t2;
const t2 table2[] = {
{ 1, 0},
{ 2, 1},
{ 3, 7},
{ 4, 2},
{ 5, 23},
{ 6, 8},
{ 7, 33},
{ 8, 13},
{ 9, 14},
{ 10, 24},
{ 12, 9},
{ 14, 34},
{ 15, 30},
{ 16, 4},
{ 18, 15},
{ 20, 25},
{ 21, 40},
{ 24, 10},
{ 25, 46},
{ 27, 21},
{ 28, 35},
{ 30, 31},
{ 32, 5},
{ 35, 56},
{ 36, 16},
{ 40, 26},
{ 42, 41},
{ 45, 37},
{ 48, 11},
{ 49, 66},
{ 54, 22},
{ 56, 36},
{ 63, 47},
{ 64, 6},
{ 72, 17},
{ 81, 28},
};
typedef struct t3 {
int log,antilog;
} t3;
const t3 table3[] = {
{ 0, 1},
{ 1, 2},
{ 2, 4},
{ 3, 8},
{ 4, 16},
{ 5, 32},
{ 6, 64},
{ 7, 3},
{ 8, 6},
{ 9, 12},
{ 10, 24},
{ 11, 48},
{ 12, -1},
{ 13, -1},
{ 14, 9},
{ 15, 18},
{ 16, 36},
{ 17, 72},
{ 18, -1},
{ 19, -1},
{ 20, -1},
{ 21, 27},
{ 22, 54},
{ 23, 5},
{ 24, 10},
{ 25, 20},
{ 26, 40},
{ 27, -1},
{ 28, 81},
{ 29, -1},
{ 30, 15},
{ 31, 30},
{ 32, -1},
{ 33, 7},
{ 34, 14},
{ 35, 28},
{ 36, 56},
{ 37, 45},
{ 38, -1},
{ 39, -1},
{ 40, 21},
{ 41, 42},
{ 42, -1},
{ 43, -1},
{ 44, -1},
{ 45, -1},
{ 46, 25},
{ 47, 63},
{ 48, -1},
{ 49, -1},
{ 50, 0},
{ 51, 0},
{ 52, 0},
{ 53, 0},
{ 54, -1},
{ 55, -1},
{ 56, 35},
{ 57, 0},
{ 58, 0},
{ 59, -1},
{ 60, -1},
{ 61, -1},
{ 62, -1},
{ 63, -1},
{ 64, 0},
{ 65, -1},
{ 66, 49},
{ 67, -2},
{ 68, -2},
{ 69, -2},
{ 70, -2},
{ 71, -2},
{ 72, -2},
{ 73, 0},
{ 74, -2},
{ 75, -2},
{ 76, -2},
{ 77, -2},
{ 78, -2},
{ 79, -2},
{ 80, -2},
{ 81, -2},
{ 82, -2},
{ 83, 0},
{ 84, -2},
{ 85, -2},
{ 86, -2},
{ 87, -2},
{ 88, -2},
{ 89, -2},
{ 90, -2},
{ 91, -2},
{ 92, -2},
{ 93, -2},
{ 94, -2},
{ 95, -2},
{ 96, -2},
{ 97, -2},
{ 98, -2},
{ 99, -2},
{ 100, 0},
};
int Multiply_Ludgate(int a, int b) {
// Multiply two single-digit decimal numbers to produce a two-digit product.
int result;
int log1, log2;
log1 = table1[a].log;
log2 = table1[b].log;
result = table3[log1+log2].antilog;
if (debug) fprintf(stderr, "%d + %d => %d\n", log1, log2, log1+log2);
if (debug) fprintf(stderr, "%d * %d = %d\n", a, b, result);
return result;
}
// In the example below I'm using the normal left-to-right order of digits in a number,
// where the piecewise multiplications are performed from right-to-left, for pedagogical
// purposes only. In practice it would make far more sense to reverse the digits and
// procede from left to right, i.e. little-endian. Our numbering system is erroneously
// big-endian due to a poor understanding of Arabic when Arabic numerals were introduced
// into countries that used the Latin alphabet and left-to-right writing systems.
// Everything below here is a decimal multiply-and-accumulate implementation and
// has little to do with the Ludgate algorithm other than being a harness for it.
// This is a one-digit multiply-and-accumulate step.
void mul_add_digit(int multiplicand_digit, char *result, int result_rightmost, int multiplier_digit) {
int result_index;
int product;
product = Multiply_Ludgate(multiplicand_digit, multiplier_digit);
// We get a 2-digit result when multiplying 2 1-digit decimal numbers. So two adds and two carries.
result_index = result_rightmost;
result[result_index] += product%10;
result[result_index-1] += product/10;
// add digit
while (result_index > 0) {
while (result[result_index] >= 10) {
result[result_index] -= 10;
result[result_index-1] += 1;
}
// ripple carry
result_index -= 1;
}
}
// This is a multiply-and-accumulate step where an arbitrary length multiplicand
// is multiplied by a single-digit multiplier.
void multiply_and_accumulate(char *multiplicand, int multiplicand_rightmost, char *result, int result_rightmost, int multiplier_digit) {
int i;
int result_index;
if (debug) fprintf(stderr, "(result before: %.*s)\n", result_rightmost+1, result);
if (debug) fprintf(stderr, "Multiply & accumulate %.*s by %c gives:\n", multiplicand_rightmost+1, multiplicand, multiplier_digit);
multiplier_digit -= '0';
for (i = 0; i <= multiplicand_rightmost; i++) multiplicand[i] -= '0'; // Ascii to decimal
for (i = 0; i <= result_rightmost; i++) result[i] -= '0';
result_index = result_rightmost;
for (i = multiplicand_rightmost; i >= 0; i -= 1) {
mul_add_digit(multiplicand[i], result, result_index, multiplier_digit);
result_index -= 1;
}
for (i = 0; i <= multiplicand_rightmost; i++) multiplicand[i] += '0'; // Decimal to ascii
for (i = 0; i <= result_rightmost; i++) result[i] += '0'; // Decimal to ascii
if (debug) fprintf(stderr, "%.*s\n", result_rightmost+1, result);
}
// And finally we perform multiple multiply-and-accumulate steps to produce the product
// of two arbitrary length decimal numbers. In hardware the 'arbitrary length' would
// be done by extending the hardware units side-by-side, though note that a ripple-carry
// is still required (which I didn't see mentioned in the Ludgate documentation)
char *multiply(char *multiplicand, char *multiplier) {
int max_result_digits;
int i;
int multiplier_rightmost, multiplicand_rightmost, result_rightmost;
char *result;
multiplicand_rightmost = strlen(multiplicand) - 1;
multiplier_rightmost = strlen(multiplier) - 1;
if (debug) fprintf(stderr, "init: multiplier_rightmost = strlen(\"%s\") = %c\n", multiplier, multiplier_rightmost);
result_rightmost = multiplicand_rightmost+1 + multiplier_rightmost+1 - 1;
max_result_digits = result_rightmost+1;
result = malloc(max_result_digits+1);
for (i = 0; i <= result_rightmost; i++) result[i] = '0'; // initialise result to all zeroes
result[max_result_digits] = '\0';
while (multiplier_rightmost >= 0) {
if (debug) fprintf(stderr, "loop: multiplier_rightmost[%d] = %c\n", multiplier_rightmost, multiplier[multiplier_rightmost]);
multiply_and_accumulate(multiplicand, multiplicand_rightmost, result, result_rightmost, multiplier[multiplier_rightmost]);
result_rightmost -= 1; // Multiply next partial sum by 10
multiplier_rightmost -= 1; // Take next more significant digit from multiplier.
}
return result;
}
int main(int argc, char **argv) {
if (argc > 1 && !strcmp(argv[1], "-d")) {
debug = 1;
argc -= 1; argv += 1;
}
if (argc != 3) {
fprintf(stderr, "syntax: ludgate [-d] nnn nnn\n\n");
exit(EXIT_FAILURE);
}
if (!isdigit(*argv[1]) || !isdigit(*argv[2])) {
fprintf(stderr, "ludgate: both arguments should be positive decimal integers: %s %s\n\n", argv[1], argv[2]);
exit(EXIT_FAILURE);
}
fprintf(stdout, "%s\n", multiply(strdup(argv[1]), strdup(argv[2])));
exit(EXIT_SUCCESS);
return EXIT_FAILURE;
}