public static IList closureStar(IList rel1) {
    Type resultType = rel1.getType().closure();
    // an exception will have been thrown if the type is not acceptable

    IListWriter reflex = List.createListWriter(resultType.getElementType());

    for (IValue e : carrier(rel1)) {
      reflex.insert(Tuple.newTuple(new IValue[] {e, e}));
    }

    return closure(rel1).concat(reflex.done());
  }
  public static IList domain(IList rel1) {
    Type lrelType = rel1.getType();
    IListWriter w = List.createListWriter(lrelType.getFieldType(0));
    HashSet<IValue> cache = new HashSet<>();

    for (IValue elem : rel1) {
      ITuple tuple = (ITuple) elem;
      IValue e = tuple.get(0);
      if (!cache.contains(e)) {
        cache.add(e);
        w.append(e);
      }
    }
    return w.done();
  }
  public static IList carrier(IList rel1) {
    Type newType = rel1.getType().carrier();
    IListWriter w = List.createListWriter(newType.getElementType());
    HashSet<IValue> cache = new HashSet<>();

    for (IValue v : rel1) {
      ITuple t = (ITuple) v;
      for (IValue e : t) {
        if (!cache.contains(e)) {
          cache.add(e);
          w.append(e);
        }
      }
    }

    return w.done();
  }
  public static IList closure(IList rel1) {
    Type resultType = rel1.getType().closure(); // will throw exception if not binary and reflexive
    IList tmp = rel1;

    int prevCount = 0;

    ShareableValuesHashSet addedTuples = new ShareableValuesHashSet();
    while (prevCount != tmp.length()) {
      prevCount = tmp.length();
      IList tcomp = compose(tmp, tmp);
      IListWriter w = List.createListWriter(resultType.getElementType());
      for (IValue t1 : tcomp) {
        if (!tmp.contains(t1)) {
          if (!addedTuples.contains(t1)) {
            addedTuples.add(t1);
            w.append(t1);
          }
        }
      }
      tmp = tmp.concat(w.done());
      addedTuples.clear();
    }
    return tmp;
  }