package scotch.data.ratio; import static java.util.Arrays.asList; import static scotch.runtime.RuntimeSupport.applicable; import static scotch.runtime.RuntimeSupport.box; import static scotch.runtime.RuntimeSupport.callable; import static scotch.symbol.Value.Fixity.LEFT_INFIX; import static scotch.symbol.type.TypeDescriptors.fn; import static scotch.symbol.type.TypeDescriptors.sum; import static scotch.symbol.type.TypeDescriptors.var; import java.util.List; import java.util.Objects; import lombok.AllArgsConstructor; import lombok.Getter; import scotch.runtime.Applicable; import scotch.runtime.Callable; import scotch.symbol.DataType; import scotch.symbol.TypeParameter; import scotch.symbol.TypeParameters; import scotch.symbol.Value; import scotch.symbol.ValueType; import scotch.symbol.type.TypeDescriptor; @SuppressWarnings("unused") @DataType(memberName = "Ratio", parameters = { @TypeParameter(name = "a") }) public abstract class Ratio<A> { public static TypeDescriptor TYPE = sum("scotch.data.ratio.Ratio", asList(var("a"))); @TypeParameters public static List<TypeDescriptor> parameters() { return asList(var("a")); } @Value(memberName = "Ratio") public static <A> Applicable<A, Applicable<A, Ratio<A>>> ratio() { return applicable(numerator -> applicable(denominator -> callable(() -> new RatioData<>(numerator, denominator)))); } public static <A> Ratio<A> ratio(A numerator, A denominator) { return new RatioData<>(box(numerator), box(denominator)); } @ValueType(forMember = "Ratio") public static TypeDescriptor ratio$type() { return fn(var("a"), fn(var("a"), TYPE)); } @Value(memberName = "%", fixity = LEFT_INFIX, precedence = 7) public static <A> Applicable<A, Applicable<A, Ratio<A>>> ratioOperator() { return ratio(); } @ValueType(forMember = "%") public static TypeDescriptor ratioOperator$type() { return ratio$type(); } private Ratio() { // intentionally empty } @Override public abstract boolean equals(Object o); public abstract Callable<A> getDenominator(); public abstract Callable<A> getNumerator(); @Override public abstract int hashCode(); @Override public abstract String toString(); @AllArgsConstructor @Getter public static class RatioData<A> extends Ratio<A> { private final Callable<A> numerator; private final Callable<A> denominator; @Override public boolean equals(Object o) { if (o == this) { return true; } else if (o instanceof RatioData) { RatioData other = (RatioData) o; return Objects.equals(numerator.call(), other.numerator.call()) && Objects.equals(denominator.call(), other.denominator.call()); } else { return false; } } @Override public int hashCode() { return Objects.hash(numerator.call(), denominator.call()); } @Override public String toString() { return "Ratio { numerator = " + numerator.call() + ", denominator = " + denominator.call() + " }"; } } }