/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jena.reasoner.rulesys.impl;

import java.io.PrintStream;
import java.util.*;

import org.apache.jena.graph.* ;
import org.apache.jena.reasoner.TriplePattern ;
import org.apache.jena.reasoner.rulesys.* ;

/**
 * Object used to hold the compiled bytecode stream for a single rule clause.
 * This uses a slightly WAM-like code stream but gluing of the clauses together
 * into disjunctions is done in the interpreter loop so a complete predicate is
 * represented as a list of RuleClauseCode objects.
 */
public class RuleClauseCode {
    
//  =======================================================================
//  Variables

    /** The rule from which is code was derived */
    protected Rule rule;
    
    /** The byte code sequence */
    protected byte[] code;
    
    /** Any Object argements needed by the byte codes */
    protected Object[] args;    
    
    /** starting byte code offset for body terms */
    protected int[] termStart;
     
//  =======================================================================
//  Instruction set constants

    /** fetch constant argument (const, Ai) */
    public static final byte GET_CONSTANT = 0x1;
    
    /** fetch permanent variable argument, first occurance (Yi, Ai) */
    public static final byte GET_VARIABLE  = 0x2;
    
    /** fetch permanent variable argument, later occurance (Yi, Ai) */
    public static final byte UNIFY_VARIABLE  = 0x3;
    
    /** fetch temporary variable argument (Ti, Ai) */
    public static final byte GET_TEMP  = 0x4;
    
    /** fetch temporary variable argument, later occurance (Ti, Ai) */
    public static final byte UNIFY_TEMP  = 0x12;
    
    /** put constant value into call parameter (const, Ai) */
    public static final byte PUT_CONSTANT = 0x5;
    
    /** put permanaent variable into call parameter, first occurance (Yi, Ai) */
    public static final byte PUT_NEW_VARIABLE = 0x6;
    
    /** put permanaent variable into call parameter (Yi, Ai) */
    public static final byte PUT_VARIABLE = 0x7;
    
    /** put a dereferenced permanent variable into call parameter ready for BUILTIN call (Yi, Ai) */
    public static final byte PUT_DEREF_VARIABLE = 0x14;
    
    /** put temp variable into call parameter (Ti, Ai) */
    public static final byte PUT_TEMP = 0x8;
    
    /** call a predicate code object (predicateCodeList) */
    public static final byte CALL_PREDICATE = 0x9;
    
    /** deconstruct a functor argument (functor) */
    public static final byte GET_FUNCTOR = 0xa;
    
    /** call a predicate code object with run time indexing (predicateCodeList) */
    public static final byte CALL_PREDICATE_INDEX = 0x17;
    
    /** call a pure triple match (predicate) */
    public static final byte CALL_TRIPLE_MATCH = 0x11;
    
    /** variant on CALL_PREDICATE using the last call optimization, only current used in chain rules */
    public static final byte LAST_CALL_PREDICATE = 0x13;
    
    /** call a table code object () */
    public static final byte CALL_TABLED = 0x18;
    
    /** call a table code object from a wildcard () */
    public static final byte CALL_WILD_TABLED = 0x19;
    
    /** return from a call, proceeed along AND tree */
    public static final byte PROCEED = 0xb;
    
    /** create a functor object from a rule template (templateFunctor) */
    public static final byte MAKE_FUNCTOR = 0xc;
    
    /** call a built-in operation defined by a rule clause (clauseIndex( */
    public static final byte CALL_BUILTIN = 0xd; 
    
    /** reset a permanent variable to an unbound variable (Yi) */
    public static final byte CLEAR_VARIABLE = 0xe;
    
    /** reset a temp variable to an unbound variable (Ti) */
    public static final byte CLEAR_TEMP = 0xf;
    
    /** reset an argument to an unbound variable (Ai) */
    public static final byte CLEAR_ARG = 0x10;
    
    /** Allocate a new environment frame */
    public static final byte ALLOCATE = 0x16;
    
    /** Test if an argument is bound (Ai) */
    public static final byte TEST_BOUND = 0x20;
    
    /** Test if an argument is unbound (Ai) */
    public static final byte TEST_UNBOUND = 0x21;
    
    // current next = 0x22
    
