I have to create my own implementation of the BigInteger class called GrosseZahl (German for “BigNumber”). I already have implemented these methods:
- init()Converts an integer or string to an integer array.
- less(GrosseZahl value)Returns true, if this number is less than value.
- add(GrosseZahl summand)
- mult(GrosseZahl factor)
- toString()
- equals(Object obj)
The last method I have to implement is fib() that calculates the Fibonacci number of this GrosseZahl. I have to use a helper method to calculate it recursively.
I ended up with the following code:
public GrosseZahl fib() {
    GrosseZahl f0 = new GrosseZahl(1);
    GrosseZahl f1 = new GrosseZahl(1);
    GrosseZahl counter = new GrosseZahl(1);
    return this.calcFib(f0, f1, counter);
}
private GrosseZahl calcFib(GrosseZahl f0, GrosseZahl f1, GrosseZahl counter) {
    GrosseZahl f2;
    if (this.equals(counter))
        return f1;
    counter = counter.add(new GrosseZahl(1));
    f2 = f0.add(f1);
    f0 = f1;
    return calcFib(f1, f2, counter);
}
When I test the class with the following test class, I get a stack overflow error.
package java2;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
public class GrosseZahl_Test {
    GrosseZahl i0;
    GrosseZahl s0;
    GrosseZahl i1;
    GrosseZahl s1;
    GrosseZahl i55;
    GrosseZahl s55;
    GrosseZahl i60;
    GrosseZahl s60;
    GrosseZahl i99;
    GrosseZahl s99;
    GrosseZahl i100;
    GrosseZahl s100;
    GrosseZahl i12345;
    GrosseZahl s12345;
    GrosseZahl i47340;
    GrosseZahl s12345678901234567890;
    GrosseZahl s10345678901234567891;
    @Before
    public void setUp() throws Exception {
        i0 = new GrosseZahl(0);
        s0 = new GrosseZahl("0");
        i1 = new GrosseZahl(1);
        s1 = new GrosseZahl("1");
        i55 = new GrosseZahl(55);
        s55 = new GrosseZahl("55");
        i60 = new GrosseZahl(60);
        s60 = new GrosseZahl("60");
        i99 = new GrosseZahl(99);
        s99 = new GrosseZahl("99");
        i100 = new GrosseZahl(100);
        s100 = new GrosseZahl("100");
        i12345 = new GrosseZahl(12345);
        s12345 = new GrosseZahl("12345");
        i47340 = new GrosseZahl(47340);
        s12345678901234567890 = new GrosseZahl("12345678901234567890");
        s10345678901234567891 = new GrosseZahl("10345678901234567891");
    }
    /* ... */
    @Test
    public void testFib() {
        assertEquals("1", new GrosseZahl(0).fib().toString());
        assertEquals("1", new GrosseZahl(1).fib().toString());
        assertEquals("2", new GrosseZahl(2).fib().toString());
        assertEquals("3", new GrosseZahl(3).fib().toString());
        assertEquals("5", new GrosseZahl(4).fib().toString());
        assertEquals("8", new GrosseZahl(5).fib().toString());
        assertEquals("89", new GrosseZahl(10).fib().toString());
        assertEquals("1346269", new GrosseZahl(30).fib().toString());
    }
}
Stack overflow error message:
java.lang.StackOverflowError
    at java.util.regex.Pattern.sequence(Pattern.java:2134)
    at java.util.regex.Pattern.expr(Pattern.java:1996)
    at java.util.regex.Pattern.group0(Pattern.java:2827)
    at java.util.regex.Pattern.sequence(Pattern.java:2051)
    at java.util.regex.Pattern.expr(Pattern.java:1996)
    at java.util.regex.Pattern.compile(Pattern.java:1696)
    at java.util.regex.Pattern.<init>(Pattern.java:1351)
    at java.util.regex.Pattern.compile(Pattern.java:1028)
    at java.lang.String.replaceFirst(String.java:2166)
    at java2.GrosseZahl.init(GrosseZahl.java:38)
    at java2.GrosseZahl.<init>(GrosseZahl.java:25)
    at java2.GrosseZahl.add(GrosseZahl.java:112)
    at java2.GrosseZahl.calcFib(GrosseZahl.java:158)
    at java2.GrosseZahl.calcFib(GrosseZahl.java:163)
    at java2.GrosseZahl.calcFib(GrosseZahl.java:163)
    at java2.GrosseZahl.calcFib(GrosseZahl.java:163)
    /* ... */
