OverviewFilesCommitsIssuesDocsReleasesPeers

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

2287 lines · cxxtest ref:
/*
 * tests/atoms/PutLinkUTest.cxxtest
 *
 * Copyright (C) 2015,2017 Linas Vepstas
 * Copyright (C) 2016 Nil Geisweiller
 * All Rights Reserved
 *
 * 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/util/exceptions.h>
#include <opencog/guile/SchemeEval.h>
#include <opencog/atoms/base/Atom.h>
#include <opencog/atomspace/AtomSpace.h>
#include <opencog/atoms/scope/PutLink.h>

#include <cxxtest/TestSuite.h>

using namespace opencog;

// Test the PutLink.
//
class PutLinkUTest: public CxxTest::TestSuite
{
private:
	AtomSpacePtr _asp;
	SchemeEval* _eval;

public:
	PutLinkUTest() : _asp(createAtomSpace()), _eval(SchemeEval::get_scheme_evaluator(_asp))
	{
		logger().set_print_to_stdout_flag(true);
		logger().set_level(Logger::INFO);
		logger().set_timestamp_flag(false);
		logger().set_sync_flag(true); // interleave with printf correctly

		_eval->eval("(add-to-load-path \"" PROJECT_SOURCE_DIR "\")");

	}

	void setUp() {}

	void tearDown() {}

	void test_define();
	void test_define_glob();
	void test_define_mixed_1();
	void test_define_mixed_2();
	void test_filtering();
	void test_free_var_renaming();
	void test_function();
	void test_lambda();
	void test_lambda_partial_substitution();
	void test_eta();
	void test_alpha();
	void test_compose();
	void test_inject_compose();
	void test_simple_alpha();
	void test_simple_replace();
	void test_typed_compose();
	void test_ident_compose();
	void test_again_compose_ident();
	void test_recursive_compose();
	void test_eval_inheritance();
	void test_eval_implication_1();
	void test_eval_implication_2();
	void test_ill_unquote();
	void test_nested_1();
	void test_nested_2();
	void test_nested_3();
	void test_nested_4();
	void test_nested_5();
	void test_and_compose_1();
	void test_and_compose_2();
	void test_quoted_put();
	void test_tricky_put_1();
	void test_tricky_put_2();
	void test_tricky_put_3();
	void test_tricky_put_4();
	void test_tricky_put_5();
	void xtest_tricky_put_6();
	void test_tricky_put_7();
	void test_quoted_exec();
	void test_quoted_eval();
	void test_quoted_lambda();
	void test_get();
	void test_get_lambda();
	void test_get_multi();
	void test_multi_set();
	void test_full_quotation();
};

#define N _asp->add_node
#define L _asp->add_link

// Test to make sure that PutLink filters.
void PutLinkUTest::test_define()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	L(DEFINE_LINK,
		N(DEFINED_SCHEMA_NODE, "foobar"),
		L(LAMBDA_LINK,
			L(INHERITANCE_LINK,
				N(CONCEPT_NODE, "aaa"),
				N(VARIABLE_NODE, "$zzz"))));

	Handle put =
	L(PUT_LINK,
		N(DEFINED_SCHEMA_NODE, "foobar"),
		N(CONCEPT_NODE, "bbb"));

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));

	Handle pat =
	L(INHERITANCE_LINK,
		N(CONCEPT_NODE, "aaa"),
		N(CONCEPT_NODE, "bbb"));

	printf("Expecting %s\n", pat->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());
	TS_ASSERT_EQUALS(putted, pat);

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

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

	Handle put =
			L(PUT_LINK,
				N(GLOB_NODE, "$g"),
				L(DONT_EXEC_LINK,
					L(AND_LINK,
						N(GLOB_NODE, "$g"))),
				L(SET_LINK,
					L(LIST_LINK,
						N(PREDICATE_NODE, "P"),
						N(PREDICATE_NODE, "Q")),
					L(LIST_LINK,
						N(PREDICATE_NODE, "R"),
						N(PREDICATE_NODE, "K"))));

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));

	Handle expected =
			L(SET_LINK,
				L(AND_LINK,
					N(PREDICATE_NODE, "P"),
					N(PREDICATE_NODE, "Q")),
				L(AND_LINK,
					N(PREDICATE_NODE, "R"),
					N(PREDICATE_NODE, "K")));

	printf("Expecting %s\n", expected->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());

	TS_ASSERT(content_eq(expected, putted))
}

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

	Handle put =
			L(PUT_LINK,
				L(VARIABLE_LIST,
					N(GLOB_NODE, "$g"),
					N(VARIABLE_NODE, "$v")),
				L(LIST_LINK,
					N(GLOB_NODE, "$g"),
					N(VARIABLE_NODE, "$v"),
					N(PREDICATE_NODE, "J")),
				L(SET_LINK,
					L(LIST_LINK,
						N(PREDICATE_NODE, "P"),
						N(PREDICATE_NODE, "Q")),
					L(LIST_LINK,
						N(PREDICATE_NODE, "R"),
						N(PREDICATE_NODE, "K"))));

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));

	Handle expected =
			L(SET_LINK,
				L(LIST_LINK,
					N(PREDICATE_NODE, "P"),
					N(PREDICATE_NODE, "Q"),
					N(PREDICATE_NODE, "J")),
				L(LIST_LINK,
					N(PREDICATE_NODE, "R"),
					N(PREDICATE_NODE, "K"),
					N(PREDICATE_NODE, "J")));

	printf("Expecting %s\n", expected->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());

	TS_ASSERT(content_eq(expected, putted))
}

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

	Handle put =
			L(PUT_LINK,
				L(VARIABLE_LIST,
					N(GLOB_NODE, "$g"),
					N(VARIABLE_NODE, "$v")),
				L(DONT_EXEC_LINK,
					L(AND_LINK,
						N(GLOB_NODE, "$g"),
						N(VARIABLE_NODE, "$v"))),
				L(SET_LINK,
					L(LIST_LINK,
						L(LIST_LINK,
							N(PREDICATE_NODE, "P"),
							N(PREDICATE_NODE, "Q")),
						N(PREDICATE_NODE, "R")),
					L(LIST_LINK,
						L(INHERITANCE_LINK,
							N(PREDICATE_NODE, "P"),
							N(PREDICATE_NODE, "Q")),
						N(PREDICATE_NODE, "R"))));

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));

	Handle expected =
			L(SET_LINK,
				L(AND_LINK,
					N(PREDICATE_NODE, "P"),
					N(PREDICATE_NODE, "Q"),
					N(PREDICATE_NODE, "R")),
				L(AND_LINK,
					L(INHERITANCE_LINK,
						N(PREDICATE_NODE, "P"),
						N(PREDICATE_NODE, "Q")),
					N(PREDICATE_NODE, "R")));

	printf("Expecting %s\n", expected->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());

	TS_ASSERT(content_eq(expected, putted))
}

// Test to make sure that PutLink filters.
void PutLinkUTest::test_filtering()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Copied from /examples/guile/filter.scm
	// This test must pass; it is used in multiple wiki pages
	// as an example, including PutLink, LambdaLink,
	// DefinedSchemaLink, etc.
	Handle foo, bar;
	Handle filt =
		L(PUT_LINK,
			L(LAMBDA_LINK,
				L(TYPED_VARIABLE_LINK,
					N(VARIABLE_NODE, "$x"),
					N(TYPE_NODE, "ConceptNode")),
				N(VARIABLE_NODE, "$x")),
			L(SET_LINK,
				N(NUMBER_NODE, "42"),
				foo = N(CONCEPT_NODE, "foo"),
				N(PREDICATE_NODE, "biffle"),
				L(EDGE_LINK,
					N(CONCEPT_NODE, "thingy")),
				N(SCHEMA_NODE, "finagle"),
				bar = N(CONCEPT_NODE, "bar")));

	// Test that execution filters as desired...
	Handle cncpts = _asp->add_atom(HandleCast(filt->execute(_asp.get())));

	printf("Filtered %s\n", cncpts->to_string().c_str());
	TS_ASSERT_EQUALS(SET_LINK, cncpts->get_type());
	TS_ASSERT_EQUALS(2, cncpts->get_arity());
	Handle a = cncpts->getOutgoingAtom(0);
	Handle b = cncpts->getOutgoingAtom(1);
	TS_ASSERT(foo == a or foo == b);
	TS_ASSERT(bar == a or bar == b);
	TS_ASSERT(a != b);

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

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

	// Test that 2+2 = 4
	// (Put (Plus) (List (Number 2) (Number 2)))
	Handle want_four =
		L(PUT_LINK,
			L(PLUS_LINK),
			L(LIST_LINK, N(NUMBER_NODE, "2"), N(NUMBER_NODE, "2")));

	Handle four = _asp->add_atom(HandleCast(want_four->execute(_asp.get())));
	Handle expected_four = N(NUMBER_NODE, "4");

	printf("Expecting %s\n", expected_four->to_string().c_str());
	printf("Got %s\n", four->to_string().c_str());
	TS_ASSERT_EQUALS(four, expected_four);

	// Test that 2 = 2
	// (Put (Plus) (Number 2))
	Handle want_two =
		L(PUT_LINK, L(PLUS_LINK), N(NUMBER_NODE, "2"));

	Handle two = _asp->add_atom(HandleCast(want_two->execute(_asp.get())));
	Handle expected_two = N(NUMBER_NODE, "2");

	printf("Expecting %s\n", expected_two->to_string().c_str());
	printf("Got %s\n", two->to_string().c_str());
	TS_ASSERT_EQUALS(two, expected_two);

	// Test the combinator (+9) (2+2) = 13
	// (Put (Plus (Number 9)) (List (Number 2) (Number 2)))
	Handle want_thirteen =
		L(PUT_LINK,
			L(PLUS_LINK, N(NUMBER_NODE, "9")),
			L(LIST_LINK, N(NUMBER_NODE, "2"), N(NUMBER_NODE, "2")));

	Handle thirteen = _asp->add_atom(HandleCast(want_thirteen->execute(_asp.get())));
	Handle expected_thirteen = N(NUMBER_NODE, "13");

	printf("Expecting %s\n", expected_thirteen->to_string().c_str());
	printf("Got %s\n", thirteen->to_string().c_str());
	TS_ASSERT_EQUALS(thirteen, expected_thirteen);

	// Test the combinator (+ 3 5) (2+2) = 12
	// (Put (Plus (Number 3) (Number 5)) (List (Number 2) (Number 2)))
	Handle want_twelve =
		L(PUT_LINK,
			L(PLUS_LINK, N(NUMBER_NODE, "3"), N(NUMBER_NODE, "5")),
			L(LIST_LINK, N(NUMBER_NODE, "2"), N(NUMBER_NODE, "2")));

	Handle twelve = _asp->add_atom(HandleCast(want_twelve->execute(_asp.get())));
	Handle expected_twelve = N(NUMBER_NODE, "12");

	printf("Expecting %s\n", expected_twelve->to_string().c_str());
	printf("Got %s\n", twelve->to_string().c_str());
	TS_ASSERT_EQUALS(twelve, expected_twelve);

	// Test a complex set.
	// (Put (Times) (Set
	//    (Number 1)
	//    (List (Number 2) (Number 2))
	//    (List (Number 3) (Number 3) (Number 3))
	//    (List (Number 4) (Number 4) (Number 4) (Number 4))
	// )))
	//
	Handle want_set =
		L(PUT_LINK,
			L(TIMES_LINK),
				L(SET_LINK,
					N(NUMBER_NODE, "1"),
					L(LIST_LINK, N(NUMBER_NODE, "2"), N(NUMBER_NODE, "2")),
					L(LIST_LINK, N(NUMBER_NODE, "3"),
					             N(NUMBER_NODE, "3"),
					             N(NUMBER_NODE, "3")),
					L(LIST_LINK, N(NUMBER_NODE, "4"),
					             N(NUMBER_NODE, "4"),
					             N(NUMBER_NODE, "4"),
					             N(NUMBER_NODE, "4"))));

	Handle set = _asp->add_atom(HandleCast(want_set->execute(_asp.get())));
	Handle expected_set =
		L(SET_LINK,
			N(NUMBER_NODE, "1"),
			N(NUMBER_NODE, "4"),
			N(NUMBER_NODE, "27"),
			N(NUMBER_NODE, "256"));

	printf("Expecting %s\n", expected_set->to_string().c_str());
	printf("Got %s\n", set->to_string().c_str());
	TS_ASSERT_EQUALS(set, expected_set);

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

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

	// Test simple substitution of free variable names.
	Handle inh =
		L(INHERITANCE_LINK,
			N(VARIABLE_NODE, "$x"),
			N(VARIABLE_NODE, "$y"));
	Handle put =
		L(PUT_LINK,
			inh,
			L(LIST_LINK,
				N(VARIABLE_NODE, "$a"),
				N(VARIABLE_NODE, "$b")));

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));

	Handle expected_putted =
		L(INHERITANCE_LINK,
			N(VARIABLE_NODE, "$a"),
			N(VARIABLE_NODE, "$b"));

	printf("Expecting %s\n", expected_putted->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());
	TS_ASSERT_EQUALS(putted, expected_putted);

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

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

	// Test that the substitution follows the order of variables
	// provided in the lambda rather than left-to-right in the body.
	Handle lam =
		L(LAMBDA_LINK,
			L(VARIABLE_LIST,
				N(VARIABLE_NODE, "$x"),
				N(VARIABLE_NODE, "$y")),
			L(INHERITANCE_LINK,
				N(VARIABLE_NODE, "$y"),
				N(VARIABLE_NODE, "$x")));
	Handle put =
		L(PUT_LINK,
			lam,
			L(LIST_LINK,
				N(CONCEPT_NODE, "animal"),
				N(CONCEPT_NODE, "coyote")));

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));

	Handle expected_putted =
		L(INHERITANCE_LINK,
			N(CONCEPT_NODE, "coyote"),
			N(CONCEPT_NODE, "animal"));

	printf("Expecting %s\n", expected_putted->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());
	TS_ASSERT_EQUALS(putted, expected_putted);

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

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

	// Test partial substitution
	// (Put
	//     (Lambda (Variable "$x")
	//         (Inheritance (Variable "$x") (Variable "$y")))
	//     (Concept "animal"))
	Handle lam =
		L(LAMBDA_LINK,
			N(VARIABLE_NODE, "$x"),
			L(INHERITANCE_LINK,
				N(VARIABLE_NODE, "$y"),
				N(VARIABLE_NODE, "$x")));
	Handle put =
		L(PUT_LINK,
			lam,
			N(CONCEPT_NODE, "animal"));

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));

	// (Inheritance (Concept "animal") (Variable "$y"))
	Handle expected_putted =
		L(INHERITANCE_LINK,
			N(VARIABLE_NODE, "$y"),
			N(CONCEPT_NODE, "animal"));

	printf("Expecting %s\n", expected_putted->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());
	TS_ASSERT_EQUALS(putted, expected_putted);

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

// Test eta-reduction of the values
void PutLinkUTest::test_eta()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Test that eta-reduction can be performed on the values. (This is
	// actually a test of function composition)
	//
	// (define func-with-three-args (Lambda
	//      (VariableList (Variable "$x")(Variable "$y")(Variable "$z"))
	//      (Inheritance (Variable "$z") (Variable "$x"))))
	//
	//
	// The above is a function that expects three arguments: x,y,z.
	// Lets compose it with a function that takes one argument, but
	// returns three values (function coposition):
	//
	// (define func-returns-three-values
	//    (Lambda (Variable "$w")
	//      (List (Concept "animal") (Concept "foobar") (Variable"$w"))))
	//
	// Composing these two should return a function that takes one
	// argument, namely $w.
	//
	//    (Put func-with-three-args func-returns-three-values)
	//
	// We expect as a result:
	//
	// (Lambda (Variable "$w") (Inheritance (Variable "$w") (Concept "animal")))
	//
	// Note that this is NOT compatible with beta-reduction in classical
	// lambda calculus, which would leave $w free. We really want to
	// eta-convert this, and keep $w bound, so that it looks like
	// ordinary function composition. Atomese is not lambda calculus.
	//
	Handle lam =
		L(LAMBDA_LINK,
			L(VARIABLE_LIST,
				N(VARIABLE_NODE, "$x"),
				N(VARIABLE_NODE, "$y"),
				N(VARIABLE_NODE, "$z")),
			L(INHERITANCE_LINK,
				N(VARIABLE_NODE, "$z"),
				N(VARIABLE_NODE, "$x")));
	Handle put =
		L(PUT_LINK,
			lam,
			L(LAMBDA_LINK,
				N(VARIABLE_NODE, "$w"),
				L(LIST_LINK,
					N(CONCEPT_NODE, "animal"),
					N(CONCEPT_NODE, "foobar"),
					N(VARIABLE_NODE, "$w"))));

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));

	Handle expected_putted =
		L(LAMBDA_LINK,
			N(VARIABLE_NODE, "$w"),
			L(INHERITANCE_LINK,
				N(VARIABLE_NODE, "$w"),
				N(CONCEPT_NODE, "animal")));

	printf("Expecting %s\n", expected_putted->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());
	TS_ASSERT_EQUALS(putted, expected_putted);

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

// Test alpha-renaming, where values are variables. Similar to
// the test_eta, except the input is pointless (aka tacit).
void PutLinkUTest::test_alpha()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Test that alpha-renaming can be performed on the values.
	// (define lam (Lambda
	//      (VariableList (Variable "$x")(Variable "$y")(Variable "$z"))
	//      (Inheritance (Variable "$z") (Variable "$x"))))
	//
	// (define put (Put lam
	//      (List (Concept "animal") (Concept "foobar") (Variable "$w"))))
	//
	// We expect the below, with $w bound, because this is how beta
	// reduction is defined in atomese. Note that it is NOT compatible
	// with beta-reduction is classical lambda calculus! In lambda calc,
	// the result would leave $w free, not bound.
	//
	// (Lambda (Variable "$w") (Inheritance (Variable "$w") (Concept "animal")))
	//
	// Note that this is very similar to the test-eta, immediately above.
	// It differs because the arguments are not a function.
	//
	Handle lam =
		L(LAMBDA_LINK,
			L(VARIABLE_LIST,
				N(VARIABLE_NODE, "$x"),
				N(VARIABLE_NODE, "$y"),
				N(VARIABLE_NODE, "$z")),
			L(INHERITANCE_LINK,
				N(VARIABLE_NODE, "$z"),
				N(VARIABLE_NODE, "$x")));
	Handle put =
		L(PUT_LINK,
			lam,
			L(LIST_LINK,
				N(CONCEPT_NODE, "animal"),
				N(CONCEPT_NODE, "foobar"),
				N(VARIABLE_NODE, "$w")));

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));

	Handle expected_putted =
		L(LAMBDA_LINK,
			N(VARIABLE_NODE, "$w"),
			L(INHERITANCE_LINK,
				N(VARIABLE_NODE, "$w"),
				N(CONCEPT_NODE, "animal")));

	printf("Expecting %s\n", expected_putted->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());
	TS_ASSERT_EQUALS(putted, expected_putted);

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

// Test simple function composition.
void PutLinkUTest::test_compose()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Test that simple function compostion can be performed.
	//
	// (define g (Lambda (Evaluation (Predicate "P") (Variable "X"))))
	// (define f (Lambda (Inheritance (Variable "X") (Variable "Y"))))
	// (cog-execute! (Put g f))
	//
	// We expect the result to be in prenex form.
	//
	Handle gee =
		L(LAMBDA_LINK,
			L(EDGE_LINK,
				N(PREDICATE_NODE, "P"),
				N(VARIABLE_NODE, "$x")));
	Handle eff =
		L(LAMBDA_LINK,
			L(INHERITANCE_LINK,
				N(VARIABLE_NODE, "$x"),
				N(VARIABLE_NODE, "$y")));
	Handle put =
		L(PUT_LINK, gee, eff);

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));

	Handle expected_putted =
		L(LAMBDA_LINK,
			L(EDGE_LINK,
				N(PREDICATE_NODE, "P"),
				L(INHERITANCE_LINK,
					N(VARIABLE_NODE, "$x"),
					N(VARIABLE_NODE, "$y"))));

	printf("Expecting %s\n", expected_putted->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());
	TS_ASSERT_EQUALS(putted, expected_putted);

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

// Test function composition.
void PutLinkUTest::test_inject_compose()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Test function compostion with injection
	//
	// (define X (Variable "X"))
	// (define Y (Variable "Y"))
	// (define Z (Variable "Z"))
	// (define W (Variable "W"))
	//
	// (define g (Lambda
	//    (VariableList X Y W Z)
	//    (And
	//       (Inheritance X Y)
	//       (Inheritance W Z))))
	//
	// (define f (Lambda (VariableList X Y Z) (List X Y Y Z)))
	//
	// (cog-execute! (Put g f))
	//
	// We expect the result to be in prenex form.
	//
	Handle X = N(VARIABLE_NODE, "X");
	Handle Y = N(VARIABLE_NODE, "Y");
	Handle Z = N(VARIABLE_NODE, "Z");
	Handle W = N(VARIABLE_NODE, "W");

	Handle gee =
		L(LAMBDA_LINK,
			L(VARIABLE_LIST, X, Y, Z, W),
			L(AND_LINK,
				L(INHERITANCE_LINK, X, Y),
				L(INHERITANCE_LINK, Z, W)));

	Handle eff =
		L(LAMBDA_LINK,
			L(VARIABLE_LIST, X, Y, Z),
			L(LIST_LINK, X, Y, Y, Z));

	Handle put = L(PUT_LINK, gee, eff);

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));

	Handle expected_putted =
		L(LAMBDA_LINK,
			L(VARIABLE_LIST, X, Y, Z),
			L(AND_LINK,
				L(INHERITANCE_LINK, X, Y),
				L(INHERITANCE_LINK, Y, Z)));

	printf("Expecting %s\n", expected_putted->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());
	TS_ASSERT_EQUALS(putted, expected_putted);

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

// Test basic alpha conversion
void PutLinkUTest::test_simple_alpha()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Test function compostion with injection
	//
	// (define X (Variable "X"))
	// (define Y (Variable "Y"))
	// (define Z (Variable "Z"))
	// (define W (Variable "W"))
	//
	// (define g (Lambda
	//    (VariableList X Y W Z)
	//    (And
	//       (Inheritance X Y)
	//       (Inheritance W Z))))
	//
	// (define f (List X Y Y Z))
	//
	// (cog-execute! (Put g f))
	//
	// We expect the result to be in prenex form.
	//
	Handle X = N(VARIABLE_NODE, "X");
	Handle Y = N(VARIABLE_NODE, "Y");
	Handle Z = N(VARIABLE_NODE, "Z");
	Handle W = N(VARIABLE_NODE, "W");

	Handle gee =
		L(LAMBDA_LINK,
			L(VARIABLE_LIST, X, Y, Z, W),
			L(AND_LINK,
				L(INHERITANCE_LINK, X, Y),
				L(INHERITANCE_LINK, Z, W)));

	Handle eff = L(LIST_LINK, X, Y, Y, Z);

	Handle put = L(PUT_LINK, gee, eff);

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));

	Handle expected_putted =
		L(LAMBDA_LINK,
			L(VARIABLE_LIST, X, Y, Z),
			L(AND_LINK,
				L(INHERITANCE_LINK, X, Y),
				L(INHERITANCE_LINK, Y, Z)));

	printf("Expecting %s\n", expected_putted->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());
	TS_ASSERT_EQUALS(putted, expected_putted);

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

// Test basic insertion.
// A lot like test_inject_compose, but simpler.
void PutLinkUTest::test_simple_replace()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Test function compostion with injection
	//
	// (define X (Variable "X"))
	// (define Y (Variable "Y"))
	// (define Z (Variable "Z"))
	// (define W (Variable "W"))
	//
	// (define g (Lambda
	//    (VariableList X Y W Z)
	//    (And
	//       (Inheritance X Y)
	//       (Inheritance W Z))))
	//
	// (define A (Concept "A"))
	// (define B (Concept "B"))
	// (define C (Concept "C"))
	//
	// (define f (List A B B C)))
	//
	// (cog-execute! (Put g f))
	//
	Handle X = N(VARIABLE_NODE, "X");
	Handle Y = N(VARIABLE_NODE, "Y");
	Handle Z = N(VARIABLE_NODE, "Z");
	Handle W = N(VARIABLE_NODE, "W");

	Handle gee =
		L(LAMBDA_LINK,
			L(VARIABLE_LIST, X, Y, Z, W),
			L(UNORDERED_LINK,
				L(INHERITANCE_LINK, X, Y),
				L(INHERITANCE_LINK, Z, W)));

	Handle A = N(CONCEPT_NODE, "A");
	Handle B = N(CONCEPT_NODE, "B");
	Handle C = N(CONCEPT_NODE, "C");

	Handle eff = L(LIST_LINK, A, B, B, C);

	Handle put = L(PUT_LINK, gee, eff);

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));

	Handle expected_putted =
		L(UNORDERED_LINK,
			L(INHERITANCE_LINK, A, B),
			L(INHERITANCE_LINK, B, C));

	printf("Expecting %s\n", expected_putted->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());
	TS_ASSERT_EQUALS(putted, expected_putted);

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

// Test function composition with types.
void PutLinkUTest::test_typed_compose()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Test typed function compostion
	//
	// (define g (Lambda
	//    (TypedVariable (Variable "X") (TypeNode "InheritanceLink"))
	//    (Evaluation (Predicate "P") (Variable "X"))))
	// (define f (Lambda
	//    (VariableList
	//       (TypedVariable (Variable "X") (TypeNode "ConceptNode"))
	//       (TypedVariable (Variable "Y") (TypeNode "ConceptNode")))
	//    (Inheritance (Variable "X") (Variable "Y"))))
	//
	// (cog-execute! (Put g f))
	//
	// We expect the result to be in prenex form.
	//
	Handle gee =
		L(LAMBDA_LINK,
			L(TYPED_VARIABLE_LINK,
				N(VARIABLE_NODE, "$x"),
				N(TYPE_NODE, "InheritanceLink")),
			L(EDGE_LINK,
				N(PREDICATE_NODE, "P"),
				N(VARIABLE_NODE, "$x")));
	Handle eff =
		L(LAMBDA_LINK,
			L(VARIABLE_LIST,
				L(TYPED_VARIABLE_LINK,
					N(VARIABLE_NODE, "$x"),
					N(TYPE_NODE, "ConceptNode")),
				L(TYPED_VARIABLE_LINK,
					N(VARIABLE_NODE, "$y"),
					N(TYPE_NODE, "ConceptNode"))),
			L(INHERITANCE_LINK,
				N(VARIABLE_NODE, "$x"),
				N(VARIABLE_NODE, "$y")));
	Handle put =
		L(PUT_LINK, gee, eff);

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));

	Handle expected_putted =
		L(LAMBDA_LINK,
			L(VARIABLE_LIST,
				L(TYPED_VARIABLE_LINK,
					N(VARIABLE_NODE, "$u"),
					N(TYPE_NODE, "ConceptNode")),
				L(TYPED_VARIABLE_LINK,
					N(VARIABLE_NODE, "$v"),
					N(TYPE_NODE, "ConceptNode"))),
			L(EDGE_LINK,
				N(PREDICATE_NODE, "P"),
				L(INHERITANCE_LINK,
					N(VARIABLE_NODE, "$u"),
					N(VARIABLE_NODE, "$v"))));

	printf("Expecting %s\n", expected_putted->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());
	TS_ASSERT_EQUALS(putted, expected_putted);

	Handle gahhh =
		L(LAMBDA_LINK,
			L(TYPED_VARIABLE_LINK,
				N(VARIABLE_NODE, "$x"),
				N(TYPE_NODE, "SimilarityLink")), // type mismatch here!
			L(EDGE_LINK,
				N(PREDICATE_NODE, "P"),
				N(VARIABLE_NODE, "$x")));

	bool caught = false;
	try
	{
		put = L(PUT_LINK, gahhh, eff);
		HandleCast(put->execute(_asp.get()));
	}
	catch(const StandardException& ex)
	{
		caught = true;
	}
	printf("Expecting throw, got %d\n", caught);
	TSM_ASSERT("Failed to catch expected exception", caught);

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

// Test function composition with identity function
void PutLinkUTest::test_ident_compose()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Test function compostion with a cartesian product
	//
	// (define g (Lambda (Evaluation (Variable "X") (Variable "Y"))))
	// (define f2 (Lambda
	//    (VariableList (Variable "X") (Variable "Y") (Variable "Z"))
	//    (Inheritance (Variable "Y")(Variable "Z")) ))
	//
	// (define ident (Lambda (Variable "W") (Variable "W")))
	// (cog-execute! (Put g (List ident f2)))
	//
	// We expect the result to be in prenex form.
	//
	Handle gee =
		L(LAMBDA_LINK,
			L(EDGE_LINK,
				N(VARIABLE_NODE, "$x"),
				N(VARIABLE_NODE, "$y")));
	Handle eff =
		L(LAMBDA_LINK,
			L(VARIABLE_LIST,
				N(VARIABLE_NODE, "$x"),
				N(VARIABLE_NODE, "$y"),
				N(VARIABLE_NODE, "$z")),
			L(INHERITANCE_LINK,
				N(VARIABLE_NODE, "$y"),
				N(VARIABLE_NODE, "$z")));
	Handle ident =
		L(LAMBDA_LINK,
			N(VARIABLE_NODE, "$x"), N(VARIABLE_NODE, "$x"));

	Handle put =
		L(PUT_LINK, gee, L(LIST_LINK, ident, eff));

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));

	Handle expected_putted =
		L(LAMBDA_LINK,
			L(VARIABLE_LIST,
				N(VARIABLE_NODE, "$w"),
				N(VARIABLE_NODE, "$x"),
				N(VARIABLE_NODE, "$y"),
				N(VARIABLE_NODE, "$z")),
			L(EDGE_LINK,
				N(VARIABLE_NODE, "$w"),
				L(INHERITANCE_LINK,
					N(VARIABLE_NODE, "$y"),
					N(VARIABLE_NODE, "$z"))));

	printf("Expecting %s\n", expected_putted->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());
	TS_ASSERT_EQUALS(putted, expected_putted);

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

// Test function composition with identity function
void PutLinkUTest::test_again_compose_ident()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// A non-empty atomspace throws off alpha-conversion collions bugs.
	// retest with an empty atomspace.
	_asp->clear();
	test_ident_compose();
	logger().info("END TEST: %s", __FUNCTION__);
}

// Test recursive function composition
void PutLinkUTest::test_recursive_compose()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	// Test that recursive function composition is performed correctly.
	//
	// (define g (Lambda (Evaluation (Variable "X") (Variable "Y"))))
	// (define f2 (Lambda
	//    (VariableList (Variable "X") (Variable "Y") (Variable "Z"))
	//    (Inheritance (Variable "Y")(Variable "Z")) ))
	//
	// (define inject ;; knock out first argument
	//    (List (Concept "junk") (Variable "S")(Variable "T")))
	//
	// (define ident (Lambda (Variable "W") (Variable "W")))
	// (cog-execute! (Put g (List ident (Put f2 inject))))
	//
	// We expect the result to be in prenex form.
	//
	Handle gee =
		L(LAMBDA_LINK,
			L(EDGE_LINK,
				N(VARIABLE_NODE, "$x"),
				N(VARIABLE_NODE, "$y")));
	Handle eff =
		L(LAMBDA_LINK,
			L(VARIABLE_LIST,
				N(VARIABLE_NODE, "$x"),
				N(VARIABLE_NODE, "$y"),
				N(VARIABLE_NODE, "$z")),
			L(INHERITANCE_LINK,
				N(VARIABLE_NODE, "$y"),
				N(VARIABLE_NODE, "$z")));
	Handle inject =
		L(LIST_LINK,
			N(CONCEPT_NODE, "junk"),
			N(VARIABLE_NODE, "$x"),
			N(VARIABLE_NODE, "$y"));

	Handle ident =
		L(LAMBDA_LINK,
			N(VARIABLE_NODE, "$x"), N(VARIABLE_NODE, "$x"));

	Handle put =
		L(PUT_LINK, gee,
			L(LIST_LINK, ident, L(PUT_LINK, eff, inject)));

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));

	Handle expected_putted =
		L(LAMBDA_LINK,
			L(VARIABLE_LIST,
				N(VARIABLE_NODE, "A"),
				N(VARIABLE_NODE, "B"),
				N(VARIABLE_NODE, "C")),
			L(EDGE_LINK,
				N(VARIABLE_NODE, "A"),
				L(INHERITANCE_LINK,
					N(VARIABLE_NODE, "B"),
					N(VARIABLE_NODE, "C"))));

	printf("Expecting %s\n", expected_putted->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());
	TS_ASSERT_EQUALS(putted, expected_putted);

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

/**
 * This test is for the case where PutLink has to evaluate the beta
 * reduced pattern and this pattern contains an inheritance link. For
 * instance
 *
 * (PutLink
 *    (LambdaLink
 *       (VariableList
 *          (TypedVariableLink
 *             (VariableNode "$X")
 *             (TypeNode "ConceptNode")
 *          )
 *          (TypedVariableLink
 *             (VariableNode "$Y")
 *             (TypeNode "ConceptNode")
 *          )
 *       )
 *       (AndLink
 *          (InheritanceLink
 *             (VariableNode "$X")
 *             (ConceptNode "human")
 *          )
 *          (InheritanceLink
 *             (VariableNode "$Y")
 *             (ConceptNode "human")
 *          )
 *          (EvaluationLink
 *             (PredicateNode "acquainted")
 *             (ListLink
 *                (VariableNode "$X")
 *                (VariableNode "$Y")
 *             )
 *          )
 *       )
 *    )
 *    (ListLink
 *       (ConceptNode "Self")
 *       (ConceptNode "Bob")
 *    )
 * )
 */
