/*
 * DlpTransformerTest.java
 * JUnit based test
 *
 * Copyright (C) 2006 - 2007 Martin Slota
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/*
 * History:
 * v0.1 (2007-02-10): initial version
 * v0.2 (2007-02-11):
 * - testTransformedRules, testTransformedRules2, testRejectionRules,
 *   testRejectionsAndDefaults, testConstraint and testConstraint2 added
 * - docs added
 * v0.2.1 (2007-02-12):
 * - changed because of changes in LpParser
 * v0.2.2 (2007-02-14):
 * - rule group tests added
 * v0.2.3 (2007-02-15):
 * - tests fixed (didn't expect the correct results of rules :o)
 * - parser is now closed after use
 * v0.9.0 (2007-05-06):
 * - changed to suit the new version of DlpTransformer
 */

package lp.trans;

import java.util.HashMap;
import java.util.Map;
import junit.framework.*;

import lp.parse.LpParser;

import lp.unit.DynamicLogicProgram;
import lp.unit.LogicProgram;
import lp.unit.TransDlpRuleType;
import static lp.unit.TransDlpRuleType.*;
import lp.unit.TransformedDlp;

/**
 * Contains tests of the {@link DlpTransformer} class.
 *
 * @author Martin Slota
 * @version 0.9.0
 * @see DlpTransformer
 */
public class DlpTransformerTest extends TestCase {
	/**
	 * A {@link LpParser} instance used in the tests to convert create object
	 * representations of rules.
	 */
	private final LpParser parser;
	
	/**
	 * A {@link DlpTransformer} instance used in the tests.
	 */
	private final DlpTransformer transformer;
	
	/**
	 * A default test case constructor.
	 *
	 * @param testName the name of the test case
	 */
	public DlpTransformerTest(String testName) {
		super(testName);
		parser = new LpParser();
		transformer = new DlpTransformer();
	}
	
	/**
	 * Tests the transformer on simple input. No rejection, default or
	 * constraint rules are expectected in the output.
	 */
	public void testTransformedRules() {
		String [] dlpSource = {
			"a. b <- a.",
			"c. d <- c, a."
		};
		
		String transformedSource =
				"a <- not _rej(1, a).\n" +
				"b <- a, not _rej(1, b).\n" +
				"c <- not _rej(2, c).\n" +
				"d <- c, a, not _rej(2, d).";
		
		
		Map<TransDlpRuleType, String> ruleGroups =
				new HashMap<TransDlpRuleType, String>();
		ruleGroups.put(REWRITTEN_RULE,
				"a <- not _rej(1, a).\n" +
				"b <- a, not _rej(1, b).\n" +
				"c <- not _rej(2, c).\n" +
				"d <- c, a, not _rej(2, d).");
		ruleGroups.put(INTRO_REJECTION, "");
		ruleGroups.put(PROPAGATION_REJECTION, "");
		ruleGroups.put(DEFAULT_RULE, "");
		ruleGroups.put(CONSTRAINT, "");
		
		doTest(dlpSource, transformedSource, ruleGroups);
	}
	
	/**
	 * Tests the transformer on simple input. No rejection, default or
	 * constraint rules are expectected in the output. But this time the
	 * literals are more complicated.
	 */
	public void testTransformedRules2() {
		String [] dlpSource = {
			"a(f(3, 4), g(x)). b(z) <- a(2, y).",
			"c(42, f(h(4))). d(x, y, z) <- c(3, 2, 1), a(4, 6, 9)."
		};
		
		String transformedSource =
				"a(f(3, 4), g(x)) <- not _rej(1, aLfL3C4RCgLxRR).\n" +
				"b(z) <- a(2, y), not _rej(1, bLzR).\n" +
				"c(42, f(h(4))) <- not _rej(2, cL42CfLhL4RRR).\n" +
				"d(x, y, z) <- c(3, 2, 1), a(4, 6, 9), not _rej(2, dLxCyCzR).";
		
		
		Map<TransDlpRuleType, String> ruleGroups =
				new HashMap<TransDlpRuleType, String>();
		ruleGroups.put(REWRITTEN_RULE,
				"a(f(3, 4), g(x)) <- not _rej(1, aLfL3C4RCgLxRR).\n" +
				"b(z) <- a(2, y), not _rej(1, bLzR).\n" +
				"c(42, f(h(4))) <- not _rej(2, cL42CfLhL4RRR).\n" +
				"d(x, y, z) <- c(3, 2, 1), a(4, 6, 9), not _rej(2, dLxCyCzR).");
		ruleGroups.put(INTRO_REJECTION, "");
		ruleGroups.put(PROPAGATION_REJECTION, "");
		ruleGroups.put(DEFAULT_RULE, "");
		ruleGroups.put(CONSTRAINT, "");
		
		doTest(dlpSource, transformedSource, ruleGroups);
	}
	