    /** The maximum number of permanent variables allowed in a single rule clause. 
     *  Now only relevent for initial holding clause. */
    public static final int MAX_PERMANENT_VARS = 15;
    
    /** The maximum number of argument variables allowed in a single goal 
     *   Future refactorings will remove this restriction. */
    public static final int MAX_ARGUMENT_VARS = 8;
    
    /** The maximum number of temporary variables allowed in a single rule clause. */
    public static final int MAX_TEMPORARY_VARS = 8;
    
    /** Dummy code block which just returns */
    public static RuleClauseCode returnCodeBlock;
    
    static {
        returnCodeBlock = new RuleClauseCode(null);
        returnCodeBlock.code = new byte[] {PROCEED};
    }
//  =======================================================================
//  Methods and constructors

    /**
     * Constructor. 
     * @param rule the rule to be compiled
     */
    public RuleClauseCode(Rule rule) {
        this.rule = rule;
    }
    
    /**
     * Return the byte code vector for this clause.
     */
    public byte[] getCode() {
        return code;
    }
    
    /**
     * Return the argument vector associated with this clauses' byte codes.
     */
    public Object[] getArgs() {
        return args;
    }
    
    /**
     * Return the rule from which this code block was compiled.
     */
    public Rule getRule() {
        return rule;
    }
    
    /**
     * Compile the rule into byte code.
     * @param ruleStore the store of LP rules through which calls to other predicates
     * can be resolved.
     */
    public void compile(LPRuleStore ruleStore) {
        CompileState state = new CompileState(rule);
        
        // Compile any early binding tests
        int skip = 0; // state.emitBindingTests();
        
        // Compile the head operations
        ClauseEntry head = rule.getHeadElement(0);
        if (!(head instanceof TriplePattern)) {
            throw new LPRuleSyntaxException("Heads of backward rules must be triple patterns", rule);
        }
        state.emitHead((TriplePattern)head);
        
        // Compile the body operations
        termStart = new int[rule.bodyLength()];
        for (int i = skip; i < rule.bodyLength(); i++) {
            termStart[i] = state.p;
            ClauseEntry entry = rule.getBodyElement(i);
            if (entry instanceof TriplePattern) {
                state.emitBody((TriplePattern)entry, ruleStore);
            } else if (entry instanceof Functor) {
                state.emitBody((Functor)entry);
            } else {
                throw new LPRuleSyntaxException("can't create new bRules in an bRule", rule);
            }
        }
        
        // Extract the final code
        code = state.getFinalCode();
        args = state.getFinalArgs();
    }
            
    /**
     * Translate a program counter offset to the index of the corresponding
     * body term (or -1 if a head term or a dummy rule).
     */
    public int termIndex(int pc) {
        if (rule == null) return -1;
        int term = 0; 
        // Trivial linear search because this is only used for logging which is
        // horribly expensive anyway.
        while (term < rule.bodyLength()) {
            if (pc <= termStart[term]) {
                return term - 1;            
            }
            term++;
        }
        return term - 1;
    }
    
