Package org.drools.phreak

Source Code of org.drools.phreak.RuleNetworkEvaluatorActivation$PhreakJoinNode

package org.drools.phreak;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import org.drools.base.DroolsQuery;
import org.drools.common.AgendaItem;
import org.drools.common.BetaConstraints;
import org.drools.common.InternalFactHandle;
import org.drools.common.InternalWorkingMemory;
import org.drools.common.LeftTupleSets;
import org.drools.common.Memory;
import org.drools.common.MemoryFactory;
import org.drools.common.NetworkNode;
import org.drools.common.RightTupleSets;
import org.drools.core.util.FastIterator;
import org.drools.core.util.LinkedList;
import org.drools.core.util.index.RightTupleList;
import org.drools.reteoo.AccumulateNode;
import org.drools.reteoo.AccumulateNode.AccumulateContext;
import org.drools.reteoo.AccumulateNode.AccumulateMemory;
import org.drools.reteoo.BetaMemory;
import org.drools.reteoo.BetaNode;
import org.drools.reteoo.ConditionalBranchEvaluator;
import org.drools.reteoo.ConditionalBranchNode;
import org.drools.reteoo.ConditionalBranchEvaluator.ConditionalExecution;
import org.drools.reteoo.ConditionalBranchNode.ConditionalBranchMemory;
import org.drools.reteoo.EvalConditionNode;
import org.drools.reteoo.EvalConditionNode.EvalMemory;
import org.drools.reteoo.ExistsNode;
import org.drools.reteoo.FromNode;
import org.drools.reteoo.FromNode.FromMemory;
import org.drools.reteoo.JoinNode;
import org.drools.reteoo.LeftInputAdapterNode;
import org.drools.reteoo.LeftTuple;
import org.drools.reteoo.LeftTupleMemory;
import org.drools.reteoo.LeftTupleSink;
import org.drools.reteoo.LeftTupleSinkNode;
import org.drools.reteoo.LeftTupleSinkPropagator;
import org.drools.reteoo.LeftTupleSource;
import org.drools.reteoo.NodeTypeEnums;
import org.drools.reteoo.NotNode;
import org.drools.reteoo.RightInputAdapterNode;
import org.drools.reteoo.RightInputAdapterNode.RiaNodeMemory;
import org.drools.reteoo.RightTuple;
import org.drools.reteoo.RightTupleMemory;
import org.drools.reteoo.RuleMemory;
import org.drools.reteoo.RuleTerminalNode;
import org.drools.reteoo.SegmentMemory;
import org.drools.rule.Accumulate;
import org.drools.rule.ContextEntry;
import org.drools.rule.EvalCondition;
import org.drools.spi.AlphaNodeFieldConstraint;
import org.drools.spi.DataProvider;
import org.drools.spi.PropagationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RuleNetworkEvaluatorActivation extends AgendaItem {

    private static final Logger    log         = LoggerFactory.getLogger( RuleNetworkEvaluatorActivation.class );

    private RuleMemory             rmem;

    private static PhreakJoinNode         pJoinNode   = new PhreakJoinNode();
    private static PhreakEvalNode         pEvalNode   = new PhreakEvalNode();
    private static PhreakFromNode         pFromNode   = new PhreakFromNode();
    private static PhreakNotNode          pNotNode    = new PhreakNotNode();
    private static PhreakExistsNode       pExistsNode = new PhreakExistsNode();
    private static PhreakAccumulateNode   pAccNode    = new PhreakAccumulateNode();
    private static PhreakRuleTerminalNode pRtnNode    = new PhreakRuleTerminalNode();
    private static PhreakBranchNode       pBranchNode = new PhreakBranchNode();

    public RuleNetworkEvaluatorActivation() {

    }

    /**
     * Construct.
     *
     * @param tuple
     *            The tuple.
     * @param rule
     *            The rule.
     */
    public RuleNetworkEvaluatorActivation(final long activationNumber,
                                          final LeftTuple tuple,
                                          final int salience,
                                          final PropagationContext context,
                                          final RuleMemory rmem,
                                          final RuleTerminalNode rtn) {
        super( activationNumber, tuple, salience, context, rtn );
        this.rmem = rmem;
    }

    public int evaluateNetwork(InternalWorkingMemory wm) {
        SegmentMemory[] smems = rmem.getSegmentMemories();

        int smemIndex = 0;
        SegmentMemory smem = smems[smemIndex]; // 0
        LeftInputAdapterNode liaNode = (LeftInputAdapterNode) smem.getRootNode();

        NetworkNode node;
        Memory nodeMem;
        if ( liaNode == smem.getTipNode() ) {
            // segment only has liaNode in it
            // nothing is staged in the liaNode, so skip to next segment          
            smem = smems[++smemIndex]; // 1
            node = smem.getRootNode();
            nodeMem = smem.getNodeMemories().getFirst();
        } else {
            // lia is in shared segment, so point to next node
            LeftTupleSinkPropagator sink = liaNode.getSinkPropagator();
            LeftTupleSinkNode firstSink = (LeftTupleSinkNode) sink.getFirstLeftTupleSink() ;
            LeftTupleSinkNode secondSink = firstSink.getNextLeftTupleSinkNode();
            if ( sink.size() == 2 ) {
                // As we check above for segment splits, if the sink size is 2, it must be a subnetwork.
                // Always take the non riaNode path
                node = secondSink;
            } else {
                node = firstSink;
            }  
            nodeMem = smem.getNodeMemories().getFirst().getNext(); // skip the liaNode memory
        }
       
        LeftTupleSets srcTuples = smem.getStagedLeftTuples();
       
        if ( log.isTraceEnabled() ) {
        //     log.trace( "Start Rule[name={}] {}", getRule().getName(), srcTuples.toStringSizes() );
        }

        eval( ( LeftTupleSink ) node, nodeMem, smems, smemIndex, srcTuples, null, wm);

        return 0;
    }
   
    public LeftTupleSets eval( NetworkNode node, Memory nodeMem, SegmentMemory[] smems, int smemIndex, LeftTupleSets trgTuples, LeftTupleSets stagedLeftTuples, InternalWorkingMemory wm) {
        LeftTupleSets srcTuples;
       
        SegmentMemory smem = smems[smemIndex];
        while ( true ) {
//            if ( log.isTraceEnabled() ) {
//                log.trace( "Start Node[offset={}, type={}] Rule[name={}]", getRule().getName() );
//            }
            srcTuples = trgTuples; // previous target, is now the source
           
            if ( NodeTypeEnums.isTerminalNode( node ) ) {
                RuleTerminalNode rtn = rmem.getRuleTerminalNode();
                pRtnNode.doNode( rtn, wm, srcTuples );
                return null;
            } else if ( NodeTypeEnums.RightInputAdaterNode == node.getType() ) {
                return trgTuples;
            }          
           
            if ( node == smem.getTipNode() && smem.getFirst() != null) {
                // we are about to process the segment tip, allow it to merge insert/update/delete clashes
                // Can happen if the next segments have not yet been initialized
                stagedLeftTuples = smem.getFirst().getStagedLeftTuples();
            } else {
                stagedLeftTuples = null;
            }         
           
            LeftTupleSinkNode sink = ((LeftTupleSource)node).getSinkPropagator().getFirstLeftTupleSink();

            trgTuples = new LeftTupleSets();
           
            if ( NodeTypeEnums.isBetaNode( node ) ) {
                BetaNode betaNode = ( BetaNode )node;
               
                BetaMemory bm = null;
                AccumulateMemory am = null;
                if ( NodeTypeEnums.AccumulateNode == node.getType() ) {
                    am = (AccumulateMemory) nodeMem;
                    bm = am.getBetaMemory();
                } else {
                    bm = (BetaMemory) nodeMem;   
                }               
               
                if ( betaNode.isRightInputIsRiaNode() ) {
                    // if the subnetwork is nested in this segment, it will create srcTuples containing
                    // peer LeftTuples, suitable for the node in the main path.
                    srcTuples = doRiaNode( wm,
                                           srcTuples,
                                           betaNode,
                                           bm );                   
                }
               
                switch( node.getType() ) {
                    case  NodeTypeEnums.JoinNode:                   
                        pJoinNode.doNode( (JoinNode) node, sink,
                                          bm, wm, srcTuples, trgTuples, stagedLeftTuples );
                        break;
                    case  NodeTypeEnums.NotNode:
                        pNotNode.doNode( (NotNode) node, sink,
                                         bm, wm, srcTuples, trgTuples, stagedLeftTuples );
                        break;
                    case  NodeTypeEnums.ExistsNode:
                        pExistsNode.doNode( (ExistsNode) node, sink,
                                            bm, wm, srcTuples, trgTuples, stagedLeftTuples );
                        break;
                    case  NodeTypeEnums.AccumulateNode:
                        pAccNode.doNode( (AccumulateNode) node, sink,
                                         am, wm, srcTuples, trgTuples, stagedLeftTuples );
                        break;                          
                }
            } else if ( node.getType() == NodeTypeEnums.EvalConditionNode ) {
                pEvalNode.doNode( (EvalConditionNode) node, (EvalMemory) nodeMem, sink,
                                   wm, srcTuples, trgTuples, stagedLeftTuples );               
            } else if ( node.getType() == NodeTypeEnums.FromNode ) {
                pFromNode.doNode( (FromNode) node, (FromMemory) nodeMem, sink,
                                  wm, srcTuples, trgTuples, stagedLeftTuples );               
           } else if (true) {
               pBranchNode.doNode( (ConditionalBranchNode) node, (ConditionalBranchMemory) nodeMem, sink,
                                 wm, srcTuples, trgTuples, stagedLeftTuples );
           }
           
            if ( node != smem.getTipNode() ) {
                // get next node and node memory in the segment
                LeftTupleSink nextSink = sink.getNextLeftTupleSinkNode();
                if ( nextSink == null ) {
                    node = sink;
                } else {
                    // there is a nested subnetwork, take out path
                    node = nextSink;
                }
               
                nodeMem = nodeMem.getNext();
            } else {                           
                // Reached end of segment, start on new segment.
                SegmentPropagator.propagate( smem, trgTuples, wm );
                smem = smems[++smemIndex];
                trgTuples = smem.getStagedLeftTuples();
                node = ( LeftTupleSink ) smem.getRootNode();
                nodeMem = smem.getNodeMemories().getFirst();
            }
        }
    }

    private LeftTupleSets doRiaNode(InternalWorkingMemory wm,
                                    LeftTupleSets srcTuples,
                                    BetaNode betaNode,
                                    BetaMemory bm) {
        SegmentMemory subSmem = bm.getSubnetworkSegmentMemory();
       
        if ( betaNode.getLeftTupleSource().getSinkPropagator().size() == 2 ) {
            // sub network is not part of  share split, so need to handle propagation
            // this ensures the first LeftTuple is actually the subnetwork node
            // and the main outer network now receives the peer, notice the swap at the end "srcTuples == peerTuples"
            LeftTupleSets peerTuples = new LeftTupleSets();
            SegmentPropagator.processPeers( srcTuples, peerTuples, betaNode );
            // Make sure subnetwork Segment has tuples to process
            LeftTupleSets subnetworkStaged =  subSmem.getStagedLeftTuples();
            subnetworkStaged.addAll( srcTuples )

            srcTuples.resetAll();
           
            srcTuples = peerTuples;
        }                   
       
        RightInputAdapterNode riaNode = ( RightInputAdapterNode ) betaNode.getRightInput();
        RiaNodeMemory riaNodeMemory = ( RiaNodeMemory ) wm.getNodeMemory( (MemoryFactory) betaNode.getRightInput() );                   
        LeftTupleSets riaStagedTuples = eval( ( LeftTupleSink ) subSmem.getRootNode(), subSmem.getNodeMemories().getFirst(), riaNodeMemory.getRiaRuleMemory().getSegmentMemories(), subSmem.getPos(), subSmem.getStagedLeftTuples(), null, wm );
       
        for ( LeftTuple leftTuple = riaStagedTuples.getInsertFirst(); leftTuple != null; ) {
            LeftTuple next = leftTuple.getStagedNext();
           
            InternalFactHandle handle = riaNode.createFactHandle( leftTuple, leftTuple.getPropagationContext(), wm );
            RightTuple rightTuple = new RightTuple( handle, betaNode );
            leftTuple.setObject( rightTuple );
            bm.getStagedRightTuples().addInsert( rightTuple );
           
            leftTuple.clearStaged();
            leftTuple = next;
        }
       
        for ( LeftTuple leftTuple = riaStagedTuples.getDeleteFirst(); leftTuple != null;) {
            LeftTuple next = leftTuple.getStagedNext();
           
            bm.getStagedRightTuples().addDelete( (RightTuple) leftTuple.getObject()  );
           
            leftTuple.clearStaged();
            leftTuple = next;           
        }
       
        for ( LeftTuple leftTuple = riaStagedTuples.getUpdateFirst(); leftTuple != null; ) {
            LeftTuple next = leftTuple.getStagedNext();
           
            bm.getStagedRightTuples().addUpdate( (RightTuple) leftTuple.getObject()  );
           
            leftTuple.clearStaged();
            leftTuple = next;              
        }  
       
        return srcTuples;
    }

    public boolean isRuleNetworkEvaluatorActivation() {
        return true;
    }

    public static class PhreakJoinNode {
        public void doNode(JoinNode joinNode,
                           LeftTupleSink sink,
                           BetaMemory bm,
                           InternalWorkingMemory wm,
                           LeftTupleSets srcLeftTuples,
                           LeftTupleSets trgLeftTuples,
                           LeftTupleSets stagedLeftTuples) {
           
            RightTupleSets srcRightTuples = bm.getStagedRightTuples();

            if ( srcRightTuples.getDeleteFirst() != null ) {
                doRightDeletes( joinNode, bm, wm, srcRightTuples, trgLeftTuples, stagedLeftTuples );
            }

            if ( srcLeftTuples.getDeleteFirst() != null ) {
                doLeftDeletes( joinNode, bm, wm, srcLeftTuples, trgLeftTuples, stagedLeftTuples  );
            }

            if ( srcLeftTuples.getUpdateFirst() != null || srcRightTuples.getUpdateFirst() != null ) {
                dpUpdatesReorderLeftMemory( bm, wm,
                                            srcRightTuples, srcLeftTuples );
                dpUpdatesReorderRightMemory( bm, wm,
                                            srcRightTuples, srcLeftTuples );
            }

            if ( srcRightTuples.getUpdateFirst() != null ) {
                doRightUpdates( joinNode, sink, bm, wm, srcRightTuples, trgLeftTuples, stagedLeftTuples   );
            }

            if ( srcLeftTuples.getUpdateFirst() != null ) {
                doLeftUpdates( joinNode, sink, bm, wm, srcLeftTuples, trgLeftTuples, stagedLeftTuples   );
            }

            if ( srcRightTuples.getInsertFirst() != null ) {
                doRightInserts( joinNode, sink, bm, wm, srcRightTuples, trgLeftTuples  );
            }

            if ( srcLeftTuples.getInsertFirst() != null ) {
                doLeftInserts( joinNode, sink, bm, wm, srcLeftTuples, trgLeftTuples );
            }
           
            srcRightTuples.resetAll();           
            srcLeftTuples.resetAll();           
        }

        public void doLeftInserts(JoinNode joinNode,
                                  LeftTupleSink sink,
                                  BetaMemory bm,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples) {
            boolean tupleMemory = true;
            boolean tupleMemoryEnabled = true;

            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            RightTupleMemory rtm = bm.getRightTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = joinNode.getRawConstraints();
            FastIterator it = joinNode.getRightIterator( rtm );

            for ( LeftTuple leftTuple = srcLeftTuples.getInsertFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                PropagationContext context = leftTuple.getPropagationContext();
                boolean useLeftMemory = true;

                if ( !tupleMemoryEnabled ) {
                    // This is a hack, to not add closed DroolsQuery objects
                    Object object = leftTuple.get( 0 ).getObject();
                    if ( !(object instanceof DroolsQuery) || !((DroolsQuery) object).isOpen() ) {
                        useLeftMemory = false;
                    }
                }

                if ( useLeftMemory ) {
                    ltm.add( leftTuple );
                }

                constraints.updateFromTuple( contextEntry,
                                             wm,
                                             leftTuple );

                for ( RightTuple rightTuple = joinNode.getFirstRightTuple( leftTuple,
                                                                           rtm,
                                                                           context,
                                                                           it ); rightTuple != null; rightTuple = (RightTuple) it.next( rightTuple ) ) {
                    if ( constraints.isAllowedCachedLeft( contextEntry,
                                                          rightTuple.getFactHandle() ) ) {
                        trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple,
                                                                       rightTuple,
                                                                       null,
                                                                       null,
                                                                       sink,
                                                                       tupleMemory ) );
                    }
                }
                leftTuple.clearStaged();
                leftTuple = next;
            }
            constraints.resetTuple( contextEntry );
        }

        public void doRightInserts(JoinNode joinNode,
                                   LeftTupleSink sink,
                                   BetaMemory bm,
                                   InternalWorkingMemory wm,
                                   RightTupleSets srcRightTuples,
                                   LeftTupleSets trgLeftTuples) {
            boolean tupleMemory = true;
            boolean tupleMemoryEnabled = true;

            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            RightTupleMemory rtm = bm.getRightTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = joinNode.getRawConstraints();
            FastIterator it = joinNode.getLeftIterator( ltm );

            for ( RightTuple rightTuple = srcRightTuples.getInsertFirst(); rightTuple != null; ) {
                RightTuple next = rightTuple.getStagedNext();
                rtm.add( rightTuple );
                PropagationContext context = rightTuple.getPropagationContext();

                constraints.updateFromFactHandle( contextEntry,
                                                  wm,
                                                  rightTuple.getFactHandle() );

                for ( LeftTuple leftTuple = joinNode.getFirstLeftTuple( rightTuple, ltm, context, it ); leftTuple != null; leftTuple = (LeftTuple) it.next( leftTuple ) ) {
                    if ( leftTuple.getStagedType() == LeftTuple.UPDATE ) {
                        // ignore, as it will get processed via left iteration. Children cannot be processed twice
                        continue;                       
                    }
                   
                    if ( constraints.isAllowedCachedRight( contextEntry,
                                                           leftTuple ) ) {
                        trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple,
                                                                       rightTuple,
                                                                       null,
                                                                       null,
                                                                       sink,
                                                                       tupleMemory ) );
                    }
                }
                rightTuple.clearStaged();
                rightTuple = next;
            }
            constraints.resetFactHandle( contextEntry );
        }

        public void doLeftUpdates(JoinNode joinNode,
                                  LeftTupleSink sink,
                                  BetaMemory bm,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples,
                                  LeftTupleSets stagedLeftTuples) {
            boolean tupleMemory = true;
            RightTupleMemory rtm = bm.getRightTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = joinNode.getRawConstraints();
            FastIterator it = joinNode.getRightIterator( rtm );

            for ( LeftTuple leftTuple = srcLeftTuples.getUpdateFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                PropagationContext context = leftTuple.getPropagationContext();

                constraints.updateFromTuple( contextEntry,
                                             wm,
                                             leftTuple );

                RightTuple rightTuple = joinNode.getFirstRightTuple( leftTuple,
                                                                     rtm,
                                                                     context,
                                                                     it );

                LeftTuple childLeftTuple = leftTuple.getFirstChild();

                // first check our index (for indexed nodes only) hasn't changed and we are returning the same bucket
                // if rightTuple is null, we assume there was a bucket change and that bucket is empty       
                if ( childLeftTuple != null && rtm.isIndexed() && !it.isFullIterator() && (rightTuple == null || (rightTuple.getMemory() != childLeftTuple.getRightParent().getMemory())) ) {
                    // our index has changed, so delete all the previous propagations
                    while ( childLeftTuple != null ) {
                        childLeftTuple = deleteLeftChild( childLeftTuple, trgLeftTuples, stagedLeftTuples );
                    }
                    // childLeftTuple is now null, so the next check will attempt matches for new bucket
                }

                // we can't do anything if RightTupleMemory is empty
                if ( rightTuple != null ) {
                    doLeftUpdatesProcessChildren( childLeftTuple, leftTuple, rightTuple, stagedLeftTuples, tupleMemory, contextEntry, constraints, sink, it, trgLeftTuples );
                }
                leftTuple.clearStaged();
                leftTuple = next;
            }
            constraints.resetTuple( contextEntry );
        }

        public LeftTuple doLeftUpdatesProcessChildren(LeftTuple childLeftTuple,
                                                      LeftTuple leftTuple,
                                                      RightTuple rightTuple,
                                                      LeftTupleSets stagedLeftTuples,
                                                      boolean tupleMemory,
                                                      ContextEntry[] contextEntry,
                                                      BetaConstraints constraints,
                                                      LeftTupleSink sink,
                                                      FastIterator it,
                                                      LeftTupleSets trgLeftTuples) {
            if ( childLeftTuple == null ) {
                // either we are indexed and changed buckets or
                // we had no children before, but there is a bucket to potentially match, so try as normal assert
                for ( ; rightTuple != null; rightTuple = (RightTuple) it.next( rightTuple ) ) {
                    if ( constraints.isAllowedCachedLeft( contextEntry,
                                                          rightTuple.getFactHandle() ) ) {
                        trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple,
                                                                       rightTuple,
                                                                       null,
                                                                       null,
                                                                       sink,
                                                                       tupleMemory ) );
                    }
                }
            } else {
                // in the same bucket, so iterate and compare
                for ( ; rightTuple != null; rightTuple = (RightTuple) it.next( rightTuple ) ) {
                    if ( constraints.isAllowedCachedLeft( contextEntry,
                                                          rightTuple.getFactHandle() ) ) {
                        // insert, childLeftTuple is not updated
                        if ( childLeftTuple == null || childLeftTuple.getRightParent() != rightTuple ) {
                            trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple,
                                                                           rightTuple,
                                                                           null,
                                                                           null,
                                                                           sink,
                                                                           tupleMemory ) );
                        } else {
                            switch ( childLeftTuple.getStagedType() ) {
                                // handle clash with already staged entries
                                case LeftTuple.INSERT :
                                    stagedLeftTuples.removeInsert( childLeftTuple );
                                    break;
                                case LeftTuple.UPDATE :
                                    stagedLeftTuples.removeUpdate( childLeftTuple );
                                    break;
                            }

                            // update, childLeftTuple is updated
                            trgLeftTuples.addUpdate( childLeftTuple );

                            childLeftTuple.reAddRight();
                            childLeftTuple = childLeftTuple.getLeftParentNext();
                        }
                    } else if ( childLeftTuple != null && childLeftTuple.getRightParent() == rightTuple ) {
                        // delete, childLeftTuple is updated
                        childLeftTuple = deleteLeftChild( childLeftTuple, trgLeftTuples, stagedLeftTuples );
                    }
                }
            }

            return childLeftTuple;
        }

        public void doRightUpdates(JoinNode joinNode,
                                   LeftTupleSink sink,
                                   BetaMemory bm,
                                   InternalWorkingMemory wm,
                                   RightTupleSets srcRightTuples,
                                   LeftTupleSets trgLeftTuples,
                                   LeftTupleSets stagedLeftTuples) {
            boolean tupleMemory = true;
            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = joinNode.getRawConstraints();
            FastIterator it = joinNode.getLeftIterator( ltm );

            for ( RightTuple rightTuple = srcRightTuples.getUpdateFirst(); rightTuple != null; ) {
                RightTuple next = rightTuple.getStagedNext();
                PropagationContext context = rightTuple.getPropagationContext();

                LeftTuple childLeftTuple = rightTuple.getFirstChild();

                LeftTuple leftTuple = joinNode.getFirstLeftTuple( rightTuple, ltm, context, it );

                constraints.updateFromFactHandle( contextEntry,
                                                  wm,
                                                  rightTuple.getFactHandle() );

                // first check our index (for indexed nodes only) hasn't changed and we are returning the same bucket
                // We assume a bucket change if leftTuple == null       
                if ( childLeftTuple != null && ltm.isIndexed() && !it.isFullIterator() && (leftTuple == null || (leftTuple.getMemory() != childLeftTuple.getLeftParent().getMemory())) ) {
                    // our index has changed, so delete all the previous propagations
                    while ( childLeftTuple != null ) {
                        childLeftTuple = deleteRightChild( childLeftTuple, trgLeftTuples, stagedLeftTuples );
                    }
                    // childLeftTuple is now null, so the next check will attempt matches for new bucket                   
                }

                // we can't do anything if LeftTupleMemory is empty
                if ( leftTuple != null ) {
                    doRightUpdatesProcessChildren( childLeftTuple, leftTuple, rightTuple, stagedLeftTuples, tupleMemory, contextEntry, constraints, sink, it, trgLeftTuples );
                }
                rightTuple.clearStaged();
                rightTuple = next;
            }
            constraints.resetFactHandle( contextEntry );
        }

        public LeftTuple doRightUpdatesProcessChildren(LeftTuple childLeftTuple,
                                                       LeftTuple leftTuple,
                                                       RightTuple rightTuple,
                                                       LeftTupleSets stagedLeftTuples,
                                                       boolean tupleMemory,
                                                       ContextEntry[] contextEntry,
                                                       BetaConstraints constraints,
                                                       LeftTupleSink sink,
                                                       FastIterator it,
                                                       LeftTupleSets trgLeftTuples) {
            if ( childLeftTuple == null ) {
                // either we are indexed and changed buckets or
                // we had no children before, but there is a bucket to potentially match, so try as normal assert
                for ( ; leftTuple != null; leftTuple = (LeftTuple) it.next( leftTuple ) ) {
                    if ( leftTuple.getStagedType() == LeftTuple.UPDATE ) {
                        // ignore, as it will get processed via left iteration. Children cannot be processed twice
                        continue;                       
                    }
                   
                    if ( constraints.isAllowedCachedRight( contextEntry,
                                                           leftTuple ) ) {
                        trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple,
                                                                       rightTuple,
                                                                       null,
                                                                       null,
                                                                       sink,
                                                                       tupleMemory ) );
                    }
                }
            } else {
                // in the same bucket, so iterate and compare
                for ( ; leftTuple != null; leftTuple = (LeftTuple) it.next( leftTuple ) ) {
                    if ( leftTuple.getStagedType() == LeftTuple.UPDATE ) {
                        // ignore, as it will get processed via left iteration. Children cannot be processed twice
                        continue;                       
                    }
                   
                    if ( constraints.isAllowedCachedRight( contextEntry,
                                                           leftTuple ) ) {
                        // insert, childLeftTuple is not updated
                        if ( childLeftTuple == null || childLeftTuple.getLeftParent() != leftTuple ) {
                            trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple,
                                                                           rightTuple,
                                                                           null,
                                                                           null,
                                                                           sink,
                                                                           tupleMemory ) );
                        } else {
                            switch ( childLeftTuple.getStagedType() ) {
                            // handle clash with already staged entries
                                case LeftTuple.INSERT :
                                    stagedLeftTuples.removeInsert( childLeftTuple );
                                    break;
                                case LeftTuple.UPDATE :
                                    stagedLeftTuples.removeUpdate( childLeftTuple );
                                    break;
                            }

                            // update, childLeftTuple is updated
                            trgLeftTuples.addUpdate( childLeftTuple );

                            childLeftTuple.reAddLeft();
                            childLeftTuple = childLeftTuple.getRightParentNext();
                        }
                    } else if ( childLeftTuple != null && childLeftTuple.getLeftParent() == leftTuple ) {
                        // delete, childLeftTuple is updated
                        childLeftTuple = deleteRightChild( childLeftTuple, trgLeftTuples, stagedLeftTuples );
                    }
                }
            }

            return childLeftTuple;
        }

        public void doLeftDeletes(JoinNode joinNode,
                                  BetaMemory bm,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples,
                                  LeftTupleSets stagedLeftTuples) {
            LeftTupleMemory ltm = bm.getLeftTupleMemory();

            for ( LeftTuple leftTuple = srcLeftTuples.getDeleteFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                if ( leftTuple.getMemory() != null ) {
                    // it may have been staged and never actually added
                    ltm.remove( leftTuple );
                }

                if ( leftTuple.getFirstChild() != null ) {
                    LeftTuple childLeftTuple = leftTuple.getFirstChild();

                    while ( childLeftTuple != null ) {
                        childLeftTuple = deleteLeftChild( childLeftTuple, trgLeftTuples, stagedLeftTuples );
                    }
                }
                leftTuple.clearStaged();
                leftTuple = next;
            }
        }

        public void doRightDeletes(JoinNode joinNode,
                                   BetaMemory bm,
                                   InternalWorkingMemory wm,
                                   RightTupleSets srcRightTuples,
                                   LeftTupleSets trgLeftTuples,
                                   LeftTupleSets stagedLeftTuples) {
            RightTupleMemory rtm = bm.getRightTupleMemory();

            for ( RightTuple rightTuple = srcRightTuples.getDeleteFirst(); rightTuple != null; ) {
                RightTuple next = rightTuple.getStagedNext();
                if rightTuple.getMemory() != null ) {
                    // it may have been staged and never actually added
                    rtm.remove( rightTuple );
                };

                if ( rightTuple.getFirstChild() != null ) {
                    LeftTuple childLeftTuple = rightTuple.getFirstChild();

                    while ( childLeftTuple != null ) {
                        childLeftTuple = deleteRightChild( childLeftTuple, trgLeftTuples, stagedLeftTuples );
                    }
                }
                rightTuple.clearStaged();
                rightTuple = next;
            }
        }
    }

    public static class PhreakNotNode {
        public void doNode(NotNode notNode,
                           LeftTupleSink sink,
                           BetaMemory bm,
                           InternalWorkingMemory wm,
                           LeftTupleSets srcLeftTuples,
                           LeftTupleSets trgLeftTuples,
                           LeftTupleSets stagedLeftTuples) {
            RightTupleSets srcRightTuples = bm.getStagedRightTuples();

            if ( srcRightTuples.getDeleteFirst() != null ) {
                doRightDeletes( notNode, sink, bm, wm, srcRightTuples, trgLeftTuples );
            }

            if ( srcLeftTuples.getDeleteFirst() != null ) {
                doLeftDeletes( notNode, sink, bm, wm, srcLeftTuples, trgLeftTuples, stagedLeftTuples );
            }

            if ( srcLeftTuples.getUpdateFirst() != null || srcRightTuples.getUpdateFirst() != null ) {
                dpUpdatesReorderLeftMemory( bm,
                                        wm,
                                        srcRightTuples,
                                        srcLeftTuples );
                // don't re-order the right memory, as update relies on getting the next rightTuple before reAdd
            }

            if ( srcRightTuples.getUpdateFirst() != null ) {
                doRightUpdates( notNode, sink, bm, wm, srcRightTuples, trgLeftTuples, stagedLeftTuples );
            }

            // not nodes must do right inserts, before doing left updates. Otherwise you end up with child
            // in trgLeftTuples, and insert thinks it's staged in stagedLeftTuples giving nullpointer.
            if ( srcRightTuples.getInsertFirst() != null ) {
                doRightInserts( notNode, sink, bm, wm, srcRightTuples, trgLeftTuples );
            }
           
            if ( srcLeftTuples.getUpdateFirst() != null ) {
                doLeftUpdates( notNode, sink, bm, wm, srcLeftTuples, trgLeftTuples, stagedLeftTuples );
            }

            if ( srcLeftTuples.getInsertFirst() != null ) {
                doLeftInserts( notNode, sink, bm, wm, srcLeftTuples, trgLeftTuples );
            }
           
            srcRightTuples.resetAll();           
            srcLeftTuples.resetAll();            
        }

        public void doLeftInserts(NotNode notNode,
                                  LeftTupleSink sink,
                                  BetaMemory bm,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples) {
            boolean tupleMemory = true;
            boolean tupleMemoryEnabled = true;

            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            RightTupleMemory rtm = bm.getRightTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = notNode.getRawConstraints();
            FastIterator it = notNode.getRightIterator( rtm );

            for ( LeftTuple leftTuple = srcLeftTuples.getInsertFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                PropagationContext context = leftTuple.getPropagationContext();

                boolean useLeftMemory = true;
                if ( !tupleMemoryEnabled ) {
                    // This is a hack, to not add closed DroolsQuery objects
                    Object object = leftTuple.get( 0 ).getObject();
                    if ( !(object instanceof DroolsQuery) || !((DroolsQuery) object).isOpen() ) {
                        useLeftMemory = false;
                    }
                }

                constraints.updateFromTuple( contextEntry,
                                             wm,
                                             leftTuple );

                for ( RightTuple rightTuple = notNode.getFirstRightTuple( leftTuple, rtm, context, it ); rightTuple != null; rightTuple = (RightTuple) it.next( rightTuple ) ) {
                    if ( constraints.isAllowedCachedLeft( contextEntry,
                                                          rightTuple.getFactHandle() ) ) {
                        leftTuple.setBlocker( rightTuple );

                        if ( useLeftMemory ) {
                            rightTuple.addBlocked( leftTuple );
                        }

                        break;
                    }
                }

                if ( leftTuple.getBlocker() == null ) {
                    // tuple is not blocked, so add to memory so other fact handles can attempt to match
                    if ( useLeftMemory ) {
                        ltm.add( leftTuple );
                    }

                    trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple,
                                                                   sink,
                                                                   tupleMemory ) );
                }
                leftTuple.clearStaged();
                leftTuple = next;
            }
            constraints.resetTuple( contextEntry );
        }

        public void doRightInserts(NotNode notNode,
                                   LeftTupleSink sink,
                                   BetaMemory bm,
                                   InternalWorkingMemory wm,
                                   RightTupleSets srcRightTuples,
                                   LeftTupleSets trgLeftTuples) {
            boolean tupleMemory = true;
            boolean tupleMemoryEnabled = true;

            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            RightTupleMemory rtm = bm.getRightTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = notNode.getRawConstraints();
            FastIterator it = notNode.getLeftIterator( ltm );

            LeftTupleSets stagedLeftTuples = null;
            if ( !bm.getSegmentMemory().isEmpty() ) {
                stagedLeftTuples = bm.getSegmentMemory().getFirst().getStagedLeftTuples();
            }

            unlinkNotNodeOnRightInsert( notNode,
                                        bm,
                                        wm );
           
            for ( RightTuple rightTuple = srcRightTuples.getInsertFirst(); rightTuple != null; ) {
                RightTuple next = rightTuple.getStagedNext();
                rtm.add( rightTuple );
                PropagationContext context = rightTuple.getPropagationContext();

                constraints.updateFromFactHandle( contextEntry,
                                                  wm,
                                                  rightTuple.getFactHandle() );
                for ( LeftTuple leftTuple = notNode.getFirstLeftTuple( rightTuple, ltm, context, it ); leftTuple != null; ) {
                    // preserve next now, in case we remove this leftTuple
                    LeftTuple temp = (LeftTuple) it.next( leftTuple );
                   
                    if ( leftTuple.getStagedType() == LeftTuple.UPDATE ) {
                        // ignore, as it will get processed via left iteration. Children cannot be processed twice
                        leftTuple = temp;
                        continue;                       
                    }

                    // we know that only unblocked LeftTuples are  still in the memory
                    if ( constraints.isAllowedCachedRight( contextEntry,
                                                           leftTuple ) ) {
                        leftTuple.setBlocker( rightTuple );
                        rightTuple.addBlocked( leftTuple );

                        // this is now blocked so remove from memory
                        ltm.remove( leftTuple );

                        // subclasses like ForallNotNode might override this propagation
                        // ** @TODO (mdp) need to not break forall
                        LeftTuple childLeftTuple = leftTuple.getFirstChild();

                        if ( childLeftTuple != null ) { // NotNode only has one child
                            childLeftTuple = deleteLeftChild( childLeftTuple, trgLeftTuples, stagedLeftTuples );
                        }                       
                    }

                    leftTuple = temp;
                }
                rightTuple.clearStaged();
                rightTuple = next;
            }
            constraints.resetFactHandle( contextEntry );
        }

        public static void unlinkNotNodeOnRightInsert(NotNode notNode,
                                                BetaMemory bm,
                                                InternalWorkingMemory wm) {
            if ( bm.getSegmentMemory().isSegmentLinked() && !notNode.isRightInputIsRiaNode() && notNode.isEmptyBetaConstraints() ) {
                    // this must be processed here, rather than initial insert, as we need to link the blocker
                    // @TODO this could be more efficient, as it means the entire StagedLeftTuples for all previous nodes where evaluated, needlessly.
                    bm.unlinkNode( wm );
            }
        }

        public void doLeftUpdates(NotNode notNode,
                                  LeftTupleSink sink,
                                  BetaMemory bm,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples,
                                  LeftTupleSets stagedLeftTuples) {
            boolean tupleMemory = true;
            boolean tupleMemoryEnabled = true;

            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            RightTupleMemory rtm = bm.getRightTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = notNode.getRawConstraints();
            FastIterator rightIt = notNode.getRightIterator( rtm );

            for ( LeftTuple leftTuple = srcLeftTuples.getUpdateFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                PropagationContext context = leftTuple.getPropagationContext();
                RightTuple firstRightTuple = notNode.getFirstRightTuple( leftTuple, rtm, context, rightIt );

                // If in memory, remove it, because we'll need to add it anyway if it's not blocked, to ensure iteration order
                RightTuple blocker = leftTuple.getBlocker();
                if ( blocker == null ) {
                    ltm.remove( leftTuple );
                } else {
                    // check if we changed bucket
                    if ( rtm.isIndexed() && !rightIt.isFullIterator() ) {
                        // if newRightTuple is null, we assume there was a bucket change and that bucket is empty               
                        if ( firstRightTuple == null || firstRightTuple.getMemory() != blocker.getMemory() ) {
                            blocker.removeBlocked( leftTuple );
                            blocker = null;
                        }
                    }
                }

                constraints.updateFromTuple( contextEntry,
                                             wm,
                                             leftTuple );

                // if we where not blocked before (or changed buckets), or the previous blocker no longer blocks, then find the next blocker
                if ( blocker == null || !constraints.isAllowedCachedLeft( contextEntry,
                                                                          blocker.getFactHandle() ) ) {
                    if ( blocker != null ) {
                        // remove previous blocker if it exists, as we know it doesn't block any more
                        blocker.removeBlocked( leftTuple );
                    }

                    // find first blocker, because it's a modify, we need to start from the beginning again       
                    for ( RightTuple newBlocker = firstRightTuple; newBlocker != null; newBlocker = (RightTuple) rightIt.next( newBlocker ) ) {
                        if ( constraints.isAllowedCachedLeft( contextEntry,
                                                              newBlocker.getFactHandle() ) ) {
                            leftTuple.setBlocker( newBlocker );
                            newBlocker.addBlocked( leftTuple );

                            break;
                        }
                    }

                    LeftTuple childLeftTuple = leftTuple.getFirstChild();

                    if ( leftTuple.getBlocker() != null ) {
                        // blocked
                        if ( leftTuple.getFirstChild() != null ) {
                            // blocked, with previous children, so must have not been previously blocked, so retract
                            // no need to remove, as we removed at the start
                            // to be matched against, as it's now blocked
                            deleteLeftChild( childLeftTuple, trgLeftTuples, stagedLeftTuples );
                        } // else: it's blocked now and no children so blocked before, thus do nothing            
                    } else if ( childLeftTuple == null ) {
                        // not blocked, with no children, must have been previously blocked so assert
                        ltm.add( leftTuple ); // add to memory so other fact handles can attempt to match
                        trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple,
                                                                       sink,
                                                                       tupleMemory ) );
                    } else {
                        switch ( childLeftTuple.getStagedType() ) {
                        // handle clash with already staged entries
                            case LeftTuple.INSERT :
                                stagedLeftTuples.removeInsert( childLeftTuple );
                                break;
                            case LeftTuple.UPDATE :
                                stagedLeftTuples.removeUpdate( childLeftTuple );
                                break;
                        }
                        // not blocked, with children, so wasn't previous blocked and still isn't so modify               
                        ltm.add( leftTuple ); // add to memory so other fact handles can attempt to match               
                        trgLeftTuples.addUpdate( childLeftTuple );
                        childLeftTuple.reAddLeft();
                    }
                }
                leftTuple.clearStaged();
                leftTuple = next;
            }
            constraints.resetTuple( contextEntry );
        }



        public void doRightUpdates(NotNode notNode,
                                   LeftTupleSink sink,
                                   BetaMemory bm,
                                   InternalWorkingMemory wm,
                                   RightTupleSets srcRightTuples,
                                   LeftTupleSets trgLeftTuples,
                                   LeftTupleSets stagedLeftTuples) {
            boolean tupleMemory = true;
            boolean tupleMemoryEnabled = true;

            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            RightTupleMemory rtm = bm.getRightTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = notNode.getRawConstraints();

            FastIterator leftIt = notNode.getLeftIterator( ltm );
            FastIterator rightIt = notNode.getRightIterator( rtm );

            for ( RightTuple rightTuple = srcRightTuples.getUpdateFirst(); rightTuple != null; ) {
                RightTuple next = rightTuple.getStagedNext();
                if ( ltm == null || (ltm.size() == 0 && rightTuple.getBlocked() == null) ) {
                    // do nothing here, as we know there are no left tuples

                    //normally do this at the end, but as we are exiting early, make sure the buckets are still correct.
                    rtm.removeAdd( rightTuple );
                    rightTuple.clearStaged();
                    rightTuple = next;                   
                    continue;
                }

                PropagationContext context = rightTuple.getPropagationContext();

                constraints.updateFromFactHandle( contextEntry,
                                                  wm,
                                                  rightTuple.getFactHandle() );

                LeftTuple firstLeftTuple = notNode.getFirstLeftTuple( rightTuple, ltm, context, leftIt );

                LeftTuple firstBlocked = rightTuple.getBlocked();
                // we now have  reference to the first Blocked, so null it in the rightTuple itself, so we can rebuild
                rightTuple.nullBlocked();

                // first process non-blocked tuples, as we know only those ones are in the left memory.
                for ( LeftTuple leftTuple = firstLeftTuple; leftTuple != null; ) {
                    // preserve next now, in case we remove this leftTuple
                    LeftTuple temp = (LeftTuple) leftIt.next( leftTuple );
                   
                    if ( leftTuple.getStagedType() == LeftTuple.UPDATE ) {
                        // ignore, as it will get processed via left iteration. Children cannot be processed twice
                        leftTuple = temp;
                        continue;                       
                    }

                    // we know that only unblocked LeftTuples are  still in the memory
                    if ( constraints.isAllowedCachedRight( contextEntry,
                                                           leftTuple ) ) {
                        leftTuple.setBlocker( rightTuple );
                        rightTuple.addBlocked( leftTuple );

                        // this is now blocked so remove from memory
                        ltm.remove( leftTuple );

                        if ( leftTuple.getFirstChild() != null ) {
                            deleteRightChild( leftTuple.getFirstChild(), trgLeftTuples, stagedLeftTuples );
                        }
                    }

                    leftTuple = temp;
                }

                if ( firstBlocked != null ) {
                    // now process existing blocks, we only process existing and not new from above loop
                    boolean useComparisonIndex = rtm.getIndexType().isComparison();
                    RightTuple rootBlocker = useComparisonIndex ? null : (RightTuple) rightIt.next( rightTuple );

                    RightTupleList list = rightTuple.getMemory();

                    // we must do this after we have the next in memory
                    // We add to the end to give an opportunity to re-match if in same bucket
                    rtm.removeAdd( rightTuple );

                    if ( !useComparisonIndex && rootBlocker == null && list == rightTuple.getMemory() ) {
                        // we are at the end of the list, so set to self, to give self a chance to rematch
                        rootBlocker = rightTuple;
                    }

                    // iterate all the existing previous blocked LeftTuples
                    for ( LeftTuple leftTuple = firstBlocked; leftTuple != null; ) {
                        LeftTuple temp = leftTuple.getBlockedNext();

                        leftTuple.clearBlocker();
                       
                        if ( leftTuple.getStagedType() == LeftTuple.UPDATE ) {
                            // ignore, as it will get processed via left iteration. Children cannot be processed twice
                            // but need to add it back into list first
                            leftTuple.setBlocker( rightTuple );
                            rightTuple.addBlocked( leftTuple );
                           
                            leftTuple = temp;
                            continue;                       
                        }                       

                        constraints.updateFromTuple( contextEntry,
                                                     wm,
                                                     leftTuple );

                        if ( useComparisonIndex ) {
                            rootBlocker = notNode.getFirstRightTuple( leftTuple, rtm, context, rightIt );
                        }

                        // we know that older tuples have been checked so continue next
                        for ( RightTuple newBlocker = rootBlocker; newBlocker != null; newBlocker = (RightTuple) rightIt.next( newBlocker ) ) {
                            if ( constraints.isAllowedCachedLeft( contextEntry,
                                                                  newBlocker.getFactHandle() ) ) {
                                leftTuple.setBlocker( newBlocker );
                                newBlocker.addBlocked( leftTuple );

                                break;
                            }
                        }

                        if ( leftTuple.getBlocker() == null ) {
                            // was previous blocked and not in memory, so add
                            ltm.add( leftTuple );

                            // subclasses like ForallNotNode might override this propagation
                            trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple,
                                                                           sink,
                                                                           tupleMemory ) );
                        }

                        leftTuple = temp;
                    }
                } else {
                    // we had to do this at the end, rather than beginning as this 'if' block needs the next memory tuple
                    rtm.removeAdd( rightTuple );
                }
                rightTuple.clearStaged();
                rightTuple = next;
            }

            constraints.resetFactHandle( contextEntry );
            constraints.resetTuple( contextEntry );
        }

        public void doLeftDeletes(NotNode notNode,
                                  LeftTupleSink sink,
                                  BetaMemory bm,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples,
                                  LeftTupleSets stagedLeftTuples) {
            LeftTupleMemory ltm = bm.getLeftTupleMemory();

            for ( LeftTuple leftTuple = srcLeftTuples.getDeleteFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                RightTuple blocker = leftTuple.getBlocker();
                if ( blocker == null ) {
                    if ( leftTuple.getMemory() != null ) {
                        // it may have been staged and never actually added
                        ltm.remove( leftTuple );
                    }

                    LeftTuple childLeftTuple = leftTuple.getFirstChild();

                    if ( childLeftTuple != null ) { // NotNode only has one child
                        deleteLeftChild( childLeftTuple, trgLeftTuples, stagedLeftTuples );
                    }
                } else {
                    blocker.removeBlocked( leftTuple );
                }
                leftTuple.clearStaged();
                leftTuple = next;
            }
        }

        public void doRightDeletes(NotNode notNode,
                                   LeftTupleSink sink,
                                   BetaMemory bm,
                                   InternalWorkingMemory wm,
                                   RightTupleSets srcRightTuples,
                                   LeftTupleSets trgLeftTuples) {
            boolean tupleMemory = true;
            boolean tupleMemoryEnabled = true;

            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            RightTupleMemory rtm = bm.getRightTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = notNode.getRawConstraints();
            FastIterator it = notNode.getRightIterator( rtm );

            for ( RightTuple rightTuple = srcRightTuples.getDeleteFirst(); rightTuple != null; ) {
                RightTuple next = rightTuple.getStagedNext();

                // assign now, so we can remove from memory before doing any possible propagations
                boolean useComparisonIndex = rtm.getIndexType().isComparison();
                RightTuple rootBlocker = useComparisonIndex ? null : (RightTuple) it.next(rightTuple);

                if rightTuple.getMemory() != null ) {
                    // it may have been staged and never actually added
                    rtm.remove( rightTuple );
                }

                if ( rightTuple.getBlocked() != null ) {
                    PropagationContext context = rightTuple.getPropagationContext();
   
                    for ( LeftTuple leftTuple = rightTuple.getBlocked(); leftTuple != null; ) {
                        LeftTuple temp = leftTuple.getBlockedNext();
   
                        leftTuple.clearBlocker();
                       
                        if ( leftTuple.getStagedType() == LeftTuple.UPDATE ) {
                            // ignore, as it will get processed via left iteration. Children cannot be processed twice
                            leftTuple = temp;
                            continue;                       
                        }                       
   
                        constraints.updateFromTuple( contextEntry,
                                                     wm,
                                                     leftTuple );

                        if (useComparisonIndex) {
                            rootBlocker = rtm.getFirst( leftTuple, (InternalFactHandle) context.getFactHandle(), it );
                        }

                        // we know that older tuples have been checked so continue next
                        for ( RightTuple newBlocker = rootBlocker; newBlocker != null; newBlocker = (RightTuple) it.next( newBlocker ) ) {
                            if ( constraints.isAllowedCachedLeft( contextEntry,
                                                                  newBlocker.getFactHandle() ) ) {
                                leftTuple.setBlocker( newBlocker );
                                newBlocker.addBlocked( leftTuple );
   
                                break;
                            }
                        }
   
                        if ( leftTuple.getBlocker() == null ) {
                            // was previous blocked and not in memory, so add
                            ltm.add( leftTuple );
   
                            trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple,
                                                                           sink,
                                                                           tupleMemory ) );
                        }
   
                        leftTuple = temp;
                    }
                }

                rightTuple.nullBlocked();
                rightTuple.clearStaged();
                rightTuple = next;
            }     

            constraints.resetTuple( contextEntry );
        }
    }

    public static class PhreakExistsNode {
        public void doNode(ExistsNode existsNode,
                           LeftTupleSink sink,
                           BetaMemory bm,
                           InternalWorkingMemory wm,
                           LeftTupleSets srcLeftTuples,
                           LeftTupleSets trgLeftTuples,
                           LeftTupleSets stagedLeftTuples) {
            RightTupleSets srcRightTuples = bm.getStagedRightTuples();

            if ( srcRightTuples.getDeleteFirst() != null ) {
                doRightDeletes( existsNode, bm, wm, srcRightTuples, trgLeftTuples, stagedLeftTuples );
            }

            if ( srcLeftTuples.getDeleteFirst() != null ) {
                doLeftDeletes( existsNode, bm, wm, srcLeftTuples, trgLeftTuples, stagedLeftTuples );
            }

            if ( srcLeftTuples.getUpdateFirst() != null || srcRightTuples.getUpdateFirst() != null ) {
                dpUpdatesReorderLeftMemory( bm,
                                        wm,
                                        srcRightTuples,
                                        srcLeftTuples );
            }

            if ( srcRightTuples.getUpdateFirst() != null ) {
                doRightUpdates( existsNode, sink, bm, wm, srcRightTuples, trgLeftTuples, stagedLeftTuples );
            }

            if ( srcLeftTuples.getUpdateFirst() != null ) {
                doLeftUpdates( existsNode, sink, bm, wm, srcLeftTuples, trgLeftTuples, stagedLeftTuples );
            }

            if ( srcRightTuples.getInsertFirst() != null ) {
                doRightInserts( existsNode, sink, bm, wm, srcRightTuples, trgLeftTuples );
            }

            if ( srcLeftTuples.getInsertFirst() != null ) {
                doLeftInserts( existsNode, sink, bm, wm, srcLeftTuples, trgLeftTuples );
            }
           
            srcRightTuples.resetAll();                        
            srcLeftTuples.resetAll( );            
        }

        public void doLeftInserts(ExistsNode existsNode,
                                  LeftTupleSink sink,
                                  BetaMemory bm,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples) {
            boolean tupleMemory = true;
            boolean tupleMemoryEnabled = true;

            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            RightTupleMemory rtm = bm.getRightTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = existsNode.getRawConstraints();
            FastIterator it = existsNode.getRightIterator( rtm );

            for ( LeftTuple leftTuple = srcLeftTuples.getInsertFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                PropagationContext context = leftTuple.getPropagationContext();
                boolean useLeftMemory = true;
                if ( !tupleMemoryEnabled ) {
                    // This is a hack, to not add closed DroolsQuery objects
                    Object object = ((InternalFactHandle) context.getFactHandle()).getObject();
                    if ( !(object instanceof DroolsQuery) || !((DroolsQuery) object).isOpen() ) {
                        useLeftMemory = false;
                    }
                }

                constraints.updateFromTuple( contextEntry,
                                             wm,
                                             leftTuple );
               
                for ( RightTuple rightTuple = existsNode.getFirstRightTuple(leftTuple, rtm, context, it); rightTuple != null; rightTuple = (RightTuple) it.next(rightTuple)) {
                    if ( constraints.isAllowedCachedLeft( contextEntry,
                                                          rightTuple.getFactHandle() ) ) {

                        leftTuple.setBlocker( rightTuple );
                        if ( useLeftMemory ) {
                            rightTuple.addBlocked( leftTuple );
                        }

                        break;
                    }
                }

                if ( leftTuple.getBlocker() != null ) {
                    // tuple is not blocked to propagate
                    trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple,
                                                                   sink,
                                                                   tupleMemory ) );
                } else if ( useLeftMemory ) {
                    // LeftTuple is not blocked, so add to memory so other RightTuples can match
                    ltm.add( leftTuple );
                }
                leftTuple.clearStaged();
                leftTuple = next;
            }
            constraints.resetTuple( contextEntry );
        }

        public void doRightInserts(ExistsNode existsNode,
                                   LeftTupleSink sink,
                                   BetaMemory bm,
                                   InternalWorkingMemory wm,
                                   RightTupleSets srcRightTuples,
                                   LeftTupleSets trgLeftTuples) {
            boolean tupleMemory = true;
            boolean tupleMemoryEnabled = true;

            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            RightTupleMemory rtm = bm.getRightTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = existsNode.getRawConstraints();
            FastIterator it = existsNode.getLeftIterator( ltm );

            for ( RightTuple rightTuple = srcRightTuples.getInsertFirst(); rightTuple != null; ) {
                RightTuple next = rightTuple.getStagedNext();
                rtm.add( rightTuple );
                PropagationContext context = rightTuple.getPropagationContext();

                constraints.updateFromFactHandle( contextEntry,
                                                  wm,
                                                  rightTuple.getFactHandle() );

                for ( LeftTuple leftTuple = existsNode.getFirstLeftTuple( rightTuple, ltm, context, it ); leftTuple != null; leftTuple = (LeftTuple) it.next( leftTuple ) ) {
                    // preserve next now, in case we remove this leftTuple
                    LeftTuple temp = (LeftTuple) it.next(leftTuple);
                   
                    if ( leftTuple.getStagedType() == LeftTuple.UPDATE ) {
                        // ignore, as it will get processed via left iteration. Children cannot be processed twice
                        leftTuple = temp;
                        continue;                       
                    }                    

                    // we know that only unblocked LeftTuples are  still in the memory
                    if ( constraints.isAllowedCachedRight( contextEntry,
                                                           leftTuple ) ) {
                        leftTuple.setBlocker( rightTuple );
                        rightTuple.addBlocked( leftTuple );

                        ltm.remove( leftTuple );

                        trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple,
                                                                       sink,
                                                                       tupleMemory ) );
                    }

                    leftTuple = temp;
                }
                rightTuple.clearStaged();
                rightTuple = next;
            }
            constraints.resetFactHandle( contextEntry );
        }

        public void doLeftUpdates(ExistsNode existsNode,
                                  LeftTupleSink sink,
                                  BetaMemory bm,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples,
                                  LeftTupleSets stagedLeftTuples) {
            boolean tupleMemory = true;
            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            RightTupleMemory rtm = bm.getRightTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = existsNode.getRawConstraints();
            FastIterator rightIt = existsNode.getRightIterator( rtm );

            for ( LeftTuple leftTuple = srcLeftTuples.getUpdateFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                PropagationContext context = leftTuple.getPropagationContext();
               
                RightTuple firstRightTuple = existsNode.getFirstRightTuple(leftTuple, rtm, context, rightIt);
               
                // If in memory, remove it, because we'll need to add it anyway if it's not blocked, to ensure iteration order
                RightTuple blocker = leftTuple.getBlocker();
                if ( blocker == null ) {
                    ltm.remove( leftTuple );
                } else {
                    // check if we changed bucket
                    if ( rtm.isIndexed()&& !rightIt.isFullIterator()  ) {               
                        // if newRightTuple is null, we assume there was a bucket change and that bucket is empty               
                        if ( firstRightTuple == null || firstRightTuple.getMemory() != blocker.getMemory() ) {
                            // we changed bucket, so blocker no longer blocks
                            blocker.removeBlocked( leftTuple );
                            blocker = null;
                        }
                    }
                }

                constraints.updateFromTuple( contextEntry,
                                             wm,
                                             leftTuple );

                // if we where not blocked before (or changed buckets), or the previous blocker no longer blocks, then find the next blocker
                if ( blocker == null || !constraints.isAllowedCachedLeft( contextEntry,
                                                                          blocker.getFactHandle() ) ) {

                    if ( blocker != null ) {
                        // remove previous blocker if it exists, as we know it doesn't block any more
                        blocker.removeBlocked( leftTuple );
                    }
                   
                    // find first blocker, because it's a modify, we need to start from the beginning again       
                    for ( RightTuple newBlocker = firstRightTuple; newBlocker != null; newBlocker = (RightTuple) rightIt.next(newBlocker) ) {
                        if ( constraints.isAllowedCachedLeft( contextEntry,
                                                              newBlocker.getFactHandle() ) ) {
                            leftTuple.setBlocker( newBlocker );
                            newBlocker.addBlocked( leftTuple );

                            break;
                        }
                    }
                }

                if ( leftTuple.getBlocker() == null ) {
                    // not blocked
                    ltm.add( leftTuple ); // add to memory so other fact handles can attempt to match                   

                    if ( leftTuple.getFirstChild() != null ) {
                        // with previous children, delete
                        if ( leftTuple.getFirstChild() != null ) {
                            LeftTuple childLeftTuple = leftTuple.getFirstChild();

                            if ( childLeftTuple != null ) {
                                childLeftTuple = deleteLeftChild( childLeftTuple, trgLeftTuples, stagedLeftTuples );
                            }
                        }
                    }
                    // with no previous children. do nothing.
                } else if ( leftTuple.getFirstChild() == null ) {
                    // blocked, with no previous children, insert
                    trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple,
                                                                   sink,
                                                                   tupleMemory ) );
                } else {
                    // blocked, with previous children, modify
                    if ( leftTuple.getFirstChild() != null ) {
                        LeftTuple childLeftTuple = leftTuple.getFirstChild();

                        while ( childLeftTuple != null ) {
                            switch ( childLeftTuple.getStagedType() ) {
                                // handle clash with already staged entries
                                case LeftTuple.INSERT :
                                    stagedLeftTuples.removeInsert( childLeftTuple );
                                    break;
                                case LeftTuple.UPDATE :
                                    stagedLeftTuples.removeUpdate( childLeftTuple );
                                    break;
                            }

                            // update, childLeftTuple is updated
                            trgLeftTuples.addUpdate( childLeftTuple );
                            childLeftTuple.reAddRight();
                            childLeftTuple = childLeftTuple.getLeftParentNext();
                        }
                    }                   
                }
               
                leftTuple.clearStaged();
                leftTuple = next;
            }
            constraints.resetTuple( contextEntry );
        }

        public void doRightUpdates(ExistsNode existsNode,
                                   LeftTupleSink sink,
                                   BetaMemory bm,
                                   InternalWorkingMemory wm,
                                   RightTupleSets srcRightTuples,
                                   LeftTupleSets trgLeftTuples,
                                   LeftTupleSets stagedLeftTuples) {
            boolean tupleMemory = true;
            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            RightTupleMemory rtm = bm.getRightTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = existsNode.getRawConstraints();
            FastIterator leftIt = existsNode.getLeftIterator( ltm );
            FastIterator rightIt = existsNode.getRightIterator( rtm );

            for ( RightTuple rightTuple = srcRightTuples.getUpdateFirst(); rightTuple != null; ) {
                RightTuple next = rightTuple.getStagedNext();
                PropagationContext context = rightTuple.getPropagationContext();
               
                LeftTuple firstLeftTuple = existsNode.getFirstLeftTuple( rightTuple, ltm, context, leftIt );
               
                LeftTuple firstBlocked = rightTuple.getBlocked();
                // we now have  reference to the first Blocked, so null it in the rightTuple itself, so we can rebuild
                rightTuple.nullBlocked();
               
                // first process non-blocked tuples, as we know only those ones are in the left memory.
                for ( LeftTuple leftTuple = firstLeftTuple; leftTuple != null; ) {
                    // preserve next now, in case we remove this leftTuple
                    LeftTuple temp = (LeftTuple) leftIt.next( leftTuple );
                   
                    if ( leftTuple.getStagedType() == LeftTuple.UPDATE ) {
                        // ignore, as it will get processed via left iteration. Children cannot be processed twice
                        leftTuple = temp;
                        continue;                       
                    }

                    // we know that only unblocked LeftTuples are  still in the memory
                    if ( constraints.isAllowedCachedRight( contextEntry,
                                                           leftTuple ) ) {
                        leftTuple.setBlocker( rightTuple );
                        rightTuple.addBlocked( leftTuple );

                        // this is now blocked so remove from memory
                        ltm.remove( leftTuple );

                        // subclasses like ForallNotNode might override this propagation
                        trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple,
                                                                       sink,
                                                                       tupleMemory ) );
                    }

                    leftTuple = temp;
                }

                if ( firstBlocked != null ) {
                    boolean useComparisonIndex = rtm.getIndexType().isComparison();

                    // now process existing blocks, we only process existing and not new from above loop
                    RightTuple rootBlocker = useComparisonIndex ? null : (RightTuple) rightIt.next(rightTuple);
                 
                    RightTupleList list = rightTuple.getMemory();
                   
                    // we must do this after we have the next in memory
                    // We add to the end to give an opportunity to re-match if in same bucket
                    rtm.removeAdd( rightTuple );

                    if ( !useComparisonIndex && rootBlocker == null && list == rightTuple.getMemory() ) {
                        // we are at the end of the list, but still in same bucket, so set to self, to give self a chance to rematch
                        rootBlocker = rightTuple;
                   
                   
                    // iterate all the existing previous blocked LeftTuples
                    for ( LeftTuple leftTuple = (LeftTuple) firstBlocked; leftTuple != null; ) {
                        LeftTuple temp = leftTuple.getBlockedNext();

                        leftTuple.clearBlocker(); // must null these as we are re-adding them to the list
                       
                        if ( leftTuple.getStagedType() == LeftTuple.UPDATE ) {
                            // ignore, as it will get processed via left iteration. Children cannot be processed twice
                            // but need to add it back into list first
                            leftTuple.setBlocker( rightTuple );
                            rightTuple.addBlocked( leftTuple );
                           
                            leftTuple = temp;
                            continue;                       
                        }

                        constraints.updateFromTuple( contextEntry,
                                                     wm,
                                                     leftTuple );

                        if (useComparisonIndex) {
                            rootBlocker = existsNode.getFirstRightTuple( leftTuple, rtm, context, rightIt );
                        }

                        // we know that older tuples have been checked so continue next
                        for ( RightTuple newBlocker = rootBlocker; newBlocker != null; newBlocker = (RightTuple) rightIt.next( newBlocker ) ) {
                            if ( constraints.isAllowedCachedLeft( contextEntry,
                                                                  newBlocker.getFactHandle() ) ) {
                                leftTuple.setBlocker( newBlocker );
                                newBlocker.addBlocked( leftTuple );

                                break;
                            }
                        }

                        if ( leftTuple.getBlocker() == null ) {
                            // was previous blocked and not in memory, so add
                            ltm.add( leftTuple );

                            LeftTuple childLeftTuple = leftTuple.getFirstChild();
                            if ( childLeftTuple != null ) {
                                childLeftTuple = deleteLeftChild( childLeftTuple, trgLeftTuples, stagedLeftTuples );
                            }
                        }

                        leftTuple = temp;
                    }
                } else {
                    // we had to do this at the end, rather than beginning as this 'if' block needs the next memory tuple
                    rtm.removeAdd( rightTuple );        
                }
               
                rightTuple.clearStaged();
                rightTuple = next;
            }
            constraints.resetFactHandle( contextEntry );
        }

        public void doLeftDeletes(ExistsNode existsNode,
                                  BetaMemory bm,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples,
                                  LeftTupleSets stagedLeftTuples) {
            LeftTupleMemory ltm = bm.getLeftTupleMemory();

            for ( LeftTuple leftTuple = srcLeftTuples.getDeleteFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                RightTuple blocker = leftTuple.getBlocker();
                if ( blocker == null ) {
                    if ( leftTuple.getMemory() != null ) {
                        // it may have been staged and never actually added
                        ltm.remove( leftTuple );
                    }                  
                } else {
                    if ( leftTuple.getFirstChild() != null ) {
                        LeftTuple childLeftTuple = leftTuple.getFirstChild();

                        if ( childLeftTuple != null ) {
                            childLeftTuple = deleteLeftChild( childLeftTuple, trgLeftTuples, stagedLeftTuples );
                        }
                    }                   
                    blocker.removeBlocked( leftTuple );
                }

                leftTuple.clearStaged();
                leftTuple = next;
            }
        }

        public void doRightDeletes(ExistsNode existsNode,
                                   BetaMemory bm,
                                   InternalWorkingMemory wm,
                                   RightTupleSets srcRightTuples,
                                   LeftTupleSets trgLeftTuples,
                                   LeftTupleSets stagedLeftTuples) {
            boolean tupleMemory = true;
            RightTupleMemory rtm = bm.getRightTupleMemory();
            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = existsNode.getRawConstraints();
            FastIterator it = existsNode.getRightIterator( rtm );           

            for ( RightTuple rightTuple = srcRightTuples.getDeleteFirst(); rightTuple != null; ) {
                RightTuple next = rightTuple.getStagedNext();

                boolean useComparisonIndex = rtm.getIndexType().isComparison();
                RightTuple rootBlocker = useComparisonIndex ? null : (RightTuple) it.next(rightTuple);

                if rightTuple.getMemory() != null ) {
                    // it may have been staged and never actually added
                    rtm.remove( rightTuple );
                }
                                      
                if ( rightTuple.getBlocked() != null ) {

                    PropagationContext context = rightTuple.getPropagationContext();

                    for ( LeftTuple leftTuple = rightTuple.getBlocked(); leftTuple != null; ) {
                        LeftTuple temp = leftTuple.getBlockedNext();
   
                        leftTuple.clearBlocker();
   
                        if ( leftTuple.getStagedType() == LeftTuple.UPDATE ) {
                            // ignore, as it will get processed via left iteration. Children cannot be processed twice
                            leftTuple = temp;
                            continue;                       
                        }
                       
                        constraints.updateFromTuple( contextEntry,
                                                      wm,
                                                      leftTuple );

                        if (useComparisonIndex) {
                            rootBlocker = rtm.getFirst( leftTuple, (InternalFactHandle) context.getFactHandle(), it );
                        }

                        // we know that older tuples have been checked so continue previously
                        for ( RightTuple newBlocker = rootBlocker; newBlocker != null; newBlocker = (RightTuple) it.next(newBlocker ) ) {
                            if ( constraints.isAllowedCachedLeft( contextEntry,
                                                                  newBlocker.getFactHandle() ) ) {
                                leftTuple.setBlocker( newBlocker );
                                newBlocker.addBlocked( leftTuple );
   
                                break;
                            }
                        }
   
                        if ( leftTuple.getBlocker() == null ) {
                            // was previous blocked and not in memory, so add
                            ltm.add( leftTuple );
   
                            LeftTuple childLeftTuple = leftTuple.getFirstChild();
                            if ( childLeftTuple != null ) {
                                childLeftTuple = deleteLeftChild( childLeftTuple, trgLeftTuples, stagedLeftTuples );
                            }
                        }
   
                        leftTuple = temp;
                    }
                }
                rightTuple.nullBlocked();
                rightTuple.clearStaged();
                rightTuple = next;
            }
        }
    }   
   
    public static class PhreakAccumulateNode {
        public void doNode(AccumulateNode accNode,
                           LeftTupleSink sink,
                           AccumulateMemory am,
                           InternalWorkingMemory wm,
                           LeftTupleSets srcLeftTuples,
                           LeftTupleSets trgLeftTuples,
                           LeftTupleSets stagedLeftTuples) {
            boolean useLeftMemory = true;
            RightTupleSets srcRightTuples = am.getBetaMemory().getStagedRightTuples();
           
            // We need to collect which leftTuple where updated, so that we can
            // add their result tuple to the real target tuples later
            LeftTupleSets tempLeftTuples = new LeftTupleSets();

            if ( srcLeftTuples.getDeleteFirst() != null ) {
                // use the real target here, as dealing direct with left tuples
                doLeftDeletes( accNode, am, wm, srcLeftTuples, trgLeftTuples );
            }
           
            if ( srcRightTuples.getDeleteFirst() != null ) {
                doRightDeletes( accNode, am, wm, srcRightTuples, tempLeftTuples );
            }           

            if ( srcLeftTuples.getUpdateFirst() != null || srcRightTuples.getUpdateFirst() != null ) {
                dpUpdatesReorderLeftMemory( am.getBetaMemory(), wm,
                                            srcRightTuples, srcLeftTuples );
                dpUpdatesReorderRightMemory( am.getBetaMemory(), wm,
                                             srcRightTuples, srcLeftTuples );               
            }
           
            if ( srcLeftTuples.getUpdateFirst() != null ) {
                doLeftUpdates( accNode, sink, am, wm, srcLeftTuples, tempLeftTuples );
            }           

            if ( srcRightTuples.getUpdateFirst() != null ) {
                doRightUpdates( accNode, sink, am, wm, srcRightTuples, tempLeftTuples );
            }

            if ( srcRightTuples.getInsertFirst() != null ) {
                doRightInserts( accNode, sink, am, wm, srcRightTuples, tempLeftTuples );
            }

            if ( srcLeftTuples.getInsertFirst() != null ) {
                doLeftInserts( accNode, sink, am, wm, srcLeftTuples, tempLeftTuples );
            }
           
            Accumulate accumulate = accNode.getAccumulate();
            // we do not need collect retracts. RightTuple retracts end up as updates for lefttuples.
            // LeftTuple retracts are already on the trgLeftTuples
            for ( LeftTuple leftTuple = tempLeftTuples.getInsertFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                evaluateResultConstraints( accNode, sink, accumulate, leftTuple, leftTuple.getPropagationContext(),
                                           wm, am, (AccumulateContext) leftTuple.getObject(), useLeftMemory,
                                           trgLeftTuples, stagedLeftTuples );
                leftTuple.clearStaged();
                leftTuple = next;               
            }
           
            for ( LeftTuple leftTuple = tempLeftTuples.getUpdateFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                evaluateResultConstraints( accNode, sink, accumulate, leftTuple, leftTuple.getPropagationContext(),
                                           wm, am, (AccumulateContext) leftTuple.getObject(), useLeftMemory,
                                           trgLeftTuples, stagedLeftTuples );
                leftTuple.clearStaged();
                leftTuple = next;                 
            }           
           
            srcRightTuples.resetAll();            
           
            srcLeftTuples.resetAll();           
        }

        public void doLeftInserts(AccumulateNode accNode,
                                  LeftTupleSink sink,
                                  AccumulateMemory am,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples) {
            boolean tupleMemory = true;
            boolean tupleMemoryEnabled = true;

            Accumulate accumulate = accNode.getAccumulate();           
            BetaMemory bm = am.getBetaMemory();
            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            RightTupleMemory rtm = bm.getRightTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = accNode.getRawConstraints();
            FastIterator it = accNode.getRightIterator( rtm );

            for ( LeftTuple leftTuple = srcLeftTuples.getInsertFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                PropagationContext context = leftTuple.getPropagationContext();
               
                AccumulateContext accresult = new AccumulateContext();

                boolean useLeftMemory = true;
                if ( !tupleMemoryEnabled ) {
                    // This is a hack, to not add closed DroolsQuery objects
                    Object object = ((InternalFactHandle) leftTuple.get( 0 )).getObject();
                    if ( !(object instanceof DroolsQuery) || !((DroolsQuery) object).isOpen() ) {
                        useLeftMemory = false;
                    }
                }

                if ( useLeftMemory ) {
                    ltm.add( leftTuple );
                    leftTuple.setObject( accresult );
                }        
               
                accresult.context = accumulate.createContext();

                accumulate.init( am.workingMemoryContext,
                                 accresult.context,
                                 leftTuple,
                                 wm );

                constraints.updateFromTuple( contextEntry,
                                             wm,
                                             leftTuple );

                FastIterator rightIt = accNode.getRightIterator( rtm );

                for ( RightTuple rightTuple = accNode.getFirstRightTuple( leftTuple,
                                                                          rtm,
                                                                          context,
                                                                          rightIt ); rightTuple != null; rightTuple = (RightTuple) rightIt.next( rightTuple ) ) {
                    InternalFactHandle handle = rightTuple.getFactHandle();
                    if ( constraints.isAllowedCachedLeft( contextEntry,
                                                          handle ) ) {
                        // add a match
                        addMatch( accNode,
                                  accumulate,
                                  leftTuple,
                                  rightTuple,
                                  null,
                                  null,
                                  wm,
                                  am,
                                  accresult,
                                  useLeftMemory );
                    }
                }

                leftTuple.clearStaged();
                trgLeftTuples.addInsert( leftTuple );
               
                constraints.resetTuple( contextEntry );             
               
                leftTuple = next;
            }
            constraints.resetTuple( contextEntry );
        }

        public void doRightInserts(AccumulateNode accNode,
                                   LeftTupleSink sink,
                                   AccumulateMemory am,
                                   InternalWorkingMemory wm,
                                   RightTupleSets srcRightTuples,
                                   LeftTupleSets trgLeftTuples) {
            boolean tupleMemory = true;
            boolean tupleMemoryEnabled = true;


            Accumulate accumulate = accNode.getAccumulate();           

            BetaMemory bm = am.getBetaMemory();
            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            RightTupleMemory rtm = bm.getRightTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = accNode.getRawConstraints();
            FastIterator it = accNode.getLeftIterator( ltm );

            for ( RightTuple rightTuple = srcRightTuples.getInsertFirst(); rightTuple != null; ) {
                RightTuple next = rightTuple.getStagedNext();
                rtm.add( rightTuple );
                PropagationContext context = rightTuple.getPropagationContext();

                constraints.updateFromFactHandle( contextEntry,
                                                  wm,
                                                  rightTuple.getFactHandle() );

                FastIterator leftIt = accNode.getLeftIterator( ltm );
               
                for ( LeftTuple leftTuple = accNode.getFirstLeftTuple( rightTuple, ltm, context, leftIt ); leftTuple != null; leftTuple = (LeftTuple) leftIt.next( leftTuple ) ) {
                    if ( constraints.isAllowedCachedRight( contextEntry,
                                                           leftTuple ) ) {
                        final AccumulateContext accctx = (AccumulateContext) leftTuple.getObject();
                        addMatch( accNode,
                                  accumulate,
                                  leftTuple,
                                  rightTuple,
                                  null,
                                  null,
                                  wm,
                                  am,
                                  accctx,
                                  tupleMemoryEnabled );
                       
                        // right inserts and updates are done first
                        // so any existing leftTuples we know are updates, but only add if not already added
                        if ( leftTuple.getStagedType() == LeftTuple.NONE ) {
                            trgLeftTuples.addUpdate( leftTuple );
                        }
                       
                    }
                }

                rightTuple.clearStaged();
                rightTuple = next;
            }
            constraints.resetFactHandle( contextEntry );
        }

        public void doLeftUpdates(AccumulateNode accNode,
                                  LeftTupleSink sink,
                                  AccumulateMemory am,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples) {
            boolean tupleMemory = true;
           
            BetaMemory bm = am.getBetaMemory();
            RightTupleMemory rtm = bm.getRightTupleMemory();           
            Accumulate accumulate = accNode.getAccumulate();                       
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = accNode.getRawConstraints();
            FastIterator rightIt = accNode.getRightIterator( rtm );

            for ( LeftTuple leftTuple = srcLeftTuples.getUpdateFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                final AccumulateContext accctx = (AccumulateContext) leftTuple.getObject();
                PropagationContext context = leftTuple.getPropagationContext();

                constraints.updateFromTuple( contextEntry,
                                             wm,
                                             leftTuple );

                RightTuple rightTuple = accNode.getFirstRightTuple( leftTuple,
                                                                    rtm,
                                                                    context,
                                                                    rightIt );

                LeftTuple childLeftTuple = leftTuple.getFirstChild();

                // first check our index (for indexed nodes only) hasn't changed and we are returning the same bucket
                // if rightTuple is null, we assume there was a bucket change and that bucket is empty
                if ( childLeftTuple != null && rtm.isIndexed() && !rightIt.isFullIterator() &&  (rightTuple == null || (rightTuple.getMemory() !=  childLeftTuple.getRightParent().getMemory())) ) {
                    // our index has changed, so delete all the previous matchings
                    removePreviousMatchesForLeftTuple( accNode,
                                                       accumulate,
                                                       leftTuple,
                                                       wm,
                                                       am,
                                                       accctx,
                                                       true);

                    childLeftTuple = null; // null so the next check will attempt matches for new bucket
                }

                // we can't do anything if RightTupleMemory is empty
                if ( rightTuple != null ) {
                    doLeftUpdatesProcessChildren( accNode,
                                                  am,
                                                  wm,
                                                  bm,
                                                  accumulate,
                                                  constraints,
                                                  rightIt,
                                                  leftTuple,
                                                  accctx,
                                                  rightTuple,
                                                  childLeftTuple );
                }
               
                leftTuple.clearStaged();
                trgLeftTuples.addUpdate( leftTuple );            
               
                leftTuple = next;
            }
            constraints.resetTuple( contextEntry );
        }

        private void doLeftUpdatesProcessChildren(AccumulateNode accNode,
                                                  AccumulateMemory am,
                                                  InternalWorkingMemory wm,
                                                  BetaMemory bm,
                                                  Accumulate accumulate,
                                                  BetaConstraints constraints,
                                                  FastIterator rightIt,
                                                  LeftTuple leftTuple,
                                                  final AccumulateContext accctx,
                                                  RightTuple rightTuple,
                                                  LeftTuple childLeftTuple) {
            if ( childLeftTuple == null ) {
                // either we are indexed and changed buckets or
                // we had no children before, but there is a bucket to potentially match, so try as normal assert
                for ( ; rightTuple != null; rightTuple = (RightTuple) rightIt.next( rightTuple ) ) {
                    final InternalFactHandle handle = rightTuple.getFactHandle();
                    if ( constraints.isAllowedCachedLeft( bm.getContext(),
                                                          handle ) ) {
                        // add a new match
                        addMatch( accNode,
                                  accumulate,
                                  leftTuple,
                                  rightTuple,
                                  null,
                                  null,
                                  wm,
                                  am,
                                  accctx,
                                  true );
                    }
                }
            } else {
                boolean isDirty = false;
                // in the same bucket, so iterate and compare
                for ( ; rightTuple != null; rightTuple = (RightTuple) rightIt.next( rightTuple ) ) {
                    final InternalFactHandle handle = rightTuple.getFactHandle();

                    if ( constraints.isAllowedCachedLeft( bm.getContext(),
                                                         handle ) ) {
                        if ( childLeftTuple == null || childLeftTuple.getRightParent() != rightTuple ) {
                            // add a new match
                            addMatch( accNode,
                                      accumulate,
                                      leftTuple,
                                      rightTuple,
                                      childLeftTuple,
                                      null,
                                      wm,
                                      am,
                                      accctx,
                                      true );
                        } else {
                            // we must re-add this to ensure deterministic iteration
                            LeftTuple temp = childLeftTuple.getLeftParentNext();
                            childLeftTuple.reAddRight();
                            childLeftTuple = temp;
                        }
                    } else if ( childLeftTuple != null && childLeftTuple.getRightParent() == rightTuple ) {
                        LeftTuple temp = childLeftTuple.getLeftParentNext();
                        // remove the match
                        removeMatch( accNode,
                                     accumulate,
                                     rightTuple,
                                     childLeftTuple,
                                     wm,
                                     am,
                                     accctx,
                                     false );
                        childLeftTuple = temp;
                        // the next line means that when a match is removed from the current leftTuple
                        // and the accumulate does not support the reverse operation, then the whole
                        // result is dirty (since removeMatch above is not recalculating the total)
                        // and we need to do this later
                        isDirty = !accumulate.supportsReverse();
                    }
                    // else do nothing, was false before and false now.
                }
                if ( isDirty ) {
                    reaccumulateForLeftTuple( accNode,
                                              accumulate,
                                              leftTuple,
                                              wm,
                                              am,
                                              accctx );
                }
            }
        }

        public void doRightUpdates(AccumulateNode accNode,
                                   LeftTupleSink sink,
                                   AccumulateMemory am,
                                   InternalWorkingMemory wm,
                                   RightTupleSets srcRightTuples,
                                   LeftTupleSets trgLeftTuples) {
            boolean tupleMemory = true;
           
            BetaMemory bm = am.getBetaMemory();
            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            BetaConstraints constraints = accNode.getRawConstraints();
            Accumulate accumulate = accNode.getAccumulate();
            FastIterator leftIt = accNode.getLeftIterator( ltm );

            for ( RightTuple rightTuple = srcRightTuples.getUpdateFirst(); rightTuple != null; ) {
                RightTuple next = rightTuple.getStagedNext();
                PropagationContext context = rightTuple.getPropagationContext();

                LeftTuple childLeftTuple = rightTuple.getFirstChild();

                LeftTuple leftTuple = accNode.getFirstLeftTuple( rightTuple, ltm, context, leftIt );

                constraints.updateFromFactHandle( contextEntry,
                                                  wm,
                                                  rightTuple.getFactHandle() );

                // first check our index (for indexed nodes only) hasn't changed and we are returning the same bucket
                // We assume a bucket change if leftTuple == null
                if ( childLeftTuple != null && ltm.isIndexed() && !leftIt.isFullIterator() && (leftTuple == null || (leftTuple.getMemory() != childLeftTuple.getLeftParent().getMemory())) ) {
                    // our index has changed, so delete all the previous matches
                    removePreviousMatchesForRightTuple( accNode,
                                                        accumulate,
                                                        rightTuple,
                                                        context,
                                                        wm,
                                                        am,
                                                        childLeftTuple,
                                                        trgLeftTuples );
                    childLeftTuple = null; // null so the next check will attempt matches for new bucket
                }

                // if LeftTupleMemory is empty, there are no matches to modify
                if ( leftTuple != null ) {
                    if ( leftTuple.getStagedType() == LeftTuple.NONE ) {
                        trgLeftTuples.addUpdate( leftTuple );
                    }
                   
                    doRightUpdatesProcessChildren( accNode,
                                                   am,
                                                   wm,
                                                   bm,
                                                   constraints,
                                                   accumulate,
                                                   leftIt,
                                                   rightTuple,
                                                   childLeftTuple,
                                                   leftTuple,
                                                   trgLeftTuples );
                }
               
                rightTuple.clearStaged();
                rightTuple = next;
            }
            constraints.resetFactHandle( contextEntry );
        }

        private void doRightUpdatesProcessChildren(AccumulateNode accNode,
                                                   AccumulateMemory am,
                                                   InternalWorkingMemory wm,
                                                   BetaMemory bm,
                                                   BetaConstraints constraints,
                                                   Accumulate accumulate,
                                                   FastIterator leftIt,
                                                   RightTuple rightTuple,
                                                   LeftTuple childLeftTuple,
                                                   LeftTuple leftTuple,
                                                   LeftTupleSets trgLeftTuples) {
            if ( childLeftTuple == null ) {
                // either we are indexed and changed buckets or
                // we had no children before, but there is a bucket to potentially match, so try as normal assert
                for ( ; leftTuple != null; leftTuple = ( LeftTuple ) leftIt.next( leftTuple ) ) {
                    if (constraints.isAllowedCachedRight( bm.getContext(),
                                                          leftTuple ) ) {
                        if ( leftTuple.getStagedType() == LeftTuple.NONE ) {
                            trgLeftTuples.addUpdate( leftTuple );
                        }                       
                        final AccumulateContext accctx = (AccumulateContext) leftTuple.getObject();
                        // add a new match
                        addMatch( accNode,
                                  accumulate,
                                  leftTuple,
                                  rightTuple,
                                  null,
                                  null,
                                  wm,
                                  am,
                                  accctx,
                                  true );
                    }
                }
            } else {
                // in the same bucket, so iterate and compare
                for ( ; leftTuple != null; leftTuple = (LeftTuple) leftIt.next( leftTuple ) ) {                   
                    if ( constraints.isAllowedCachedRight( bm.getContext(),
                                                          leftTuple ) ) { 
                        if ( leftTuple.getStagedType() == LeftTuple.NONE ) {
                            trgLeftTuples.addUpdate( leftTuple );
                       
                        final AccumulateContext accctx = (AccumulateContext) leftTuple.getObject();
                        LeftTuple temp = null;
                        if ( childLeftTuple != null && childLeftTuple.getLeftParent() == leftTuple ) {
                            temp = childLeftTuple.getRightParentNext();
                            // we must re-add this to ensure deterministic iteration                           
                            removeMatch( accNode,
                                         accumulate,
                                         rightTuple,
                                         childLeftTuple,
                                         wm,
                                         am,
                                         accctx,
                                         true );
                            childLeftTuple = temp;
                        }
                        // add a new match
                        addMatch( accNode,
                                  accumulate,
                                  leftTuple,
                                  rightTuple,
                                  null,
                                  childLeftTuple,
                                  wm,
                                  am,
                                  accctx,
                                  true );
                        if ( temp != null ) {
                            childLeftTuple = temp;
                        }
                    } else if ( childLeftTuple != null && childLeftTuple.getLeftParent() == leftTuple ) {
                        if ( leftTuple.getStagedType() == LeftTuple.NONE ) {
                            trgLeftTuples.addUpdate( leftTuple );
                       
                       
                        LeftTuple temp = childLeftTuple.getRightParentNext();
                        final AccumulateContext accctx = (AccumulateContext)leftTuple.getObject();
                        // remove the match
                        removeMatch( accNode,
                                     accumulate,
                                     rightTuple,
                                     childLeftTuple,
                                     wm,
                                     am,
                                     accctx,
                                     true );

                        childLeftTuple = temp;
                    }
                    // else do nothing, was false before and false now.
                }
            }
        }


        public void doLeftDeletes(AccumulateNode accNode,
                                  AccumulateMemory am,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples) {
            BetaMemory bm = am.getBetaMemory();
            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            ContextEntry[] contextEntry = bm.getContext();
            Accumulate accumulate = accNode.getAccumulate();
           
            for ( LeftTuple leftTuple = srcLeftTuples.getDeleteFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                if ( leftTuple.getMemory() != null ) {
                    // it may have been staged and never actually added
                    ltm.remove( leftTuple );
   
               
                    final AccumulateContext accctx = (AccumulateContext) leftTuple.getObject();
                    leftTuple.setObject( null );
   
                    removePreviousMatchesForLeftTuple( accNode,
                                                       accumulate,
                                                       leftTuple,
                                                       wm,
                                                       am,
                                                       accctx,
                                                       false );
   
                    if ( accctx.propagated ) {
                        trgLeftTuples.addDelete( accctx.resultLeftTuple );
                    } else {
                        // if not propagated, just destroy the result fact handle
                        // workingMemory.getFactHandleFactory().destroyFactHandle( accctx.result.getFactHandle() );
                    }
                }
               
                leftTuple.clearStaged();
                leftTuple = next;
            }
        }

        public void doRightDeletes(AccumulateNode accNode,
                                   AccumulateMemory am,
                                   InternalWorkingMemory wm,
                                   RightTupleSets srcRightTuples,
                                   LeftTupleSets trgLeftTuples) {
            RightTupleMemory rtm = am.getBetaMemory().getRightTupleMemory();
            Accumulate accumulate = accNode.getAccumulate();

            for ( RightTuple rightTuple = srcRightTuples.getDeleteFirst(); rightTuple != null; ) {
                RightTuple next = rightTuple.getStagedNext();
                if rightTuple.getMemory() != null ) {
                    // it may have been staged and never actually added
                    rtm.remove( rightTuple );

                    if ( rightTuple.getFirstChild() != null ) {
                        LeftTuple match = rightTuple.getFirstChild();
   
                        while ( match != null ) {
                            LeftTuple nextLeft = match.getRightParentNext();;

                            LeftTuple leftTuple = match.getLeftParent();
                            final AccumulateContext accctx = (AccumulateContext) leftTuple.getObject();
                            removeMatch(accNode, accumulate, rightTuple, match, wm, am, accctx, true );  
                         
                            if ( leftTuple.getStagedType() == LeftTuple.NONE ) {
                                  trgLeftTuples.addUpdate( leftTuple);
                            }
                           
                            match.unlinkFromLeftParent();     
                           
                            match = nextLeft;
                        }
                    }
                }
                rightTuple.clearStaged();
                rightTuple = next;
            }
        }       

        public void evaluateResultConstraints( final AccumulateNode accNode,
                                               final LeftTupleSink sink,
                                               final Accumulate accumulate,
                                               final LeftTuple leftTuple,
                                               final PropagationContext context,
                                               final InternalWorkingMemory workingMemory,
                                               final AccumulateMemory memory,
                                               final AccumulateContext accctx,
                                               final boolean useLeftMemory,
                                               final LeftTupleSets trgLeftTuples,
                                               final LeftTupleSets stagedLeftTuples ) {
            // get the actual result
            final Object[] resultArray = accumulate.getResult( memory.workingMemoryContext,
                                                               accctx.context,
                                                               leftTuple,
                                                               workingMemory );
            Object result = accumulate.isMultiFunction() ? resultArray : resultArray[0];
            if (result == null) {
                return;
            }

            if ( accctx.getResultFactHandle() == null ) {
                final InternalFactHandle handle = accNode.createResultFactHandle( context,
                                                                                  workingMemory,
                                                                                  leftTuple,
                                                                                  result );

                accctx.setResultFactHandle( handle );
               
                accctx.setResultLeftTuple( accNode.createLeftTuple( handle, leftTuple, sink )  );
            } else {
                accctx.getResultFactHandle().setObject( result );
            }

            // First alpha node filters
            AlphaNodeFieldConstraint[] resultConstraints = accNode.getResultConstraints();
            BetaConstraints resultBinder = accNode.getResultBinder();
            boolean isAllowed = result != null;
            for ( int i = 0, length = resultConstraints.length; isAllowed && i < length; i++ ) {
                if ( !resultConstraints[i].isAllowed( accctx.resultFactHandle,
                                                      workingMemory,
                                                      memory.alphaContexts[i] ) ) {
                    isAllowed = false;
                }
            }
            if ( isAllowed ) {
                resultBinder.updateFromTuple( memory.resultsContext,
                                              workingMemory,
                                              leftTuple );
                if ( !resultBinder.isAllowedCachedLeft( memory.resultsContext,
                                                        accctx.getResultFactHandle() ) ) {
                    isAllowed = false;
                }
                resultBinder.resetTuple( memory.resultsContext );
            }

           
            LeftTuple childLeftTuple = ( LeftTuple ) accctx.getResultLeftTuple();
            childLeftTuple.setPropagationContext( leftTuple.getPropagationContext() );
            if ( accctx.propagated == true ) {
                switch ( childLeftTuple.getStagedType() ) {
                    // handle clash with already staged entries
                    case LeftTuple.INSERT :
                        stagedLeftTuples.removeInsert( childLeftTuple );
                        break;
                    case LeftTuple.UPDATE :
                        stagedLeftTuples.removeUpdate( childLeftTuple );
                        break;
                }  
               
                if ( isAllowed ) {
                    // modify
                    trgLeftTuples.addUpdate( childLeftTuple);
                } else {
                    // retract                
                    trgLeftTuples.addDelete( childLeftTuple );
                    accctx.propagated = false;
                }
            } else if ( isAllowed ) {
                // assert
                trgLeftTuples.addInsert( childLeftTuple );
                accctx.propagated = true;
            }

        }       
       
        public  static void addMatch( final AccumulateNode accNode,
                                      final Accumulate accumulate,
                                      final LeftTuple leftTuple,
                                      final RightTuple rightTuple,
                                      final LeftTuple currentLeftChild,
                                      final LeftTuple currentRightChild,
                                      final InternalWorkingMemory wm,
                                      final AccumulateMemory am,
                                      final AccumulateContext accresult,
                                      final boolean useLeftMemory ) {
            LeftTuple tuple = leftTuple;
            InternalFactHandle handle = rightTuple.getFactHandle();
            if ( accNode.isUnwrapRightObject() ) {
                // if there is a subnetwork, handle must be unwrapped
                tuple = (LeftTuple) handle.getObject();
                //handle = tuple.getLastHandle();
            }
            accumulate.accumulate( am.workingMemoryContext,
                                   accresult.context,
                                   tuple,
                                   handle,
                                   wm );

            // in sequential mode, we don't need to keep record of matched tuples
            if ( useLeftMemory ) {
                // linking left and right by creating a new left tuple
                accNode.createLeftTuple( leftTuple,
                                         rightTuple,
                                         currentLeftChild,
                                         currentRightChild,
                                         accNode,
                                         true );
            }
        }
       
        /**
         * Removes a match between left and right tuple
         *
         * @param rightTuple
         * @param match
         * @param result
         */
        public static void removeMatch( final AccumulateNode accNode,
                                         final Accumulate accumulate,
                                         final RightTuple rightTuple,
                                         final LeftTuple match,
                                         final InternalWorkingMemory wm,
                                         final AccumulateMemory am,
                                         final AccumulateContext accctx,
                                         final boolean reaccumulate ) {
            // save the matching tuple
            LeftTuple leftTuple = match.getLeftParent();

            // removing link between left and right
            match.unlinkFromLeftParent();
            match.unlinkFromRightParent();

            // if there is a subnetwork, we need to unwrap the object from inside the tuple
            InternalFactHandle handle = rightTuple.getFactHandle();
            LeftTuple tuple = leftTuple;
            if ( accNode.isUnwrapRightObject() ) {
                tuple = (LeftTuple) handle.getObject();
            }

            if ( accumulate.supportsReverse() ) {
                // just reverse this single match
                accumulate.reverse( am.workingMemoryContext,
                                         accctx.context,
                                         tuple,
                                         handle,
                                         wm );
            } else {
                // otherwise need to recalculate all matches for the given leftTuple
                if ( reaccumulate ) {
                    reaccumulateForLeftTuple( accNode,
                                              accumulate,
                                              leftTuple,
                                              wm,
                                              am,
                                              accctx );

                }
            }
        }       
       

        public static void reaccumulateForLeftTuple( final AccumulateNode accNode,
                                                     final Accumulate accumulate,
                                                     final LeftTuple leftTuple,
                                                     final InternalWorkingMemory wm,
                                                     final AccumulateMemory am,
                                                     final AccumulateContext accctx ) {
            accumulate.init( am.workingMemoryContext,
                             accctx.context,
                             leftTuple,
                             wm );
            for ( LeftTuple childMatch = leftTuple.getFirstChild(); childMatch != null; childMatch = childMatch.getLeftParentNext() ) {
                InternalFactHandle childHandle = childMatch.getRightParent().getFactHandle();
                LeftTuple tuple = leftTuple;
                if ( accNode.isUnwrapRightObject() ) {
                    tuple = (LeftTuple) childHandle.getObject();
                    childHandle = tuple.getLastHandle();
                }
                accumulate.accumulate( am.workingMemoryContext,
                                       accctx.context,
                                       tuple,
                                       childHandle,
                                       wm );
            }
        }       
       
        public static void removePreviousMatchesForRightTuple( final AccumulateNode accNode,
                                                               final Accumulate accumulate,
                                                               final RightTuple rightTuple,
                                                               final PropagationContext context,
                                                               final InternalWorkingMemory workingMemory,
                                                               final AccumulateMemory memory,
                                                               final LeftTuple firstChild,
                                                               final LeftTupleSets trgLeftTuples) {
           for ( LeftTuple match = firstChild; match != null; ) {
               final LeftTuple next = match.getRightParentNext();
              
               final LeftTuple leftTuple = match.getLeftParent();
               final AccumulateContext accctx = (AccumulateContext) leftTuple.getObject();
               removeMatch( accNode,
                            accumulate,
                            rightTuple,
                            match,
                            workingMemory,
                            memory,
                            accctx,
                            true );  
              
               if ( leftTuple.getStagedType() == LeftTuple.NONE ) {
                   trgLeftTuples.addUpdate( leftTuple);
             }              
              
               match = next;
           }
       } 
       
        public static void removePreviousMatchesForLeftTuple( final AccumulateNode accNode,
                                                              final Accumulate accumulate,
                                                              final LeftTuple leftTuple,
                                                              final InternalWorkingMemory workingMemory,
                                                              final AccumulateMemory memory,
                                                              final AccumulateContext accctx,
                                                              boolean reInit) {
            for ( LeftTuple match = leftTuple.getFirstChild(); match != null) {
                LeftTuple next = match.getLeftParentNext();
                match.unlinkFromRightParent();
                match.unlinkFromLeftParent();
                match = next;
            }
           
            if ( reInit ) {
                // since there are no more matches, the following call will just re-initialize the accumulation
                accumulate.init( memory.workingMemoryContext,
                                 accctx.context,
                                 leftTuple,
                                 workingMemory );
            }
        }
          
    }

    public static class PhreakEvalNode {
        public void doNode(EvalConditionNode evalNode,
                           EvalMemory em,
                           LeftTupleSink sink,
                           InternalWorkingMemory wm,
                           LeftTupleSets srcLeftTuples,
                           LeftTupleSets trgLeftTuples,
                           LeftTupleSets stagedLeftTuples) {

            if ( srcLeftTuples.getDeleteFirst() != null ) {
                doLeftDeletes( evalNode, em, wm, srcLeftTuples, trgLeftTuples, stagedLeftTuples  );
            }

            if ( srcLeftTuples.getUpdateFirst() != null ) {
                doLeftUpdates( evalNode, em, sink, wm, srcLeftTuples, trgLeftTuples, stagedLeftTuples   );
            }

            if ( srcLeftTuples.getInsertFirst() != null ) {
                doLeftInserts( evalNode, em, sink, wm, srcLeftTuples, trgLeftTuples );
            }
              
            srcLeftTuples.resetAll();           
        }

        public void doLeftInserts(EvalConditionNode evalNode,
                                  EvalMemory em,
                                  LeftTupleSink sink,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples) {

            boolean tupleMemoryEnabled = true;

            EvalCondition condition = evalNode.getCondition();
            for ( LeftTuple leftTuple = srcLeftTuples.getInsertFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
               
                final boolean allowed = condition.isAllowed( leftTuple,
                                                             wm,
                                                             em.context );

                if ( allowed ) {
                    trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple, 
                                                                   sink,
                                                                   tupleMemoryEnabled ) );
                }               

                leftTuple.clearStaged();
                leftTuple = next;
            }
        }

        public void doLeftUpdates(EvalConditionNode evalNode,
                                  EvalMemory em,
                                  LeftTupleSink sink,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples,
                                  LeftTupleSets stagedLeftTuples) {
            boolean tupleMemoryEnabled = true;

            EvalCondition condition = evalNode.getCondition();
            for ( LeftTuple leftTuple = srcLeftTuples.getUpdateFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
               
                boolean wasPropagated = leftTuple.getFirstChild() != null;
               
                boolean allowed = condition.isAllowed( leftTuple,
                                                       wm,
                                                       em.context );
                if ( allowed ) {
                    if ( wasPropagated ) {
                        // update
                        LeftTuple childLeftTuple = leftTuple.getFirstChild();
                       
                        switch ( childLeftTuple.getStagedType() ) {
                            // handle clash with already staged entries
                            case LeftTuple.INSERT :
                                stagedLeftTuples.removeInsert( childLeftTuple );
                                break;
                            case LeftTuple.UPDATE :
                                stagedLeftTuples.removeUpdate( childLeftTuple );
                                break;
                        }     
                       
                        trgLeftTuples.addUpdate( childLeftTuple );
                    } else {
                        // assert
                        trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple, 
                                                                       sink,
                                                                       tupleMemoryEnabled ) );
                    }
                } else {
                    if ( wasPropagated ) {
                        // retract
                       
                        LeftTuple childLeftTuple = leftTuple.getFirstChild();
                        switch ( childLeftTuple.getStagedType() ) {
                            // handle clash with already staged entries
                            case LeftTuple.INSERT :
                                stagedLeftTuples.removeInsert( childLeftTuple );
                                break;
                            case LeftTuple.UPDATE :
                                stagedLeftTuples.removeUpdate( childLeftTuple );
                                break;
                       
                       
                        trgLeftTuples.addDelete( childLeftTuple );
                    }
                    // else do nothing
                }             

                leftTuple.clearStaged();
                leftTuple = next;
            }
        }

        public void doLeftDeletes(EvalConditionNode evalNode,
                                  EvalMemory em,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples,
                                  LeftTupleSets stagedLeftTuples) {
            for ( LeftTuple leftTuple = srcLeftTuples.getDeleteFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
               
               
                LeftTuple childLeftTuple = leftTuple.getFirstChild();
                if ( childLeftTuple != null ) {                   
                    switch ( childLeftTuple.getStagedType() ) {
                        // handle clash with already staged entries
                        case LeftTuple.INSERT :
                            stagedLeftTuples.removeInsert( childLeftTuple );
                            break;
                        case LeftTuple.UPDATE :
                            stagedLeftTuples.removeUpdate( childLeftTuple );
                            break;
                    }                    
                    trgLeftTuples.addDelete( childLeftTuple );
                }                            

                leftTuple.clearStaged();
                leftTuple = next;
            }
        }
    }   
   
    public static class PhreakBranchNode {
        public void doNode(ConditionalBranchNode branchNode,
                           ConditionalBranchMemory cbm,
                           LeftTupleSink sink,
                           InternalWorkingMemory wm,
                           LeftTupleSets srcLeftTuples,
                           LeftTupleSets trgLeftTuples,
                           LeftTupleSets stagedLeftTuples) {

            if ( srcLeftTuples.getDeleteFirst() != null ) {
                doLeftDeletes( branchNode, cbm, wm, srcLeftTuples, trgLeftTuples, stagedLeftTuples  );
            }

            if ( srcLeftTuples.getUpdateFirst() != null ) {
                doLeftUpdates( branchNode, cbm, sink, wm, srcLeftTuples, trgLeftTuples, stagedLeftTuples   );
            }

            if ( srcLeftTuples.getInsertFirst() != null ) {
                doLeftInserts( branchNode, cbm, sink, wm, srcLeftTuples, trgLeftTuples );
            }
              
            srcLeftTuples.resetAll();           
        }

        public void doLeftInserts(ConditionalBranchNode branchNode,
                                  ConditionalBranchMemory cbm,
                                  LeftTupleSink sink,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples) {
            boolean tupleMemoryEnabled = true;

            ConditionalBranchEvaluator branchEvaluator = branchNode.getBranchEvaluator();           
           
            for ( LeftTuple leftTuple = srcLeftTuples.getInsertFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
               
                boolean breaking = false;
                ConditionalExecution conditionalExecution = branchEvaluator.evaluate( leftTuple, wm, cbm.context );

                if ( conditionalExecution != null ) {
                    RuleTerminalNode rtn = ( RuleTerminalNode ) conditionalExecution.getSink().getFirstLeftTupleSink();
                    LeftTuple branchedLeftTuple = rtn.createLeftTuple( leftTuple, 
                                                                       rtn,
                                                                       tupleMemoryEnabled );
                   
                    leftTuple.setObject( branchedLeftTuple );
                   
                    rtn.assertLeftTuple( branchedLeftTuple, leftTuple.getPropagationContext(), wm );
                    breaking = conditionalExecution.isBreaking();
                }

                if ( !breaking ) {
                    trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple, 
                                                                   sink,
                                                                   tupleMemoryEnabled ) );                   
                }                            

                leftTuple.clearStaged();
                leftTuple = next;
            }
        }

        public void doLeftUpdates(ConditionalBranchNode branchNode,
                                  ConditionalBranchMemory cbm,
                                  LeftTupleSink sink,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples,
                                  LeftTupleSets stagedLeftTuples) {
            boolean tupleMemoryEnabled = true;

            ConditionalBranchEvaluator branchEvaluator = branchNode.getBranchEvaluator();
           
            for ( LeftTuple leftTuple = srcLeftTuples.getUpdateFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
               
                LeftTuple rtnLeftTuple = ( LeftTuple ) leftTuple.getObject();
                LeftTuple mainLeftTuple = leftTuple.getFirstChild();
                          
                RuleTerminalNode oldRtn = null;
                if ( rtnLeftTuple != null ) {
                    oldRtn = ( RuleTerminalNode ) rtnLeftTuple.getSink();
                }
               
                ConditionalExecution conditionalExecution = branchEvaluator.evaluate( leftTuple, wm, cbm.context );
               
                RuleTerminalNode newRtn = null;
                boolean breaking = false;
                if ( conditionalExecution != null ) {
                    newRtn = ( RuleTerminalNode ) conditionalExecution.getSink().getFirstLeftTupleSink();
                    breaking = conditionalExecution.isBreaking();
                }
               
                // Handle conditional branches
                if ( oldRtn != null ) {
                    if (newRtn == null ) {
                        // old exits, new does not, so delete
                        oldRtn.retractLeftTuple( rtnLeftTuple, rtnLeftTuple.getPropagationContext(), wm );
                       
                    } else if (newRtn == oldRtn ) {
                        // old and new on same branch, so update
                        oldRtn.modifyLeftTuple( rtnLeftTuple, rtnLeftTuple.getPropagationContext(), wm );
                       
                    } else {
                        // old and new on different branches, delete one and insert the other                               
                        oldRtn.retractLeftTuple( rtnLeftTuple, rtnLeftTuple.getPropagationContext(), wm );
                       
                        rtnLeftTuple = newRtn.createLeftTuple( leftTuple, 
                                                               newRtn,
                                                               tupleMemoryEnabled );
                       
                        leftTuple.setObject( rtnLeftTuple );                       
                        newRtn.assertLeftTuple( rtnLeftTuple, rtnLeftTuple.getPropagationContext(), wm );                       
                    }

                } else if ( newRtn != null) {
                    // old does not exist, new exists, so insert
                    rtnLeftTuple = newRtn.createLeftTuple( leftTuple, 
                                                           newRtn,
                                                           tupleMemoryEnabled );
                   
                    leftTuple.setObject( rtnLeftTuple );                       
                    newRtn.assertLeftTuple( rtnLeftTuple, rtnLeftTuple.getPropagationContext(), wm );                   
                }
               
                // Handle main branch
                if ( mainLeftTuple != null ) {
                    switch ( mainLeftTuple.getStagedType() ) {
                        // handle clash with already staged entries
                        case LeftTuple.INSERT :
                            stagedLeftTuples.removeInsert( mainLeftTuple );
                            break;
                        case LeftTuple.UPDATE :
                            stagedLeftTuples.removeUpdate( mainLeftTuple );
                            break;
                   
                   
                    if ( !breaking) {                       
                        // child exist, new one does, so update
                        trgLeftTuples.addUpdate( mainLeftTuple );
                    } else {
                        // child exist, new one does not, so delete
                        trgLeftTuples.addDelete( mainLeftTuple );                        
                    }
                } else if ( !breaking ) {
                    // child didn't exist, new one does, so insert
                    trgLeftTuples.addInsert( sink.createLeftTuple( leftTuple, 
                                                                   sink,
                                                                   tupleMemoryEnabled ) );
                }

                leftTuple.clearStaged();
                leftTuple = next;
            }
        }

        public void doLeftDeletes(ConditionalBranchNode branchNode,
                                  ConditionalBranchMemory cbm,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples,
                                  LeftTupleSets stagedLeftTuples) {
            for ( LeftTuple leftTuple = srcLeftTuples.getDeleteFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
               
                LeftTuple rtnLeftTuple = ( LeftTuple ) leftTuple.getObject();
                LeftTuple mainLeftTuple = leftTuple.getFirstChild();
                          
                if ( rtnLeftTuple != null ) {
                    RuleTerminalNode rtn = ( RuleTerminalNode ) rtnLeftTuple.getSink();
                    rtn.retractLeftTuple( rtnLeftTuple, rtnLeftTuple.getPropagationContext(), wm );
                }
               
              if ( mainLeftTuple != null ) {                   
                  switch ( mainLeftTuple.getStagedType() ) {
                      // handle clash with already staged entries
                      case LeftTuple.INSERT :
                          stagedLeftTuples.removeInsert( mainLeftTuple );
                          break;
                      case LeftTuple.UPDATE :
                          stagedLeftTuples.removeUpdate( mainLeftTuple );
                          break;
                  }                    
                  trgLeftTuples.addDelete( mainLeftTuple );
              }                   
               
               
                leftTuple.clearStaged();
                leftTuple = next;
            }
        }
    }   
       
   
    public static class PhreakFromNode {
        public void doNode(FromNode fromNode,
                           FromMemory fm,
                           LeftTupleSink sink,
                           InternalWorkingMemory wm,
                           LeftTupleSets srcLeftTuples,
                           LeftTupleSets trgLeftTuples,
                           LeftTupleSets stagedLeftTuples) {

            if ( srcLeftTuples.getDeleteFirst() != null ) {
                doLeftDeletes( fromNode, fm, sink, wm, srcLeftTuples, trgLeftTuples, stagedLeftTuples  );
            }

            if ( srcLeftTuples.getUpdateFirst() != null ) {
                doLeftUpdates( fromNode, fm, sink, wm, srcLeftTuples, trgLeftTuples, stagedLeftTuples   );
            }

            if ( srcLeftTuples.getInsertFirst() != null ) {
                doLeftInserts( fromNode, fm, sink, wm, srcLeftTuples, trgLeftTuples );
            }
              
            srcLeftTuples.resetAll();           
        }

        public void doLeftInserts(FromNode fromNode,
                                  FromMemory fm,
                                  LeftTupleSink sink,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples) {

            boolean tupleMemoryEnabled = true;
           
            BetaMemory bm = fm.getBetaMemory();
            ContextEntry[] context = bm.getContext();
            BetaConstraints betaConstraints = fromNode.getBetaConstraints();
            AlphaNodeFieldConstraint[] alphaConstraints = fromNode.getAlphaConstraints();
            DataProvider dataProvider = fromNode.getDataProvider();
            Class resultClass = fromNode.getResultClass();

            for ( LeftTuple leftTuple = srcLeftTuples.getInsertFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
               
                PropagationContext propagationContext = leftTuple.getPropagationContext();
               
                Map<Object, RightTuple> matches = null;
                boolean useLeftMemory = true;      
                if ( !tupleMemoryEnabled ) {
                    // This is a hack, to not add closed DroolsQuery objects
                    Object object = leftTuple.get( 0 ).getObject();
                    if ( !(object instanceof DroolsQuery) || !((DroolsQuery) object).isOpen() ) {
                        useLeftMemory = false;
                    }
                }

                if ( useLeftMemory ) {
                    fm.betaMemory.getLeftTupleMemory().add( leftTuple );
                    matches = new LinkedHashMap<Object, RightTuple>();
                    leftTuple.setObject( matches );
                }        

                betaConstraints.updateFromTuple( context,
                                                 wm,
                                                 leftTuple );

                for ( final java.util.Iterator< ? > it = dataProvider.getResults( leftTuple,
                                                                                  wm,
                                                                                  propagationContext,
                                                                                  fm.providerContext ); it.hasNext(); ) {
                    final Object object = it.next();
                    if ( !resultClass.isAssignableFrom( object.getClass() ) ) {
                        continue; // skip anything if it not assignable
                    }

                    RightTuple rightTuple = fromNode.createRightTuple( leftTuple,
                                                                       propagationContext,
                                                                       wm,
                                                                       object );

                    checkConstraintsAndPropagate( sink,
                                                  leftTuple,
                                                  rightTuple,
                                                  alphaConstraints,
                                                  betaConstraints,
                                                  propagationContext,
                                                  wm,
                                                  fm,
                                                  bm,
                                                  context,
                                                  useLeftMemory,
                                                  trgLeftTuples,
                                                  null);
                    if ( useLeftMemory ) {                       
                        fromNode.addToCreatedHandlesMap( matches,
                                                         rightTuple );
                    }
                }

                leftTuple.clearStaged();
                leftTuple = next;
            }
            betaConstraints.resetTuple( context );
        }

        public void doLeftUpdates(FromNode fromNode,
                                  FromMemory fm,
                                  LeftTupleSink sink,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples,
                                  LeftTupleSets stagedLeftTuples) {
            boolean tupleMemoryEnabled = true;
           
            BetaMemory bm = fm.getBetaMemory();
            LeftTupleMemory ltm = bm.getLeftTupleMemory();
            ContextEntry[] context = bm.getContext();
            BetaConstraints betaConstraints = fromNode.getBetaConstraints();
            AlphaNodeFieldConstraint[] alphaConstraints = fromNode.getAlphaConstraints();
            DataProvider dataProvider = fromNode.getDataProvider();
            Class resultClass = fromNode.getResultClass();

            for ( LeftTuple leftTuple = srcLeftTuples.getUpdateFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                              
                PropagationContext propagationContext = leftTuple.getPropagationContext();

                ltm.removeAdd( leftTuple );

                final Map<Object, RightTuple> previousMatches = (Map<Object, RightTuple>) leftTuple.getObject();
                final Map<Object, RightTuple> newMatches = new HashMap<Object, RightTuple>();
                leftTuple.setObject( newMatches );

                betaConstraints.updateFromTuple( context,
                                                      wm,
                                                      leftTuple );

                FastIterator rightIt = LinkedList.fastIterator;
                for ( final java.util.Iterator< ? > it = dataProvider.getResults( leftTuple,
                                                                                  wm,
                                                                                  propagationContext,
                                                                                  fm.providerContext ); it.hasNext(); ) {
                    final Object object = it.next();
                    if ( !resultClass.isAssignableFrom( object.getClass() ) ) {
                        continue; // skip anything if it not assignable
                    }
                   
                    RightTuple rightTuple = previousMatches.remove( object );

                    if ( rightTuple == null ) {
                        // new match, propagate assert
                        rightTuple = fromNode.createRightTuple( leftTuple,
                                                                propagationContext,
                                                                wm,
                                                                object );
                    } else {
                        // previous match, so reevaluate and propagate modify
                        if ( rightIt.next( rightTuple ) != null ) {
                            // handle the odd case where more than one object has the same hashcode/equals value
                            previousMatches.put( object,
                                                 (RightTuple) rightIt.next( rightTuple ) );
                            rightTuple.setNext( null );
                        }
                    }

                    checkConstraintsAndPropagate( sink,
                                                  leftTuple,
                                                  rightTuple,
                                                  alphaConstraints,
                                                  betaConstraints,
                                                  propagationContext,
                                                  wm,
                                                  fm,
                                                  bm,
                                                  context,
                                                  tupleMemoryEnabled,
                                                  trgLeftTuples,
                                                  null);
                   
                    if ( tupleMemoryEnabled ) {                       
                        fromNode.addToCreatedHandlesMap( newMatches,
                                                         rightTuple );
                    }
                }

                for ( RightTuple rightTuple : previousMatches.values() ) {
                    for ( RightTuple current = rightTuple; current != null; current = (RightTuple) rightIt.next( current ) ) {
                        LeftTuple childLeftTuple = current.getFirstChild();
                        childLeftTuple.unlinkFromLeftParent();
                        childLeftTuple.unlinkFromRightParent();
                       
                        switch ( childLeftTuple.getStagedType() ) {
                            // handle clash with already staged entries
                            case LeftTuple.INSERT :
                                stagedLeftTuples.removeInsert( childLeftTuple );
                                break;
                            case LeftTuple.UPDATE :
                                stagedLeftTuples.removeUpdate( childLeftTuple );
                                break;
                        }          
                       
                        childLeftTuple.setPropagationContext( propagationContext );
                        trgLeftTuples.addDelete( childLeftTuple );
                    }
                }                                                               

                leftTuple.clearStaged();
                leftTuple = next;
            }
            betaConstraints.resetTuple( context);
        }

        public void doLeftDeletes(FromNode fromNode,
                                  FromMemory fm,
                                  LeftTupleSink sink,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples,
                                  LeftTupleSets trgLeftTuples,
                                  LeftTupleSets stagedLeftTuples) {
            BetaMemory bm = fm.getBetaMemory();
            LeftTupleMemory ltm = bm.getLeftTupleMemory();
           
            for ( LeftTuple leftTuple = srcLeftTuples.getDeleteFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
               
                ltm.remove( leftTuple );
               
                Map<Object, RightTuple> matches = (Map<Object, RightTuple>) leftTuple.getObject();
                               
                if ( leftTuple.getFirstChild() != null ) {
                    LeftTuple childLeftTuple = leftTuple.getFirstChild();

                    while ( childLeftTuple != null ) {
                        childLeftTuple = deleteLeftChild( childLeftTuple, trgLeftTuples, stagedLeftTuples );
                    }
                }


                // @TODO (mdp) is this really necessary? won't the entire FH and RightTuple chaines just et GC'd?
                unlinkCreatedHandles( leftTuple );
               
                leftTuple.clearStaged();
                leftTuple = next;
            }
        }
       
        public static void unlinkCreatedHandles(final LeftTuple leftTuple) {
            Map<Object, RightTuple> matches = (Map<Object, RightTuple>) leftTuple.getObject();
            FastIterator rightIt = LinkedList.fastIterator;
            for ( RightTuple rightTuple : matches.values() ) {
                for ( RightTuple current = rightTuple; current != null; ) {
                    RightTuple next = (RightTuple) rightIt.next( current );
                    current.unlinkFromRightParent();
                    current = next;
                }
            }
        }       
       
        protected void checkConstraintsAndPropagate( final LeftTupleSink sink,
                                                     final LeftTuple leftTuple,
                                                     final RightTuple rightTuple,
                                                     final AlphaNodeFieldConstraint[] alphaConstraints,
                                                     final BetaConstraints betaConstraints,
                                                     final PropagationContext propagationContext,
                                                     final InternalWorkingMemory wm,
                                                     final FromMemory fm,
                                                     final BetaMemory bm,
                                                     final ContextEntry[] context,
                                                     final boolean useLeftMemory,
                                                     LeftTupleSets trgLeftTuples,
                                                     LeftTupleSets stagedLeftTuples) {
            boolean isAllowed = true;
            if ( alphaConstraints != null ) {
                // First alpha node filters
                for ( int i = 0, length = alphaConstraints.length; i < length; i++ ) {
                    if ( !alphaConstraints[i].isAllowed( rightTuple.getFactHandle(),
                                                         wm,
                                                         fm.alphaContexts[i] ) ) {
                        // next iteration
                        isAllowed = false;
                        break;
                    }
                }
            }

            if ( isAllowed && betaConstraints.isAllowedCachedLeft( context,
                                                                  rightTuple.getFactHandle() ) ) {

                if ( rightTuple.firstChild == null ) {
                    // this is a new match, so propagate as assert
                    LeftTuple childLeftTuple = sink.createLeftTuple( leftTuple, 
                                                                     rightTuple,
                                                                     null,
                                                                     null,
                                                                     sink,
                                                                     useLeftMemory );
                    childLeftTuple.setPropagationContext( propagationContext );
                    trgLeftTuples.addInsert( childLeftTuple );
                } else {
                    LeftTuple childLeftTuple = rightTuple.firstChild;
                   
                    switch ( childLeftTuple.getStagedType() ) {
                        // handle clash with already staged entries
                        case LeftTuple.INSERT :
                            stagedLeftTuples.removeInsert( childLeftTuple );
                            break;
                        case LeftTuple.UPDATE :
                            stagedLeftTuples.removeUpdate( childLeftTuple );
                            break;
                    }     
                   
                    childLeftTuple.setPropagationContext( propagationContext );
                    trgLeftTuples.addUpdate( childLeftTuple );
                }
            } else {
                LeftTuple childLeftTuple = rightTuple.firstChild;
                if ( childLeftTuple != null ) {                   
                    switch ( childLeftTuple.getStagedType() ) {
                        // handle clash with already staged entries
                        case LeftTuple.INSERT :
                            stagedLeftTuples.removeInsert( childLeftTuple );
                            break;
                        case LeftTuple.UPDATE :
                            stagedLeftTuples.removeUpdate( childLeftTuple );
                            break;
                    }  
                    childLeftTuple.setPropagationContext( propagationContext );
                    trgLeftTuples.addDelete( childLeftTuple );
                }                 
            }
        }        
    }    

    public static class PhreakRuleTerminalNode {
        public void doNode(RuleTerminalNode rtnNode,
                           InternalWorkingMemory wm,
                           LeftTupleSets srcLeftTuples) {

            if ( log.isTraceEnabled() ) {
                //log.trace( "End Rule[name={}] {}", rtnNode.getRule().getName(), srcLeftTuples.toStringSizes() );
            }
           
            if ( srcLeftTuples.getDeleteFirst() != null ) {
                doLeftDeletes( rtnNode, wm, srcLeftTuples );
            }

            if ( srcLeftTuples.getUpdateFirst() != null ) {
                doLeftUpdates( rtnNode, wm, srcLeftTuples );
            }

            if ( srcLeftTuples.getInsertFirst() != null ) {
                doLeftInserts( rtnNode, wm, srcLeftTuples );
            }
           
            srcLeftTuples.resetAll();
        }

        public void doLeftInserts(RuleTerminalNode rtnNode,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples) {

            for ( LeftTuple leftTuple = srcLeftTuples.getInsertFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                rtnNode.assertLeftTuple( leftTuple, leftTuple.getPropagationContext(), wm );
                leftTuple.clearStaged();
                leftTuple = next;
            }
        }

        public void doLeftUpdates(RuleTerminalNode rtnNode,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples) {

            for ( LeftTuple leftTuple = srcLeftTuples.getUpdateFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                rtnNode.modifyLeftTuple( leftTuple, leftTuple.getPropagationContext(), wm );
                leftTuple.clearStaged();
                leftTuple = next;
            }
        }

        public void doLeftDeletes(RuleTerminalNode rtnNode,
                                  InternalWorkingMemory wm,
                                  LeftTupleSets srcLeftTuples) {

            for ( LeftTuple leftTuple = srcLeftTuples.getDeleteFirst(); leftTuple != null; ) {
                LeftTuple next = leftTuple.getStagedNext();
                rtnNode.retractLeftTuple( leftTuple, leftTuple.getPropagationContext(), wm );
                leftTuple.clearStaged();
                leftTuple = next;
            }
        }
    }

    public static LeftTuple deleteLeftChild(LeftTuple childLeftTuple,
                                            LeftTupleSets trgLeftTuples,
                                            LeftTupleSets stagedLeftTuples) {
        switch ( childLeftTuple.getStagedType() ) {
            // handle clash with already staged entries
            case LeftTuple.INSERT :
                stagedLeftTuples.removeInsert( childLeftTuple );
                break;
            case LeftTuple.UPDATE :
                stagedLeftTuples.removeUpdate( childLeftTuple );
                break;
        }

        LeftTuple next = childLeftTuple.getLeftParentNext();

        trgLeftTuples.addDelete( childLeftTuple );
        childLeftTuple.unlinkFromRightParent();
        childLeftTuple.unlinkFromLeftParent();

        return next;
    }

    public static LeftTuple deleteRightChild(LeftTuple childLeftTuple,
                                             LeftTupleSets trgLeftTuples,
                                             LeftTupleSets stagedLeftTuples) {
        switch ( childLeftTuple.getStagedType() ) {
            // handle clash with already staged entries
            case LeftTuple.INSERT :
                stagedLeftTuples.removeInsert( childLeftTuple );
                break;
            case LeftTuple.UPDATE :
                stagedLeftTuples.removeUpdate( childLeftTuple );
                break;
        }

        LeftTuple next = childLeftTuple.getRightParentNext();

        trgLeftTuples.addDelete( childLeftTuple );
        childLeftTuple.unlinkFromRightParent();
        childLeftTuple.unlinkFromLeftParent();

        return next;
    }

    public static void dpUpdatesReorderLeftMemory(BetaMemory bm,
                                                  InternalWorkingMemory wm,
                                                  RightTupleSets srcRightTuples,
                                                  LeftTupleSets srcLeftTuples) {
        LeftTupleMemory ltm = bm.getLeftTupleMemory();

        // sides must first be re-ordered, to ensure iteration integrity
        for ( LeftTuple leftTuple = srcLeftTuples.getUpdateFirst(); leftTuple != null; ) {
            LeftTuple next = leftTuple.getStagedNext();
            if ( leftTuple.getMemory() != null ) {
                ltm.removeAdd( leftTuple );
                for ( LeftTuple childLeftTuple = leftTuple.getFirstChild(); childLeftTuple != null; ) {
                    LeftTuple childNext = childLeftTuple.getLeftParentNext();
                    childLeftTuple.reAddRight();
                    childLeftTuple = childNext;
                }
            }
            leftTuple = next;
        }
    }
   
    public static void dpUpdatesReorderRightMemory(BetaMemory bm,
                                                   InternalWorkingMemory wm,
                                                   RightTupleSets srcRightTuples,
                                                   LeftTupleSets srcLeftTuples) {
        RightTupleMemory rtm = bm.getRightTupleMemory();

        for ( RightTuple rightTuple = srcRightTuples.getUpdateFirst(); rightTuple != null; ) {
            RightTuple next = rightTuple.getStagedNext();
            if ( rightTuple.getMemory() != null ) {
                rtm.removeAdd( rightTuple );
                for ( LeftTuple childLeftTuple = rightTuple.getFirstChild(); childLeftTuple != null; ) {
                    LeftTuple childNext = childLeftTuple.getRightParentNext();
                    childLeftTuple.reAddLeft();
                    childLeftTuple = childNext;
                }
            }
            rightTuple = next;
        }
    }   

}
TOP

Related Classes of org.drools.phreak.RuleNetworkEvaluatorActivation$PhreakJoinNode

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.