// 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);
        }
      }
    }
  }
  // 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();
    }
  }