/*@ also
  @  public normal_behavior
  @    requires obj != null && obj instanceof JMLEqualsToEqualsRelation;
  @    ensures \result ==
  @            this.theRelation.equals(
  @                    ((JMLEqualsToEqualsRelation)obj).theRelation);
  @ also
  @  public normal_behavior
  @    requires obj == null
  @          || !(obj instanceof JMLEqualsToEqualsRelation);
  @    ensures !\result;
  @*/
  public boolean equals(/*@ nullable @*/ Object obj) {
    if (obj == null || !(obj instanceof JMLEqualsToEqualsRelation)) {
      return false;
    }

    JMLEqualsToEqualsRelation rel = (JMLEqualsToEqualsRelation) obj;

    if (size_ != rel.int_size()) {
      return false;
    }

    JMLEqualsToEqualsRelationImageEnumerator imagePairEnum = this.imagePairs();
    JMLEqualsValuePair imagePair;
    JMLEqualsSet img;
    while (imagePairEnum.hasMoreElements()) {
      imagePair = imagePairEnum.nextImagePair();
      // @ assume imagePair.value != null;
      // @ assume imagePair.value instanceof JMLEqualsSet;
      img = (JMLEqualsSet) imagePair.value;
      if (!img.equals(rel.elementImage(imagePair.key))) {
        return false;
      }
    }
    return true;
  }
 /*@  public normal_behavior
 @    ensures (\forall JMLEqualsEqualsPair pair; ;
 @                 \result.theRelation.has(pair)
 @                    == elementImage(pair.value).has(pair.key));
 @*/
 public /*@ non_null @*/ JMLEqualsToEqualsRelation inverse() {
   JMLEqualsToEqualsRelation invRel = new JMLEqualsToEqualsRelation();
   JMLEqualsToEqualsRelationEnumerator assocEnum = this.associations();
   JMLEqualsEqualsPair pair;
   while (assocEnum.hasMoreElements()) {
     pair = assocEnum.nextPair();
     invRel = invRel.add(pair.value, pair.key);
   }
   return invRel;
 } // @ nowarn Exception;
  /*@  public normal_behavior
  @    requires othRel != null;
  @    requires int_size()
  @             < Integer.MAX_VALUE - othRel.difference(this).int_size();
  @    ensures \result.theRelation.equals(
  @                    this.theRelation.union(othRel.theRelation));
  @*/
  public /*@ non_null @*/ JMLEqualsToEqualsRelation union(
      /*@ non_null @*/ JMLEqualsToEqualsRelation othRel) throws IllegalStateException {
    JMLValueSet newImagePairSet = new JMLValueSet();
    JMLEqualsSet newDom = domain_;
    int newSize = 0;

    JMLEqualsToEqualsRelationImageEnumerator imagePairEnum = this.imagePairs();
    JMLEqualsValuePair imagePair;
    JMLEqualsSet img;
    while (imagePairEnum.hasMoreElements()) {
      imagePair = imagePairEnum.nextImagePair();
      // @ assume imagePair.value != null;
      // @ assume imagePair.value instanceof JMLEqualsSet;
      img = (JMLEqualsSet) imagePair.value;
      img = img.union(othRel.elementImage(imagePair.key));
      newImagePairSet = newImagePairSet.insert(new JMLEqualsValuePair(imagePair.key, img));
      int size_inc = img.int_size();
      if (newSize <= Integer.MAX_VALUE - size_inc) {
        newSize = newSize + size_inc;
      } else {
        throw new IllegalStateException(TOO_BIG_TO_UNION);
      }
    }
    JMLEqualsSet diffDom = othRel.domain().difference(this.domain_);
    imagePairEnum = othRel.imagePairs();
    while (imagePairEnum.hasMoreElements()) {
      imagePair = imagePairEnum.nextImagePair();
      // @ assume imagePair.value != null;
      // @ assume imagePair.value instanceof JMLEqualsSet;
      if (diffDom.has(imagePair.key)) {
        newImagePairSet = newImagePairSet.insert(imagePair);
        newDom = newDom.insert(imagePair.key);
        // @ assume imagePair.value != null;
        // @ assume imagePair.value instanceof JMLEqualsSet;
        img = (JMLEqualsSet) imagePair.value;
        int size_inc = img.int_size();
        if (newSize <= Integer.MAX_VALUE - size_inc) {
          newSize = newSize + size_inc;
        } else {
          throw new IllegalStateException(TOO_BIG_TO_UNION);
        }
      }
    }
    return new JMLEqualsToEqualsRelation(newImagePairSet, newDom, newSize);
  }
  /*@  public normal_behavior
  @    requires othRel != null;
  @    ensures \result.theRelation.equals(
  @                    this.theRelation.difference(othRel.theRelation));
  @*/
  public /*@ non_null @*/ JMLEqualsToEqualsRelation difference(
      /*@ non_null @*/ JMLEqualsToEqualsRelation othRel) {
    JMLValueSet newImagePairSet = new JMLValueSet();
    JMLEqualsSet newDom = new JMLEqualsSet();
    int newSize = 0;

    JMLEqualsToEqualsRelationImageEnumerator imagePairEnum = this.imagePairs();
    JMLEqualsValuePair imagePair;
    JMLEqualsSet img;
    while (imagePairEnum.hasMoreElements()) {
      imagePair = imagePairEnum.nextImagePair();
      // @ assume imagePair.value != null;
      // @ assume imagePair.value instanceof JMLEqualsSet;
      img = (JMLEqualsSet) imagePair.value;
      img = img.difference(othRel.elementImage(imagePair.key));
      if (!img.isEmpty()) {
        newImagePairSet = newImagePairSet.insert(new JMLEqualsValuePair(imagePair.key, img));
        newDom = newDom.insert(imagePair.key);
        newSize = newSize + img.int_size();
      }
    }
    return new JMLEqualsToEqualsRelation(newImagePairSet, newDom, newSize);
  } // @ nowarn Exception;