package net.ranides.assira.math;

import java.math.BigInteger;

/**
 * This abstract class is, obviously, base class for all mutable numbers
 */
public abstract class MutableNumber extends Number {

    /**
     * Mutable operation: this = value
     *
     * @param value value
     */
    public abstract void set(Number value);

    /**
     * Mutable operation: this++
     */
    public abstract void increment();

    /**
     * Mutable operation: this--
     */
    public abstract void decrement();

    /**
     * Mutable operation: this += value
     *
     * @param value value
     */
    public abstract void add(Number value);

    /**
     * Mutable operation: this -= value
     *
     * @param value value
     */
    public abstract void subtract(Number value);

    /**
     * Mutable operation: this /= value
     *
     * @param value value
     */
    public abstract void divide(Number value);

    /**
     * Mutable operation: this *= value
     *
     * @param value value
     */
    public abstract void multiply(Number value);

    /**
     * Mutable operation: this = pow(this, value)
     *
     * @param value value
     */
    public abstract void pow(Number value);

    /**
     * Mutable operation: this = -this
     */
    public abstract void negate();

    /**
     * Mutable operation: this = abs(this)
     */
    public abstract void abs();

    /**
     * Creates new MutableInteger
     *
     * @param value value
     * @return MutableNumber
     */
    public static MutableInteger of(int value) {
        return new MutableInteger(value);
    }

    /**
     * Creates new MutableLong
     *
     * @param value value
     * @return MutableNumber
     */
    public static MutableLong of(long value) {
        return new MutableLong(value);
    }

    /**
     * Creates new MutableDouble
     *
     * @param value value
     * @return MutableNumber
     */
    public static MutableDouble of(double value) {
        return new MutableDouble(value);
    }

    /**
     * This class is, obviously, mutable integer
     */
    public static class MutableInteger extends MutableNumber implements Comparable<MutableInteger> {

        private static final long serialVersionUID = 2L;

        private int value;

        /**
         * Default constructor.
         * Factory method "MutableNumber.of" is preffered over calling constructors.
         *
         * @param value value
         */
        public MutableInteger(int value) {
            this.value = value;
        }

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

        @Override
        public long longValue() {
            return value;
        }

        @Override
        public float floatValue() {
            return value;
        }

        @Override
        public double doubleValue() {
            return value;
        }

        @Override
        public int compareTo(MutableInteger value) {
            return Integer.compare(this.value, value.value);
        }

        @Override
        public void set(Number value) {
            this.value = value.intValue();
        }

        /**
         * Mutable operation:  this = value
         *
         * @param value value
         */
        public void set(int value) {
            this.value = value;
        }

        @Override
        public void increment() {
            this.value++;
        }

        @Override
        public void decrement() {
            this.value--;
        }

        /**
         * Mutable operation: this++
         *
         * @return value before increment
         */
        public int getAndIncrement() {
            return this.value++;
        }

        /**
         * Mutable operation: ++this
         *
         * @return value atfer increment
         */
        public int incrementAndGet() {
            return ++this.value;
        }

        /**
         * Mutable operation: this--
         *
         * @return value before decrement
         */
        public int getAndDecrement() {
            return this.value--;
        }

        /**
         * Mutable operation: --this
         *
         * @return value after decrement
         */
        public int decrementAndGet() {
            return --this.value;
        }

        @Override
        public void add(Number value) {
            this.value += value.intValue();
        }

        @Override
        public void subtract(Number value) {
            this.value -= value.intValue();
        }

        @Override
        public void divide(Number value) {
            this.value /= value.intValue();
        }

        @Override
        public void multiply(Number value) {
            this.value *= value.intValue();
        }

        @Override
        public void pow(Number value) {
            this.value = BigInteger.valueOf(this.value).pow(value.intValue()).intValue();
        }

        @Override
        public void negate() {
            this.value = -this.value;
        }

        @Override
        public void abs() {
            this.value = Math.abs(this.value);
        }

    }

    /**
     * This class is, obviously, mutable long
     */
    public static class MutableLong extends MutableNumber implements Comparable<MutableLong> {

        private static final long serialVersionUID = 2L;

        private long value;