    /**
     * Debug helper - list the code to a stream
     */
    public void print(PrintStream out) {
        if (code == null) {
            out.println("Code not available");
        } else {
            int argi = 0;
            for (int p = 0; p < code.length; ) {
                byte instruction = code[p++];
                switch (instruction) {
                case GET_CONSTANT:
                    out.println("GET_CONSTANT " + args[argi++] + ", A" + code[p++]);
                    break;
                case GET_VARIABLE:
                    out.println("GET_VARIABLE " + "Y" + code[p++] + ", A" + code[p++]);
                    break;
                case UNIFY_VARIABLE:
                    out.println("UNIFY_VARIABLE " + "Y" + code[p++] + ", A" + code[p++]);
                    break;
                case GET_TEMP:
                    out.println("GET_TEMP " + "T" + code[p++] + ", A" + code[p++]);
                    break;
                case UNIFY_TEMP:
                    out.println("UNIFY_TEMP " + "T" + code[p++] + ", A" + code[p++]);
                    break;
                case PUT_CONSTANT:
                    out.println("PUT_CONSTANT " + args[argi++] + ", A" + code[p++]);
                    break;
                case PUT_NEW_VARIABLE:
                    out.println("PUT_NEW_VARIABLE " + "Y" + code[p++] + ", A" + code[p++]);
                    break;
                case PUT_TEMP:
                    out.println("PUT_TEMP " + "T" + code[p++] + ", A" + code[p++]);
                    break;
                case PUT_VARIABLE:
                    out.println("PUT_VARIABLE " + "Y" + code[p++] + ", A" + code[p++]);
                    break;
                case PUT_DEREF_VARIABLE:
                    out.println("PUT_DEREF_VARIABLE " + "Y" + code[p++] + ", A" + code[p++]);
                    break;
                case TEST_BOUND:
                    out.println("TEST_BOUND A" + code[p++]);
                    break;
                case TEST_UNBOUND:
                    out.println("TEST_UNBOUND A" + code[p++]);
                    break;
                case CALL_PREDICATE:
                    out.println("CALL_PREDICATE " + args[argi++]);
                    break;
                case CALL_TABLED:
                    out.println("CALL_TABLED ");
                    break;
                case CALL_WILD_TABLED:
                    out.println("CALL_WILD_TABLED ");
                    break;
                case CALL_PREDICATE_INDEX:
                    out.println("CALL_PREDICATE_INDEX " + args[argi++]);
                    break;
                case LAST_CALL_PREDICATE:
                    out.println("LAST_CALL_PREDICATE " + args[argi++]);
                    break;
                case CALL_TRIPLE_MATCH:
                        out.println("CALL_TRIPLE_MATCH");
                        break;
                case PROCEED:
                    out.println("PROCEED");
                    break;
                case MAKE_FUNCTOR:
                    out.println("MAKE_FUNCTOR " + args[argi++]); 
                    break;
                case GET_FUNCTOR:
                    out.println("GET_FUNCTOR " + args[argi++]); 
                    break;
                case CALL_BUILTIN:
                    out.println("CALL_BUILTIN " + ((Builtin)args[argi++]).getName() + "/" + code[p++]);
                    break;
                case CLEAR_ARG:
                    out.println("CLEAR_ARG " + "A" + code[p++]);
                    break;
                case ALLOCATE:
                    out.println("ALLOCATE + " + code[p++]);
                    break;
                default:
                    out.println("Unused code: " + instruction);
                    break;
                }
            }
        }
    }
    
    /**
     * Print clause as rule for tracing.
     */
    @Override
    public String toString() {
        if (rule == null) {
            return "[anon]";
        } else {
            return "[" + rule.toShortString() + "]";
        }
    }
    
    /**
     * Inner class - compiler state.
     */
    static class CompileState {
        
        /** The temporary code vector during construction */
        byte[] code;
        
        /** The temporary arg list during construction */
        ArrayList<Object> args;
        
        /** The code pointer during construction */
        int p;
        
        /** array of lists of variables in the rule clauses, array index is 0 for head, body starts at 1 */
        private List<Node>[] termVarTable;
        
        /** Map from variables to the list of term positions in which it occurs */
        private Map<Node_RuleVariable, List<TermIndex>> varOccurrence = new HashMap<>();
        
        /** List of all permanent variables */
        private List<Node_RuleVariable> permanentVars = new ArrayList<>();
        
        /** List of all temporary variables */
        private List<Node_RuleVariable> tempVars = new ArrayList<>();
        
        /** The total number of var occurrences */
        int totalOccurrences = 0;
        
        /** the set of variables processed so far during the compile phase */
        Set<Node_RuleVariable> seen = new HashSet<>();
         
        /** The rule being parsed */
        Rule rule;

        
        /** 
         * Constructor. 
         */
        CompileState(Rule rule) {
            classifyVariables(rule);
            this.rule = rule;
            // Create a scratch area for assembling the code, use a worst-case size estimate
            code = new byte[10 + totalOccurrences + rule.bodyLength()*10];
            args = new ArrayList<>();
        }
        
