// assumes input data fall within capacity of list
 private void updateNodeList(PositionNode node, int finalPos, List<QueryModelNode> list) {
   int initialPos = node.getPosition();
   QueryModelNode qNode = list.remove(initialPos);
   if (finalPos < list.size()) {
     list.add(finalPos, qNode);
   } else {
     list.add(qNode);
   }
 }
  // assumes g.l.b <= l.u.b.
  // iterates through pcj nodes in query from lowest position to
  // highest looking for a difference in index position greater than
  // one. Given the leftmost pair of nodes separated by two or more
  // spaces, the leftmost node in the pair is moved so that its final
  // position is one position to the left of the rightmost node. For
  // example, given nodes at index 1 and index 3, the node at index 1
  // is advanced to index 2. This method returns the suggested Move,
  // but does not actually perform the Move.
  private Move getNextMove() {

    Iterator<PositionNode> posIterator = pcjPosSet.iterator();
    PositionNode current;
    if (posIterator.hasNext()) {
      current = posIterator.next();
    } else {
      throw new IllegalStateException("PCJ has no nodes!");
    }
    PositionNode next;
    int pos1 = -1;
    int pos2 = -1;
    while (posIterator.hasNext()) {
      next = posIterator.next();
      pos1 = current.getPosition();
      pos2 = next.getPosition();
      // move nodes are not adjacent
      if (pos1 + 1 < pos2) {
        if (leastUpperBound > pos2) {
          return new Move(current, pos2 - 1);
        }
        // pos1 < leastUpperBound < pos2 b/c pos1 < leastUpperBound by
        // design
        else if (greatestLowerBound < pos1) {
          return new Move(next, pos1 + 1);
        }
        // move current to node after greatestLowerBound
        else {
          return new Move(current, greatestLowerBound);
        }
      }

      current = next;
    }

    return new Move();
  }
  // updates nodes in given TreeSet between node.getPosition() and position
  private void updatePositionNodeSet(PositionNode node, int position, TreeSet<PositionNode> set) {

    if (set.size() == 0) {
      return;
    }

    int oldPos = node.getPosition();
    int diff = position - oldPos;
    SortedSet<PositionNode> posNodes;
    boolean containsNode = false;

    if (diff == 0) {
      return;
    }

    // remove node before decrementing or incrementing to prevent overwriting
    if (set.contains(node)) {
      containsNode = true;
      set.remove(node);
    }

    if (diff > 0) {
      posNodes = set.subSet(node, false, new PositionNode(position), true);

      List<PositionNode> pNodeList = new ArrayList<>();
      for (PositionNode pos : posNodes) {
        pNodeList.add(pos);
      }
      // decrement posNodes
      for (PositionNode pos : pNodeList) {
        int p = pos.getPosition() - 1;
        updatePositionNode(pos, p, set);
      }
    } else {
      posNodes = set.subSet(new PositionNode(position), true, node, false);
      // create list to iterate in reverse order
      List<PositionNode> pNodeList = new ArrayList<>();
      for (PositionNode pos : posNodes) {
        pNodeList.add(pos);
      }
      // increment elements of TreeSet in reverse order so
      // that no collisions occur - PositionNodes are incremented
      // into slot created by removing node
      for (int i = pNodeList.size() - 1; i >= 0; i--) {
        PositionNode pNode = pNodeList.get(i);
        int p = pNode.getPosition() + 1;
        updatePositionNode(pNode, p, set);
      }
    }

    if (containsNode) {
      node.setPosition(position);
      set.add(node);
    }
  }
  // adds bound to either lowerBoundSet or uppderBoundSet, depending on initial and
  // final position of move
  private void addBound(PositionNode bound, PositionNode node, int finalPos) {
    int diff = finalPos - node.getPosition();

    if (diff == 0) {
      return;
    }

    if (diff > 0) {
      if (upperBoundSet.contains(bound)) {
        return;
      } else {
        upperBoundSet.add(bound);
      }
    } else {
      if (lowerBoundSet.contains(bound)) {
        return;
      } else {
        lowerBoundSet.add(bound);
      }
    }
  }
  // determine if given node can be moved to finalPos
  // assumes node.position and finalPos fall within index range of list
  private PositionNode getBounds(
      PositionNode node,
      int finalPos,
      List<QueryModelNode> list,
      TreeSet<PositionNode> leftJoinNodes) {

    // filters can be moved up and pushed down join segment
    // without affecting bound and unbound variables of
    // FlattenedOptionals -- Filters can be pushed down as
    // far as possible because it is assumed that any variable
    // that appears in a Filter also appears in a PCJ node
    // if Filters can be grouped, then Filter variables will
    // automatically be bound
    if (node.getNode() instanceof ValueExpr) {
      return new PositionNode();
    }

    int diff = finalPos - node.getPosition();

    if (diff == 0) {
      return new PositionNode();
    }

    if (node.isOptional) {
      FlattenedOptional optional = ((FlattenedOptional) node.getNode()).clone();
      if (diff < 0) {
        for (int i = node.getPosition() - 1; i > finalPos - 1; i--) {
          QueryModelNode tempNode = list.get(i);
          if (tempNode instanceof ValueExpr) {
            continue;
          }

          if (!optional.canAddTuple((TupleExpr) tempNode)) {
            return new PositionNode(tempNode, i);
          }

          if (tempNode instanceof FlattenedOptional) {
            FlattenedOptional tempOptional = (FlattenedOptional) tempNode;
            if (!tempOptional.canRemoveTuple(optional)) {
              return new PositionNode(tempNode, i);
            }
          }
          optional.addArg((TupleExpr) tempNode);
        }
      } else {
        for (int i = node.getPosition() + 1; i < finalPos + 1; i++) { // TODO
          // check
          // bounds
          QueryModelNode tempNode = list.get(i);
          if (tempNode instanceof ValueExpr) {
            continue;
          }
          if (!optional.canRemoveTuple((TupleExpr) tempNode)) {
            return new PositionNode(tempNode, i);
          }

          if (tempNode instanceof FlattenedOptional) {
            FlattenedOptional tempOptional = (FlattenedOptional) tempNode;
            if (!tempOptional.canAddTuple(optional)) {
              return new PositionNode(tempNode, i);
            }
          }
          optional.removeArg((TupleExpr) tempNode);
        }
      }

      return new PositionNode();

    } else {
      TupleExpr te = (TupleExpr) node.getNode();
      SortedSet<PositionNode> leftJoins;
      if (diff < 0) {
        leftJoins = leftJoinNodes.subSet(new PositionNode(finalPos), true, node, false);

        for (PositionNode pNode : leftJoins) {
          FlattenedOptional optional = (FlattenedOptional) pNode.getNode();
          if (!optional.canRemoveTuple(te)) {
            return new PositionNode(pNode);
          }
        }
      } else {

        leftJoins = leftJoinNodes.subSet(node, false, new PositionNode(finalPos), true);
        for (PositionNode pNode : leftJoins) {
          FlattenedOptional optional = (FlattenedOptional) pNode.getNode();
          if (!optional.canAddTuple(te)) {
            return new PositionNode(pNode);
          }
        }
      }

      return new PositionNode();
    }
  }
 // works only if moving node to final position does not move it across
 // another node in set
 private void updatePositionNode(PositionNode node, int position, TreeSet<PositionNode> set) {
   set.remove(node);
   node.setPosition(position);
   set.add(node);
 }
  // updates the var counts in specified left join index
  private void updateLeftJoinNodes(PositionNode node, int finalPos) {
    if (node.getNode() instanceof ValueExpr) {
      return;
    }

    int diff = finalPos - node.getPosition();

    if (diff == 0) {
      return;
    }

    if (node.isOptional) {
      leftJoinPosSet.remove(node);
      FlattenedOptional optional = (FlattenedOptional) node.getNode();
      if (diff < 0) {
        for (int i = node.getPosition() - 1; i > finalPos - 1; i--) {
          QueryModelNode tempNode = queryNodes.get(i);
          if (tempNode instanceof ValueExpr) {
            continue;
          }
          optional.addArg((TupleExpr) tempNode);
        }
      } else {
        for (int i = node.getPosition() + 1; i < finalPos + 1; i++) {
          QueryModelNode tempNode = queryNodes.get(i);
          if (tempNode instanceof ValueExpr) {
            continue;
          }
          optional.removeArg((TupleExpr) tempNode);
        }
      }
      node.setNode(optional);
      // FlattenedOptional equals does not take into account var counts
      // The following three lines update the var count in the optional in list
      int index = queryNodes.indexOf(optional);
      queryNodes.remove(optional);
      queryNodes.add(index, optional);
      leftJoinPosSet.add(node);

    } else {
      TupleExpr te = (TupleExpr) node.getNode();
      SortedSet<PositionNode> optionals;
      if (diff < 0) {
        optionals = leftJoinPosSet.subSet(new PositionNode(finalPos), true, node, false);
        for (PositionNode pNode : optionals) {
          FlattenedOptional optional = (FlattenedOptional) pNode.getNode();
          optional.removeArg(te);
        }
      } else {
        optionals = leftJoinPosSet.subSet(node, false, new PositionNode(finalPos), true);
        for (PositionNode pNode : optionals) {
          FlattenedOptional optional = (FlattenedOptional) pNode.getNode();
          optional.addArg(te);
        }
      }
    }
  }