void PutLinkUTest::test_eval_inheritance()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle X = N(VARIABLE_NODE, "$X"),
		Y = N(VARIABLE_NODE, "$Y"),
		Concept = N(TYPE_NODE, "ConceptNode"),
		human = N(CONCEPT_NODE, "human"),
		Self = N(CONCEPT_NODE, "Self"),
		Bob = N(CONCEPT_NODE, "Bob"),
		acquainted = N(PREDICATE_NODE, "acquainted"),
		X_human = L(INHERITANCE_LINK, X, human),
		Y_human = L(INHERITANCE_LINK, Y, human),
		Self_human = L(INHERITANCE_LINK, Self, human),
		Bob_human = L(INHERITANCE_LINK, Bob, human),
		X_Y_acquainted = L(EDGE_LINK,
		                   acquainted,
		                   L(LIST_LINK, X, Y)),
		Self_Bob_acquainted = L(EDGE_LINK,
		                        acquainted,
		                        L(LIST_LINK, Self, Bob)),
		vardecl = L(VARIABLE_LIST,
		            L(TYPED_VARIABLE_LINK, X, Concept),
		            L(TYPED_VARIABLE_LINK, Y, Concept)),
		body = L(UNORDERED_LINK, X_human, Y_human, X_Y_acquainted),
		args = L(LIST_LINK, Self, Bob),
		put = L(PUT_LINK, L(LAMBDA_LINK, vardecl, body), args);

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));
	Handle expected = L(UNORDERED_LINK, Self_human, Bob_human, Self_Bob_acquainted);

	printf("Expecting %s\n", expected->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());
	TS_ASSERT_EQUALS(putted, expected);

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