Update: I’ve done some research and found out that this is always 0 in the calcFib(...) method, when I run the test (GrosseZahl_Test). So it will never equals the counter and so the if statement will never be true. But why this is always 0? You can test it yourself if you insert System.out.println(this) above the if statement and run the test:
private GrosseZahl calcFib(GrosseZahl f0, GrosseZahl f1, GrosseZahl counter) {
    GrosseZahl f2;
    System.out.println(this);
    if (this.equals(counter))
        return f1;
    counter = counter.add(new GrosseZahl(1));
    f2 = f0.add(f1);
    f0 = f1;
    return calcFib(f1, f2, counter);
}
If I use the less() method instead of the equals(), it does work:
public GrosseZahl fib() {
    GrosseZahl f0 = GrosseZahl.ONE;
    GrosseZahl f1 = GrosseZahl.ONE;
    GrosseZahl counter = new GrosseZahl(2);
    return this.calcFib(f0, f1, counter);
}
private GrosseZahl calcFib(GrosseZahl f0, GrosseZahl f1, GrosseZahl counter) {
    GrosseZahl f2;
    System.out.println(this);
    if (this.less(counter))
        return f1;
    counter = counter.add(GrosseZahl.ONE);
    f2 = f0.add(f1);
    f0 = f1;
    return calcFib(f1, f2, counter);
}
This is all code of the class GrosseZahl:
package java2;
import java.util.Arrays;
public class GrosseZahl {
    private int[] digits;
    private int length;
    private String value;
    public GrosseZahl(int value) {
        this.value = Integer.toString(value);
        this.init();
    }
    public GrosseZahl(String value) {
        this.value = value;
        this.init();
    }
    private void init() throws NumberFormatException {
        String[] digits;
        String value = this.value;
        if (value.charAt(0) == '-')
            throw new NumberFormatException("Number must be non-negative.");
        if (!value.matches("\\d+"))
            throw new NumberFormatException("Number must only contain digits.");
        value = value.replaceFirst("^0+(?!$)", "");
        digits = value.split("");
        this.length = digits.length;
        this.digits = new int[this.length];
        for (int i = 0; i < this.length; i++) {
            this.digits[i] = Integer.parseInt(digits[i]);
        }
    }
    public boolean less(GrosseZahl value) {
        if (this.length < value.length)
            return true;
        if (this.length == value.length)
            for (int i = 0; i < this.length; i++) {
                if (this.digits[i] < value.digits[i])
                    return true;
                if (this.digits[i] > value.digits[i])
                    return false;
            }
        return false;
    }
    public GrosseZahl add(GrosseZahl summand) {
        GrosseZahl a;
        GrosseZahl b;
        GrosseZahl result;
        StringBuilder number;
        int carry;
        int offset;
        int sum;
        if (!this.less(summand)) {
            a = this;
            b = summand;
        } else {
            a = summand;
            b = this;
        }
        offset = a.length - b.length;
        carry = 0;
        number = new StringBuilder();
        for (int i = a.length - 1; i >= 0; i--) {
            sum = a.digits[i] + carry;
            if (i >= offset)
                sum += b.digits[i - offset];
            if (sum > 9)
                carry = 1;
            else
                carry = 0;
            /*
             * We append the digit because it uses less memory than prepending.
             */
            number.append(sum % 10);
        }
        /*
         * If carry is still one we need to append an one.
         */
        if (carry == 1) {
            number.append(1);
        }
        /*
         * Since we’ve appended the numbers we need to reverse the order.
         */
        result = new GrosseZahl(number.reverse().toString());
        return result;
    }
    public GrosseZahl mult(GrosseZahl factor) {
        GrosseZahl a;
        GrosseZahl b;
        GrosseZahl result;
        a = this;
        b = factor;
        result = new GrosseZahl(0);
        for (int i = 0; i < a.length; i++)
            for (int j = 0; j < b.length; j++) {
                int zeroes = a.length - i + b.length - j - 2;
                int c = a.digits[i] * b.digits[j];
                StringBuilder sum = new StringBuilder();
                sum.append(c);
                for (int k = 0; k < zeroes; k++)
                    sum.append(0);
                result = result.add(new GrosseZahl(sum.toString()));
            }
        return result;
    }
    public GrosseZahl fib() {
        GrosseZahl f0 = new GrosseZahl(1);
        GrosseZahl f1 = new GrosseZahl(1);
        GrosseZahl counter = new GrosseZahl(1);
        return this.calcFib(f0, f1, counter);
    }
    private GrosseZahl calcFib(GrosseZahl f0, GrosseZahl f1, GrosseZahl counter) {
        GrosseZahl f2;
        if (this.equals(counter))
            return f1;
        counter = counter.add(new GrosseZahl(1));
        f2 = f0.add(f1);
        f0 = f1;
        return calcFib(f1, f2, counter);
    }
    @Override
    public String toString() {
        StringBuilder number = new StringBuilder();
        for (int digit : digits)
            number.append(digit);
        return number.toString();
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        GrosseZahl other = (GrosseZahl) obj;
        if (!Arrays.equals(digits, other.digits))
            return false;
        if (length != other.length)
            return false;
        return true;
    }
}
 
     
     
    