package org.nwapw.abacus.number;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * A number that uses a BigDecimal to store its value,
 * leading to infinite possible precision.
 */
public class PreciseNumber extends NumberInterface {

    /**
     * The number one.
     */
    public static final PreciseNumber ONE = new PreciseNumber(BigDecimal.ONE);
    /**
     * The number zero.
     */
    public static final PreciseNumber ZERO = new PreciseNumber(BigDecimal.ZERO);
    /**
     * The number ten.
     */
    public static final PreciseNumber TEN = new PreciseNumber(BigDecimal.TEN);

    /**
     * The value of the PreciseNumber.
     */
    BigDecimal value;

    /**
     * Constructs a precise number from the given string.
     *
     * @param string a string representation of the number meeting the same conditions
     *               as the BidDecimal(String) constructor.
     */
    public PreciseNumber(String string) {
        value = new BigDecimal(string);
    }

    /**
     * Constructs a precise number from the given BigDecimal.
     *
     * @param value a BigDecimal object representing the value of the number.
     */
    public PreciseNumber(BigDecimal value) {
        this.value = value;
    }

    @Override
    public int getMaxPrecision() {
        return 65;
    }

    @Override
    public NumberInterface multiplyInternal(NumberInterface multiplier) {
        return new PreciseNumber(this.value.multiply(((PreciseNumber) multiplier).value));
    }

    @Override
    public NumberInterface divideInternal(NumberInterface divisor) {
        return new PreciseNumber(value.divide(((PreciseNumber) divisor).value, this.getMaxPrecision(), RoundingMode.HALF_UP));
    }

    @Override
    public NumberInterface addInternal(NumberInterface summand) {
        return new PreciseNumber(value.add(((PreciseNumber) summand).value));
    }

    @Override
    public NumberInterface subtractInternal(NumberInterface subtrahend) {
        return new PreciseNumber(value.subtract(((PreciseNumber) subtrahend).value));
    }

    @Override
    public NumberInterface intPowInternal(int exponent) {
        if (exponent == 0) {
            return PreciseNumber.ONE;
        }
        boolean takeReciprocal = exponent < 0;
        exponent = Math.abs(exponent);
        NumberInterface power = this;
        for (int currentExponent = 1; currentExponent < exponent; currentExponent++) {
            power = power.multiply(this);
        }
        if (takeReciprocal) {
            power = PreciseNumber.ONE.divide(power);
        }
        return power;
    }

    @Override
    public int compareTo(NumberInterface number) {
        return value.compareTo(((PreciseNumber) number).value);
    }

    @Override
    public int signum() {
        return value.signum();
    }

    @Override
    public NumberInterface ceilingInternal() {
        String str = value.toPlainString();
        int decimalIndex = str.indexOf('.');
        if (decimalIndex != -1) {
            return this.floor().add(ONE);
        }
        return this;
    }

    @Override
    public NumberInterface floorInternal() {
        String str = value.toPlainString();
        int decimalIndex = str.indexOf('.');
        if (decimalIndex != -1) {
            return new PreciseNumber(str.substring(0, decimalIndex));
        }
        return this;
    }

    @Override
    public NumberInterface fractionalPartInternal() {
        String str = value.toPlainString();
        int decimalIndex = str.indexOf('.');
        if (decimalIndex != -1) {
            return new PreciseNumber(str.substring(decimalIndex + 1));
        }
        return ZERO;
    }

    @Override
    public int intValue() {
        return value.intValue();
    }

    @Override
    public NumberInterface negateInternal() {
        return new PreciseNumber(value.negate());
    }

    @Override
    public NumberInterface promoteToInternal(Class<? extends NumberInterface> toClass) {
        if (toClass == this.getClass()) {
            return this;
        }
        return null;
    }

    @Override
    public String toString() {
        BigDecimal rounded = value.setScale(getMaxPrecision() - 15, RoundingMode.HALF_UP);
        return rounded.stripTrailingZeros().toPlainString();
    }
}

@@ -3381,38 +3381,55 @@
  %7B%0D%0A            
-return
+NumberInterface floor =
  new PreciseNumb
@@ -3477,340 +3477,286 @@
     
-%7D%0D%0A        return this;%0D%0A    %7D%0D%0A%0D%0A    @Override%0D%0A    public NumberInterface fractionalPartInternal() %7B%0D%0A        String str = value.toPlainString();%0D%0A        int decimalIndex = str.indexOf('.');%0D%0A        if (decimalIndex != -1) %7B%0D%0A            return new PreciseNumber(str.substring(decimalIndex + 1));%0D%0A        %7D%0D%0A        return ZERO
+    if(signum() == -1)%7B%0D%0A                floor = floor.subtract(ONE);%0D%0A            %7D%0D%0A            return floor;%0D%0A        %7D%0D%0A        return this;%0D%0A    %7D%0D%0A%0D%0A    @Override%0D%0A    public NumberInterface fractionalPartInternal() %7B%0D%0A        return this.subtractInternal(floorInternal())
 ;%0D%0A 

Modify some functions in PreciseNumber for consistency.