/**
 * Another test for the case where PutLink has to evaluate the beta
 * reduced pattern and this pattern contains an implication link. For
 * instance
 *
 * (PutLink
 *    (LambdaLink
 *       (TypedVariableLink
 *          (VariableNode "$X")
 *          (TypeNode "ConceptNode")
 *       )
 *       (RuleLink
 *          (EvaluationLink
 *             (PredicateNode "P")
 *             (VariableNode "$X")
 *          )
 *          (EvaluationLink
 *             (PredicateNode "P")
 *             (VariableNode "$X")
 *          )
 *       )
 *    )
 *    (ConceptNode "A")
 * )
 */
void PutLinkUTest::test_eval_implication_1()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle X = N(VARIABLE_NODE, "$X"),
		A = N(CONCEPT_NODE, "A"),
		CT = N(TYPE_NODE, "ConceptNode"),
		P = N(PREDICATE_NODE, "P"),
		P_X = L(EDGE_LINK, P, X),
		P_A = L(EDGE_LINK, P, A),
		body = L(QUOTE_LINK,
		         L(RULE_LINK,
		           L(UNQUOTE_LINK, P_X),
		           L(UNQUOTE_LINK, P_X))),
		vardecl = L(TYPED_VARIABLE_LINK, X, CT),
		put = L(PUT_LINK, L(LAMBDA_LINK, vardecl, body), A);

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));
	Handle expected = L(RULE_LINK, P_A, P_A);

	printf("Executing %s\n", put->to_string().c_str());
	printf("Expecting %s\n", expected->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());
	TS_ASSERT_EQUALS(putted, expected);

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

