/*
 * 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
 *
 *   https://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.
 *
 *   SPDX-License-Identifier: Apache-2.0
 */

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

import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.GraphMemFactory;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.graph.Triple;
import org.apache.jena.reasoner.InfGraph;
import org.apache.jena.reasoner.Reasoner;
import org.apache.jena.reasoner.TriplePattern;
import org.apache.jena.reasoner.rulesys.Functor;
import org.apache.jena.reasoner.rulesys.LPBackwardRuleReasoner;
import org.apache.jena.reasoner.rulesys.Node_RuleVariable;
import org.apache.jena.reasoner.rulesys.Rule;
import org.apache.jena.reasoner.rulesys.Util;
import org.apache.jena.reasoner.rulesys.impl.BindingVector;
import org.apache.jena.reasoner.test.TestUtil;
import org.apache.jena.util.iterator.ExtendedIterator;
import org.apache.jena.vocabulary.OWL;
import org.apache.jena.vocabulary.RDF;
import org.apache.jena.vocabulary.RDFS;

import java.util.ArrayList;
import java.util.List;

/**
 * Test harness for the backward chainer.
 * Parameterizable in subclasses by overriding createReasoner.
 * The original version was developed for the original backchaining interpeter.
 * That has now been obsoleted at this is now used to double check the
 * LP engine, though the bulk of such tests are really done by TestBasicLP.
 */
public class TestBackchainer extends TestCase {

    // Maximum size of binding environment needed in the tests
    private static final int MAX_VARS = 10;

    // Useful constants
    protected Node p = NodeFactory.createURI("p");
    protected Node q = NodeFactory.createURI("q");
    protected Node r = NodeFactory.createURI("r");
    protected Node s = NodeFactory.createURI("s");
    protected Node t = NodeFactory.createURI("t");
    protected Node a = NodeFactory.createURI("a");
    protected Node b = NodeFactory.createURI("b");
    protected Node c = NodeFactory.createURI("c");
    protected Node d = NodeFactory.createURI("d");
    protected Node C1 = NodeFactory.createURI("C1");
    protected Node C2 = NodeFactory.createURI("C2");
    protected Node C3 = NodeFactory.createURI("C3");
    protected Node sP = RDFS.Nodes.subPropertyOf;
    protected Node sC = RDFS.Nodes.subClassOf;
    protected Node ty = RDF.Nodes.type;

    String testRules1 =
        "(?x ?q ?y) <- (?p rdfs:subPropertyOf ?q)(?x ?p ?y). " +
        "(?a rdfs:subPropertyOf ?c) <- (?a rdfs:subPropertyOf ?b)(?b rdfs:subPropertyOf ?c). ";

    String testRuleAxioms = "[ -> (p rdfs:subPropertyOf q)]" +
                            "[ -> (q rdfs:subPropertyOf r) ]" +
                            "[ -> (a p b) ]";

    Triple[] dataElts = new Triple[] {
                            Triple.create(p, sP, q),
                            Triple.create(q, sP, r),
                            Triple.create(a,  p, b)
                            };

    /**
     * Boilerplate for junit
     */
    public TestBackchainer( String name ) {
        super( name );
    }

    /**
     * Boilerplate for junit.
     * This is its own test suite
     */
    public static TestSuite suite() {
        return new TestSuite( TestBackchainer.class );
//        TestSuite suite = new TestSuite();
//        suite.addTest(new TestBackchainer( "testRDFSProblemsb" ));
//        return suite;
    }

    private static  Graph createGraphForTest() {
        return GraphMemFactory.createDefaultGraph();
    }

    /**
     * Override in subclasses to test other reasoners.
     */
    public Reasoner createReasoner(List<Rule> rules) {
        LPBackwardRuleReasoner reasoner = new LPBackwardRuleReasoner(rules);
        reasoner.tablePredicate(sP);
        reasoner.tablePredicate(sC);
        reasoner.tablePredicate(ty);
        reasoner.tablePredicate(p);
        reasoner.tablePredicate(a);
        reasoner.tablePredicate(b);
        return reasoner;
    }

    /**
     * Test parser modes to support backarrow notation are working
     */
    public void testParse() {
        List<Rule> rules = Rule.parseRules(testRules1);
        assertEquals("BRule parsing",
                        "[ (?x ?q ?y) <- (?p rdfs:subPropertyOf ?q) (?x ?p ?y) ]",
                        rules.get(0).toString());
        assertEquals("BRule parsing",
                        "[ (?a rdfs:subPropertyOf ?c) <- (?a rdfs:subPropertyOf ?b) (?b rdfs:subPropertyOf ?c) ]",
                        rules.get(1).toString());
    }