        /**
         * Emit the code for any bound/unbound tests add start of body
         * and return the number of body clauses dealt with.
         */
        int emitBindingTests() {
            int i = 0;
            while  (i < rule.bodyLength()) {
                ClauseEntry term = rule.getBodyElement(i);
                if (term instanceof Functor) {
                    Functor f = (Functor)term;
                    if (f.getArgLength() != 1) break;
                    int ai = aIndex(f.getArgs()[0]);
                    if (ai >= 0) {
                        if (f.getName().equals("bound")) {
                            code[p++] = TEST_BOUND;
                            code[p++] = (byte)ai;
                        } else if (f.getName().equals("unbound")) {
                            code[p++] = TEST_UNBOUND;
                            code[p++] = (byte)ai;
                        } else {
                            break;
                        }
                    }
                } else {
                    break;
                }
                i++;
            }
            return i;
        }
        
        /**
         * Return the argument index of the given variable.
         */
        int aIndex(Node n) {
            TriplePattern tp = (TriplePattern)rule.getHeadElement(0);
            if (tp.getSubject() == n) {
                return 0;
            } else if (tp.getPredicate() == n) {
                return 1;
            } else if (tp.getObject() == n) {
                return 2;
            } else {
                return -1;
            }
        }
        
        /** 
         * emit the code for the head clause
         */
        void emitHead(TriplePattern head) {
            if (permanentVars.size() > 0) {
                code[p++] = ALLOCATE;
                code[p++] = (byte)permanentVars.size();
            }
            emitHeadGet(head.getSubject(), 0);
            emitHeadGet(head.getPredicate(), 1);
            emitHeadGet(head.getObject(), 2);
        }
        
        /**
         * Emit a single head get operation
         * @param node the node to emit
         * @param argi the argument register to address
         */
        void emitHeadGet(Node node, int argi) {
            if (node instanceof Node_RuleVariable) {
                Node_RuleVariable var = (Node_RuleVariable)node;
                if (isDummy(var)) {
                    // Node code required, var not used
                    return;
                }
                if (isTemp(var)) {
                    List<TermIndex>occurrences = varOccurrence.get(var);
                    if (occurrences.size() == 2 &&
                        occurrences.get(0).index <= 2 && 
                        occurrences.get(0).index ==occurrences.get(1).index) {
                            // No movement code required, var in right place  
                    } else {
                        code[p++] = seen.add(var) ? GET_TEMP : UNIFY_TEMP;
                        code[p++] = (byte)tempVars.indexOf(var);
                        code[p++] = (byte)argi;
                    }
                } else {
                    code[p++] = seen.add(var) ? GET_VARIABLE : UNIFY_VARIABLE;
                    code[p++] = (byte)permanentVars.indexOf(var);
                    code[p++] = (byte)argi;
                }
            } else if (Functor.isFunctor(node)) {
                Functor f = (Functor)node.getLiteralValue();
                code[p++] = GET_FUNCTOR;
                args.add(f);
                Node[] fargs = f.getArgs();
                for (int i = 0; i < fargs.length; i++) {
                    emitHeadGet(fargs[i], i+3);
                }
            } else {
                code[p++] = GET_CONSTANT;
                code[p++] = (byte)argi;
                args.add(node);
            }
        }
        
        /**
         * Emit code for a body clause.
         * @param goal the triple pattern to be called
         */
        void emitBody(TriplePattern goal, LPRuleStore store) {
            emitBodyPut(goal.getSubject(), 0, false);
            emitBodyPut(goal.getPredicate(), 1, false);
            emitBodyPut(goal.getObject(), 2, false);
            List<RuleClauseCode> predicateCode = store.codeFor(goal);
            if (predicateCode == null || predicateCode.size() == 0) {
                code[p++] = CALL_TRIPLE_MATCH;
            } else {
//                code[p++] = CALL_TABLED;
                if (goal.getPredicate().isVariable()) {
                    // Experimental. Force tabling of any wildcard predicate calls
                    code[p++] = CALL_TABLED;
                } else if (store.isTabled(goal)) {
//                if (store.isTabled(goal)) {
                    code[p++] = goal.getPredicate().isVariable() ? CALL_WILD_TABLED : CALL_TABLED;
                } else {
                    if (permanentVars.size() == 0) {
                        code[p++] = LAST_CALL_PREDICATE;
                    } else {
                        // Normal call, but can it be indexed further?
                        if (store.isIndexedPredicate(goal.getPredicate()) && goal.getObject().isVariable()) {
                            code[p++] = CALL_PREDICATE_INDEX;
                        } else {
                            code[p++] = CALL_PREDICATE;
                        }
                    }
                    args.add( new RuleClauseCodeList( predicateCode ) );
                }
            }
        }
        
