package haskell.prelude;

import haskell.lang.Class;

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

    /**
     * this < x
     */
    Bool _lt_(A x);

    /**
     * this <= x
     */
    Bool _le_(A x);

    /**
     * this > x
     */
    Bool _gt_(A x);

    /**
     * this >= x
     */
    Bool _ge_(A x);

    static abstract class Support<A extends Ord<A>>
            extends Eq.Support<A> {

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

        // Minimal complete definition:
        //     (<=) or compare
        // Using compare can be more efficient for complex types.

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

        /**
         * x < y = compare x y == LT
         */
        public Bool lt(final A x, final A y) {
            return compare(x, y)._eq_(Ordering.LT());
        }

        /**
         * (<=) :: a -> a -> Bool
         */
        public final Operator<A, A, Bool> le() {
            return new Operator<A, A, Bool>("<=", this, Bool.class, "le");
        }

        /**
         * x <= y = compare x y /= GT
         */
        public Bool le(final A x, final A y) {
            return compare(x, y)._ne_(Ordering.GT());
        }

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

        /**
         * x > y = compare x y == GT
         */
        public Bool gt(final A x, final A y) {
            return compare(x, y)._eq_(Ordering.GT());
        }

        /**
         * (>=) :: a -> a -> Bool
         */
        public final Operator<A, A, Bool> ge() {
            return new Operator<A, A, Bool>(">=", this, Bool.class, "ge");
        }

        /**
         * x >= y = compare(x, y) /= LT
         */
        public Bool ge(final A x, final A y) {
            return compare(x, y)._ne_(Ordering.LT());
        }

        /**
         * compare :: a -> a -> Ordering
         */
        public final Function2<A, A, Ordering> compare() {
            return new Function2<A, A, Ordering>(this, Ordering.class, "compare");
        }

        /**
         * compare x y
         *   | x == y    = EQ
         *   | x <= y    = LT
         *   | otherwise = GT
         */
        public Ordering compare(final A x, final A y) {
            if (eq(x, y).isTrue()) {
                return Ordering.EQ();
            } else if (le(x, y).isTrue()) {
                return Ordering.LT();
            } else {
                return Ordering.GT();
            }
        }

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

        /**
         * min x y
         *   | x <= y    = x
         *   | otherwise = y
         */
        public A min(final A x, final A y) {
            if (le(x, y).isTrue()) {
                return x;
            } else {
                return y;
            }
        }

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

        /**
         * max x y
         *   | x <= y    = y
         *   | otherwise = x
         */
        public A max(final A x, final A y) {
            if (le(x, y).isTrue()) {
                return y;
            } else {
                return x;
            }
        }

    }

}
