// ---------------------------------------------------------------------------
//
//  This file is part of PermLib.
//
// Copyright (c) 2009-2010 Thomas Rehn <thomas@carmen76.de>
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. The name of the author may not be used to endorse or promote products
//    derived from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// ---------------------------------------------------------------------------


#define BOOST_TEST_DYN_LINK 
#define BOOST_TEST_MODULE invariant magic
#include <boost/test/unit_test.hpp>

#include <iostream>
#include <fstream>
#include <algorithm>
#include <set>
#include <ctime>

#include <gmpxx.h>

#include <permlib/permutation.h>
#include <permlib/transversal/explicit_transversal.h>

#include "test-common.h"
#include <permlib/invariant/dade_invariants.h>


using namespace permlib;

typedef  boost::uint64_t eval_t;
typedef  std::vector<eval_t> result_t;

void evaluate(const std::list<LinearFormList>& invariants, const LinearForm& form, result_t& results) {
	results.resize(invariants.size());
	unsigned int i = 0;
	BOOST_FOREACH(const LinearFormList& invariant, invariants) {
		//std::cout << "result of " << i << std::endl;
		results[i++] = invariant.evaluate<eval_t>(form);
		/*
		if (results[i-1] != 0) {
			std::cout << "not null!\n";
		}*/
	}
}

void testGroup	(const GroupInformation& info)
{
	std::srand(std::time(NULL));
	typedef Permutation PERM;
	typedef ExplicitTransversal<PERM> TRANS;
    
	BSGS<PERM,TRANS> bsgs = construct<PERM,TRANS>(info.filename);
	BOOST_REQUIRE(bsgs.order() == info.order);

	//std::cout << "BSGS " << bsgs << std::endl;
	
    DadeInvariants<BSGS<PERM,TRANS> > dinv(bsgs);
	
	std::list<LinearFormList> invariants;
	dinv.invariants(invariants);
	std::cout << "found " << invariants.size() << " invariants" << std::endl;
	BOOST_FOREACH(const LinearFormList& invariant, invariants) {
		// degree of Dade-invariant polynomials divide the group order
		BOOST_CHECK(bsgs.order() % invariant.size() == 0);
	}
	const unsigned long numberOfTests = 10;
	
	for (unsigned int i = 0; i < numberOfTests; ++i) {
		std::cout << "TEST " << i << std::endl;
		LinearForm test(bsgs.n);
		for (unsigned int k = 0; k < bsgs.n; ++k) {
			unsigned int value = randomInt(100);
			if (value < 5+randomInt(50))
				test.set(k, 1);
		}
		//std::cout << "test " << test << std::endl;
		
		result_t reference;
		evaluate(invariants, test, reference);
		//print_iterable(reference.begin(),reference.end(), 0, "testEval");
		
		OrbitSet<PERM, LinearForm> orbit;
		result_t testEval;
		orbit.orbit(test, bsgs.S, LinearFormAction<PERM>());
		BOOST_FOREACH(const LinearForm& orbitForm, std::make_pair(orbit.begin(), orbit.end())) {
			evaluate(invariants, orbitForm, testEval);
			//print_iterable(testEval.begin(),testEval.end(), 0, "testEval");
			BOOST_CHECK(testEval == reference);
		}
		
		// not a unit test check, just to get a feeling how the invariants work
		/*
		LinearForm test2(bsgs.n);
		bool found = false;
		for (unsigned int k = 0; k < 100; ++k) {
			test2.reset();
			for (unsigned int l = 0; l < test.count(); ++l) {
				unsigned long pos = 0;
				while (test2[pos = randomInt(test2.size())])
					;
				test2.set(pos, 1);
			}
			if (!orbit.contains(test2)) {
				found = true;
				break;
			}
		}
		
		if (found) {
			evaluate(invariants, test2, testEval);
			std::cout << "test2 " << (testEval == reference) << std::endl;
			print_iterable(testEval.begin(),testEval.end(), 0, "test2Eval");
		} else {
			std::cout << "no test2 found" << std::endl;
		}
		*/
		
	}
}

BOOST_AUTO_TEST_CASE( dade_invariants ) {
	testGroup(info_1997);
	testGroup(info_cyclic500);
}