/**
 * This test is for the case where PutLink has to evaluate the beta
 * reduced pattern and this pattern contains an implication link. For
 * instance
 *
 * (PutLink
 *    (LambdaLink
 *       (VariableList
 *          (TypedVariableLink
 *             (VariableNode "$X")
 *             (TypeNode "PredicateNode")
 *          )
 *          (TypedVariableLink
 *             (VariableNode "$Y")
 *             (TypeNode "PredicateNode")
 *          )
 *       )
 *       (AndLink
 *          (ImplicationLink
 *             (VariableNode "$X")
 *             (PredicateNode "is-human")
 *          )
 *          (ImplicationLink
 *             (VariableNode "$Y")
 *             (PredicateNode "is-human")
 *          )
 *          (EvaluationLink
 *             (PredicateNode "acquainted")
 *             (ListLink
 *                (VariableNode "$X")
 *                (VariableNode "$Y")
 *             )
 *          )
 *       )
 *    )
 *    (ListLink
 *       (PredicateNode "is-Self")
 *       (PredicateNode "is-Bob")
 *    )
 * )
 */
void PutLinkUTest::test_eval_implication_2()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle X = N(VARIABLE_NODE, "$X"),
		Y = N(VARIABLE_NODE, "$Y"),
		Predicate = N(TYPE_NODE, "PredicateNode"),
		human = N(PREDICATE_NODE, "is-human"),
		Self = N(PREDICATE_NODE, "is-Self"),
		Bob = N(PREDICATE_NODE, "is-Bob"),
		acquainted = N(PREDICATE_NODE, "acquainted"),
		X_human = L(IMPLICATION_LINK, X, human),
		Y_human = L(IMPLICATION_LINK, Y, human),
		Self_human = L(IMPLICATION_LINK, Self, human),
		Bob_human = L(IMPLICATION_LINK, Bob, human),
		X_Y_acquainted = L(EDGE_LINK,
		                   acquainted,
		                   L(LIST_LINK, X, Y)),
		Self_Bob_acquainted = L(EDGE_LINK,
		                        acquainted,
		                        L(LIST_LINK, Self, Bob)),
		vardecl = L(VARIABLE_LIST,
		            L(TYPED_VARIABLE_LINK, X, Predicate),
		            L(TYPED_VARIABLE_LINK, Y, Predicate)),
		body = L(UNORDERED_LINK, X_human, Y_human, X_Y_acquainted),
		args = L(LIST_LINK, Self, Bob),
		put = L(PUT_LINK, L(LAMBDA_LINK, vardecl, body), args);

	Handle putted = _asp->add_atom(HandleCast(put->execute(_asp.get())));
	Handle expected = L(UNORDERED_LINK, Self_human, Bob_human, Self_Bob_acquainted);

	printf("Executing %s\n", put->to_string().c_str());
	printf("Expecting %s\n", expected->to_string().c_str());
	printf("Got %s\n", putted->to_string().c_str());
	TS_ASSERT_EQUALS(putted, expected);

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