        /**
         * Default constructor.
         * Factory method "MutableNumber.of" is preffered over calling constructors.
         *
         * @param value value
         */
        public MutableLong(long value) {
            this.value = value;
        }

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

        @Override
        public long longValue() {
            return value;
        }

        @Override
        public float floatValue() {
            return value;
        }

        @Override
        public double doubleValue() {
            return value;
        }

        @Override
        public int compareTo(MutableLong value) {
            return Long.compare(this.value, value.value);
        }

        @Override
        public void set(Number value) {
            this.value = value.longValue();
        }

        /**
         * Mutable operation:  this = value
         *
         * @param value value
         */
        public void set(long value) {
            this.value = value;
        }

        @Override
        public void increment() {
            this.value++;
        }

        @Override
        public void decrement() {
            this.value--;
        }

        /**
         * Mutable operation: this++
         *
         * @return value before increment
         */
        public long getAndIncrement() {
            return this.value++;
        }

        /**
         * Mutable operation: ++this
         *
         * @return value atfer increment
         */
        public long incrementAndGet() {
            return ++this.value;
        }

        /**
         * Mutable operation: this--
         *
         * @return value before decrement
         */
        public long getAndDecrement() {
            return this.value--;
        }

        /**
         * Mutable operation: --this
         *
         * @return value after decrement
         */
        public long decrementAndGet() {
            return --this.value;
        }

        @Override
        public void add(Number value) {
            this.value += value.longValue();
        }

        @Override
        public void subtract(Number value) {
            this.value -= value.longValue();
        }

        @Override
        public void divide(Number value) {
            this.value /= value.longValue();
        }

        @Override
        public void multiply(Number value) {
            this.value *= value.longValue();
        }

        @Override
        public void pow(Number value) {
            this.value = BigInteger.valueOf(this.value).pow(value.intValue()).longValue();
        }

        @Override
        public void negate() {
            this.value = -this.value;
        }

        @Override
        public void abs() {
            this.value = Math.abs(this.value);
        }

    }

    /**
     * This class is, obviously, mutable double
     */
    public static class MutableDouble extends MutableNumber implements Comparable<MutableDouble> {

        private static final long serialVersionUID = 2L;

        private double value;

        /**
         * Default constructor.
         * Factory method "MutableNumber.of" is preffered over calling constructors.
         *
         * @param value value
         */
        public MutableDouble(double value) {
            this.value = value;
        }

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

        @Override
        public long longValue() {
            return (long)value;
        }

        @Override
        public float floatValue() {
            return (float)value;
        }

        @Override
        public double doubleValue() {
            return value;
        }

        @Override
        public int compareTo(MutableDouble value) {
            return Double.compare(this.value, value.value);
        }

        @Override
        public void set(Number value) {
            this.value = value.doubleValue();
        }

        /**
         * Mutable operation:  this = value
         *
         * @param value value
         */
        public void set(double value) {
            this.value = value;
        }

        @Override
        public void increment() {
            this.value++;
        }

        @Override
        public void decrement() {
            this.value--;
        }

        /**
         * Mutable operation: this++
         *
         * @return value before increment
         */
        public double getAndIncrement() {
            return this.value++;
        }

        /**
         * Mutable operation: ++this
         *
         * @return value atfer increment
         */
        public double incrementAndGet() {
            return ++this.value;
        }

        /**
         * Mutable operation: this--
         *
         * @return value before decrement
         */
        public double getAndDecrement() {
            return this.value--;
        }

        /**
         * Mutable operation: --this
         *
         * @return value after decrement
         */
        public double decrementAndGet() {
            return --this.value;
        }

        @Override
        public void add(Number value) {
            this.value += value.doubleValue();
        }

        @Override
        public void subtract(Number value) {
            this.value -= value.doubleValue();
        }

        @Override
        public void divide(Number value) {
            this.value /= value.doubleValue();
        }

        @Override
        public void multiply(Number value) {
            this.value *= value.doubleValue();
        }

        @Override
        public void pow(Number value) {
            this.value = Math.pow(this.value, value.doubleValue());
        }

        @Override
        public void negate() {
            this.value = -this.value;
        }

        @Override
        public void abs() {
            this.value = Math.abs(this.value);
        }

    }

}
