import java.util.Arrays; public class LongMultBinary { /** * A very basic arbitrary-precision integer class. It only handles * non-negative numbers and doesn't implement any arithmetic not necessary * for the task at hand. */ public static class MyLongNum implements Cloneable { /* * The actual bits of the integer, with the least significant place * first. The biggest native integer type of Java is the 64-bit long, * but since we need to be able to store the result of two digits * multiplied, we have to use the second biggest native type, the 32-bit * int. All numeric types are signed in Java, but we don't want to waste * the sign bit, so we need to take extra care while doing arithmetic to * ensure unsigned semantics. */ private int[] digits; /* * The number of digits actually used in the digits array. Since arrays * cannot be resized in Java, we are better off remembering the logical * size ourselves, instead of reallocating and copying every time we need to shrink. */ private int digitsUsed; @Override public MyLongNum clone() { try { MyLongNum clone = (MyLongNum) super.clone(); clone.digits = clone.digits.clone(); return clone; } catch (CloneNotSupportedException e) { throw new Error("Object.clone() threw exception", e); } } private void resize(int newLength) { if (digits.length < newLength) { digits = Arrays.copyOf(digits, newLength); } } private void adjustDigitsUsed() { while (digitsUsed > 0 && digits[digitsUsed - 1] == 0) { digitsUsed--; } } /** * "Short" multiplication by one digit. Used to convert strings to long numbers. */ public void multiply(int multiplier) { if (multiplier < 0) { throw new IllegalArgumentException( "Signed arithmetic isn't supported"); } resize(digitsUsed + 1); long temp = 0; for (int i = 0; i < digitsUsed; i++) { temp += (digits[i] & 0xFFFFFFFFL) * multiplier; digits[i] = (int) temp; // store the low 32 bits temp >>>= 32; } digits[digitsUsed] = (int) temp; digitsUsed++; adjustDigitsUsed(); } /** * "Short" addition (adding a one-digit number). Used to convert strings to long numbers. */ public void add(int addend) { if (addend < 0) { throw new IllegalArgumentException( "Signed arithmetic isn't supported"); } long temp = addend; for (int i = 0; i < digitsUsed && temp != 0; i++) { temp += (digits[i] & 0xFFFFFFFFL); digits[i] = (int) temp; // store the low 32 bits temp >>>= 32; } if (temp != 0) { resize(digitsUsed + 1); digits[digitsUsed] = (int) temp; digitsUsed++; } } /** * "Short" division (dividing by a one-digit number). Used to convert numbers to strings. * @param divisor The digit to divide by. * @return The remainder of the division. */ public int divide(int divisor) { if (divisor < 0) { throw new IllegalArgumentException( "Signed arithmetic isn't supported"); } int remainder = 0; for (int i = digitsUsed - 1; i >= 0; i--) { long twoDigits = (((long) remainder << 32) | (digits[i] & 0xFFFFFFFFL)); remainder = (int) (twoDigits % divisor); digits[i] = (int) (twoDigits / divisor); } adjustDigitsUsed(); return remainder; } public MyLongNum(String value) { // each of our 32-bit digits can store at least 9 decimal digit's worth this.digits = new int[value.length() / 9 + 1]; this.digitsUsed = 0; // To lower the number of bignum operations, handle nine digits at a time. for (int i = 0; i < value.length(); i+=9) { String chunk = value.substring(i, Math.min(i+9, value.length())); int multiplier = 1; int addend = 0; for (int j=0; j '9') { throw new IllegalArgumentException("Invalid digit " + c + " found in input"); } multiplier *= 10; addend *= 10; addend += c - '0'; } multiply(multiplier); add(addend); } } @Override public String toString() { if (digitsUsed == 0) { return "0"; } MyLongNum dummy = this.clone(); StringBuilder resultBuilder = new StringBuilder(digitsUsed * 9); while (dummy.digitsUsed > 0) { // To limit the number of bignum divisions, handle nine digits at a time. int decimalDigits = dummy.divide(1000000000); for (int i=0; i<9; i++) { resultBuilder.append((char) (decimalDigits % 10 + '0')); decimalDigits /= 10; } } // Trim any leading zeros we may have created. while (resultBuilder.charAt(resultBuilder.length()-1) == '0') { resultBuilder.deleteCharAt(resultBuilder.length()-1); } return resultBuilder.reverse().toString(); } /** * Long multiplication. */ public void multiply(MyLongNum multiplier) { MyLongNum left, right; // Make sure the shorter number is on the right-hand side to make things a bit more efficient. if (this.digitsUsed > multiplier.digitsUsed) { left = this; right = multiplier; } else { left = multiplier; right = this; } int[] newDigits = new int[left.digitsUsed + right.digitsUsed]; for (int rightPos = 0; rightPos < right.digitsUsed; rightPos++) { long rightDigit = right.digits[rightPos] & 0xFFFFFFFFL; long temp = 0; for (int leftPos = 0; leftPos < left.digitsUsed; leftPos++) { temp += (newDigits[leftPos + rightPos] & 0xFFFFFFFFL); temp += rightDigit * (left.digits[leftPos] & 0xFFFFFFFFL); newDigits[leftPos + rightPos] = (int) temp; temp >>>= 32; } // Roll forward any carry we may have. int destPos = rightPos + digitsUsed; while (temp != 0) { temp += (newDigits[destPos] & 0xFFFFFFFFL); newDigits[destPos] = (int) temp; temp >>>= 32; destPos++; } } this.digits = newDigits; this.digitsUsed = newDigits.length; adjustDigitsUsed(); } } public static void main(String[] args) { MyLongNum one = new MyLongNum("18446744073709551616"); MyLongNum two = one.clone(); one.multiply(two); System.out.println(one); } }