OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / tests / atoms / scope / VariablesUTest.cxxtest

1052 lines · cxxtest ref:
/*
 * tests/atoms/scope/VariablesUTest.cxxtest
 *
 * Copyright (C) 2016 OpenCog Foundation
 * All Rights Reserved
 * Author: Nil Geisweiller
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * 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 Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/atoms/scope/Variables.h>
#include <opencog/atoms/scope/VariableList.h>
#include <opencog/atomspace/AtomSpace.h>

#include <cxxtest/TestSuite.h>

using namespace opencog;

#define al _as.add_link
#define an _as.add_node

class VariablesUTest : public CxxTest::TestSuite
{
private:
	Handle A, B, N, DPN, W, X, Y, Z, G1, G2, NT, PNT, CNT, NTI, DPTCI, LLT, LTI;

	AtomSpace _as;

public:
	VariablesUTest()
	{
		logger().set_level(Logger::DEBUG);
		logger().set_timestamp_flag(false);
		logger().set_print_to_stdout_flag(true);

		A = an(CONCEPT_NODE, "A");
		B = an(CONCEPT_NODE, "B");
		N = an(NODE, "N");
		DPN = an(DEFINED_PREDICATE_NODE, "DPN");
		X = an(VARIABLE_NODE, "$X");
		Y = an(VARIABLE_NODE, "$Y");
		Z = an(VARIABLE_NODE, "$Z");
		W = an(VARIABLE_NODE, "$W");
		G1 = an(GLOB_NODE, "$G1");
		G2 = an(GLOB_NODE, "$G2");
		NT = an(TYPE_NODE, "Node");
		PNT = an(TYPE_NODE, "PredicateNode");
		CNT = an(TYPE_NODE, "ConceptNode");
		NTI = an(TYPE_INH_NODE, "Node");
		DPTCI = an(TYPE_CO_INH_NODE, "DefinedPredicateNode");
		LLT = an(TYPE_NODE, "ListLink");
		LTI = an(TYPE_INH_NODE, "Link");
	}

	void test_extend_intersect_1();
	void test_extend_intersect_2();
	void test_extend_intersect_3();
	void test_extend_intersect_4();
	void test_extend_intersect_5();
	void test_extend_intersect_6();

	void test_find_variables_ordered_1();
	void test_find_variables_ordered_2();
	void test_find_variables_ordered_3();
	void test_find_variables_ordered_4();
	void test_find_variables_unordered_1();
	void test_find_variables_unordered_2();
	void test_find_variables_unordered_3();
	void test_find_variables_unordered_4();
	void test_find_variables_unordered_5();
	void test_find_variables_mixed_1();
	void test_find_variables_mixed_2();
	void test_find_variables_mixed_3();
	void test_find_variables_mixed_4();
	void test_find_variables_mixed_5();
	void test_find_variables_mixed_6();
	void test_find_variables_mixed_7();
	void test_find_variables_mixed_8();
	void test_find_variables_mixed_9();
	void test_find_variables_mixed_10();
	void test_find_variables_mixed_11();
	void test_find_variables_alt_as();

	void test_is_type_1();
	void test_is_type_2();
	void test_is_type_3();
	void test_is_type_4();
	void test_is_type_5();
	void test_is_type_6();

	void test_substitute_nocheck_scope();
};

void VariablesUTest::test_extend_intersect_1()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle
		vardecl1 = X,
		vardecl2 = al(VARIABLE_LIST,
		              al(TYPED_VARIABLE_LINK, X, PNT),
		              al(TYPED_VARIABLE_LINK, Y, CNT)),
		expect = al(VARIABLE_LIST,
		            al(TYPED_VARIABLE_LINK, X, PNT),
		            al(TYPED_VARIABLE_LINK, Y, CNT));

	Variables v1(vardecl1), v2(vardecl2);

	v1.extend_intersect(v2);

	Handle result = _as.add_atom(v1.get_vardecl());

	logger().debug() << "expect = " << oc_to_string(expect);
	logger().debug() << "result = " << oc_to_string(result);

	TS_ASSERT_EQUALS(result, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_extend_intersect_2()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle
		vardecl1 = al(VARIABLE_LIST,
		              al(TYPED_VARIABLE_LINK, X, NT),
		              al(TYPED_VARIABLE_LINK, Y, CNT)),
		vardecl2 = al(VARIABLE_LIST,
		              al(TYPED_VARIABLE_LINK, X, PNT),
		              al(TYPED_VARIABLE_LINK, Y, al(TYPE_CHOICE, PNT, CNT))),
		expect = al(VARIABLE_LIST,
		            al(TYPED_VARIABLE_LINK, X, al(TYPE_CHOICE)),
		            al(TYPED_VARIABLE_LINK, Y, CNT));

	Variables v1(vardecl1), v2(vardecl2);

	v1.extend_intersect(v2);

	Handle result = _as.add_atom(v1.get_vardecl());

	logger().debug() << "expect = " << oc_to_string(expect);
	logger().debug() << "result = " << oc_to_string(result);

	TS_ASSERT_EQUALS(result, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_extend_intersect_3()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle
			vardecl1 = al(VARIABLE_LIST,
			              al(TYPED_VARIABLE_LINK, X, PNT),
			              al(TYPED_VARIABLE_LINK, Y, CNT)),
			vardecl2 = al(VARIABLE_LIST,
			              al(TYPED_VARIABLE_LINK, X, NTI),
			              al(TYPED_VARIABLE_LINK, Y, al(TYPE_CHOICE, PNT, CNT))),
			expect = al(VARIABLE_LIST,
			            al(TYPED_VARIABLE_LINK, X, PNT),
			            al(TYPED_VARIABLE_LINK, Y, CNT));

	Variables v1(vardecl1), v2(vardecl2);

	v1.extend_intersect(v2);

	Handle result = _as.add_atom(v1.get_vardecl());

	logger().debug() << "expect = " << oc_to_string(expect);
	logger().debug() << "result = " << oc_to_string(result);

	TS_ASSERT_EQUALS(result, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_extend_intersect_4()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle
			vardecl1 = al(VARIABLE_LIST,
			              al(TYPED_VARIABLE_LINK, X, al(TYPE_CHOICE, LLT, NTI)),
			              al(TYPED_VARIABLE_LINK, Y, NT)),
			vardecl2 = al(VARIABLE_LIST,
			              al(TYPED_VARIABLE_LINK, X, al(TYPE_CHOICE, LTI, CNT)),
			              al(TYPED_VARIABLE_LINK, Y, DPTCI)),
			expect = al(VARIABLE_LIST,
			            al(TYPED_VARIABLE_LINK, X, al(TYPE_CHOICE, CNT, LLT)),
			            al(TYPED_VARIABLE_LINK, Y, NT));

	Variables v1(vardecl1), v2(vardecl2);

	v1.extend_intersect(v2);

	Handle result = _as.add_atom(v1.get_vardecl());

	logger().debug() << "expect = " << oc_to_string(expect);
	logger().debug() << "result = " << oc_to_string(result);

	TS_ASSERT_EQUALS(result, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_extend_intersect_5()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle vardecl1 = al(VARIABLE_LIST,
	                    al(TYPED_VARIABLE_LINK, G1,
	                       al(TYPE_INTERSECTION_LINK,
	                          al(INTERVAL_LINK,
	                             an(NUMBER_NODE, "0"),
	                             an(NUMBER_NODE, "2")),
	                          NTI)),
	                    al(TYPED_VARIABLE_LINK, G2,
	                       al(TYPE_INTERSECTION_LINK,
	                          al(INTERVAL_LINK,
	                             an(NUMBER_NODE, "0"),
	                             an(NUMBER_NODE, "2")),
	                          PNT))),
			vardecl2 = al(VARIABLE_LIST,
			              al(TYPED_VARIABLE_LINK, G1,
			                 al(TYPE_INTERSECTION_LINK,
			                    al(INTERVAL_LINK,
			                       an(NUMBER_NODE, "1"),
			                       an(NUMBER_NODE, "-1")),
			                    PNT)),
			              al(TYPED_VARIABLE_LINK, G2,
			                 al(TYPE_INTERSECTION_LINK,
			                    al(INTERVAL_LINK,
			                       an(NUMBER_NODE, "1"),
			                       an(NUMBER_NODE, "3")),
			                    PNT))),
			expect = al(VARIABLE_LIST,
			            al(TYPED_VARIABLE_LINK, G1,
			               al(TYPE_INTERSECTION_LINK,
			                  al(INTERVAL_LINK,
			                     an(NUMBER_NODE, "1"),
			                     an(NUMBER_NODE, "2")),
			                  PNT)),
			            al(TYPED_VARIABLE_LINK, G2,
			               al(TYPE_INTERSECTION_LINK,
			                  al(INTERVAL_LINK,
			                     an(NUMBER_NODE, "1"),
			                     an(NUMBER_NODE, "2")),
			                  PNT)));

	Variables v1(vardecl1), v2(vardecl2);

	v1.extend_intersect(v2);

	Handle result = _as.add_atom(v1.get_vardecl());

	logger().debug() << "expect = " << oc_to_string(expect);
	logger().debug() << "result = " << oc_to_string(result);

	TS_ASSERT_EQUALS(result, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_extend_intersect_6()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle vardecl1 = al(VARIABLE_LIST,
	                     al(TYPED_VARIABLE_LINK, X, NTI),
	                     al(TYPED_VARIABLE_LINK, G2, PNT)),
			vardecl2 = al(VARIABLE_LIST,
			              al(TYPED_VARIABLE_LINK, X, PNT),
			              al(TYPED_VARIABLE_LINK, G2, PNT)),
			expect = al(VARIABLE_LIST,
			            al(TYPED_VARIABLE_LINK, X, PNT),
			            al(TYPED_VARIABLE_LINK, G2, PNT));

	Variables v1(vardecl1), v2(vardecl2);

	v1.extend_intersect(v2);

	Handle result = _as.add_atom(v1.get_vardecl());

	logger().debug() << "expect = " << oc_to_string(expect);
	logger().debug() << "result = " << oc_to_string(result);

	TS_ASSERT_EQUALS(result, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_ordered_1()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Inferred vardecl should be in infix order since all links are
	// ordered, that is [X, Y].
	Handle body = al(INHERITANCE_LINK, X, Y);
	Variables vars;
	vars.find_variables(body);
	HandleSeq varseq = vars.varseq;
	HandleSeq expect{X, Y};

	logger().debug() << "varseq = " << oc_to_string(varseq);
	logger().debug() << "expect = " << oc_to_string(expect);

	TS_ASSERT_EQUALS(varseq, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_ordered_2()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Inferred vardecl should be in infix order since all links are
	// ordered, that is [X, Y, Z, W].
	Handle body = al(LIST_LINK,
	                 al(INHERITANCE_LINK, X, Y),
	                 al(INHERITANCE_LINK, Z, W));
	Variables vars;
	vars.find_variables(body);
	HandleSeq varseq = vars.varseq;
	HandleSeq expect{X, Y, Z, W};

	logger().debug() << "varseq = " << oc_to_string(varseq);
	logger().debug() << "expect = " << oc_to_string(expect);

	TS_ASSERT_EQUALS(varseq, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_ordered_3()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Inferred vardecl should be in infix order since all links are
	// ordered, that is [X, Y], as it eliminate redundant variables.
	Handle body = al(LIST_LINK,
	                 al(INHERITANCE_LINK, X, Y),
	                 al(INHERITANCE_LINK, Y, X));
	Variables vars;
	vars.find_variables(body);
	HandleSeq varseq = vars.varseq;
	HandleSeq expect{X, Y};

	logger().debug() << "varseq = " << oc_to_string(varseq);
	logger().debug() << "expect = " << oc_to_string(expect);

	TS_ASSERT_EQUALS(varseq, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_ordered_4()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Simple ordered body with quoted and scoped variables (thus that
	// should be ignored). Result should be [X, Y] as the first
	// occurrence of Y is quoted, and other variables like Z and W are
	// scoped.
	Handle body = al(LIST_LINK,
	                 al(QUOTE_LINK, Y),
	                 al(INHERITANCE_LINK, X, Y),
	                 al(SCOPE_LINK, al(INHERITANCE_LINK, Z, W)));
	Variables vars;
	vars.find_variables(body);
	HandleSeq varseq = vars.varseq;
	HandleSeq expect{X, Y};

	logger().debug() << "varseq = " << oc_to_string(varseq);
	logger().debug() << "expect = " << oc_to_string(expect);

	TS_ASSERT_EQUALS(varseq, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_unordered_1()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Inferred vardecl may not follow any particular order since the
	// body structure imposes no asymmetry.
	Handle body = al(AND_LINK, X, Y);
	Variables vars;
	vars.find_variables(body);
	HandleSet result = vars.varset;
	HandleSet expect{X, Y};

	logger().debug() << "result = " << oc_to_string(result);
	logger().debug() << "expect = " << oc_to_string(expect);

	TS_ASSERT_EQUALS(result, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_unordered_2()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Inferred vardecl can be one of the possibilities
	//
	// 1. sorted({X, Y}) + sorted({Z, W})
	// 2. sorted({Z, W}) + sorted({X, Y})
	//
	// X, Y and Z, W can be interleaved because it is would not be
	// semantically equivalent.
	Handle body = al(AND_LINK,
	                 al(OR_LINK, X, Y),
	                 al(OR_LINK, Z, W));
	Variables vars;
	vars.find_variables(body);
	HandleSeq varseq = vars.varseq;

	HandleSet XY{X, Y}, ZW{Z, W};
	HandleSeq expect1;
	expect1.insert(expect1.end(), XY.begin(), XY.end());
	expect1.insert(expect1.end(), ZW.begin(), ZW.end());
	HandleSeq expect2;
	expect2.insert(expect2.end(), ZW.begin(), ZW.end());
	expect2.insert(expect2.end(), XY.begin(), XY.end());

	logger().debug() << "varseq = " << oc_to_string(varseq);
	logger().debug() << "expect1 = " << oc_to_string(expect1);
	logger().debug() << "expect2 = " << oc_to_string(expect2);

	TS_ASSERT(varseq == expect1 or varseq == expect2);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_unordered_3()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Inferred vardecl should have Z come last because it occurs more
	// often than X and Y.
	Handle body = al(SEQUENTIAL_AND_LINK,
	                 al(OR_LINK, X, Y),
	                 al(OR_LINK, Z, Z));
	Variables vars;
	vars.find_variables(body);
	HandleSeq varseq = vars.varseq;

	HandleSet XY = HandleSet{X, Y};
	HandleSeq expect;
	expect.insert(expect.end(), XY.begin(), XY.end());
	expect.push_back(Z);

	logger().debug() << "varseq = " << oc_to_string(varseq);
	logger().debug() << "expect = " << oc_to_string(expect);

	TS_ASSERT_EQUALS(varseq, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_unordered_4()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Inferred vardecl should be sorted({X, Y}) + sorted({Z, W})
	// because AndLink type is less than OrLink type.
	Handle body = al(AND_LINK,
	                 al(AND_LINK, X, Y),
	                 al(OR_LINK, Z, W));
	Variables vars;
	vars.find_variables(body);
	HandleSeq varseq = vars.varseq;

	HandleSet XY = HandleSet{X, Y};
	HandleSet ZW = HandleSet{Z, W};
	HandleSeq expect;
	expect.insert(expect.end(), XY.begin(), XY.end());
	expect.insert(expect.end(), ZW.begin(), ZW.end());

	logger().debug() << "varseq = " << oc_to_string(varseq);
	logger().debug() << "expect = " << oc_to_string(expect);

	TS_ASSERT_EQUALS(varseq, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_unordered_5()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Inferred vardecl of body1 should be the reverse of inferred
	// vardecl of body2.
	Handle body1 = al(OR_LINK,
	                  al(AND_LINK, X, A),
	                  al(AND_LINK, Y, B));
	Handle body2 = al(OR_LINK,
	                  al(AND_LINK, Y, A),
	                  al(AND_LINK, X, B));
	Variables vars1;
	Variables vars2;
	vars1.find_variables(body1);
	vars2.find_variables(body2);
	HandleSeq varseq1 = vars1.varseq;
	HandleSeq varseq2 = vars2.varseq;

	logger().debug() << "varseq1 = " << oc_to_string(varseq1);
	logger().debug() << "varseq2 = " << oc_to_string(varseq2);

	// Reverse varseq2 to test it if it equal to varseq1
	std::reverse(varseq2.begin(), varseq2.end());

	TS_ASSERT_EQUALS(varseq1, varseq2);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_mixed_1()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Inferred vardecl may not follow any particular order since the
	// body structure imposes no asymmetry.
	Handle body = al(AND_LINK,
	                 al(INHERITANCE_LINK, X, Y),
	                 al(INHERITANCE_LINK, Y, X));
	Variables vars;
	vars.find_variables(body);
	HandleSet result = vars.varset;
	HandleSet expect{X, Y};

	logger().debug() << "result = " << oc_to_string(result);
	logger().debug() << "expect = " << oc_to_string(expect);

	// Just make sure the vardecl has X and Y, regardless of the order
	TS_ASSERT_EQUALS(result, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_mixed_2()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Inferred vardecl should place X before Y because the
	// ExtensionalSimilarityLink type is ordered before
	// IntensionalSimilarityLink type.
	Handle body = al(AND_LINK,
	                 al(SUBSET_LINK, X, Y),
	                 al(INHERITANCE_LINK, Y, X));
	Variables vars;
	vars.find_variables(body);
	HandleSeq varseq = vars.varseq;
	HandleSeq expect{X, Y};

	logger().debug() << "varseq = " << oc_to_string(varseq);
	logger().debug() << "expect = " << oc_to_string(expect);

	TS_ASSERT_EQUALS(varseq, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_mixed_3()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Make sure the variable declarations of body1 and body2 are
	// the reverses of each other, that is if one is [X, Y], then
	// the other is [Y, X].
	Handle body1 = al(INHERITANCE_LINK, al(LIST_LINK, X, Y), Y);
	Handle body2 = al(INHERITANCE_LINK, al(LIST_LINK, Y, X), X);

	Variables vars1;
	Variables vars2;
	vars1.find_variables(body1);
	vars2.find_variables(body2);
	HandleSeq varseq1 = vars1.varseq;
	HandleSeq varseq2 = vars2.varseq;

	logger().debug() << "varseq1 = " << oc_to_string(varseq1);
	logger().debug() << "varseq2 = " << oc_to_string(varseq2);

	// Reverse varseq2 to test it if it equal to varseq1
	std::reverse(varseq2.begin(), varseq2.end());

	TS_ASSERT_EQUALS(varseq1, varseq2);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_mixed_4()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Inferred vardecl can be any combination where {X, Y} and {Z, W}
	// do not interleave.
	Handle body = al(AND_LINK,
	                 al(INHERITANCE_LINK, X, Y),
	                 al(INHERITANCE_LINK, Y, X),
	                 al(INHERITANCE_LINK, Z, W),
	                 al(INHERITANCE_LINK, W, Z));
	Variables vars;
	vars.find_variables(body);
	HandleSeq result = vars.varseq;
	HandleSeq expect1{X, Y, Z, W};
	HandleSeq expect2{Y, X, Z, W};
	HandleSeq expect3{X, Y, W, Z};
	HandleSeq expect4{Y, X, W, Z};
	HandleSeq expect5{Z, W, X, Y};
	HandleSeq expect6{Z, W, Y, X};
	HandleSeq expect7{W, Z, X, Y};
	HandleSeq expect8{W, Z, Y, X};

	logger().debug() << "result = " << oc_to_string(result);
	logger().debug() << "expect1 = " << oc_to_string(expect1);
	logger().debug() << "expect2 = " << oc_to_string(expect2);
	logger().debug() << "expect3 = " << oc_to_string(expect3);
	logger().debug() << "expect4 = " << oc_to_string(expect4);
	logger().debug() << "expect5 = " << oc_to_string(expect5);
	logger().debug() << "expect6 = " << oc_to_string(expect6);
	logger().debug() << "expect7 = " << oc_to_string(expect7);
	logger().debug() << "expect8 = " << oc_to_string(expect8);

	TS_ASSERT(result == expect1 or
	          result == expect2 or
	          result == expect3 or
	          result == expect4 or
	          result == expect5 or
	          result == expect6 or
	          result == expect7 or
	          result == expect8);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_mixed_5()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Like test_find_variables_mixed_1 but incorporates non variable
	// nodes to cover some, otherwise non covered, code.
	Handle body = al(AND_LINK,
	                 al(INHERITANCE_LINK, X, Y),
	                 al(INHERITANCE_LINK, Y, X),
	                 al(INHERITANCE_LINK, A, B));
	Variables vars;
	vars.find_variables(body);
	HandleSet result = vars.varset;
	HandleSet expect{X, Y};

	logger().debug() << "result = " << oc_to_string(result);
	logger().debug() << "expect = " << oc_to_string(expect);

	// Just make sure the vardecl has X and Y, regardless of the order
	TS_ASSERT_EQUALS(result, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_mixed_6()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Make sure the variable declarations of body1 and body2 are
	// the reverses of each other, that is if one is [X, Y], then
	// the other is [Y, X].
	Handle body1 = al(LIST_LINK,
	                  al(SET_LINK, X, Y),
	                  al(LIST_LINK, X, Y));
	Handle body2 = al(LIST_LINK,
	                  al(SET_LINK, Y, X),
	                  al(LIST_LINK, Y, X));

	Variables vars1;
	Variables vars2;
	vars1.find_variables(body1);
	vars2.find_variables(body2);
	HandleSeq varseq1 = vars1.varseq;
	HandleSeq varseq2 = vars2.varseq;

	logger().debug() << "varseq1 = " << oc_to_string(varseq1);
	logger().debug() << "varseq2 = " << oc_to_string(varseq2);

	// Reverse varseq2 to test it if it equal to varseq1
	std::reverse(varseq2.begin(), varseq2.end());

	TS_ASSERT_EQUALS(varseq1, varseq2);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_mixed_7()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Make sure the variable declarations of body1 and body2 are
	// the reverses of each other, that is if one is [X, Y], then
	// the other is [Y, X].
	Handle body1 = al(SET_LINK,
	                  al(SET_LINK, X, Y),
	                  al(LIST_LINK, X, Y));
	Handle body2 = al(SET_LINK,
	                  al(SET_LINK, Y, X),
	                  al(LIST_LINK, Y, X));

	Variables vars1;
	Variables vars2;
	vars1.find_variables(body1);
	vars2.find_variables(body2);
	HandleSeq varseq1 = vars1.varseq;
	HandleSeq varseq2 = vars2.varseq;

	logger().debug() << "varseq1 = " << oc_to_string(varseq1);
	logger().debug() << "varseq2 = " << oc_to_string(varseq2);

	// Reverse varseq2 to test it if it equal to varseq1
	std::reverse(varseq2.begin(), varseq2.end());

	TS_ASSERT_EQUALS(varseq1, varseq2);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_mixed_8()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// The order should be XYZ, because Z appears after X in the
	// ordered link XZY, which makes XYZ before ordered before ZYX in
	// the SetLink.
	Handle XYZ = al(LIST_LINK, X, Y, Z);
	Handle ZYX = al(LIST_LINK, Z, Y, X);
	Handle XZY = al(LIST_LINK, X, Z, Y);
	Handle body = al(LIST_LINK, al(SET_LINK, XYZ, ZYX), XZY);
	Variables vars;
	vars.find_variables(body);
	HandleSeq result = vars.varseq;
	HandleSeq expect{X, Y, Z};

	logger().debug() << "result = " << oc_to_string(result);
	logger().debug() << "expect = " << oc_to_string(expect);

	TS_ASSERT_EQUALS(result, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_mixed_9()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// The vardecl order of body1 should be the reverse of the vardecl
	// order of body2.
	Handle body1 = al(LIST_LINK,
	                 al(SET_LINK, X, Y),
	                 al(SET_LINK, X, al(LIST_LINK, Y)));
	Handle body2 = al(LIST_LINK,
	                  al(SET_LINK, Y, X),
	                  al(SET_LINK, Y, al(LIST_LINK, X)));
	Variables vars1;
	Variables vars2;
	vars1.find_variables(body1);
	vars2.find_variables(body2);
	HandleSeq varseq1 = vars1.varseq;
	HandleSeq varseq2 = vars2.varseq;

	logger().debug() << "varseq1 = " << oc_to_string(varseq1);
	logger().debug() << "varseq2 = " << oc_to_string(varseq2);

	// Reverse varseq2 to test it if it equal to varseq1
	std::reverse(varseq2.begin(), varseq2.end());

	TS_ASSERT_EQUALS(varseq1, varseq2);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_mixed_10()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// The vardecl should be empty because no variable are free.
	Handle body = al(LAMBDA_LINK,
	                 al(VARIABLE_LIST, X, Y),
	                 al(LOCAL_QUOTE_LINK,
	                    al(AND_LINK, X, Y)));
	Variables vars;
	vars.find_variables(body);
	HandleSeq varseq = vars.varseq;

	logger().debug() << "varseq = " << oc_to_string(varseq);

	TS_ASSERT_EQUALS(varseq, HandleSeq());

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_mixed_11()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Inferred vardecl of body1 should be the reverse of vardecl of
	// body2 because swapping names makes them equivalent.
	Handle body1 = al(PRESENT_LINK,
	                  al(INHERITANCE_LINK, X, A),
	                  al(INHERITANCE_LINK, Y, B));
	Handle body2 = al(PRESENT_LINK,
	                  al(INHERITANCE_LINK, Y, A),
	                  al(INHERITANCE_LINK, X, B));
	Variables vars1;
	Variables vars2;
	vars1.find_variables(body1);
	vars2.find_variables(body2);
	HandleSeq varseq1 = vars1.varseq;
	HandleSeq varseq2 = vars2.varseq;

	logger().debug() << "varseq1 = " << oc_to_string(varseq1);
	logger().debug() << "varseq2 = " << oc_to_string(varseq2);

	// Reverse varseq2 to test it if it equal to varseq1
	std::reverse(varseq2.begin(), varseq2.end());

	TS_ASSERT_EQUALS(varseq1, varseq2);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_find_variables_alt_as()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Make sure that variables coming from another atomspace are
	// treated equally.  That is body should have variables X, Y, Z,
	// without duplicates.
	AtomSpace _alt_as;
	Handle altX = _alt_as.add_node(VARIABLE_NODE, "$X");
	Handle body = al(LIST_LINK,
	                 al(INHERITANCE_LINK, X, Y),
	                 al(INHERITANCE_LINK, Z, altX));
	Variables vars;
	vars.find_variables(body);
	HandleSeq varseq = vars.varseq;
	HandleSeq expect{X, Y, Z};

	logger().debug() << "varseq = " << oc_to_string(varseq);
	logger().debug() << "expect = " << oc_to_string(expect);

	TS_ASSERT_EQUALS(varseq, expect);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_is_type_1()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle vardecl = al(VARIABLE_LIST,
	                    al(TYPED_VARIABLE_LINK, X, PNT),
	                    al(TYPED_VARIABLE_LINK, Y, CNT));

	Variables v1(vardecl);

	bool is_x = v1.is_type(X, A);
	bool is_y = v1.is_type(Y, A);

	TS_ASSERT(not is_x);
	TS_ASSERT(is_y);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_is_type_2()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle vardecl = al(VARIABLE_LIST,
	                    al(TYPED_VARIABLE_LINK, G1,
	                       al(TYPE_INTERSECTION_LINK,
	                          al(INTERVAL_LINK,
	                             an(NUMBER_NODE, "0"),
	                             an(NUMBER_NODE, "1")),
	                          CNT)),
	                    al(TYPED_VARIABLE_LINK, G2,
	                       al(TYPE_INTERSECTION_LINK,
	                          al(INTERVAL_LINK,
	                             an(NUMBER_NODE, "0"),
	                             an(NUMBER_NODE, "2")),
	                          CNT)));

	Variables v1(vardecl);

	bool is_x = v1.is_type(G1, al(LIST_LINK, A, B));
	bool is_y = v1.is_type(G2, al(LIST_LINK, A, B));

	TS_ASSERT(not is_x);
	TS_ASSERT(is_y);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_is_type_3()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle vardecl = al(VARIABLE_LIST,
	                    al(TYPED_VARIABLE_LINK, G1, CNT),
	                    al(TYPED_VARIABLE_LINK, G2,
	                       al(TYPE_INTERSECTION_LINK,
	                          al(INTERVAL_LINK,
	                             an(NUMBER_NODE, "0"),
	                             an(NUMBER_NODE, "2")),
	                          PNT)));

	Variables v1(vardecl);

	bool is_x = v1.is_type(G1, al(LIST_LINK, A, B));
	bool is_y = v1.is_type(G2, al(LIST_LINK, A, B));

	TS_ASSERT(is_x);
	TS_ASSERT(not is_y);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_is_type_4()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle vardecl = al(VARIABLE_LIST,
	                    al(TYPED_VARIABLE_LINK, G1,
	                       al(TYPE_INTERSECTION_LINK,
	                          al(INTERVAL_LINK,
	                             an(NUMBER_NODE, "1"),
	                             an(NUMBER_NODE, "1")),
	                          CNT)),
	                    al(TYPED_VARIABLE_LINK, G2,
	                       al(TYPE_INTERSECTION_LINK,
	                          al(INTERVAL_LINK,
	                             an(NUMBER_NODE, "1"),
	                             an(NUMBER_NODE, "1")),
	                          CNT)));

	Variables v1(vardecl);

	bool is_x = v1.is_type(G1, al(LIST_LINK, A));
	bool is_y = v1.is_type(G2, A);

	TS_ASSERT(is_x);
	TS_ASSERT(is_y);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_is_type_5()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle vardecl = al(VARIABLE_LIST,
	                    al(TYPED_VARIABLE_LINK, X, NTI),
	                    al(TYPED_VARIABLE_LINK, Y, DPTCI));

	Variables v1(vardecl);

	bool is_x_n = v1.is_type(X, N);
	bool is_x_cn = v1.is_type(X, A);
	bool is_y_dpn = v1.is_type(Y, DPN);
	bool is_y_n = v1.is_type(Y, N);

	TS_ASSERT( is_x_n);
	TS_ASSERT( is_x_cn);
	TS_ASSERT(is_y_dpn);
	TS_ASSERT(is_y_n);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_is_type_6()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle vardecl = al(VARIABLE_LIST,
	                    al(TYPED_VARIABLE_LINK, G1,
	                       al(TYPE_INTERSECTION_LINK,
	                          al(INTERVAL_LINK,
	                             an(NUMBER_NODE, "1"),
	                             an(NUMBER_NODE, "1")),
	                          NTI)),
	                    al(TYPED_VARIABLE_LINK, G2,
	                       al(TYPE_INTERSECTION_LINK,
	                          al(INTERVAL_LINK,
	                             an(NUMBER_NODE, "1"),
	                             an(NUMBER_NODE, "1")),
	                          DPTCI)));

	Variables v1(vardecl);

	bool is_g1_n = v1.is_type(G1, al(LIST_LINK, N));
	bool is_g1_cn = v1.is_type(G1, al(LIST_LINK, A));
	bool is_g2_dpn = v1.is_type(G2, al(LIST_LINK, DPN));
	bool is_g2_n = v1.is_type(G2, al(LIST_LINK, N));

	TS_ASSERT(is_g1_n);
	TS_ASSERT(is_g1_cn);
	TS_ASSERT(is_g2_dpn);
	TS_ASSERT(is_g2_n);

	logger().info("END TEST: %s", __FUNCTION__);
}

void VariablesUTest::test_substitute_nocheck_scope()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Body with only one variable declared X, thus Y can be
	// substituted but not X.
	Handle body = al(SATISFACTION_LINK,
	                 X,
	                 al(PRESENT_LINK, al(INHERITANCE_LINK, X, Y)));

	// Replace Y by X, thus X in the satisfaction link above should be
	// renamed to avoid name collision.
	HandleMap var2val{{Y, X}};
	Handle result = Variables(Y).substitute_nocheck(body, var2val);
	Handle expect = al(SATISFACTION_LINK,
	                   Z,
	                   al(PRESENT_LINK, al(INHERITANCE_LINK, Z, X)));

	logger().debug() << "expect = " << oc_to_string(expect);
	logger().debug() << "result = " << oc_to_string(result);

	TS_ASSERT(content_eq(result, expect));

	logger().info("END TEST: %s", __FUNCTION__);
}