	/**
	 * Tests the transformer on input that requires generation of introducing
	 * rejection rules. No propagation rejection rules, default rules or
	 * constraints are expectected in the output.
	 */
	public void testRejectionRules() {
		String [] dlpSource = {
			"a.",
			"not a.",
			"a."
		};
		
		String transformedSource =
				"a <- not _rej(1, a).\n" +
				"_Na <- not _rej(2, _Na).\n" +
				"_rej(1, a).\n" +
				"a <- not _rej(3, a).\n" +
				"_rej(2, _Na).\n";
		
		Map<TransDlpRuleType, String> ruleGroups =
				new HashMap<TransDlpRuleType, String>();
		ruleGroups.put(REWRITTEN_RULE,
				"a <- not _rej(1, a).\n" +
				"_Na <- not _rej(2, _Na).\n" +
				"a <- not _rej(3, a).");
		ruleGroups.put(INTRO_REJECTION,
				"_rej(1, a).\n" +
				"_rej(2, _Na).");
		ruleGroups.put(PROPAGATION_REJECTION, "");
		ruleGroups.put(DEFAULT_RULE, "");
		ruleGroups.put(CONSTRAINT, "");
		
		doTest(dlpSource, transformedSource, ruleGroups);
	}
	
	/**
	 * Here the input is more complicated. All types of rules are expected,
	 * except constraints.
	 */
	public void testRejectionsAndDefaults() {
		String [] dlpSource = {
			"a <- not b. b <- not a. c <- a.",
			"not c. a <- c.",
			"c <- b. not a."
		};
		
		String transformedSource =
				"a <- _Nb, not _rej(1, a).\n" +
				"b <- _Na, not _rej(1, b).\n" +
				"c <- a, not _rej(1, c).\n" +
				"_Nc <- not _rej(2, _Nc).\n" +
				"a <- c, not _rej(2, a).\n" +
				"c <- b, not _rej(3, c).\n" +
				"_Na <- not _rej(3, _Na).\n" +
				"_Nb <- not _rej(0, _Nb).\n" +
				"_Na <- not _rej(0, _Na).\n" +
				"_rej(1, c).\n" +
				"_rej(2, _Nc) <- b.\n" +
				"_rej(2, a).\n" +
				"_rej(1, a) <- _rej(2, a).\n" +
				"_rej(0, _Na) <- c.\n" +
				"_rej(0, _Na) <- _Nb.\n" +
				"_rej(0, _Nb) <- _Na.";
		
		Map<TransDlpRuleType, String> ruleGroups =
				new HashMap<TransDlpRuleType, String>();
		ruleGroups.put(REWRITTEN_RULE,
				"a <- _Nb, not _rej(1, a).\n" +
				"b <- _Na, not _rej(1, b).\n" +
				"c <- a, not _rej(1, c).\n" +
				"_Nc <- not _rej(2, _Nc).\n" +
				"a <- c, not _rej(2, a).\n" +
				"c <- b, not _rej(3, c).\n" +
				"_Na <- not _rej(3, _Na).");
		ruleGroups.put(INTRO_REJECTION,
				"_rej(1, c).\n" +
				"_rej(2, _Nc) <- b.\n" +
				"_rej(2, a).\n" +
				"_rej(0, _Na) <- c.\n" +
				"_rej(0, _Na) <- _Nb.\n" +
				"_rej(0, _Nb) <- _Na.");
		ruleGroups.put(PROPAGATION_REJECTION,
				"_rej(1, a) <- _rej(2, a).\n");
		ruleGroups.put(DEFAULT_RULE,
				"_Nb <- not _rej(0, _Nb).\n" +
				"_Na <- not _rej(0, _Na).");
		ruleGroups.put(CONSTRAINT, "");
		
		doTest(dlpSource, transformedSource, ruleGroups);
	}
	
