package haskell.prelude;

import haskell.lang.Class;

/**
 * class (Eq a, Show a) => Num a
 */
public interface Num<A extends Num<A>>
        extends Class<A>,
                Eq<A> {

    /**
     * this + x
     */
    A _plus_(A x);

    /**
     * this - x
     */
    A _minus_(A x);

    /**
     * this * x
     */
    A _multiply_(A x);

    abstract class Support<A extends Num<A>>
            extends Eq.Support<A> {

        protected Support(final java.lang.Class<A> a) {
            super(a);
        }

        // Minimal complete definition:
        //     All, except negate or (-)

        public abstract A zero();

        /**
         * (+) :: a -> a -> a
         */
        public final Operator<A, A, A> plus() {
            return new Operator<A, A, A>("+", this, a, "plus");
        }

        /**
         * x + y
         */
        public abstract A plus(A x, A y);

        /**
         * (-) :: a -> a -> a
         */
        public final Operator<A, A, A> minus() {
            return new Operator<A, A, A>("-", this, a, "minus");
        }

        /**
         * x - y = x + negate y
         */
        public A minus(final A x, final A y) {
            return plus(x, negate(y));
        }

        /**
         * (*) :: a -> a -> a
         */
        public final Operator<A, A, A> multiply() {
            return new Operator<A, A, A>("*", this, a, "multiply");
        }

        /**
         * x * y
         */
        public abstract A multiply(A x, A y);

        /**
         * negate :: a -> a
         */
        public final Function<A, A> negate() {
            return new Function<A, A>(this, a, "negate");
        }

        /**
         * negate x = 0 - x
         */
        public A negate(final A x) {
            return minus(zero(), x);
        }

        /**
         * abs :: a -> a
         */

        /**
         * signum :: a -> a
         */

        /**
         * fromInteger :: Integer -> a
         */

        /**
         * subtract :: (Num a) => a -> a -> a
         * subtract = flip (-)
         */
        public final Function2<A, A, A> subtract() {
            return new Function2<A, A, A>("subtract") {
                @Override
                public A apply(final A x, final A y) {
                    return minus(y, x);
                }
            };
        }

        public final Function<A, A> subtract(final A x) {
            return subtract().apply(x);
        }

    }

}