        /**
            Wrapper class with reified
         */
        public static class RuleClauseCodeList 
            {
            private final List<RuleClauseCode> list;
            public RuleClauseCodeList( List<RuleClauseCode> list ) { this.list = list; }
            List<RuleClauseCode> getList()  { return list; }
            }
        
        /**
         * Emit code a single body put operation.
         * @param node the node to emit
         * @param argi the argument register to use
         * @param deref if true force a dereference of the variable binding at this point for
         * use in calling builtins
         */
        void emitBodyPut(Node node, int argi, boolean deref) {
            if (argi >= MAX_ARGUMENT_VARS) {
                throw new LPRuleSyntaxException("Rule too complex for current implementation\n" 
                            + "Rule clauses are limited to " + MAX_ARGUMENT_VARS + " argument variables\n", rule); 

            }
            if (node instanceof Node_RuleVariable) {
                Node_RuleVariable var = (Node_RuleVariable)node;
                if (isDummy(var)) {
                    code[p++] = CLEAR_ARG;
                    code[p++] = (byte)argi;
                    return;
                }
                if (isTemp(var)) {
                    List<TermIndex> occurrences = varOccurrence.get(var);
                    if (occurrences.size() == 2 && 
                        occurrences.get(0).index ==occurrences.get(1).index) {
                            // No movement code required, var in right place  
                    } else {
                        code[p++] = PUT_TEMP;
                        code[p++] = (byte)tempVars.indexOf(var);
                        code[p++] = (byte)argi;
                    }
                } else {
                    if (! seen.add(var)) {
                        code[p++] = (deref ? PUT_DEREF_VARIABLE : PUT_VARIABLE);
                    } else {
                        code[p++] = PUT_NEW_VARIABLE;
                    }
                    code[p++] = (byte)permanentVars.indexOf(var);
                    code[p++] = (byte)argi;
                }
            } else if (Functor.isFunctor(node)) {
                Functor f = (Functor)node.getLiteralValue();
                Node[] fargs = f.getArgs();
                for (int i = 0; i < fargs.length; i++) {
                    emitBodyPut(fargs[i], i+3, deref);
                }
                code[p++] = MAKE_FUNCTOR;
                args.add(f);
            } else {
                code[p++] = PUT_CONSTANT;
                code[p++] = (byte)argi;
                args.add(node);
            }
        }
        
        /**
         * Emit code for a call to a built-in predicate (functor).
         * @param functor the built-in to be invoked.
         */
        void emitBody(Functor functor) {
            Node[] fargs = functor.getArgs();
            Builtin builtin = functor.getImplementor();
            if (builtin == null) {
                throw new LPRuleSyntaxException("Unknown builtin operation " + functor.getName(), rule);
            }
            if (builtin.getArgLength() != 0 && builtin.getArgLength() != fargs.length) {
                throw new LPRuleSyntaxException("Wrong number of arguments to functor " + functor.getName() 
                                                  + " : got " + functor.getArgLength()
                                                  + " : expected " + builtin.getArgLength(), rule);
            }
            for (int i = 0; i < fargs.length; i++) {
                Node node = fargs[i];
                // We optionally force an eager dereference of variables here.
                // We used to force this but the current builtin implementations
                // now robust against it (the do a deref themselves anyway).
                 emitBodyPut(node, i, true);
            }
            code[p++] = CALL_BUILTIN;
            code[p++] = (byte)fargs.length;
            args.add(builtin);
        }
         
        /**
         * Complete the byte stream with a PROCEED and return the packed version of the code.
         * @return the byte code array
         */
        byte[] getFinalCode() {
            code[p++] = PROCEED;
            byte[] finalCode = new byte[p];
            System.arraycopy(code, 0, finalCode, 0, p);
            return finalCode;
        }
        
        /**
         * Fetch the packed array of argument objects for the byte code.
         * @return arg array
         */
        Object[] getFinalArgs() {
            return args.toArray();
        }
        
