// Path: src/main/java/inference/interfaces/Substitutable.java
// public abstract class Substitutable<T extends Substitutable<T>> {
//     public T apply(Substitution s) {
//         T result = identity();
//         for (TVariable var : s.variables()) {
//             result = result.substitute(var, s.substituteOf(var));
//         }
//         return result;
//     }
//     abstract public HashSet<TVariable> ftv();
// 
//     abstract public T substitute(TVariable var, Type t);
// 
//     abstract public T identity();
// }
// 
// Path: src/main/java/types/TVariable.java
// public class TVariable extends Type {
//     private final String name;
//     private final static HashSet<TVariable> instantiated = new HashSet<>();
// 
//     private TVariable(String name) {
//         this.name = name;
//     }
// 
//     public String name() {
//         return name;
//     }
// 
//     public static TVariable variable(String name) {
//         TVariable var = new TVariable(name);
//         if(!instantiated.add(var)) System.out.println("Creation avoidable : TVariable(" + name + ")");
//         return var;
//     }
// 
//     public static List<TVariable> variables(String... names) {
//         List<TVariable> variables = new LinkedList<>();
//         for (String name : names) variables.add(new TVariable(name));
//         return variables;
//     }
// 
//     @Override
//     public Type substitute(TVariable var, Type type) {
//         return this.equals(var) ? type : this;
//     }
// 
//     @Override
//     public HashSet<TVariable> ftv() {
//         return new HashSet<>(Collections.singleton(this));
//     }
// 
//     @Override
//     protected Substitution unifyWith(TFunction fun) {
//         return bind(this, fun);
//     }
// 
//     @Override
//     protected Substitution unifyWith(TConstructor cons) {
//         return bind(this, cons);
//     }
// 
//     @Override
//     public Type instantiate(Substitution sub) {
//         return sub.variables().contains(this) ? sub.substituteOf(this) : this;
//     }
// 
//     @Override
//     protected Substitution unifyWith(TVariable var) {
//         return bind(this, var);
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) return true;
//         if (o == null || getClass() != o.getClass()) return false;
// 
//         TVariable tVariable = (TVariable) o;
// 
//         return name.equals(tVariable.name);
// 
//     }
// 
//     @Override
//     public int hashCode() {
//         return ("TVariable_" + name).hashCode();
//     }
// 
//     @Override
//     public String toString() {
//         return name();
//     }
// }
// 
// Path: src/main/java/types/Type.java
// public abstract class Type extends Substitutable<Type> implements Unifyable{
// 
//     @Override
//     public Type identity() {
//         return this;
//     }
// 
//     @Override
//     public final Substitution unifyWith(Type type) {
//         if(this.equals(type)) return new Substitution();
//         if(type instanceof TVariable) return unifyWith((TVariable) type);
//         if(type instanceof TFunction) return unifyWith((TFunction) type);
//         if(type instanceof TConstructor) return unifyWith((TConstructor) type);
//         throw new UnificationFailException(this, type);
//     }
// 
//     protected Substitution unifyWith(TVariable var) {
//         return bind(var, this);
//     }
// 
//     protected Substitution unifyWith(TFunction fun) {
//         throw new UnificationFailException(this, fun);
//     }
// 
//     protected Substitution unifyWith(TConstructor cons) {
//         throw new UnificationFailException(this, cons);
//     }
// 
//     public abstract Type instantiate(Substitution sub);
// }


import inference.interfaces.Substitutable;
import types.TVariable;
import types.Type;
import java.util.HashSet;
package inference;



/**
 * Created by valentin on 24/10/2016.
 */
public class Constraint extends Substitutable<Constraint> {
    private final Type left, right;

    public Constraint(Type left, Type right) {
        this.left = left;
        this.right = right;
    }

    public Type left() {
        return left;
    }

    public Type right() {
        return right;
    }

