package scotch.data.ord; import static java.util.Arrays.asList; import static scotch.runtime.RuntimeSupport.applicable; import static scotch.runtime.RuntimeSupport.callable; import static scotch.runtime.RuntimeSupport.flatCallable; import static scotch.symbol.Value.Fixity.LEFT_INFIX; import static scotch.symbol.type.TypeDescriptors.fn; import static scotch.symbol.type.TypeDescriptors.var; import scotch.data.bool.Bool; import scotch.data.eq.Eq; import scotch.runtime.Applicable; import scotch.runtime.Callable; import scotch.symbol.TypeClass; import scotch.symbol.TypeParameter; import scotch.symbol.Value; import scotch.symbol.ValueType; import scotch.symbol.type.TypeDescriptor; @SuppressWarnings("unused") @TypeClass(memberName = "Ord", parameters = { @TypeParameter(name = "a", constraints = { "scotch.data.eq.Eq", }), }) public interface Ord<A> { TypeDescriptor a = var("a", asList("scotch.data.ord.Ord", "scotch.data.eq.Eq")); @Value(memberName = "compare") static <A> Applicable<Eq<A>, Applicable<Ord<A>, Applicable<A, Applicable<A, Ordering>>>> compare() { return applicable(eq -> applicable(ord -> applicable(left -> applicable(right -> flatCallable(() -> ord.call().compare(eq, left, right)))))); } @ValueType(forMember = "compare") static TypeDescriptor compare$type() { return fn(a, fn(a, Ordering.TYPE)); } @Value(memberName = ">", fixity = LEFT_INFIX, precedence = 4) static <A> Applicable<Eq<A>, Applicable<Ord<A>, Applicable<A, Applicable<A, Boolean>>>> greaterThan() { return applicable(eq -> applicable(ord -> applicable(left -> applicable(right -> flatCallable(() -> ord.call().greaterThan(eq, left, right)))))); } @ValueType(forMember = ">") static TypeDescriptor greaterThan$type() { return fn(a, fn(a, Bool.TYPE)); } @Value(memberName = ">=", fixity = LEFT_INFIX, precedence = 4) static <A> Applicable<Eq<A>, Applicable<Ord<A>, Applicable<A, Applicable<A, Boolean>>>> greaterThanEquals() { return applicable(eq -> applicable(ord -> applicable(left -> applicable(right -> flatCallable(() -> ord.call().greaterThanEquals(eq, left, right)))))); } @ValueType(forMember = ">=") static TypeDescriptor greaterThanEquals$type() { return fn(a, fn(a, Bool.TYPE)); } @Value(memberName = "<", fixity = LEFT_INFIX, precedence = 4) static <A> Applicable<Eq<A>, Applicable<Ord<A>, Applicable<A, Applicable<A, Boolean>>>> lessThan() { return applicable(eq -> applicable(ord -> applicable(left -> applicable(right -> flatCallable(() -> ord.call().lessThan(eq, left, right)))))); } @ValueType(forMember = "<") static TypeDescriptor lessThan$type() { return fn(a, fn(a, Bool.TYPE)); } @Value(memberName = "<=", fixity = LEFT_INFIX, precedence = 4) static <A> Applicable<Eq<A>, Applicable<Ord<A>, Applicable<A, Applicable<A, Boolean>>>> lessThanEquals() { return applicable(eq -> applicable(ord -> applicable(left -> applicable(right -> flatCallable(() -> ord.call().lessThanEquals(eq, left, right)))))); } @ValueType(forMember = "<=") static TypeDescriptor lessThanEquals$type() { return fn(a, fn(a, Bool.TYPE)); } @Value(memberName = "max") static <A> Applicable<Eq<A>, Applicable<Ord<A>, Applicable<A, Applicable<A, A>>>> max() { return applicable(eq -> applicable(ord -> applicable(left -> applicable(right -> flatCallable(() -> ord.call().max(eq, left, right)))))); } @ValueType(forMember = "max") static TypeDescriptor max$type() { return fn(a, fn(a, a)); } @Value(memberName = "min") static <A> Applicable<Eq<A>, Applicable<Ord<A>, Applicable<A, Applicable<A, A>>>> min() { return applicable(eq -> applicable(ord -> applicable(left -> applicable(right -> flatCallable(() -> ord.call().min(eq, left, right)))))); } @ValueType(forMember = "min") static TypeDescriptor min$type() { return fn(a, fn(a, a)); } default Callable<Ordering> compare(Callable<Eq<A>> eq, Callable<A> left, Callable<A> right) { return flatCallable(() -> { if (eq.call().eq(left, right).call()) { return Ordering.equalTo(); } else if (lessThanEquals(eq, left, right).call()) { return Ordering.lessThan(); } else { return Ordering.greaterThan(); } }); } default Callable<Boolean> greaterThan(Callable<Eq<A>> eq, Callable<A> left, Callable<A> right) { return callable(() -> compare(eq, left, right).call() == Ordering.greaterThan().call()); } default Callable<Boolean> greaterThanEquals(Callable<Eq<A>> eq, Callable<A> left, Callable<A> right) { return callable(() -> compare(eq, left, right).call() != Ordering.lessThan().call()); } default Callable<Boolean> lessThan(Callable<Eq<A>> eq, Callable<A> left, Callable<A> right) { return callable(() -> compare(eq, left, right).call() == Ordering.lessThan().call()); } default Callable<Boolean> lessThanEquals(Callable<Eq<A>> eq, Callable<A> left, Callable<A> right) { return callable(() -> compare(eq, left, right).call() != Ordering.greaterThan().call()); } default Callable<A> max(Callable<Eq<A>> eq, Callable<A> left, Callable<A> right) { return flatCallable(() -> lessThanEquals(eq, left, right).call() ? right : left); } default Callable<A> min(Callable<Eq<A>> eq, Callable<A> left, Callable<A> right) { return flatCallable(() -> lessThanEquals(eq, left, right).call() ? left : right); } }