/*@  public normal_behavior
 @    ensures (\forall JMLEqualsEqualsPair pair;
 @                      theRelation.has(pair);
 @                      pair.keyEquals(dv) ==> \result.has(pair.value));
 @    ensures (\forall Object o; \result.has(o);
 @                  (\exists JMLEqualsEqualsPair pair;
 @                      theRelation.has(pair);
 @                      pair.keyEquals(dv) && pair.valueEquals(o)));
 @    ensures_redundantly !isDefinedAt(dv) ==> \result.isEmpty();
 @
 @ implies_that
 @    ensures \result != null && !\result.containsNull;
 @*/
 public /*@ non_null @*/ JMLEqualsSet elementImage(/*@ nullable @*/ Object dv) {
   JMLEqualsToEqualsRelationImageEnumerator imagePairEnum = this.imagePairs();
   JMLEqualsValuePair imagePair;
   while (imagePairEnum.hasMoreElements()) {
     imagePair = imagePairEnum.nextImagePair();
     if (imagePair.keyEquals(dv)) {
       // @ assume imagePair.value != null;
       // @ assume imagePair.value instanceof JMLEqualsSet;
       JMLEqualsSet res = (JMLEqualsSet) imagePair.value;
       // @ assume !res.containsNull;
       return res;
     }
   }
   return new JMLEqualsSet();
 }
  /*@  public normal_behavior
  @    requires dv != null && rv != null;
  @    requires int_size() < Integer.MAX_VALUE || elementImage(dv).has(rv);
  @    ensures \result.theRelation.equals(
  @           this.theRelation.insert(new JMLEqualsEqualsPair(dv, rv)));
  @*/
  public /*@ pure @*/ /*@ non_null @*/ JMLEqualsToEqualsRelation add(
      /*@ non_null @*/ Object dv, /*@ non_null @*/ Object rv)
      throws NullPointerException, IllegalStateException {
    if (rv == null) {
      throw new NullPointerException();
    }

    JMLValueSet newImagePairSet;
    JMLEqualsSet newDom;
    int newSize;
    JMLEqualsSet img;

    if (!domain_.has(dv)) {
      if (size_ == Integer.MAX_VALUE) {
        throw new IllegalStateException(TOO_BIG_TO_INSERT);
      }
      newDom = domain_.insert(dv);
      newSize = size_ + 1;
      img = new JMLEqualsSet(rv);
      newImagePairSet = imagePairSet_.insert(new JMLEqualsValuePair(dv, img));
    } else {
      newImagePairSet = new JMLValueSet();
      newDom = domain_;
      newSize = 0;

      JMLEqualsToEqualsRelationImageEnumerator imagePairEnum = this.imagePairs();
      JMLEqualsValuePair imagePair;
      while (imagePairEnum.hasMoreElements()) {
        imagePair = imagePairEnum.nextImagePair();
        // @ assume imagePair.value != null;
        // @ assume imagePair.value instanceof JMLEqualsSet;
        img = (JMLEqualsSet) imagePair.value;
        if (imagePair.keyEquals(dv)) {
          img = img.insert(rv);
        }
        int size_inc = img.int_size();
        if (newSize <= Integer.MAX_VALUE - size_inc) {
          newSize = newSize + size_inc;
        } else {
          throw new IllegalStateException(TOO_BIG_TO_INSERT);
        }
        newImagePairSet = newImagePairSet.insert(new JMLEqualsValuePair(imagePair.key, img));
      }
    }
    return new JMLEqualsToEqualsRelation(newImagePairSet, newDom, newSize);
  }
  /*@  public normal_behavior
  @    ensures \result != null
  @         && (\forall Object val; domain().has(val);
  @             (\forall Object r; r != null;
  @                   (elementImage(val).has(r)
  @                      <==> \result.theRelation
  @                            .has(new JMLEqualsEqualsPair(val,r))
  @                          && !val.equals(dv))));
  @ implies_that
  @   public normal_behavior
  @    requires dv == null;
  @    ensures \result != null && \result.equals(this);
  @*/
  public /*@ non_null @*/ JMLEqualsToEqualsRelation removeFromDomain(/*@ nullable @*/ Object dv) {
    if (!domain_.has(dv)) {
      return (this);
    }

    JMLValueSet newImagePairSet = new JMLValueSet();
    JMLEqualsSet newDom = domain_.remove(dv);
    int newSize = 0;

    JMLEqualsToEqualsRelationImageEnumerator imagePairEnum = this.imagePairs();
    JMLEqualsValuePair imagePair;
    while (imagePairEnum.hasMoreElements()) {
      imagePair = imagePairEnum.nextImagePair();
      // @ assume imagePair.value != null;
      // @ assume imagePair.value instanceof JMLEqualsSet;
      if (!imagePair.keyEquals(dv)) {
        newImagePairSet = newImagePairSet.insert(imagePair);
        JMLEqualsSet img = (JMLEqualsSet) imagePair.value;
        newSize = newSize + img.int_size();
      }
    }
    return new JMLEqualsToEqualsRelation(newImagePairSet, newDom, newSize);
  } // @ nowarn Exception;
  /*@  public normal_behavior
  @    requires dv != null && rv != null;
  @    ensures \result.theRelation.equals(
  @                theRelation.remove(new JMLEqualsEqualsPair(dv, rv)));
  @   also
  @    requires dv == null || rv == null;
  @    ensures \result != null && \result.equals(this);
  @*/
  public /*@ non_null @*/ JMLEqualsToEqualsRelation remove(Object dv, Object rv) {
    if (!domain_.has(dv)) {
      return (this);
    }
    // @ assume dv != null;

    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;
      int imgSize = img.int_size();
      if (imagePair.keyEquals(dv)) {
        img = img.remove(rv);
        imgSize = img.int_size();
        if (imgSize > 0) {
          newImagePairSet = newImagePairSet.insert(new JMLEqualsValuePair(dv, img));
          newSize = newSize + imgSize;
        } else {
          // @ assert imgSize == 0;
          newDom = newDom.remove(dv);
        }
      } else {
        newImagePairSet = newImagePairSet.insert(imagePair);
        newSize = newSize + imgSize;
      }
    }
    return new JMLEqualsToEqualsRelation(newImagePairSet, newDom, newSize);
  } // @ nowarn Exception;