    @Override
// Path: src/main/java/inference/interfaces/Substitutable.java
// public abstract class Substitutable<T extends Substitutable<T>> {
//     public T apply(Substitution s) {
//         T result = identity();
//         for (TVariable var : s.variables()) {
//             result = result.substitute(var, s.substituteOf(var));
//         }
//         return result;
//     }
//     abstract public HashSet<TVariable> ftv();
// 
//     abstract public T substitute(TVariable var, Type t);
// 
//     abstract public T identity();
// }
// 
// Path: src/main/java/types/TVariable.java
// public class TVariable extends Type {
//     private final String name;
//     private final static HashSet<TVariable> instantiated = new HashSet<>();
// 
//     private TVariable(String name) {
//         this.name = name;
//     }
// 
//     public String name() {
//         return name;
//     }
// 
//     public static TVariable variable(String name) {
//         TVariable var = new TVariable(name);
//         if(!instantiated.add(var)) System.out.println("Creation avoidable : TVariable(" + name + ")");
//         return var;
//     }
// 
//     public static List<TVariable> variables(String... names) {
//         List<TVariable> variables = new LinkedList<>();
//         for (String name : names) variables.add(new TVariable(name));
//         return variables;
//     }
// 
//     @Override
//     public Type substitute(TVariable var, Type type) {
//         return this.equals(var) ? type : this;
//     }
// 
//     @Override
//     public HashSet<TVariable> ftv() {
//         return new HashSet<>(Collections.singleton(this));
//     }
// 
//     @Override
//     protected Substitution unifyWith(TFunction fun) {
//         return bind(this, fun);
//     }
// 
//     @Override
//     protected Substitution unifyWith(TConstructor cons) {
//         return bind(this, cons);
//     }
// 
//     @Override
//     public Type instantiate(Substitution sub) {
//         return sub.variables().contains(this) ? sub.substituteOf(this) : this;
//     }
// 
//     @Override
//     protected Substitution unifyWith(TVariable var) {
//         return bind(this, var);
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) return true;
//         if (o == null || getClass() != o.getClass()) return false;
// 
//         TVariable tVariable = (TVariable) o;
// 
//         return name.equals(tVariable.name);
// 
//     }
// 
//     @Override
//     public int hashCode() {
//         return ("TVariable_" + name).hashCode();
//     }
// 
//     @Override
//     public String toString() {
//         return name();
//     }
// }
// 
// Path: src/main/java/types/Type.java
// public abstract class Type extends Substitutable<Type> implements Unifyable{
// 
//     @Override
//     public Type identity() {
//         return this;
//     }
// 
//     @Override
//     public final Substitution unifyWith(Type type) {
//         if(this.equals(type)) return new Substitution();
//         if(type instanceof TVariable) return unifyWith((TVariable) type);
//         if(type instanceof TFunction) return unifyWith((TFunction) type);
//         if(type instanceof TConstructor) return unifyWith((TConstructor) type);
//         throw new UnificationFailException(this, type);
//     }
// 
//     protected Substitution unifyWith(TVariable var) {
//         return bind(var, this);
//     }
// 
//     protected Substitution unifyWith(TFunction fun) {
//         throw new UnificationFailException(this, fun);
//     }
// 
//     protected Substitution unifyWith(TConstructor cons) {
//         throw new UnificationFailException(this, cons);
//     }
// 
//     public abstract Type instantiate(Substitution sub);
// }


// Path: src/main/java/inference/Constraint.java
import inference.interfaces.Substitutable;
import types.TVariable;
import types.Type;
import java.util.HashSet;

package inference;



/**
 * Created by valentin on 24/10/2016.
 */
public class Constraint extends Substitutable<Constraint> {
    private final Type left, right;

    public Constraint(Type left, Type right) {
        this.left = left;
        this.right = right;
    }

    public Type left() {
        return left;
    }

    public Type right() {
        return right;
    }

    @Override
    public HashSet<TVariable> ftv() {