/** Remember to clean the is_new flag */
  @Override
  public void do_after_propagation() {
    for (HeapInsIntervalManager im : new_pts.values()) {
      im.flush();
    }

    new_pts = new HashMap<AllocNode, HeapInsIntervalManager>();
  }
  /** An efficient implementation of differential propagation. */
  @Override
  public void propagate(GeomPointsTo ptAnalyzer, IWorklist worklist) {
    int i, j;
    AllocNode obj;
    SegmentNode pts, pe, int_entry1[], int_entry2[];
    HeapInsIntervalManager him;
    HeapInsNode qn, objn;
    boolean added, has_new_edges;

    // We first build the new flow edges via the field dereferences
    if (complex_cons != null) {
      for (Map.Entry<AllocNode, HeapInsIntervalManager> entry : new_pts.entrySet()) {
        obj = entry.getKey();
        int_entry1 = entry.getValue().get_intervals();

        for (PlainConstraint pcons : complex_cons) {
          // Construct the two variables in assignment
          objn = (HeapInsNode) ptAnalyzer.findAndInsertInstanceField(obj, pcons.f);
          qn = (HeapInsNode) pcons.otherSide;

          for (i = 0; i < HeapInsIntervalManager.Divisions; ++i) {
            pts = int_entry1[i];
            while (pts != null && pts.is_new) {
              switch (pcons.type) {
                case GeomPointsTo.STORE_CONS:
                  // Store, qv -> pv.field
                  // pts.I2 may be zero, pts.L may be less than zero
                  if (qn.add_simple_constraint_3(
                      objn,
                      pcons.code == GeomPointsTo.ONE_TO_ONE ? pts.I1 : 0,
                      pts.I2,
                      pts.L < 0 ? -pts.L : pts.L)) worklist.push(qn);
                  break;

                case GeomPointsTo.LOAD_CONS:
                  // Load, pv.field -> qv
                  if (objn.add_simple_constraint_3(
                      qn,
                      pts.I2,
                      pcons.code == GeomPointsTo.ONE_TO_ONE ? pts.I1 : 0,
                      pts.L < 0 ? -pts.L : pts.L)) worklist.push(objn);
                  break;

                default:
                  throw new RuntimeException("Wrong Complex Constraint");
              }

              pts = pts.next;
            }
          }
        }
      }
    }

    for (Map.Entry<HeapInsNode, HeapInsIntervalManager> entry1 : flowto.entrySet()) {
      // Second get the flow-to intervals
      added = false;
      qn = entry1.getKey();
      him = entry1.getValue();
      int_entry2 = him.get_intervals();
      has_new_edges = him.isThereUnprocessedObject();
      Map<AllocNode, HeapInsIntervalManager> objs = (has_new_edges ? pt_objs : new_pts);

      for (Map.Entry<AllocNode, HeapInsIntervalManager> entry2 : objs.entrySet()) {
        // First get the points-to intervals
        obj = entry2.getKey();
        if (!ptAnalyzer.castNeverFails(obj.getType(), qn.getWrappedNode().getType())) continue;

        int_entry1 = entry2.getValue().get_intervals();

        // We pair up all the interval points-to tuples and interval flow edges
        for (i = 0; i < HeapInsIntervalManager.Divisions; ++i) {
          pts = int_entry1[i];
          while (pts != null) {
            if (!has_new_edges && !pts.is_new) break;

            for (j = 0; j < HeapInsIntervalManager.Divisions; ++j) {
              pe = int_entry2[j];
              while (pe != null) {
                if (pts.is_new || pe.is_new) {
                  // Propagate this object
                  if (add_new_points_to_tuple(pts, pe, obj, qn)) added = true;
                } else break;

                pe = pe.next;
              }
            }

            pts = pts.next;
          }
        }
      }

      if (added) worklist.push(qn);

      // Now, we clean the new edges if necessary
      if (has_new_edges) {
        him.flush();
      }
    }
  }