    /**
     * Test goal/head unify operation.
     */
    public void testUnify() {
        Node_RuleVariable xg = new Node_RuleVariable("?x", 0);
        Node_RuleVariable yg = new Node_RuleVariable("?y", 1);
        Node_RuleVariable zg = new Node_RuleVariable("?z", 2);

        Node_RuleVariable xh = new Node_RuleVariable("?x", 0);
        Node_RuleVariable yh = new Node_RuleVariable("?y", 1);
        Node_RuleVariable zh = new Node_RuleVariable("?z", 2);

        TriplePattern g1 = new TriplePattern(xg, p, yg);
        TriplePattern g2 = new TriplePattern(xg, p, xg);
        TriplePattern g3 = new TriplePattern( a, p, xg);
        TriplePattern g4 = new TriplePattern( a, p,  b);

        TriplePattern h1 = new TriplePattern(xh, p, yh);
        TriplePattern h2 = new TriplePattern(xh, p, xh);
        TriplePattern h3 = new TriplePattern( a, p, xh);
        TriplePattern h4 = new TriplePattern( a, p,  b);
        TriplePattern h5 = new TriplePattern(xh, p,  a);

        doTestUnify(g1, h1, true, new Node[] {null, null});
        doTestUnify(g1, h2, true, new Node[] {null, null});
        doTestUnify(g1, h3, true, new Node[] {null, null});
        doTestUnify(g1, h4, true, new Node[] {null, null});
        doTestUnify(g1, h5, true, new Node[] {null, null});

        doTestUnify(g2, h1, true, new Node[] {null, xh});
        doTestUnify(g2, h2, true, new Node[] {null, null});
        doTestUnify(g2, h3, true, new Node[] {a, null});
        doTestUnify(g2, h4, false, null);
        doTestUnify(g2, h5, true, new Node[] {a, null});

        doTestUnify(g3, h1, true, new Node[] {a, null});
        doTestUnify(g3, h2, true, new Node[] {a, null});
        doTestUnify(g3, h3, true, new Node[] {null, null});
        doTestUnify(g3, h4, true, new Node[] {null, null});
        doTestUnify(g3, h5, true, new Node[] {a, null});

        doTestUnify(g4, h1, true, new Node[] {a, b});
        doTestUnify(g4, h2, false, null);
        doTestUnify(g4, h3, true, new Node[] {b});
        doTestUnify(g4, h4, true, null);
        doTestUnify(g4, h5, false, null);

        // Recursive case
        doTestUnify(h1, h1, true, new Node[] {null, null});

        // Wildcard case
        doTestUnify(new TriplePattern(null, null, null), h2, true, new Node[] {null, null});

        // Test functor cases as well!
        TriplePattern gf = new TriplePattern(xg, p,
                                Functor.makeFunctorNode("f", new Node[]{xg, b}));
        TriplePattern hf1 = new TriplePattern(yh, p,
                                Functor.makeFunctorNode("f", new Node[]{zh, b}));
        TriplePattern hf2 = new TriplePattern(yh, p,
                                Functor.makeFunctorNode("f", new Node[]{a, yh}));
        TriplePattern hf3 = new TriplePattern(yh, p,
                                Functor.makeFunctorNode("f", new Node[]{b, yh}));
        doTestUnify(gf, hf1, true, new Node[] {null, null, yh});
        doTestUnify(gf, hf2, false, null);
        doTestUnify(gf, hf3, true, new Node[] {null, b});

        // Check binding environment use
        BindingVector env = BindingVector.unify(g2, h1, MAX_VARS);
        env.bind(xh, c);
        assertEquals(env.getBinding(yh), c);
        env = BindingVector.unify(g2, h1, MAX_VARS);
        env.bind(yh, c);
        assertEquals(env.getBinding(xh), c);
    }

    /**
     * Helper for testUnify.
     * @param goal goal triple pattern
     * @param head head triple pattern
     * @param succeed whether match should succeeed or fail
     * @param env list list of expected environment bindings
     *
     */
    private void doTestUnify(TriplePattern goal, TriplePattern head, boolean succeed, Node[] env) {
        BindingVector result = BindingVector.unify(goal, head, MAX_VARS);
        if (succeed) {
            assertNotNull(result);
            if (env != null) {
                for (int i = 0; i < env.length; i++) {
                    Node n = result.getEnvironment()[i];
                    if (env[i] != null) {
                        assertEquals(env[i], n);
                    } else {
                        assertNull(n);
                    }
                }
            }
        } else {
            assertNull(result);
        }
    }

