protected final boolean mergeLists(List<UnclosedBranchDescriptor> child1open,
										List<UnclosedBranchDescriptor> child2open,
										List<UnclosedBranchDescriptor> result,
										boolean markJoinedBranchesAsPipelineBreaking) {

		//remove branches which have already been closed
		removeClosedBranches(child1open);
		removeClosedBranches(child2open);
		
		result.clear();
		
		// check how many open branches we have. the cases:
		// 1) if both are null or empty, the result is null
		// 2) if one side is null (or empty), the result is the other side.
		// 3) both are set, then we need to merge.
		if (child1open == null || child1open.isEmpty()) {
			if(child2open != null && !child2open.isEmpty()) {
				result.addAll(child2open);
			}
			return false;
		}
		
		if (child2open == null || child2open.isEmpty()) {
			result.addAll(child1open);
			return false;
		}

		int index1 = child1open.size() - 1;
		int index2 = child2open.size() - 1;
		
		boolean didCloseABranch = false;

		// as both lists (child1open and child2open) are sorted in ascending ID order
		// we can do a merge-join-like loop which preserved the order in the result list
		// and eliminates duplicates
		while (index1 >= 0 || index2 >= 0) {
			int id1 = -1;
			int id2 = index2 >= 0 ? child2open.get(index2).getBranchingNode().getId() : -1;

			while (index1 >= 0 && (id1 = child1open.get(index1).getBranchingNode().getId()) > id2) {
				result.add(child1open.get(index1));
				index1--;
			}
			while (index2 >= 0 && (id2 = child2open.get(index2).getBranchingNode().getId()) > id1) {
				result.add(child2open.get(index2));
				index2--;
			}

			// match: they share a common branching child
			if (id1 == id2) {
				didCloseABranch = true;
				
				// if this is the latest common child, remember it
				OptimizerNode currBanchingNode = child1open.get(index1).getBranchingNode();
				
				long vector1 = child1open.get(index1).getJoinedPathsVector();
				long vector2 = child2open.get(index2).getJoinedPathsVector();
				
				// check if this is the same descriptor, (meaning that it contains the same paths)
				// if it is the same, add it only once, otherwise process the join of the paths
				if (vector1 == vector2) {
					result.add(child1open.get(index1));
				}
				else {
					// we merge (re-join) a branch

					// mark the branch as a point where we break the pipeline
					if (markJoinedBranchesAsPipelineBreaking) {
						currBanchingNode.markAllOutgoingConnectionsAsPipelineBreaking();
					}

					if (this.hereJoinedBranches == null) {
						this.hereJoinedBranches = new ArrayList<OptimizerNode>(2);
					}
					this.hereJoinedBranches.add(currBanchingNode);

					// see, if this node closes the branch
					long joinedInputs = vector1 | vector2;

					// this is 2^size - 1, which is all bits set at positions 0..size-1
					long allInputs = (0x1L << currBanchingNode.getOutgoingConnections().size()) - 1;

					if (joinedInputs == allInputs) {
						// closed - we can remove it from the stack
						addClosedBranch(currBanchingNode);
					} else {
						// not quite closed
						result.add(new UnclosedBranchDescriptor(currBanchingNode, joinedInputs));
					}
				}

				index1--;
				index2--;
			}
		}

		// merged. now we need to reverse the list, because we added the elements in reverse order
		Collections.reverse(result);
		return didCloseABranch;
	}
protected final boolean mergeLists ( List < UnclosedBranchDescriptor > child1open , List < UnclosedBranchDescriptor > child2open , List < UnclosedBranchDescriptor > result , boolean markJoinedBranchesAsPipelineBreaking ) { removeClosedBranches ( child1open ) ; removeClosedBranches ( child2open ) ; result . clear ( ) ; if ( child1open == null || child1open . isEmpty ( ) ) { if ( child2open != null && ! child2open . isEmpty ( ) ) { result . addAll ( child2open ) ; } return false ; } if ( child2open == null || child2open . isEmpty ( ) ) { result . addAll ( child1open ) ; return false ; } int index1 = child1open . size ( ) - 1 ; int index2 = child2open . size ( ) - 1 ; boolean didCloseABranch = false ; while ( index1 >= 0 || index2 >= 0 ) { int id1 = - 1 ; int id2 = index2 >= 0 ? child2open . get ( index2 ) . getBranchingNode ( ) . getId ( ) : - 1 ; while ( index1 >= 0 && ( id1 = child1open . get ( index1 ) . getBranchingNode ( ) . getId ( ) ) > id2 ) { result . add ( child1open . get ( index1 ) ) ; index1 -- ; } while ( index2 >= 0 && ( id2 = child2open . get ( index2 ) . getBranchingNode ( ) . getId ( ) ) > id1 ) { result . add ( child2open . get ( index2 ) ) ; index2 -- ; } if ( id1 == id2 ) { didCloseABranch = true ; OptimizerNode currBanchingNode = child1open . get ( index1 ) . getBranchingNode ( ) ; long vector1 = child1open . get ( index1 ) . getJoinedPathsVector ( ) ; long vector2 = child2open . get ( index2 ) . getJoinedPathsVector ( ) ; if ( vector1 == vector2 ) { result . add ( child1open . get ( index1 ) ) ; } else { if ( markJoinedBranchesAsPipelineBreaking ) { currBanchingNode . markAllOutgoingConnectionsAsPipelineBreaking ( ) ; } if ( this . hereJoinedBranches == null ) { this . hereJoinedBranches = new ArrayList < OptimizerNode > ( 2 ) ; } this . hereJoinedBranches . add ( currBanchingNode ) ; long joinedInputs = vector1 | vector2 ; long allInputs = ( 0x1L << currBanchingNode . getOutgoingConnections ( ) . size ( ) ) - 1 ; if ( joinedInputs == allInputs ) { addClosedBranch ( currBanchingNode ) ; } else { result . add ( new UnclosedBranchDescriptor ( currBanchingNode , joinedInputs ) ) ; } } index1 -- ; index2 -- ; } } Collections . reverse ( result ) ; return didCloseABranch ; }
The node IDs are assigned in graph-traversal order (pre-order), hence, each list is
sorted by ID in ascending order and all consecutive lists start with IDs in ascending order.

@param markJoinedBranchesAsPipelineBreaking True, if the