/**
 * Test that the ill quotation use doesn't crash execution
 *
 * (PutLink
 *   (LambdaLink
 *     (VariableNode "$X")
 *     (EvaluationLink
 *       (PredicateNode "P")
 *       (VariableNode "$X")
 *     )
 *   )
 *   (LambdaLink
 *     (UnquoteLink
 *       (VariableNode "$x")
 *     )
 *     (UnquoteLink
 *       (VariableNode "$g")
 *     )
 *   )
 * )
 */
void PutLinkUTest::test_ill_unquote()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle X = N(VARIABLE_NODE, "$X"),
		P = N(PREDICATE_NODE, "P"),
		x = N(VARIABLE_NODE, "$x"),
		g = N(VARIABLE_NODE, "$g"),
		put = L(PUT_LINK,
		        L(LAMBDA_LINK,
		          X,
		          L(EDGE_LINK, P, X)),
		        L(LAMBDA_LINK,
		          L(UNQUOTE_LINK, x),
		          L(UNQUOTE_LINK, g)));

	TS_ASSERT_THROWS_ANYTHING(HandleCast(put->execute(_asp.get())));

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

/**
 * Test that nested PutLinks is supported, specifically that executing
 *
 * (Put
 *   (Put
 *     (Lambda
 *       (Variable "$X")
 *       (Variable "$X"))
 *     (Lambda
 *       (VariableList
 *         (Variable "$Y")
 *         (Variable "$Z"))
 *       (Inheritance
 *         (Variable "$Y")
 *         (Variable "$Z"))))
 *   (List
 *     (Concept "A")
 *     (Variable "$X")))
 *
 * outputs
 *
 * (Lambda
 *   (Variable "X")
 *   (Inheritance
 *     (Concept "A")
 *     (Variable "$X")))
 *
 */