        /**
         * Classifies the variables now and side effects the
         * index number of the variables so they lie in two sequences - temp
         * and permanent separately. 
         */
        void classifyVariables( Rule rule ) {
            // Build index data structure into var use in each term in the rule
            @SuppressWarnings("unchecked") List<Node>[] termListArray = new List[rule.bodyLength() + 1];
            termVarTable = termListArray;
            termVarTable[0] = termVars( rule.getHeadElement(0) );
            totalOccurrences += termVarTable[0].size();
            for (int i = 0; i < rule.bodyLength(); i++) {
                termVarTable[i+1] = termVars( rule.getBodyElement(i) );
                totalOccurrences += termVarTable[i+1].size();
            }
            
            // Build the inverted data structure
            for (int i = 0; i < rule.bodyLength() + 1; i++ ) {
                List<Node> varEnts = termVarTable[i];
                for (int j = 0; j < varEnts.size(); j++) {
                    Node n = varEnts.get(j);
                    if (n.isVariable()) {
                        Node_RuleVariable var = (Node_RuleVariable)n; 
                        List<TermIndex> occurrences = varOccurrence.get(var);
                        if (occurrences == null) {
                            occurrences = new ArrayList<>();
                            varOccurrence.put(var, occurrences);
                        }
                        occurrences.add(new TermIndex(var, i, j));
                    }
                }
            }
            
            // Classify vars as permanent unless they are just dummies (only used once at all)
            // or only used head+first body goal (but not if used in a builtin)
            for ( List<TermIndex> occurrences : varOccurrence.values() )
            {
                Node_RuleVariable var = null;
                boolean inFirst = false;
                boolean inLaterBody = false;
                for ( Iterator<TermIndex> oi = occurrences.iterator(); oi.hasNext(); )
                {
                    TermIndex occurence = oi.next();
                    var = occurence.var;
                    int termNumber = occurence.termNumber;
                    if ( termNumber == 0 )
                    {
                        inFirst = true;
                    }
                    else if ( termNumber > 1 )
                    {
                        inLaterBody = true;
                    }
                    if ( termNumber > 0 && rule.getBodyElement( termNumber - 1 ) instanceof Functor )
                    {
                    }
                }
                // Don't think we need to protected builtin's any more, so ignore that test
//                if (inBuiltin) {
//                    permanentVars.add(var);
//                } else {
                if ( !isDummy( var ) )
                {
                    if ( inLaterBody || !inFirst )
                    {
                        permanentVars.add( var );
                    }
                    else
                    {
                        tempVars.add( var );
                    }
                }
//                }

            }
            
            if (permanentVars.size() > MAX_PERMANENT_VARS) {
                throw new LPRuleSyntaxException("Rule too complex for current implementation\n" 
                            + "Rule clauses are limited to " + MAX_PERMANENT_VARS + " permanent variables\n", rule); 
            }
            
            if (tempVars.size() > MAX_TEMPORARY_VARS) {
                throw new LPRuleSyntaxException("Rule too complex for current implementation\n" 
                            + "Rule clauses are limited to " + MAX_TEMPORARY_VARS + " temporary variables\n", rule); 
            }
            
            // Builtins in the forward system use the var index to modify variable bindings.
            // At one time I though the LP system might need to emulate this but (a) it doesn't and
            // (b) renumber vars to make that possible wreaks rule equality which wreaks add/remove in
            // hybrid rule sets. This code left in but commented out as a reminder to not go down
            // that path again.
            
            // Renumber the vars
//            for (int i = 0; i < permanentVars.size(); i++ ) {
//                Node_RuleVariable var = (Node_RuleVariable)permanentVars.get(i);
//                var.setIndex(i);
//            }
//            for (int i = 0; i < tempVars.size(); i++ ) {
//                Node_RuleVariable var = (Node_RuleVariable)tempVars.get(i);
//                var.setIndex(i);
//            }
            
        }
       
        /** Return true if the given rule variable is a temp */
        boolean isTemp(Node_RuleVariable var) {
            return !isDummy(var) && !permanentVars.contains(var);
        }
        
        /** Return true if the given rule variable is a dummy */
        boolean isDummy(Node_RuleVariable var) {
            List<TermIndex> occurances = varOccurrence.get(var);
            if (occurances == null || occurances.size() <= 1) return true;
            return false;
        }
                