    /**
     * Check that a reasoner over an empty rule set accesses
     * the raw data successfully.
     */
    public void testListData() {
        Graph data = createGraphForTest();
        for ( Triple dataElt : dataElts )
        {
            data.add( dataElt );
        }
        Graph schema = createGraphForTest();
        schema.add(Triple.create(c, p, c));

        // Case of schema and data but no rule axioms
        Reasoner reasoner =  createReasoner(new ArrayList<Rule>());
        InfGraph infgraph = reasoner.bindSchema(schema).bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(null, null, null),
            new Object[] {
                Triple.create(p, sP, q),
                Triple.create(q, sP, r),
                Triple.create(a,  p, b),
                Triple.create(c, p, c)});

        // Case of data and rule axioms but no schema
        List<Rule> rules = Rule.parseRules("-> (d p d).");
        reasoner =  createReasoner(rules);
        infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(null, null, null),
            new Object[] {
                Triple.create(p, sP, q),
                Triple.create(q, sP, r),
                Triple.create(a,  p, b),
                Triple.create(d, p, d)});

        // Case of data and rule axioms and schema
        infgraph = reasoner.bindSchema(schema).bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(null, null, null),
            new Object[] {
                Triple.create(p, sP, q),
                Triple.create(q, sP, r),
                Triple.create(a,  p, b),
                Triple.create(c, p, c),
                Triple.create(d, p, d)});

    }

    /**
     * Test basic rule operations - simple AND rule
     */
    public void testBaseRules1() {
        List<Rule> rules = Rule.parseRules("[r1: (?a r ?c) <- (?a p ?b),(?b p ?c)]");
        Graph data = createGraphForTest();
        data.add(Triple.create(a, p, b));
        data.add(Triple.create(b, p, c));
        data.add(Triple.create(b, p, d));
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(null, r, null),
            new Object[] {
                Triple.create(a, r, c),
                Triple.create(a, r, d)
            } );
    }

    /**
     * Test basic rule operations - simple OR rule
     */
    public void testBaseRules2() {
        List<Rule> rules = Rule.parseRules(
                "[r1: (?a r ?b) <- (?a p ?b)]" +
                "[r2: (?a r ?b) <- (?a q ?b)]" +
                "[r3: (?a r ?b) <- (?a s ?c), (?c s ?b)]"
        );
        Graph data = createGraphForTest();
        data.add(Triple.create(a, p, b));
        data.add(Triple.create(b, q, c));
        data.add(Triple.create(a, s, b));
        data.add(Triple.create(b, s, d));
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(null, r, null),
            new Object[] {
                Triple.create(a, r, b),
                Triple.create(b, r, c),
                Triple.create(a, r, d)
            } );
    }

    /**
     * Test basic rule operations - simple OR rule with chaining
     */
    public void testBaseRules2b() {
        List<Rule> rules = Rule.parseRules(
                "[r1: (?a r ?b) <- (?a p ?b)]" +
                "[r2: (?a r ?b) <- (?a q ?b)]" +
                "[r3: (?a r ?b) <- (?a t ?c), (?c t ?b)]" +
                "[r4: (?a t ?b) <- (?a s ?b)]"
        );
        Graph data = createGraphForTest();
        data.add(Triple.create(a, p, b));
        data.add(Triple.create(b, q, c));
        data.add(Triple.create(a, s, b));
        data.add(Triple.create(b, s, d));
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(null, r, null),
            new Object[] {
                Triple.create(a, r, b),
                Triple.create(b, r, c),
                Triple.create(a, r, d)
            } );
    }

    /**
     * Test basic rule operations - simple AND rule check with tabling.
     */
    public void testBaseRules3() {
        List<Rule> rules = Rule.parseRules("[rule: (?a rdfs:subPropertyOf ?c) <- (?a rdfs:subPropertyOf ?b),(?b rdfs:subPropertyOf ?c)]");
        Reasoner reasoner =  createReasoner(rules);
        Graph data = createGraphForTest();
        data.add(Triple.create(p, sP, q) );
        data.add(Triple.create(q, sP, r) );
        data.add(Triple.create(p, sP, s) );
        data.add(Triple.create(s, sP, t) );
        data.add(Triple.create(a,  p, b) );
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(null, RDFS.subPropertyOf.asNode(), null),
            new Object[] {
                Triple.create(p, sP, q),
                Triple.create(q, sP, r),
                Triple.create(p, sP, s),
                Triple.create(s, sP, t),
                Triple.create(p, sP, t),
                Triple.create(p, sP, r)
            } );
    }

    /**
     * Test basic rule operations - simple AND rule check with tabling.
     */
    public void testBaseRules3b() {
        List<Rule> rules = Rule.parseRules("[rule: (?a rdfs:subPropertyOf ?c) <- (?a rdfs:subPropertyOf ?b),(?b rdfs:subPropertyOf ?c)]");
        Reasoner reasoner =  createReasoner(rules);
        Graph data = createGraphForTest();
        data.add(Triple.create(p, sP, q) );
        data.add(Triple.create(q, sP, r) );
        data.add(Triple.create(r, sP, t) );
        data.add(Triple.create(q, sP, s) );
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(null, RDFS.subPropertyOf.asNode(), null),
            new Object[] {
                Triple.create(p, sP, q),
                Triple.create(q, sP, r),
                Triple.create(r, sP, t),
                Triple.create(q, sP, s),
                Triple.create(p, sP, s),
                Triple.create(p, sP, r),
                Triple.create(p, sP, t),
                Triple.create(q, sP, t),
                Triple.create(p, sP, r)
            } );
    }

    /**
     * Test basic rule operations - simple AND/OR with tabling.
     */
    public void testBaseRules4() {
        Graph data = createGraphForTest();
        data.add(Triple.create(a, r, b));
        data.add(Triple.create(b, r, c));
        data.add(Triple.create(b, r, b));
        data.add(Triple.create(b, r, d));
        List<Rule> rules = Rule.parseRules(
                        "[r1: (?x p ?y) <- (?x r ?y)]" +
                        "[r2: (?x p ?z) <- (?x p ?y), (?y r ?z)]"
                        );
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(a, p, null),
            new Object[] {
                Triple.create(a, p, b),
                Triple.create(a, p, d),
                Triple.create(a, p, c)
            } );
    }

    /**
     * Test basic rule operations - simple AND/OR with tabling.
     */
    public void testBaseRulesXSB1() {
        Graph data = createGraphForTest();
        data.add(Triple.create(p, c, q));
        data.add(Triple.create(q, c, r));
        data.add(Triple.create(p, d, q));
        data.add(Triple.create(q, d, r));
        List<Rule> rules = Rule.parseRules(
            "[r1: (?x a ?y) <- (?x c ?y)]" +
            "[r2: (?x a ?y) <- (?x b ?z), (?z c ?y)]" +
            "[r3: (?x b ?y) <- (?x d ?y)]" +
            "[r4: (?x b ?y) <- (?x a ?z), (?z d ?y)]"
        );
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(p, a, null),
            new Object[] {
                Triple.create(p, a, q),
                Triple.create(p, a, r)
            } );
    }

    /**
     * Test basic functor usage.
     */
    public void testFunctors1() {
        Graph data = createGraphForTest();
        data.add(Triple.create(a, p, b));
        data.add(Triple.create(a, q, c));
        List<Rule> rules = Rule.parseRules(
            "[r1: (?x r f(?y,?z)) <- (?x p ?y), (?x q ?z)]" +
            "[r2: (?x s ?y) <- (?x r f(?y, ?z))]"
        );
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(a, s, null),
            new Object[] {
                Triple.create(a, s, b)
            } );
    }

    /**
     * Test basic functor usage.
     */
    public void testFunctors2() {
        Graph data = createGraphForTest();
        data.add(Triple.create(a, p, b));
        data.add(Triple.create(a, q, c));
        data.add(Triple.create(a, t, d));
        List<Rule> rules = Rule.parseRules(
            "[r1: (?x r f(?y,?z)) <- (?x p ?y), (?x q ?z)]" +
            "[r2: (?x s ?y) <- (?x r f(?y, ?z))]" +
            "[r3: (?x r g(?y,?z)) <- (?x p ?y), (?x t ?z)]" +
            "[r4: (?x s ?z) <- (?x r g(?y, ?z))]"
        );
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(a, s, null),
            new Object[] {
                Triple.create(a, s, b),
                Triple.create(a, s, d)
            } );
    }

    /**
     * Test basic functor usage.
     */
    public void testFunctors3() {
        Graph data = createGraphForTest();
        data.add(Triple.create(a, s, b));
        data.add(Triple.create(a, t, c));
        List<Rule> rules = Rule.parseRules(
            "[r1: (a q f(?x,?y)) <- (a s ?x), (a t ?y)]" +
            "[r2: (a p ?x) <- (a q ?x)]" +
            "[r3: (a r ?y) <- (a p f(?x, ?y))]"
        );
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(a, r, null),
            new Object[] {
                Triple.create(a, r, c)
            } );
    }

    /**
     * Test basic builtin usage.
     */
    public void testBuiltin1() {
        Graph data = createGraphForTest();
        List<Rule> rules = Rule.parseRules(
            "[a1: -> (a p 2) ]" +
            "[a2: -> (a q 3) ]" +
            "[r1: (?x r ?s) <- (?x p ?y), (?x q ?z), sum(?y, ?z, ?s)]"
        );
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(a, r, null),
            new Object[] {
                Triple.create(a, r, Util.makeIntNode(5))
            } );
    }

    /**
     * Test basic builtin usage.
     */
    public void testBuiltin2() {
        Graph data = createGraphForTest();
        data.add(Triple.create(a, p, b));
        data.add(Triple.create(a, q, c));
        List<Rule> rules = Rule.parseRules(
            "[r1: (?x r ?y ) <- bound(?x), (?x p ?y) ]" +
            "[r2: (?x r ?y) <- unbound(?x), (?x q ?y)]"
        );
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(a, r, null),
            new Object[] {
                Triple.create(a, r, b)
            } );
        TestUtil.assertIteratorValues(this,
            infgraph.find(null, r, null),
            new Object[] {
                Triple.create(a, r, c)
            } );
    }

    /**
     * Test basic builtin usage.
     */
    public void testBuiltin3() {
        Graph data = createGraphForTest();
        List<Rule> rules = Rule.parseRules(
            "[r1: (a p b ) <- unbound(?x) ]"
        );
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(a, null, null),
            new Object[] {
                Triple.create(a, p, b)
            } );
    }

    /**
     * Test basic ground head patterns.
     */
    public void testGroundHead() {
        Graph data = createGraphForTest();
        data.add(Triple.create(a, r, b));
        List<Rule> rules = Rule.parseRules(
            "[r1: (a p b ) <- (a r b) ]"
        );
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(a, null, null),
            new Object[] {
                Triple.create(a, p, b),
                Triple.create(a, r, b)
            } );
    }