void PutLinkUTest::test_nested_1()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-nested.scm\")");
	logger().debug() << "rs = " << rs;

	Handle nested_put = _eval->eval_h("nested-put-1"),
		result = _asp->add_atom(HandleCast(nested_put->execute(_asp.get()))),
		expected = _eval->eval_h("expected-1");

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

	TS_ASSERT_EQUALS(result, expected);

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

/**
 * Test that the following
 *
 * (PutLink
 *   (PutLink
 *     (LambdaLink
 *       (VariableNode "$X")
 *       (VariableNode "$X"))
 *     (ListLink
 *       (ConceptNode "texts")
 *       (VariableNode "$W")))
 *   (LambdaLink
 *     (VariableList
 *       (VariableNode "$Y")
 *       (VariableNode "$Z"))
 *     (InheritanceLink
 *       (VariableNode "$Y")
 *       (VariableNode "$Z"))))
 *
 * outputs
 *
 * (Lambda
 *   (VariableList
 *     (VariableNode "$Y")
 *     (VariableNode "$Z"))
 *   (ListLink
 *     (ConceptNode "texts")
 *     (Inheritance
 *       (VariableNode "$Y")
 *       (VariableNode "$Z"))))
 *
 * We expect the inner put to be evaluated first, resulting in
 *   (ListLink (Concept "texts") (Variable "$W"))
 * and then the outer put will just paste the lambda.yz where the $W goes.
 * Finally, the whole expression is re-written in prenex form, so that the
 * variable bindings are moved to outer-most.
 */
void PutLinkUTest::test_nested_2()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-nested.scm\")");
	logger().debug() << "rs = " << rs;

	Handle nested = _eval->eval_h("nested-put-2");
	Handle expected = _eval->eval_h("expected-2");

	Handle result = _asp->add_atom(HandleCast(nested->execute(_asp.get())));

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

	TS_ASSERT_EQUALS(result, expected);

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

/**
 * Test that that executing the below raises an exception
 *
 * (PutLink
 *   (PutLink
 *     (PutLink
 *       (LambdaLink
 *         (VariableNode "$X")
 *         (VariableNode "$X"))
 *       (ConceptNode "A"))
 *     (Concept "B"))
 *   (Concept "C"))
 *
 * The reason for this is that the innermost Put should evaluate
 * to just (ConceptNode "A"), and so the middle one would be
 *    (Put (Concept "A") (Concept "B"))
 * which is ill-defined, and throws.
 */
void PutLinkUTest::test_nested_3()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-nested.scm\")");
	logger().debug() << "rs = " << rs;

	Handle nested_put = _eval->eval_h("nested-put-3");

	TS_ASSERT_THROWS_ANYTHING(HandleCast(nested_put->execute(_asp.get())));

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

/**
 * Test that the following PutLinks raise an exception when executed
 * This is essentially the same test as that above, and throws for the
 * same reason (viz. its "texts" instead of "A", but same idea).
 *
 * (PutLink
 *   (PutLink
 *     (PutLink
 *       (LambdaLink
 *         (VariableNode "$X")
 *         (VariableNode "$X"))
 *       (ConceptNode "texts"))
 *     (ListLink
 *       (ConceptNode "texts")
 *       (VariableNode "$x-1")))
 *   (ConceptNode "texts"))
 */
void PutLinkUTest::test_nested_4()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-nested.scm\")");
	logger().debug() << "rs = " << rs;

	Handle nested_put = _eval->eval_h("nested-put-4");

	TS_ASSERT_THROWS_ANYTHING(HandleCast(nested_put->execute(_asp.get())));

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

/**
 * Test that the following nested sequence of PutLinks
 *
 * (Put
 *   (Put
 *     (Put
 *       (Lambda
 *         (Variable "$top-arg")
 *         (Variable "$top-arg"))
 *       (Lambda
 *         (Variable "$top-arg")
 *         (Variable "$top-arg")))
 *     (List
 *       (Variable "$spe-arg-0")
 *       (Concept "D")))
 *   (Lambda
 *     (VariableList
 *       (Variable "$sha-arg-0")
 *       (Variable "$sha-arg-1"))
 *     (Inheritance
 *       (Variable "$sha-arg-0")
 *       (Variable "$sha-arg-1"))))
 *
 * reduces to
 *
 * (Lambda
 *   (VariableList
 *     (Variable "$sha-arg-0")
 *     (Variable "$sha-arg-1"))
 *   (List
 *     (Inheritance
 *       (Variable "$sha-arg-0")
 *       (Variable "$sha-arg-1"))
 *     (Concept "D")))
 *
 * The reason for this is that the outermost Put should be of the form
 * (Put
 *   (ListLink (Variable "$spe-arg-0") (Concept "D"))
 *   (Lambda stuff)
 * and so the (Lambda stuff) should get pasted verbatim into where the
 * (Variable "$spe-arg-0") is. Finally, its reassembled in prenex form,
 * so that the variable declarations are outermost.
 */
void PutLinkUTest::test_nested_5()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-nested.scm\")");
	logger().debug() << "rs = " << rs;

	Handle simple_put = _eval->eval_h("simple-put-5");
	Handle result = _asp->add_atom(HandleCast(simple_put->execute(_asp.get())));
	Handle expected = _eval->eval_h("expected-5");

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

	TS_ASSERT_EQUALS(result, expected);

	Handle nested_put = _eval->eval_h("nested-put-5");
	result = _asp->add_atom(HandleCast(nested_put->execute(_asp.get())));

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

	TS_ASSERT_EQUALS(result, expected);

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

/**
 * Make sure that composing a top abstraction with aquoted AndLink
 * doesn't remove the quotes, because otherwise the AndLink is
 * interpreted as being a pattern matcher logic connector. So
 * specifically check that
 *
 * (PutLink
 *   (LambdaLink
 *     (VariableNode "$top-arg")
 *     (VariableNode "$top-arg"))
 *   (LambdaLink
 *     (VariableList
 *       (VariableNode "$sha-arg-0")
 *       (VariableNode "$sha-arg-1")))
 *     (LocalQuoteLink
 *       (AndLink
 *         (VariableNode "$sha-arg-0")
 *         (VariableNode "$sha-arg-1"))))
 *
 * upon execution outputs
 *
 * (LambdaLink
 *   (VariableList
 *     (VariableNode "$sha-arg-0")
 *     (VariableNode "$sha-arg-1"))
 *   (LocalQuoteLink
 *     (AndLink
 *       (VariableNode "$sha-arg-0")
 *       (VariableNode "$sha-arg-1"))))
 *
 */
void PutLinkUTest::test_and_compose_1()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-and.scm\")");
	logger().debug() << "rs = " << rs;

	Handle top = _eval->eval_h("top"),
		and_sha_abs = _eval->eval_h("and-shallow-abstraction"),
		put_top_and = L(PUT_LINK, top,
		                and_sha_abs),
		result = _asp->add_atom(HandleCast(put_top_and->execute(_asp.get()))),
		expected = and_sha_abs;

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

	TS_ASSERT_EQUALS(result, expected);

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

