private static int compareLowerLower(ContinuousRealInterval a, ContinuousRealInterval other) {
   int ll;
   if (!a.boundLower()) {
     if (!other.boundLower()) ll = 0;
     else ll = -1;
   } else {
     if (!other.boundLower()) ll = 1;
     else {
       ll = OWLRealUtils.compare(a.getLower(), other.getLower());
       if (ll == 0) {
         if (a.inclusiveLower()) {
           if (!other.inclusiveLower()) ll = -1;
         } else if (other.inclusiveLower()) ll = 1;
       }
     }
   }
   return ll;
 }
  public List<ContinuousRealInterval> union(ContinuousRealInterval other) {

    switch (compare(other)) {
      case CONTAINS:
      case EQUALS:
      case FINISHED_BY:
      case STARTED_BY:
        return Collections.singletonList(this);

      case DURING:
      case FINISHES:
      case STARTS:
        return Collections.singletonList(other);

      case MEETS:
        return Collections.singletonList(
            new ContinuousRealInterval(
                getLower(), other.getUpper(), inclusiveLower(), other.inclusiveUpper()));

      case MET_BY:
        return Collections.singletonList(
            new ContinuousRealInterval(
                other.getLower(), getUpper(), other.inclusiveLower(), inclusiveUpper()));

      case OVERLAPPED_BY:
        return Collections.singletonList(
            new ContinuousRealInterval(
                other.getLower(), getUpper(), other.inclusiveLower(), inclusiveUpper()));

      case OVERLAPS:
        return Collections.singletonList(
            new ContinuousRealInterval(
                getLower(), other.getUpper(), inclusiveLower(), other.inclusiveUpper()));

      case PRECEDED_BY:
      case PRECEDES:
        return Arrays.asList(this, other);

      default:
        throw new IllegalStateException();
    }
  }
  private static IntervalRelations compare(ContinuousRealInterval a, ContinuousRealInterval b) {
    int ll = compareLowerLower(a, b);

    if (ll < 0) {
      int ul = compareUpperLower(a, b);
      if (ul < 0) return IntervalRelations.PRECEDES;
      else if (ul == 0) {
        if (a.inclusiveUpper()) {
          if (b.inclusiveLower()) return IntervalRelations.OVERLAPS;
          else return IntervalRelations.MEETS;
        } else if (b.inclusiveLower()) return IntervalRelations.MEETS;
        else return IntervalRelations.PRECEDES;
      } else {
        int uu = compareUpperUpper(a, b);
        if (uu < 0) return IntervalRelations.OVERLAPS;
        else if (uu == 0) return IntervalRelations.FINISHED_BY;
        else return IntervalRelations.CONTAINS;
      }
    } else if (ll == 0) {
      int uu = compareUpperUpper(a, b);
      if (uu < 0) return IntervalRelations.STARTS;
      else if (uu == 0) return IntervalRelations.EQUALS;
      else return IntervalRelations.STARTED_BY;
    } else {
      int lu = -compareUpperLower(b, a);
      if (lu < 0) {
        int uu = compareUpperUpper(a, b);
        if (uu < 0) return IntervalRelations.DURING;
        else if (uu == 0) return IntervalRelations.FINISHES;
        else return IntervalRelations.OVERLAPPED_BY;
      } else if (lu == 0) {
        if (b.inclusiveUpper()) {
          if (a.inclusiveLower()) return IntervalRelations.OVERLAPPED_BY;
          else return IntervalRelations.MET_BY;
        } else if (a.inclusiveLower()) return IntervalRelations.MET_BY;
        else return IntervalRelations.PRECEDED_BY;
      } else return IntervalRelations.PRECEDED_BY;
    }
  }
  public List<ContinuousRealInterval> remove(ContinuousRealInterval other) {

    ContinuousRealInterval before, after;
    switch (compare(other)) {
      case CONTAINS:
        before =
            new ContinuousRealInterval(
                this.getLower(), other.getLower(), inclusiveLower(), !other.inclusiveLower());
        after =
            new ContinuousRealInterval(
                other.getUpper(), this.getUpper(), !other.inclusiveUpper(), this.inclusiveUpper());
        break;

      case DURING:
      case EQUALS:
      case FINISHES:
      case STARTS:
        return Collections.emptyList();

      case MEETS:
        before =
            new ContinuousRealInterval(
                this.getLower(), this.getUpper(), this.inclusiveLower(), false);
        after = null;
        break;

      case MET_BY:
        before = null;
        after =
            new ContinuousRealInterval(
                this.getLower(), this.getUpper(), false, this.inclusiveUpper());
        break;

      case OVERLAPPED_BY:
      case STARTED_BY:
        before = null;
        after =
            new ContinuousRealInterval(
                other.getUpper(), this.getUpper(), !other.inclusiveUpper(), this.inclusiveUpper());
        break;

      case OVERLAPS:
      case FINISHED_BY:
        before =
            new ContinuousRealInterval(
                this.getLower(), other.getLower(), this.inclusiveLower(), !other.inclusiveLower());
        after = null;
        break;

      case PRECEDED_BY:
      case PRECEDES:
        return Collections.singletonList(this);

      default:
        throw new IllegalStateException();
    }

    List<ContinuousRealInterval> ret = new ArrayList<ContinuousRealInterval>();
    if (before != null) ret.add(before);
    if (after != null) ret.add(after);

    return ret;
  }
  public ContinuousRealInterval intersection(ContinuousRealInterval that) {
    Number lower, upper;
    boolean inclusiveUpper, inclusiveLower;

    switch (compare(that)) {
      case CONTAINS:
      case STARTED_BY:
        lower = that.getLower();
        inclusiveLower = that.inclusiveLower();
        upper = that.getUpper();
        inclusiveUpper = that.inclusiveUpper();
        break;

      case EQUALS:
        lower = this.getLower();
        inclusiveLower = this.inclusiveLower();
        upper = this.getUpper();
        inclusiveUpper = this.inclusiveUpper();
        break;

      case DURING:
      case STARTS:
        lower = this.getLower();
        inclusiveLower = this.inclusiveLower();
        upper = this.getUpper();
        inclusiveUpper = this.inclusiveUpper();
        break;

      case FINISHED_BY:
        lower = that.getLower();
        inclusiveLower = that.inclusiveLower();
        upper = that.getUpper();
        inclusiveUpper = (this.inclusiveUpper() && that.inclusiveUpper());
        break;

      case FINISHES:
        lower = this.getLower();
        inclusiveLower = this.inclusiveLower();
        upper = this.getUpper();
        inclusiveUpper = (this.inclusiveUpper() && that.inclusiveUpper());
        break;

      case MEETS:
      case MET_BY:
        return null;

      case OVERLAPPED_BY:
        lower = this.getLower();
        inclusiveLower = this.inclusiveLower();
        upper = that.getUpper();
        inclusiveUpper = that.inclusiveUpper();
        break;

      case OVERLAPS:
        lower = that.getLower();
        inclusiveLower = that.inclusiveLower();
        upper = this.getUpper();
        inclusiveUpper = this.inclusiveUpper();
        break;

      case PRECEDED_BY:
      case PRECEDES:
        return null;

      default:
        throw new IllegalStateException();
    }

    return new ContinuousRealInterval(lower, upper, inclusiveLower, inclusiveUpper);
  }