	/**
	 * The simplest DLP without a model. A constraint is required in the output.
	 */
	public void testConstraint() {
		String [] dlpSource = {
			"a. not a."
		};
		
		String transformedSource =
				"a <- not _rej(1, a).\n" +
				"_Na <- not _rej(1, _Na).\n" +
				"_rej(1, a).\n" +
				"_rej(1, _Na).\n" +
				"_Na <- not _rej(0, _Na).\n" +
				"_rej(0, _Na) <- _rej(1, _Na).\n" +
				"<- not a, not _Na.";
		
		Map<TransDlpRuleType, String> ruleGroups =
				new HashMap<TransDlpRuleType, String>();
		ruleGroups.put(REWRITTEN_RULE,
				"a <- not _rej(1, a).\n" +
				"_Na <- not _rej(1, _Na).");
		ruleGroups.put(INTRO_REJECTION,
				"_rej(1, a).\n" +
				"_rej(1, _Na).");
		ruleGroups.put(PROPAGATION_REJECTION, "_rej(0, _Na) <- _rej(1, _Na).");
		ruleGroups.put(DEFAULT_RULE, "_Na <- not _rej(0, _Na).");
		ruleGroups.put(CONSTRAINT, "<- not a, not _Na.");
		
		doTest(dlpSource, transformedSource, ruleGroups);
	}
	
	/**
	 * A more complicated input requiring a constraint in the output.
	 */
	public void testConstraint2() {
		String [] dlpSource = {
			"a <- not b. b <- not a.",
			"c <- a. not c <- a."
		};
		
		String transformedSource =
				"a <- _Nb, not _rej(1, a).\n" +
				"b <- _Na, not _rej(1, b).\n" +
				"c <- a, not _rej(2, c).\n" +
				"_Nc <- a, not _rej(2, _Nc).\n" +
				"_rej(0, _Na) <- _Nb.\n" +
				"_rej(0, _Nb) <- _Na.\n" +
				"_rej(2, c) <- a.\n" +
				"_rej(2, _Nc) <- a.\n" +
				"_rej(0, _Nc) <- _rej(2, _Nc).\n" +
				"_Na <- not _rej(0, _Na).\n" +
				"_Nb <- not _rej(0, _Nb).\n" +
				"_Nc <- not _rej(0, _Nc).\n" +
				"<- not c, not _Nc.";
		
		Map<TransDlpRuleType, String> ruleGroups =
				new HashMap<TransDlpRuleType, String>();
		ruleGroups.put(REWRITTEN_RULE,
				"a <- _Nb, not _rej(1, a).\n" +
				"b <- _Na, not _rej(1, b).\n" +
				"c <- a, not _rej(2, c).\n" +
				"_Nc <- a, not _rej(2, _Nc).");
		ruleGroups.put(INTRO_REJECTION,
				"_rej(0, _Na) <- _Nb.\n" +
				"_rej(0, _Nb) <- _Na.\n" +
				"_rej(2, c) <- a.\n" +
				"_rej(2, _Nc) <- a.");
		ruleGroups.put(PROPAGATION_REJECTION, "_rej(0, _Nc) <- _rej(2, _Nc).");
		ruleGroups.put(DEFAULT_RULE,
				"_Na <- not _rej(0, _Na).\n" +
				"_Nb <- not _rej(0, _Nb).\n" +
				"_Nc <- not _rej(0, _Nc).");
		ruleGroups.put(CONSTRAINT, "<- not c, not _Nc.");
		
		doTest(dlpSource, transformedSource, ruleGroups);
	}
	
	/**
	 * Performs a single test. Parses {@code dlpSource} into a
	 * {@link DynamicLogicProgram} instance, performs the transformation and
	 * compares the resulting rules with rules found in
	 * {@code expTransformedSource}.
	 *
	 * @param dlpSource source of a dynamic logic program
	 * @param expTransformedSource representation of the expected transformational
	 * equivalent
	 */
	private void doTest(String [] dlpSource, String expTransformedSource,
			Map<TransDlpRuleType, String> expRuleGroupsSource) {
		// parse the DLP
		DynamicLogicProgram dlp = new DynamicLogicProgram();
		for (String programSource : dlpSource) {
			parser.setInput(programSource);
			dlp.add(parser.parseAllRules());
			parser.close();
		}
		
		// parse the expected transformed source
		parser.setInput(expTransformedSource);
		LogicProgram expTransformed = parser.parseAllRules();
		parser.close();
		
		// parse the expected rule groups
		Map<TransDlpRuleType, LogicProgram> expRuleGroups =
				new HashMap<TransDlpRuleType, LogicProgram>();
		for (TransDlpRuleType t : TransDlpRuleType.values()) {
			parser.setInput(expRuleGroupsSource.get(t));
			expRuleGroups.put(t, parser.parseAllRules());
			parser.close();
		}
		
		// perform the real transformation
		TransformedDlp transformed = transformer.transform(dlp);
		
		// compare groups with expected values
		for (TransDlpRuleType t : TransDlpRuleType.values()) {
			assertEquals("The group " + t + " of the transformed program is not as expected!",
					expRuleGroups.get(t), transformed.getGroup(t));
		}
		
		// compare the whole transformed program with the expected value
		assertEquals("The transformed program is not as expected!",
				expTransformed, transformed);
	}
}