/**
 * Make sure that composing a non top abstraction with a quoted
 * AndLink does remove the quotation as it is useless. Specifically
 *
 * (PutLink
 *   (LambdaLink
 *     (VariableList
 *       (VariableNode "$sha-arg-0")
 *       (VariableNode "$sha-arg-1"))
 *     (InheritanceLink
 *       (VariableNode "$sha-arg-0")
 *       (VariableNode "$sha-arg-1")))
 *   (ListLink
 *     (VariableNode "spe-0")
 *     (LambdaLink
 *       (VariableList
 *         (VariableNode "$sha-arg-0")
 *         (VariableNode "$sha-arg-1"))
 *       (LocalQuoteLink
 *         (AndLink
 *           (VariableNode "$sha-arg-0")
 *           (VariableNode "$sha-arg-1"))))))
 *
 * upon execution outputs
 *
 * (LambdaLink
 *   (VariableList
 *     (VariableNode "spe-0")
 *     (VariableNode "$sha-arg-0")
 *     (VariableNode "$sha-arg-1"))
 *   (InheritanceLink
 *     (VariableNode "spe-0")
 *     (AndLink
 *       (VariableNode "$sha-arg-0")
 *       (VariableNode "$sha-arg-1"))))
 *
 */
void PutLinkUTest::test_and_compose_2()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-and.scm\")");
	logger().debug() << "rs = " << rs;

	Handle inh_sha_abs = _eval->eval_h("inheritance-shallow-abstraction"),
		and_sha_abs = _eval->eval_h("and-shallow-abstraction"),
		spe_var = N(VARIABLE_NODE, "$spe-0"),
		sha_var_1 = N(VARIABLE_NODE, "$sha-arg-0"),
		sha_var_2 = N(VARIABLE_NODE, "$sha-arg-1"),
		put_inh_and = L(PUT_LINK, inh_sha_abs,
		                L(LIST_LINK, spe_var, and_sha_abs)),
		result = _asp->add_atom(HandleCast(put_inh_and->execute(_asp.get()))),
		expected = L(LAMBDA_LINK,
		             L(VARIABLE_LIST,
		               spe_var,
		               sha_var_1,
		               sha_var_2),
		              L(INHERITANCE_LINK,
		                spe_var,
		                L(AND_LINK,
		                  sha_var_1,
		                  sha_var_2)));

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

	TS_ASSERT_EQUALS(result, expected);

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

/**
 * Make sure one can build quoted PutLink like
 *
 * Quote
 *   Put
 *     Lambda
 *       Inheritance
 *         Variable "$X"
 *         Variable "$Y"
 *     Unquote Variable "$patvar"
 */
void PutLinkUTest::test_quoted_put()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle quoted_put = L(QUOTE_LINK,
	                      L(PUT_LINK,
	                        L(LAMBDA_LINK,
	                          L(INHERITANCE_LINK,
	                            N(VARIABLE_NODE, "$X"),
	                            N(VARIABLE_NODE, "$Y"))),
	                        L(UNQUOTE_LINK,
	                          N(VARIABLE_NODE, "$patvar"))));

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

	TS_ASSERT_DIFFERS(quoted_put, nullptr);

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

/**
 * The following
 *
 * (Put
 *   (Lambda (Inheritance (Variable "$X") (Variable "$Y")))
 *   (List (Variable "$Z") (Variable "$Z")))
 *
 * should output upon execution
 *
 * (LambdaLink
 *   (VariableNode "$Z")
 *   (InheritanceLink
 *     (VariableNode "$Z")
 *     (VariableNode "$Z")))
 */
void PutLinkUTest::test_tricky_put_1()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-tricky.scm\")");
	logger().debug() << "rs = " << rs;

	Handle put = _eval->eval_h("put-1"),
		result = _asp->add_atom(HandleCast(put->execute(_asp.get()))),
		expected = _eval->eval_h("expected-1");

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

	TS_ASSERT_EQUALS(result, expected);

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

/**
 * The following
 *
 * (Put
 *   (Inheritance (Variable "$X") (Variable "$Y"))
 *   (List (Variable "$Z") (Variable "$Z")))
 *
 * should output upon execution
 *
 * (InheritanceLink
 *   (VariableNode "$Z")
 *   (VariableNode "$Z"))
 */
void PutLinkUTest::test_tricky_put_2()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-tricky.scm\")");
	logger().debug() << "rs = " << rs;

	Handle put = _eval->eval_h("put-2"),
		result = _asp->add_atom(HandleCast(put->execute(_asp.get()))),
		expected = _eval->eval_h("expected-2");

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

	TS_ASSERT_EQUALS(result, expected);

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

/**
 * The following
 *
 * (Put
 *   (Lambda (Inheritance (Variable "$X") (Variable "$Y")))
 *   (List (Variable "$Z") (Variable "$W")))
 *
 * should output upon execution
 *
 * (LambdaLink
 *   (InheritanceLink
 *     (VariableNode "$X")
 *     (VariableNode "$Y")))
 */
void PutLinkUTest::test_tricky_put_3()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-tricky.scm\")");
	logger().debug() << "rs = " << rs;

	Handle put = _eval->eval_h("put-3");
	Handle result = _asp->add_atom(HandleCast(put->execute(_asp.get())));
	Handle expected = _eval->eval_h("expected-3");

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

	TS_ASSERT_EQUALS(result, expected);

	expected = _eval->eval_h("expected-3-alt");
	logger().debug() << "alternative expected = " << oc_to_string(expected);
	TS_ASSERT_EQUALS(result, expected);

	expected = _eval->eval_h("expected-3-alt-b");
	logger().debug() << "alt-b expected = " << oc_to_string(expected);
	TS_ASSERT_EQUALS(result, expected);

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

/**
 * The following
 *
 * (Put
 *   (Inheritance (Variable "$X") (Variable "$Y"))
 *   (List (Variable "$Z") (Variable "$W")))
 *
 * should output upon execution
 *
 * (InheritanceLink
 *   (VariableNode "$Z")
 *   (VariableNode "$W"))
 */
void PutLinkUTest::test_tricky_put_4()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-tricky.scm\")");
	logger().debug() << "rs = " << rs;

	Handle put = _eval->eval_h("put-4");
	Handle result = _asp->add_atom(HandleCast(put->execute(_asp.get())));
	Handle expected = _eval->eval_h("expected-4");
	Handle unexpected = _eval->eval_h("unexpected-4");

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

	TS_ASSERT_EQUALS(result, expected);
	TS_ASSERT_DIFFERS(result, unexpected);

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

/**
 * The following
 *
 * (Put
 *   (List
 *     (Variable "$spe-arg-0")
 *     (Concept "D"))
 *   (Lambda
 *     (VariableList
 *       (Variable "$sha-arg-0")
 *       (Variable "$sha-arg-1"))
 *     (Inheritance
 *       (Variable "$sha-arg-0")
 *       (Variable "$sha-arg-1"))))
 *
 * should output upon execution
 *
 * (Lambda
 *   (VariableList
 *     (Variable "$sha-arg-0")
 *     (Variable "$sha-arg-1"))
 *   (List
 *     (Inheritance
 *       (Variable "$sha-arg-0")
 *       (Variable "$sha-arg-1"))
 *     (Concept "D")))
 */
void PutLinkUTest::test_tricky_put_5()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-tricky.scm\")");
	logger().debug() << "rs = " << rs;

	Handle put = _eval->eval_h("put-5"),
		result = _asp->add_atom(HandleCast(put->execute(_asp.get()))),
		expected = _eval->eval_h("expected-5");

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

	TS_ASSERT_EQUALS(result, expected);

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

/**
 * Just like tricky_put_5, but with a non-lambda prenex link.
 */
// XXX FIXME this currently triggers an exception in RUleLink::execute()
// complaining of mal-formed syntax. I don't understand why execute()
// is even being called, but am too lazy to debug. Something seems off.
void PutLinkUTest::xtest_tricky_put_6()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-tricky.scm\")");
	logger().debug() << "rs = " << rs;

	Handle put = _eval->eval_h("put-6"),
		result = _asp->add_atom(HandleCast(put->execute(_asp.get()))),
		expected = _eval->eval_h("expected-6");

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

	TS_ASSERT_EQUALS(result, expected);

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

/**
 * Just like tricky_put_5, but with a non-prenex link.
 * Unlike trick_put_5, this should NOT be rewritten into prenex form!
 */
void PutLinkUTest::test_tricky_put_7()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-tricky.scm\")");
	logger().debug() << "rs = " << rs;

	Handle put = _eval->eval_h("put-7"),
		result = _asp->add_atom(HandleCast(put->execute(_asp.get()))),
		expected = _eval->eval_h("expected-7");

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

	TS_ASSERT_EQUALS(result, expected);

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