        /** Return an list of variables or nodes in a ClauseEntry, in flattened order */
        private List<Node> termVars(ClauseEntry term) {
            List<Node> result = new ArrayList<>();
            if (term instanceof TriplePattern) {
                TriplePattern goal = (TriplePattern)term;
                result.add(goal.getSubject());
                result.add(goal.getPredicate());
                Node obj = goal.getObject();
                if (Functor.isFunctor(obj)) {
                    result.add(obj);
                    result.addAll(termVars((Functor)obj.getLiteralValue()));
                } else {
                    result.add(obj);
                }
            } else if (term instanceof Functor) {
                Node[] args = ((Functor)term).getArgs();
                for ( Node arg : args )
                {
                    result.add( arg );
                }
            }
            return result;
        }
    }
                
    
    /**
     * Inner class used to represent the occurance of a variable in a term.
     * Not an abstract data type, just a structure directly accessed.
     */
    static class TermIndex {
        /** The term number in the rule - 0 for head, body starting from 1 */
        int termNumber;
        
        /** The variable position within the term */
        int index;
        
        /** The variable being indexed */
        Node_RuleVariable var;
        
        /** Constructor */
        TermIndex(Node_RuleVariable var, int termNumber, int index) {
            this.var = var;
            this.termNumber = termNumber;
            this.index = index;
        }
    }
      
    /**
     * Debug support - not unit testing.
     */
    // TODO document the derivation of these strings and their intended use?
    public static void main(String[] args) {
        try {
            LPRuleStore store = new LPRuleStore();
            String test1 = "(?a p ?y) <- (?a p2 ?z).";
            String test2 = "(?a p ?y) <- (?a q2 ?z) (?z q2 ?w).";
            String test3 =  "(?a p ?a) <- (?z r2 ?w) (?z r2 ?w).";
            String test4 =  "(?a p ?a) <- (?z r2 ?w) (?a r2 ?w).";
            String test5 = "(?a p ?y) <- (?a p ?z), (?z p ?y).";
            String test6 = "(?x p C3) <- (C1 r ?x).";
            String test7 = "(?x p ?y) <- (?x r ?y) (?x q ?y).";
            String test8 = "(?x p ?y) <- (?x p ?z) addOne(?z, ?y).";
            String test9 = "(?x p ?y) <- (?x p ?z) sum(?z, 2, ?y).";
            String test10 = "(?x p ?y) <- (?x p ?v), sum(?v 2 ?y).";
            String test11 = "(b p ?y) <- (a ?y ?v).";
            String test12 = "(?x p ?y) <- (?x p foo(?z, ?y)).";
            String test13 = "(?x p foo(?y,?z)) <- (?x q ?y), (?x q ?z).";
            String test14 = "(?x p ?z) <- (?x e ?z), (?z q ?z).";
            String test15 = "(?x p ?y ) <- bound(?x), (?x p ?y).";
            String test16 = "(a p b ) <- unbound(?x).";
            String test17 = "(?a p ?a) <- (?a q class).";
            String test18 = "(?a p ?a) <- (?a s ?a).";
            String test19 = "(?X p ?T) <- (?X rdf:type c), noValue(?X, p), makeInstance(?X, p, ?T).";
            String test20 = "(a p b ) <- unbound(?x).";
            String testLong = "(?P p ?C) <- (?P q ?D), (?D r xsd(?B, ?S1, ?L1)),(?P p ?E), notEqual(?D, ?E) " +
                    "(?E e xsd(?B, ?S2, ?L2)),min(?S1, ?S2, ?S3),min(?L1, ?L2, ?L3), (?C r xsd(?B, ?S3, ?L3)).";
            String test21 = "(?a p ?y) <- (?x s ?y) (?a p ?x).";
            String test22 = "(?C p ?D) <- (?C rb:xsdBase ?BC), (?D rb:xsdBase ?BD), notEqual(?BC, ?BD).";
            store.addRule(Rule.parseRule(test22));
            System.out.println("Code for p:");
            List<RuleClauseCode> codeList = store.codeFor(NodeFactory.createURI("p"));
            RuleClauseCode code = codeList.get(0);
            code.print(System.out);
        } catch (Exception e) {
            System.out.println("Problem: " + e);
            e.printStackTrace();
        }
    }
}
