import java.util.*;
class BigNumber {
private final static int base = 1000000000;
private static int karatsubaThreshold = 25;
private int[] digits;
private BigNumber() {
}
public BigNumber(int arg) {
if (0 > arg) {
throw new IllegalArgumentException("Numbers must be positive");
}
digits = new int[2];
digits[0] = arg % base;
digits[1] = arg / base;
}
public BigNumber(String arg) {
BigNumber temp, ten;
ten = new BigNumber(10);
temp = new BigNumber(0);
for (int k = 0; arg.length() > k; ++k) {
char c = arg.charAt(k);
if (('0' <= c) && (c <= '9')) {
temp = temp.multiplySimple(ten).add(new BigNumber(c - '0'));
}
}
digits = temp.digits;
}
public BigNumber(BigNumber arg) {
digits = new int[arg.digits.length];
for (int k = 0; arg.digits.length > k; ++k) {
digits[k] = arg.digits[k];
}
}
/*
private BigNumber(BigNumber arg, int padSize) {
if (0 > padSize) {
throw new IllegalArgumentException("Pad size must be positive");
}
digits = new int[arg.digits.length + padSize];
for (int k = 0; arg.digits.length > k; ++k) {
digits[k] = arg.digits[k];
}
}
//*/
public BigNumber(Random rng, int size) {
digits = new int[size];
for (int k = 0; size > k; ++k) {
digits[k] = rng.nextInt(base);
}
}
public BigNumber add(BigNumber arg) {
int k, size1, size2, sum;
BigNumber augend, addend, result;
size1 = this.digits.length - 1;
if (0 != this.digits[size1]) {
++size1;
}
size2 = arg.digits.length - 1;
if (0 != arg.digits[size2]) {
++size2;
}
if (size1 < size2) {
augend = arg;
addend = this;
sum = size1;
size1 = size2;
size2 = sum;
} else {
augend = this;
addend = arg;
}
result = new BigNumber();
result.digits = new int[size1 + 1];
for (k = 0; size1 > k; ++k) {
result.digits[k] = augend.digits[k];
}
sum = 0;
for (k = 0; size2 > k; ++k) {
sum += result.digits[k];
sum += addend.digits[k];
if (base > sum) {
result.digits[k] = sum;
sum = 0;
} else {
result.digits[k] = sum - base;
sum = 1;
}
}
while (0 != sum) {
sum += result.digits[k];
if (base > sum) {
result.digits[k] = sum;
sum = 0;
} else {
result.digits[k] = sum - base;
sum = 1;
}
++k;
}
return result;
}
public BigNumber multiplyKaratsuba(BigNumber arg) {
int k, l, size1, size2, offset, limit, sum;
BigNumber result, a, b, a1, a2, b1, b2, axb1, axb2, apa, bpb, temp;
size1 = digits.length;
if (0 == digits[size1 - 1]) {
--size1;
}
size2 = arg.digits.length;
if (0 == arg.digits[size2 - 1]) {
--size2;
}
if (karatsubaThreshold > size1 || karatsubaThreshold > size2) {
return this.multiplySimple(arg);
}
if (size2 < size1) {
a = this;
b = arg;
offset = (size1 + 1) / 2;
} else {
a = arg;
b = this;
offset = size2;
size2 = size1;
size1 = offset;
offset = (size1 + 1) / 2;
}
//==============================
result = new BigNumber();
result.digits = new int[size1 + size2];
a1 = new BigNumber();
a2 = new BigNumber();
a1.digits = new int[offset];
a2.digits = new int[size1 - offset];
for (k = 0; offset > k; ++k) {
a1.digits[k] = a.digits[k];
}
for (l = 0; size1 > k; ++k, ++l) {
a2.digits[l] = a.digits[k];
}
//==============================
if (size2 > offset) {
b1 = new BigNumber();
b2 = new BigNumber();
b1.digits = new int[offset];
b2.digits = new int[size2 - offset];
for (k = 0; offset > k; ++k) {
b1.digits[k] = b.digits[k];
}
for (l = 0; size2 > k; ++k, ++l) {
b2.digits[l] = b.digits[k];
}
//==============================
axb1 = a1.multiplyKaratsuba(b1);
axb2 = a2.multiplyKaratsuba(b2);
apa = a1.add(a2);
bpb = b1.add(b2);
temp = apa.multiplyKaratsuba(bpb);
//==============================
limit = axb1.digits.length - 1;
if (0 != axb1.digits[limit]) {
++limit;
}
sum = 0;
for (k = 0; limit > k; ++k) {
sum += temp.digits[k];
sum -= axb1.digits[k];
if (0 > sum) {
temp.digits[k] = sum + base;
sum = -1;
} else {
temp.digits[k] = sum;
sum = 0;
}
}
while (0 != sum) {
sum += temp.digits[k];
if (0 > sum) {
temp.digits[k] = sum + base;
sum = -1;
} else {
temp.digits[k] = sum;
sum = 0;
}
++k;
}
//==============================
limit = axb2.digits.length - 1;
if (0 != axb2.digits[limit]) {
++limit;
}
sum = 0;
for (k = 0; limit > k; ++k) {
sum += temp.digits[k];
sum -= axb2.digits[k];
if (0 > sum) {
temp.digits[k] = sum + base;
sum = -1;
} else {
temp.digits[k] = sum;
sum = 0;
}
}
while (0 != sum) {
sum += temp.digits[k];
if (0 > sum) {
temp.digits[k] = sum + base;
sum = -1;
} else {
temp.digits[k] = sum;
sum = 0;
}
++k;
}
//==============================
limit = axb1.digits.length;
for (k = 0; limit > k; ++k) {
result.digits[k] = axb1.digits[k];
}
//==============================
limit = temp.digits.length - 1;
while (0 == temp.digits[limit]) {
--limit;
}
++limit;
sum = 0;
for (k = 0, l = offset; limit > k; ++k, ++l) {
sum += result.digits[l];
sum += temp.digits[k];
if (base > sum) {
result.digits[l] = sum;
sum = 0;
} else {
result.digits[l] = sum - base;
sum = 1;
}
}
if (0 != sum) {
result.digits[l] = sum;
}
//==============================
limit = axb2.digits.length - 1;
if (0 != axb2.digits[limit]) {
++limit;
}
sum = 0;
for (k = 0, l = 2 * offset; limit > k; ++k, ++l) {
sum += result.digits[l];
sum += axb2.digits[k];
if (base > sum) {
result.digits[l] = sum;
sum = 0;
} else {
result.digits[l] = sum - base;
sum = 1;
}
}
if (0 != sum) {
result.digits[l] = sum;
}
} else {
axb1 = a1.multiplyKaratsuba(b);
axb2 = a2.multiplyKaratsuba(b);
//==============================
limit = axb1.digits.length;
for (k = 0; limit > k; ++k) {
result.digits[k] = axb1.digits[k];
}
//==============================
limit = axb2.digits.length - 1;
if (0 != axb2.digits[limit]) {
++limit;
}
sum = 0;
for (k = 0, l = offset; limit > k; ++k, ++l) {
sum += result.digits[l];
sum += axb2.digits[k];
if (base > sum) {
result.digits[l] = sum;
sum = 0;
} else {
result.digits[l] = sum - base;
sum = 1;
}
}
if (0 != sum) {
result.digits[l] = sum;
}
}
return result;
}
public BigNumber multiplySimple(BigNumber arg) {
int k, l, size1, size2;
long mult, sum;
BigNumber result;
size1 = digits.length - 1;
if (0 != digits[size1]) {
++size1;
}
size2 = arg.digits.length - 1;
if (0 != arg.digits[size2]) {
++size2;
}
result = new BigNumber();
result.digits = new int[size1 + size2];
for (k = 0; size1 > k; ++k) {
mult = digits[k];
sum = 0;
for (l = 0; size2 > l; ++l) {
sum += result.digits[k + l];
sum += mult * arg.digits[l];
result.digits[k + l] = (int) (sum % base);
sum /= base;
}
if (0 != sum) {
result.digits[k + l] = (int) sum;
}
}
return result;
}
public BigNumber squareKaratsuba() {
int k, l, size, offset, limit, sum;
BigNumber result, a, b, a2, b2, apb, temp;
size = digits.length - 1;
if (0 != digits[size]) {
++size;
}
offset = (size + 1) / 2;
if (karatsubaThreshold > size) {
return this.multiplySimple(this);
}
result = new BigNumber();
result.digits = new int[2 * size];
a = new BigNumber();
b = new BigNumber();
a.digits = new int[offset];
b.digits = new int[size - offset];
for (k = 0; offset > k; ++k) {
a.digits[k] = digits[k];
}
for (l = 0; size > k; ++k, ++l) {
b.digits[l] = digits[k];
}
//==============================
a2 = a.squareKaratsuba();
b2 = b.squareKaratsuba();
apb = a.add(b);
temp = apb.squareKaratsuba();
//==============================
limit = a2.digits.length - 1;
if (0 != a2.digits[limit]) {
++limit;
}
sum = 0;
for (k = 0; limit > k; ++k) {
sum += temp.digits[k];
sum -= a2.digits[k];
if (0 > sum) {
temp.digits[k] = sum + base;
sum = -1;
} else {
temp.digits[k] = sum;
sum = 0;
}
}
while (0 != sum) {
sum += temp.digits[k];
if (0 > sum) {
temp.digits[k] = sum + base;
sum = -1;
} else {
temp.digits[k] = sum;
sum = 0;
}
++k;
}
//==============================
limit = b2.digits.length - 1;
if (0 != b2.digits[limit]) {
++limit;
}
sum = 0;
for (k = 0; limit > k; ++k) {
sum += temp.digits[k];
sum -= b2.digits[k];
if (0 > sum) {
temp.digits[k] = sum + base;
sum = -1;
} else {
temp.digits[k] = sum;
sum = 0;
}
}
while (0 != sum) {
sum += temp.digits[k];
if (0 > sum) {
temp.digits[k] = sum + base;
sum = -1;
} else {
temp.digits[k] = sum;
sum = 0;
}
++k;
}
//==============================
limit = a2.digits.length;
for (k = 0; limit > k; ++k) {
result.digits[k] = a2.digits[k];
}
//==============================
limit = temp.digits.length - 1;
while (0 == temp.digits[limit]) {
--limit;
}
++limit;
sum = 0;
for (k = 0, l = offset; limit > k; ++k, ++l) {
sum += result.digits[l];
sum += temp.digits[k];
if (base > sum) {
result.digits[l] = sum;
sum = 0;
} else {
result.digits[l] = sum - base;
sum = 1;
}
}
if (0 != sum) {
result.digits[l] = sum;
}
//==============================
limit = b2.digits.length - 1;
if (0 != b2.digits[limit]) {
++limit;
}
sum = 0;
for (k = 0, l = 2 * offset; limit > k; ++k, ++l) {
sum += result.digits[l];
sum += b2.digits[k];
if (base > sum) {
result.digits[l] = sum;
sum = 0;
} else {
result.digits[l] = sum - base;
sum = 1;
}
}
if (0 != sum) {
result.digits[l] = sum;
}
//==============================
return result;
}
public int compareTo(BigNumber arg) {
int k, flag;
BigNumber a, b;
if (this.digits.length > arg.digits.length) {
a = this;
b = arg;
flag = 1;
} else {
a = arg;
b = this;
flag = -1;
}
k = a.digits.length;
while (b.digits.length > k) {
--k;
if (0 != a.digits[k]) {
return flag;
}
}
do {
--k;
if (b.digits[k] < a.digits[k]) {
return flag;
} else if (b.digits[k] > a.digits[k]) {
return -flag;
}
} while (0 != k);
return 0;
}
public String toString() {
int k, l, value;
l = 12 * digits.length;
char[] buffer = new char[l];
for (k = 0; digits.length > k; ++k) {
value = digits[k];
buffer[--l] = (char) ('0' + value % 10);
value /= 10;
buffer[--l] = (char) ('0' + value % 10);
value /= 10;
buffer[--l] = (char) ('0' + value % 10);
value /= 10;
buffer[--l] = ' ';
buffer[--l] = (char) ('0' + value % 10);
value /= 10;
buffer[--l] = (char) ('0' + value % 10);
value /= 10;
buffer[--l] = (char) ('0' + value % 10);
value /= 10;
buffer[--l] = ' ';
buffer[--l] = (char) ('0' + value % 10);
value /= 10;
buffer[--l] = (char) ('0' + value % 10);
value /= 10;
buffer[--l] = (char) ('0' + value);
buffer[--l] = ' ';
}
return new String(buffer);
}
}
public class Study_Karatsuba_method_2 {
private final static Random rng = new Random();
public static void main(String[] args) {
int k, numberSize, tableSize;
long time1, time2, time3;
double temp, mean1, std1, mean2, std2;
double[] table1, table2;
BigNumber a, b, c;
Locale.setDefault(new Locale("en", "US"));
tableSize = 12;
table1 = new double[tableSize];
table2 = new double[tableSize];
numberSize = 99000;
while (109 < numberSize) {
mean1 = 0.0;
mean2 = 0.0;
for (k = 0; tableSize > k; ++k) {
a = new BigNumber(rng, numberSize);
time1 = System.nanoTime();
b = a.multiplyKaratsuba(a);
time2 = System.nanoTime();
c = a.squareKaratsuba();
time3 = System.nanoTime();
if (0 != c.compareTo(b)) {
System.out.println("ERROR");
}
temp = 1e-9 * (time2 - time1);
mean1 += temp;
table1[k] = temp;
temp = 1e-9 * (time3 - time2);
mean2 += temp;
table2[k] = temp;
}
mean1 /= tableSize;
mean2 /= tableSize;
std1 = 0.0;
std2 = 0.0;
for (k = 0; tableSize > k; ++k) {
temp = table1[k] - mean1;
std1 += temp * temp;
temp = table2[k] - mean2;
std2 += temp * temp;
}
std1 = Math.sqrt(std1 / tableSize / (tableSize - 1));
std2 = Math.sqrt(std2 / tableSize / (tableSize - 1));
System.out.println(String.format("%6d %12.9f %12.9f %12.9f %12.9f", numberSize, mean1, std1, mean2, std2));
numberSize -= numberSize / 8;
}
}
}