/**
 * Test
 *
 * (PutLink
 *    (LambdaLink
 *       (VariableNode "$X")
 *    )
 *    (LambdaLink
 *       (VariableList
 *          (VariableNode "$gsn")
 *          (VariableNode "$arg")
 *       )
 *       (QuoteLink
 *          (ExecutionOutputLink
 *             (UnquoteLink
 *                (VariableNode "$gsn")
 *             )
 *             (UnquoteLink
 *                (VariableNode "$arg")
 *             )
 *          )
 *       )
 *    )
 * )
 *
 * which should return the argument, without touching the quotation.
 */
void PutLinkUTest::test_quoted_exec()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-quoted.scm\")");
	logger().debug() << "rs = " << rs;

	Handle put = _eval->eval_h("put-quoted-exec"),
		result = _asp->add_atom(HandleCast(put->execute(_asp.get()))),
		expected = _eval->eval_h("quoted-exec");

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

	TS_ASSERT_EQUALS(result, expected);

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

/**
 * Test
 *
 * (PutLink
 *    (LambdaLink
 *       (VariableNode "$X")
 *    )
 *    (LambdaLink
 *       (VariableList
 *          (VariableNode "$gpn")
 *          (VariableNode "$arg")
 *       )
 *       (QuoteLink
 *          (EvaluationLink
 *             (UnquoteLink
 *                (VariableNode "$gpn")
 *             )
 *             (UnquoteLink
 *                (VariableNode "$arg")
 *             )
 *          )
 *       )
 *    )
 * )
 *
 * which should return the argument, without touching the quotation.
 */
void PutLinkUTest::test_quoted_eval()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-quoted.scm\")");
	logger().debug() << "rs = " << rs;

	Handle put = _eval->eval_h("put-quoted-eval"),
		result = _asp->add_atom(HandleCast(put->execute(_asp.get()))),
		expected = _eval->eval_h("quoted-eval");

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

	TS_ASSERT_EQUALS(result, expected);

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

/**
 * Test
 *
 * (Put
 *   (VariableList
 *     (Variable "$vardecl")
 *     (Variable "$clause-1")
 *     (Variable "$clause-2"))
 *   (Quote
 *     (Lambda
 *       (Unquote (Variable "$vardecl"))
 *       (And
 *         (Unquote (Variable "$clause-1"))
 *         (Unquote (Variable "$clause-2")))))
 *   (List
 *     (VariableList (Variable "$X") (Variable "$Y"))
 *     (Inheritance (Concept "A") (Variable "$X"))
 *     (Inheritance (Concept "$X") (Variable "$Y"))))
 *
 * which should return
 *
 * (LambdaLink
 *    (VariableList
 *       (VariableNode "$X")
 *       (VariableNode "$Y")
 *    )
 *    (AndLink
 *       (InheritanceLink
 *          (ConceptNode "$X")
 *          (VariableNode "$Y")
 *       )
 *       (InheritanceLink
 *          (ConceptNode "A")
 *          (VariableNode "$X")
 *       )
 *    )
 * )
 *
 * That is the quotation should be entirely consumed as the lambda
 * link is fully constructed at the end of the substitution.
 */
void PutLinkUTest::test_quoted_lambda()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-quoted-lambda.scm\")");
	logger().debug() << "rs = " << rs;

	Handle put = _eval->eval_h("put-quoted-conjuction-lambda"),
		result = _asp->add_atom(HandleCast(put->execute(_asp.get()))),
		expected = _eval->eval_h("put-quoted-conjuction-lambda-result");

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

	TS_ASSERT_EQUALS(result, expected);

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

/**
 *
 * Test
 *
 * (Inheritance (Concept "B") (Concept "Parent"))
 * (Inheritance (Concept "C") (Concept "Parent"))
 *
 *  (PutLink
 *  (LambdaLink
 *   (VariableNode "x")
 *   (EvaluationLink
 *    (PredicateNode "relation")
 *    (ListLink
 *    (VariableNode "x")
 *     (VariableNode "A"))))
 *  (MeetLink
 *   (Inheritance (Variable "$X") (Concept "Parent"))))
 *
 * which should return
 *
 *  (SetLink
 *  (EvaluationLink
 *   (PredicateNode "relation")
 *   (ListLink
 *   (ConceptNode "B")
 *  (VariableNode "A")))
 *  (EvaluationLink
 *   (PredicateNode "relation")
 *   (ListLink
 *    (ConceptNode "C")
 *    (VariableNode "A"))))
*/
void PutLinkUTest::test_get()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-get.scm\")");
	logger().debug() << "rs = " << rs;

	Handle put = _eval->eval_h("put-get"),
		result = _asp->add_atom(HandleCast(put->execute(_asp.get()))),
		expected = _eval->eval_h("expected-put-get");

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

	TS_ASSERT_EQUALS(result, expected);

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

/**
 *
 * Test
 *
 * (Inheritance (Concept "B") (Concept "Parent"))
 * (Inheritance (Concept "C") (Concept "Parent"))
 *
 *  (PutLink
 *  (LambdaLink
 *   (VariableNode "x")
 *   (EvaluationLink
 *    (PredicateNode "relation")
 *    (ListLink
 *    (VariableNode "x")
 *     (VariableNode "A"))))
 *  (MeetLink
 *   (Inheritance (Variable "$X") (Concept "Parent"))))
 *
 * which should return
 *
 *  (SetLink
 *  (EvaluationLink
 *   (PredicateNode "relation")
 *   (ListLink
 *   (ConceptNode "B")
 *  (VariableNode "A")))
 *  (EvaluationLink
 *   (PredicateNode "relation")
 *   (ListLink
 *    (ConceptNode "C")
 *    (VariableNode "A"))))
*/
void PutLinkUTest::test_get_lambda()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-get-lambda.scm\")");
	logger().debug() << "rs = " << rs;

	Handle put = _eval->eval_h("put-get-lambda"),
		result = _asp->add_atom(HandleCast(put->execute(_asp.get()))),
		expected = _eval->eval_h("expected-put-get-lambda");

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

	TS_ASSERT_EQUALS(result, expected);

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

/*
 * Like above, but with multiple arguments
 */
void PutLinkUTest::test_get_multi()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-get-multi.scm\")");
	logger().debug() << "rs = " << rs;

	Handle result = _eval->eval_h("(cog-execute! put-get-multi)");

	Handle expected =
		L(EDGE_LINK,
			N(PREDICATE_NODE, "relatives"),
			L(LIST_LINK,
				N(CONCEPT_NODE, "Jim"),
				N(CONCEPT_NODE, "Jane"),
				N(CONCEPT_NODE, "mom and pop")));

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

	TS_ASSERT_EQUALS(result, expected);

	TS_ASSERT_THROWS_ANYTHING(
		_eval->eval_h("(cog-execute! put-get-multi-empty)"));

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

/*
 * Like above, but with pre-existing sets
 */
void PutLinkUTest::test_multi_set()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	std::string rs = _eval->eval("(load-from-path \"tests/atoms/scope/put-multi-set.scm\")");
	logger().debug() << "rs = " << rs;

	Handle result = _eval->eval_h("(cog-execute! put-multi-set)");

	Handle expected =
		L(EDGE_LINK,
			N(PREDICATE_NODE, "relatives"),
			L(LIST_LINK,
				N(CONCEPT_NODE, "Jim"),
				N(CONCEPT_NODE, "Jane"),
				N(CONCEPT_NODE, "mom and pop")));

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

	TS_ASSERT_EQUALS(result, expected);

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

/**
 * Test quoted put link, where vardecl, body and arguments are
 * unquoted variables. Make just sure that it doesn't crash or throw
 * exception.
 */
void PutLinkUTest::test_full_quotation()
{
	logger().info("BEGIN TEST: %s", __FUNCTION__);

	Handle unquoted_vardecl = L(UNQUOTE_LINK, N(VARIABLE_NODE, "$vardecl"));
	Handle unquoted_body = L(UNQUOTE_LINK, N(VARIABLE_NODE, "$body"));
	Handle unquoted_args = L(UNQUOTE_LINK, N(VARIABLE_NODE, "$args"));

	TS_ASSERT_THROWS_NOTHING(L(QUOTE_LINK,
	                           L(PUT_LINK,
	                             unquoted_vardecl,
	                             unquoted_body,
	                             unquoted_args)));

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