//    /**
//     * Test multiheaded rule.
//     */
//    public void testMutliHead() {
//        Graph data = createGraphForTest();
//        data.add(Triple.create(a, p, b));
//        data.add(Triple.create(b, r, c));
//        List<Rule> rules = Rule.parseRules(
//            "[r1: (?x s ?z), (?z s ?x) <- (?x p ?y) (?y r ?z) ]"
//        );
//        Reasoner reasoner =  createReasoner(rules);
//        InfGraph infgraph = reasoner.bind(data);
//        TestUtil.assertIteratorValues(this,
//            infgraph.find(null, s, null),
//            new Object[] {
//                Triple.create(a, s, c),
//                Triple.create(c, s, a)
//            } );
//    }

    /**
     * Test rebind operation
     */
    public void testRebind() {
        List<Rule> rules = Rule.parseRules("[r1: (?a r ?c) <- (?a p ?b),(?b p ?c)]");
        Graph data = createGraphForTest();
        data.add(Triple.create(a, p, b));
        data.add(Triple.create(b, p, c));
        data.add(Triple.create(b, p, d));
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(null, r, null),
            new Object[] {
                Triple.create(a, r, c),
                Triple.create(a, r, d)
            } );
        Graph ndata = createGraphForTest();
        ndata.add(Triple.create(a, p, d));
        ndata.add(Triple.create(d, p, b));
        infgraph.rebind(ndata);
        TestUtil.assertIteratorValues(this,
            infgraph.find(null, r, null),
            new Object[] {
                Triple.create(a, r, b)
            } );

    }

    /**
     * Test troublesome rdfs rules
     */
    public void testRDFSProblemsb() {
        Graph data = createGraphForTest();
        data.add(Triple.create(C1, sC, C2));
        data.add(Triple.create(C2, sC, C3));
        data.add(Triple.create(C1, ty, RDFS.Class.asNode()));
        data.add(Triple.create(C2, ty, RDFS.Class.asNode()));
        data.add(Triple.create(C3, ty, RDFS.Class.asNode()));
        List<Rule> rules = Rule.parseRules(
        "[rdfs8:  (?a rdfs:subClassOf ?b), (?b rdfs:subClassOf ?c) -> (?a rdfs:subClassOf ?c)]" +
        "[rdfs7:  (?a rdf:type rdfs:Class) -> (?a rdfs:subClassOf ?a)]"
                        );
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(null, sC, null),
            new Object[] {
                Triple.create(C1, sC, C2),
                Triple.create(C1, sC, C3),
                Triple.create(C1, sC, C1),
                Triple.create(C2, sC, C3),
                Triple.create(C2, sC, C2),
                Triple.create(C3, sC, C3),
            } );
    }

    /**
     * Test troublesome rdfs rules
     */
    public void testRDFSProblems() {
        Graph data = createGraphForTest();
        data.add(Triple.create(p, sP, q));
        data.add(Triple.create(q, sP, r));
        data.add(Triple.create(C1, sC, C2));
        data.add(Triple.create(C2, sC, C3));
        data.add(Triple.create(a, ty, C1));
        List<Rule> rules = Rule.parseRules(
        "[rdfs8:  (?a rdfs:subClassOf ?b), (?b rdfs:subClassOf ?c) -> (?a rdfs:subClassOf ?c)]" +
        "[rdfs9:  (?x rdfs:subClassOf ?y), (?a rdf:type ?x) -> (?a rdf:type ?y)]" +
//        "[-> (rdf:type rdfs:range rdfs:Class)]" +
        "[rdfs3:  (?x ?p ?y), (?p rdfs:range ?c) -> (?y rdf:type ?c)]" +
        "[rdfs7:  (?a rdf:type rdfs:Class) -> (?a rdfs:subClassOf ?a)]"
                        );
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(a, ty, null),
            new Object[] {
                Triple.create(a, ty, C1),
                Triple.create(a, ty, C2),
                Triple.create(a, ty, C3)
            } );
        TestUtil.assertIteratorValues(this,
            infgraph.find(C1, sC, a),
            new Object[] {
            } );
    }

    /**
     * Test complex rule head unification
     */
    public void testHeadUnify() {
        Graph data = createGraphForTest();
        data.add(Triple.create(c, q, d));
        List<Rule> rules = Rule.parseRules(
            "[r1: (c r ?x) <- (?x p f(?x b))]" +
            "[r2: (?y p f(a ?y)) <- (c q ?y)]"
                          );
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
              infgraph.find(c, r, null), new Object[] { } );

        data.add(Triple.create(c, q, a));
        rules = Rule.parseRules(
        "[r1: (c r ?x) <- (?x p f(?x a))]" +
        "[r2: (?y p f(a ?y)) <- (c q ?y)]"
                          );
        reasoner =  createReasoner(rules);
        infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
              infgraph.find(c, r, null),
              new Object[] {
                  Triple.create(c, r, a)
              } );

        data = createGraphForTest();
        data.add(Triple.create(a, q, a));
        data.add(Triple.create(a, q, b));
        data.add(Triple.create(a, q, c));
        data.add(Triple.create(b, q, d));
        data.add(Triple.create(b, q, b));
        rules = Rule.parseRules(
          "[r1: (c r ?x) <- (?x p ?x)]" +
          "[r2: (?x p ?y) <- (a q ?x), (b q ?y)]"
                          );
        reasoner =  createReasoner(rules);
        infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
              infgraph.find(c, r, null),
              new Object[] {
                  Triple.create(c, r, b)
              } );

        rules = Rule.parseRules(
          "[r1: (c r ?x) <- (?x p ?x)]" +
          "[r2: (a p ?x) <- (a q ?x)]"
                          );
        reasoner =  createReasoner(rules);
        infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
              infgraph.find(c, r, null),
              new Object[] {
                  Triple.create(c, r, a)
              } );
    }

    /**
     * Test restriction example
     */
    public void testRestriction1() {
        Graph data = createGraphForTest();
        data.add(Triple.create(a, ty, r));
        data.add(Triple.create(a, p, b));
        data.add(Triple.create(r, sC, C1));
        data.add(Triple.create(C1, OWL.onProperty.asNode(), p));
        data.add(Triple.create(C1, OWL.allValuesFrom.asNode(), c));
        List<Rule> rules = Rule.parseRules(
    "[rdfs9:  (?x rdfs:subClassOf ?y) (?a rdf:type ?x) -> (?a rdf:type ?y)]" +
    "[restriction2: (?C owl:onProperty ?P), (?C owl:allValuesFrom ?D) -> (?C owl:equivalentClass all(?P, ?D))]" +
    "[rs2: (?D owl:equivalentClass all(?P,?C)), (?X rdf:type ?D) -> (?X rdf:type all(?P,?C))]" +
    "[rp4: (?X rdf:type all(?P, ?C)), (?X ?P ?Y) -> (?Y rdf:type ?C)]"
                          );
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
              infgraph.find(b, ty, c), new Object[] {
                  Triple.create(b, ty, c)
              } );
    }


    /**
     * Test restriction example. The rules are more than the minimum required
     * to solve the query and they interact to given run away seaches if there
     * is a problem.
     */
    public void testRestriction2() {
        Graph data = createGraphForTest();
        data.add(Triple.create(a, ty, OWL.Thing.asNode()));
        data.add(Triple.create(p, ty, OWL.FunctionalProperty.asNode()));
        data.add(Triple.create(c, OWL.equivalentClass.asNode(), C1));
        data.add(Triple.create(C1, ty, OWL.Restriction.asNode()));
        data.add(Triple.create(C1, OWL.onProperty.asNode(), p));
        data.add(Triple.create(C1, OWL.maxCardinality.asNode(), Util.makeIntNode(1)));
        List<Rule> rules = Rule.parseRules(
        // these ones are required for the inference.
        "[rdfs9:  bound(?y)   (?x rdfs:subClassOf ?y) (?a rdf:type ?x) -> (?a rdf:type ?y)]" +
        "[restriction4: (?C rdf:type owl:Restriction), (?C owl:onProperty ?P), (?C owl:maxCardinality ?X) -> (?C owl:equivalentClass max(?P, ?X))]" +
        "[restrictionProc11: (?P rdf:type owl:FunctionalProperty), (?X rdf:type owl:Thing) -> (?X rdf:type max(?P, 1))]" +
//        "[equivalentClass1: (?P owl:equivalentClass ?Q) -> (?P rdfs:subClassOf ?Q), (?Q rdfs:subClassOf ?P) ]" +
        "[equivalentClass1: (?P owl:equivalentClass ?Q) -> (?P rdfs:subClassOf ?Q) ]" +
        "[equivalentClass1: (?P owl:equivalentClass ?Q) -> (?Q rdfs:subClassOf ?P) ]" +
        "[restrictionSubclass1: bound(?D) (?D owl:equivalentClass ?R), isFunctor(?R) (?X rdf:type ?R)-> (?X rdf:type ?D)]" +
         // these ones are noise which can cause run aways or failures if there are bugs
        "[rdfs8:  unbound(?c) (?a rdfs:subClassOf ?b) (?b rdfs:subClassOf ?c) -> (?a rdfs:subClassOf ?c)]" +
        "[rdfs8:  bound(?c)   (?b rdfs:subClassOf ?c) (?a rdfs:subClassOf ?b) -> (?a rdfs:subClassOf ?c)]" +
        "[rdfs9:  unbound(?y) (?a rdf:type ?x) (?x rdfs:subClassOf ?y) -> (?a rdf:type ?y)]" +
        "[-> (rdf:type      rdfs:range rdfs:Class)]" +
        "[rdfs3:  bound(?c)   (?p rdfs:range ?c) (?x ?p ?y) -> (?y rdf:type ?c)]" +
        "[rdfs7:  (?a rdf:type rdfs:Class) -> (?a rdfs:subClassOf ?a)]" +
        "[restrictionProc13: (owl:Thing rdfs:subClassOf all(?P, ?C)) -> (?P rdfs:range ?C)]" +
        "[restrictionSubclass1: unbound(?D) (?X rdf:type ?R), isFunctor(?R) (?D owl:equivalentClass ?R) -> (?X rdf:type ?D)]" +
        "[restrictionSubclass2: bound(?R), isFunctor(?R), (?D owl:equivalentClass ?R),(?X rdf:type ?D) -> (?X rdf:type ?R)]" +
        "[restrictionSubclass2: unbound(?R), (?X rdf:type ?D), (?D owl:equivalentClass ?R) isFunctor(?R) -> (?X rdf:type ?R)]" +
                       ""  );
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
              infgraph.find(a, ty, C1), new Object[] {
                  Triple.create(a, ty, C1)
              } );
        TestUtil.assertIteratorValues(this,
              infgraph.find(a, ty, c), new Object[] {
                  Triple.create(a, ty, c)
              } );
    }

    /**
     * Test restriction example
     */
    public void testRestriction3() {
        Graph data = createGraphForTest();
        data.add(Triple.create(a, ty, r));
        data.add(Triple.create(r, sC, C1));
        data.add(Triple.create(C1, ty, OWL.Restriction.asNode()));
        data.add(Triple.create(C1, OWL.onProperty.asNode(), p));
        data.add(Triple.create(C1, OWL.allValuesFrom.asNode(), c));
        List<Rule> rules = Rule.parseRules(
        "[-> (rdfs:subClassOf rdfs:range rdfs:Class)]" +
//        "[-> (owl:Class rdfs:subClassOf rdfs:Class)]" +
        "[rdfs3:  bound(?c)   (?p rdfs:range ?c) (?x ?p ?y) -> (?y rdf:type ?c)]" +
        "[rdfs3:  unbound(?c) (?x ?p ?y), (?p rdfs:range ?c) -> (?y rdf:type ?c)]" +
        "[rdfs7:  (?a rdf:type rdfs:Class) -> (?a rdfs:subClassOf ?a)]" +
        "[rdfs8:  (?a rdfs:subClassOf ?b) (?b rdfs:subClassOf ?c) -> (?a rdfs:subClassOf ?c)]" +
        "[restrictionProc4b: bound(?Y) (?X ?P ?Y), notEqual(?P, rdf:type), (?X rdf:type all(?P, ?C)),-> (?Y rdf:type ?C)]" +
        "[restrictionProc4b: unbound(?Y), (?X rdf:type all(?P, ?C)), (?X ?P ?Y), notEqual(?P, rdf:type),-> (?Y rdf:type ?C)]" +
                       ""  );
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
              infgraph.find(null, ty, c), new Object[] {
              } );
    }

    /**
     * Test close and halt operation.
     */
    public void testClose() {
        Graph data = createGraphForTest();
        data.add(Triple.create(p, sP, q));
        data.add(Triple.create(q, sP, r));
        data.add(Triple.create(C1, sC, C2));
        data.add(Triple.create(C2, sC, C3));
        data.add(Triple.create(a, ty, C1));
        data.add(Triple.create(ty, RDFS.range.asNode(), RDFS.Class.asNode()));
        List<Rule> rules = Rule.parseRules(
        "[rdfs8:  (?a rdfs:subClassOf ?b), (?b rdfs:subClassOf ?c) -> (?a rdfs:subClassOf ?c)]" +
        "[rdfs9:  (?x rdfs:subClassOf ?y), (?a rdf:type ?x) -> (?a rdf:type ?y)]" +
//        "[-> (rdf:type rdfs:range rdfs:Class)]" +
        "[rdfs3:  (?x ?p ?y), (?p rdfs:range ?c) -> (?y rdf:type ?c)]" +
        "[rdfs7:  (?a rdf:type rdfs:Class) -> (?a rdfs:subClassOf ?a)]"
                        );
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        // Get just one result
        ExtendedIterator<Triple> it = infgraph.find(a, ty, null);
        Triple result = it.next();
        assertEquals(result.getSubject(), a);
        assertEquals(result.getPredicate(), ty);
        it.close();
        // Make sure if we start again we get the full listing.
        TestUtil.assertIteratorValues(this,
            infgraph.find(a, ty, null),
            new Object[] {
                Triple.create(a, ty, C1),
                Triple.create(a, ty, C2),
                Triple.create(a, ty, C3)
            } );
    }

    /**
     * Test problematic rdfs case
     */
    public void testBug1() {
        Graph data = createGraphForTest();
        Node p = NodeFactory.createURI("http://www.hpl.hp.com/semweb/2003/eg#p");
        Node r = NodeFactory.createURI("http://www.hpl.hp.com/semweb/2003/eg#r");
        Node C1 = NodeFactory.createURI("http://www.hpl.hp.com/semweb/2003/eg#C1");
        data.add(Triple.create(a, p, b));
        List<Rule> rules = Rule.parseRules(Util.loadRuleParserFromResourceFile("testing/reasoners/bugs/rdfs-error1.brules"));
        Reasoner reasoner =  createReasoner(rules);
        InfGraph infgraph = reasoner.bind(data);
        TestUtil.assertIteratorValues(this,
            infgraph.find(b, ty, C1),
            new Object[] {
                Triple.create(b, ty, C1)
            } );

    }

}
