/* $Id: monoms.cpp 148 2005-04-19 15:12:26Z kamenik $ */
/* Copyright 2004, Ondra Kamenik */

#include "monoms.h"
#include "tl_exception.h"
#include "fs_tensor.h"

#include <cstdlib>
#include <cmath>
#include <cstdio>

IntGenerator intgen;

void IntGenerator::init(int nf, int ny, int nv, int nw, int nu,
						int mx, double prob)
{
	maxim = mx;
	probab = prob;
	long int seed = nf;
	seed = 256*seed + ny;
	seed = 256*seed + nv;
	seed = 256*seed + nw;
	seed = 256*seed + nu;
	srand48(seed);
}

int IntGenerator::get() const
{
	double d = drand48();
	int num_inter = (int)( ((double)2*maxim)/(1.0-probab));
	int num_zero_inter = num_inter - 2*maxim;
	if (d < ((double)num_zero_inter)/num_inter)
		return 0;
	return (int)(d*num_inter)-num_zero_inter-maxim;
}

Monom::Monom(int len)
	: IntSequence(len)
{
	for (int i = 0; i < len; i++)
		operator[](i) = intgen.get();
}

Monom::Monom(int len, int item)
	: IntSequence(len, item)
{
}

double Monom::deriv(const IntSequence& vars) const
{
	double res = 1.0;
	int first_same_i = 0;
	for (int i = 0; i < vars.size(); i++) {
		TL_RAISE_IF(vars[i] < 0 || vars[i] >= size(),
					"Wrong variable index in Monom::deriv");
		if (vars[i] != vars[first_same_i])
			first_same_i = i;
		int mult = operator[](vars[i]) - (i-first_same_i);
		if (mult == 0)
			return 0;
		res *= mult;
	}
	return res;
}

void Monom::multiplyWith(int ex, const Monom& m)
{
	TL_RAISE_IF(size() != m.size(),
				"Wrong sizes of monoms in Monom::multiplyWith");
	if (ex == 0)
		return;
	for (int i = 0; i < size(); i++)
		operator[](i) += m[i]*ex;
}

void Monom::print() const
{
	printf("[");
	for (int i = 0; i < size(); i++)
		printf("%3d", operator[](i));
	printf("]");
}

Monom1Vector::Monom1Vector(int nxx, int l)
	: nx(nxx), len(l), x(new Monom*[len])
{
	for (int i = 0; i < len; i++) {
		x[i] = new Monom(nx);
	}
}

Monom1Vector::~Monom1Vector()
{
	for (int i = 0; i < len; i++) {
		delete x[i];
	}
	delete [] x;
}

void Monom1Vector::deriv(const IntSequence& c, Vector& out) const
{
	TL_RAISE_IF(out.length() != len,
				"Wrong length of output vector in Monom1Vector::deriv");

	for (int i = 0; i < len; i++) {
		out[i] = x[i]->deriv(c);
	}
}


FGSTensor* Monom1Vector::deriv(int dim) const
{
	FGSTensor* res =
		new FGSTensor(len, TensorDimens(Symmetry(dim), IntSequence(1, nx)));
	for (Tensor::index it = res->begin(); it != res->end(); ++it) {
		Vector outcol(*res, *it);
		deriv(it.getCoor(), outcol);
	}
	return res;
}

void Monom1Vector::print() const
{
	printf("Variables: x(%d)\n", nx);
	printf("Rows: %d\n", len);
	for (int i = 0; i < len; i++) {
		printf("%2d: ", i);
		x[i]->print();
		printf("\n");
	}
}

Monom2Vector::Monom2Vector(int nyy, int nuu, int l)
	: ny(nyy), nu(nuu), len(l), y(new Monom*[len]), u(new Monom*[len])
{
	for (int i = 0; i < len; i++) {
		y[i] = new Monom(ny);
		u[i] = new Monom(nu);
	}
}

Monom2Vector::Monom2Vector(const Monom1Vector& g, const Monom2Vector& xmon)
	: ny(xmon.ny), nu(xmon.nu), len(g.len),
	  y(new Monom*[len]), u(new Monom*[len])	
{
	TL_RAISE_IF(xmon.len != g.nx,
				"Wrong number of x's in Monom2Vector constructor");

	for (int i = 0; i < len; i++) {
		y[i] = new Monom(ny, 0);
		u[i] = new Monom(nu, 0);
	}

	for (int i = 0; i < len; i++) {
		// multiply from xmon
		for (int j = 0; j < g.nx; j++) {
			int ex = g.x[i]->operator[](j);
			y[i]->multiplyWith(ex, *(xmon.y[j]));
			u[i]->multiplyWith(ex, *(xmon.u[j]));
		}
	}
}

Monom2Vector::~Monom2Vector()
{
	for (int i = 0; i < len; i++) {
		delete y[i];
		delete u[i];
	}
	delete [] y;
	delete [] u;
}

void Monom2Vector::deriv(const Symmetry& s, const IntSequence& c,
						 Vector& out) const
{
	TL_RAISE_IF(out.length() != len,
				"Wrong length of output vector in Monom2Vector::deriv");
	TL_RAISE_IF(s.num() != 2,
				"Wrong symmetry for Monom2Vector::deriv");
	TL_RAISE_IF(s.dimen() != c.size(),
				"Incompatible symmetry and coordinates in Monom2Vector::deriv");
	IntSequence cy(c, 0, s[0]);
	IntSequence cu(c, s[0], s.dimen());
	for (int i = 0; i < len; i++) {
		out[i] = y[i]->deriv(cy) * u[i]->deriv(cu);
	}
}

FGSTensor* Monom2Vector::deriv(const Symmetry& s) const
{
	IntSequence nvs(2); nvs[0] = ny; nvs[1] = nu;
	FGSTensor* t = new FGSTensor(len, TensorDimens(s, nvs));
	for (Tensor::index it = t->begin(); it != t->end(); ++it) {
		Vector col(*t, *it);
		deriv(s, it.getCoor(), col);
	}
	return t;
}

FGSContainer* Monom2Vector::deriv(int maxdim) const
{
	FGSContainer* res = new FGSContainer(2);
	for (int dim = 1; dim <= maxdim; dim++) {
		for (int ydim = 0; ydim <= dim; ydim++) {
			int udim = dim - ydim;
			Symmetry s(ydim, udim);
			res->insert(deriv(s));
		}
	}
	return res;
}

void Monom2Vector::print() const
{
	printf("Variables: y(%d) u(%d)\n", ny, nu);
	printf("Rows: %d\n", len);
	for (int i = 0; i < len; i++) {
		printf("%2d: ", i);
		y[i]->print();
		printf("    ");
		u[i]->print();
		printf("\n");
	}
}

Monom4Vector::~Monom4Vector()
{
	for (int i = 0; i < len; i++) {
		delete x1[i];
		delete x2[i];
		delete x3[i];
		delete x4[i];
	}
	delete [] x1;
	delete [] x2;
	delete [] x3;
	delete [] x4;
}

void Monom4Vector::init_random()
{
	for (int i = 0; i < len; i++) {
		x1[i] = new Monom(nx1);
		x2[i] = new Monom(nx2);
		x3[i] = new Monom(nx3);
		x4[i] = new Monom(nx4);
	}
}

Monom4Vector::Monom4Vector(int l, int ny, int nu)
	: len(l), nx1(ny), nx2(nu), nx3(0), nx4(1),
	  x1(new Monom*[len]),
	  x2(new Monom*[len]),
	  x3(new Monom*[len]),
	  x4(new Monom*[len])	  
{
	init_random();
}

Monom4Vector::Monom4Vector(int l, int ny, int nu, int nup)
	: len(l), nx1(ny), nx2(nu), nx3(nup), nx4(1),
	  x1(new Monom*[len]),
	  x2(new Monom*[len]),
	  x3(new Monom*[len]),
	  x4(new Monom*[len])
{
	init_random();
}

Monom4Vector::Monom4Vector(int l, int nbigg, int ng, int ny, int nu)
	: len(l), nx1(nbigg), nx2(ng), nx3(ny), nx4(nu),
	  x1(new Monom*[len]),
	  x2(new Monom*[len]),
	  x3(new Monom*[len]),
	  x4(new Monom*[len])
{
	init_random();
}

Monom4Vector::Monom4Vector(const Monom4Vector& f, const Monom4Vector& bigg,
						   const Monom4Vector& g)
	: len(f.len), nx1(bigg.nx1), nx2(bigg.nx2), nx3(bigg.nx3), nx4(1),
	  x1(new Monom*[len]),
	  x2(new Monom*[len]),
	  x3(new Monom*[len]),
	  x4(new Monom*[len])
{
	TL_RAISE_IF(!(bigg.nx1 == g.nx1 && bigg.nx2 == g.nx2 && g.nx3 == 0 &&
				  bigg.nx4 == 1 && g.nx4 == 1),
				"Incompatible g with G");
	TL_RAISE_IF(!(bigg.len == f.nx1 && g.len == f.nx2 &&
				  bigg.nx1 == f.nx3 && bigg.nx2 == f.nx4),
				"Incompatible g or G with f");

	for (int i = 0; i < len; i++) {
		x1[i] = new Monom(nx1, 0);
		x2[i] = new Monom(nx2, 0);
		x3[i] = new Monom(nx3, 0);
		x4[i] = new Monom(nx4, 0);
	}

	for (int i = 0; i < len; i++) {
		// multiply from G (first argument)
		for (int j = 0; j < f.nx1; j++) {
			int ex = f.x1[i]->operator[](j);
			x1[i]->multiplyWith(ex, *(bigg.x1[j]));
			x2[i]->multiplyWith(ex, *(bigg.x2[j]));
			x3[i]->multiplyWith(ex, *(bigg.x3[j]));
			x4[i]->multiplyWith(ex, *(bigg.x4[j]));
		}
		// multiply from g (second argument)
		for (int j = 0; j < f.nx2; j++) {
			int ex = f.x2[i]->operator[](j);
			x1[i]->multiplyWith(ex, *(g.x1[j]));
			x2[i]->multiplyWith(ex, *(g.x2[j]));
			x4[i]->multiplyWith(ex, *(g.x4[j]));
		}
		// add y as third argument of f
		x1[i]->add(1, *(f.x3[i]));
		// add u as fourth argument of f
		x2[i]->add(1, *(f.x4[i]));
	}
}

void Monom4Vector::deriv(const Symmetry& s, const IntSequence& coor,
						 Vector& out) const
{
	TL_RAISE_IF(out.length() != len,
				"Wrong length of output vector in Monom4Vector::deriv");
	TL_RAISE_IF(s.num() != 4,
				"Wrong symmetry for Monom4Vector::deriv");
	TL_RAISE_IF(s.dimen() != coor.size(),
				"Incompatible symmetry and coordinates in Monom4Vector::deriv");

	for (int i = 0; i < len; i++) {
		out[i] = 1;
		int off = 0;
		out[i] *= x1[i]->deriv(IntSequence(coor, off, off+s[0]));
		off += s[0];
		out[i] *= x2[i]->deriv(IntSequence(coor, off, off+s[1]));
		off += s[1];
		out[i] *= x3[i]->deriv(IntSequence(coor, off, off+s[2]));
		off += s[2];
		out[i] *= x4[i]->deriv(IntSequence(coor, off, off+s[3]));
	}
}

FGSTensor* Monom4Vector::deriv(const Symmetry& s) const
{
	IntSequence nvs(4);
	nvs[0] = nx1; nvs[1] = nx2; 
	nvs[2] = nx3; nvs[3] = nx4;

	FGSTensor* res = new FGSTensor(len, TensorDimens(s, nvs));
	for (Tensor::index run = res->begin(); run != res->end(); ++run) {
		Vector col(*res, *run);
		deriv(s, run.getCoor(), col);
	}
	return res;
}

FSSparseTensor* Monom4Vector::deriv(int dim) const
{
	IntSequence cum(4);
	cum[0] = 0; cum[1] = nx1; cum[2] = nx1+nx2; cum[3] = nx1+nx2+nx3;

	FSSparseTensor* res = new FSSparseTensor(dim, nx1+nx2+nx3+nx4, len);

	FFSTensor dummy(0, nx1+nx2+nx3+nx4, dim);
	for (Tensor::index run = dummy.begin(); run != dummy.end(); ++run) {
		Symmetry ind_sym(0,0,0,0);
		IntSequence ind(run.getCoor());
		for (int i = 0; i < ind.size(); i++) {
			int j = 3;
			while (j >= 0 && ind[i] < cum[j]) j--;
			ind_sym[j]++;
			ind[i] -= cum[j];
		}

		Vector col(len);
		deriv(ind_sym, ind, col);
		for (int i = 0; i < len; i++) {
			if (col[i] != 0.0) {
				res->insert(run.getCoor(), i, col[i]);
			}
		}
	}

	return res;
}

void Monom4Vector::print() const
{
	printf("Variables: x1(%d) x2(%d) x3(%d) x4(%d)\n",
		   nx1, nx2, nx3, nx4);
	printf("Rows: %d\n", len);
	for (int i = 0; i < len; i++) {
		printf("%2d: ", i);
		x1[i]->print();
		printf("    ");
		x2[i]->print();
		printf("    ");
		x3[i]->print();
		printf("    ");
		x4[i]->print();
		printf("\n");
	}
}

SparseDerivGenerator::SparseDerivGenerator(
	int nf, int ny, int nu, int nup, int nbigg, int ng,
	int mx, double prob, int maxdim)
	: maxdimen(maxdim), ts(new FSSparseTensor*[maxdimen])
{
	intgen.init(nf, ny, nu, nup, nbigg, mx, prob);

	Monom4Vector bigg_m(nbigg, ny, nu, nup);
	Monom4Vector g_m(ng, ny, nu);
	Monom4Vector f(nf, nbigg, ng, ny, nu);
	Monom4Vector r(f, bigg_m, g_m);
	bigg = new FGSContainer(4);
	g = new FGSContainer(4);
	rcont = new FGSContainer(4);

	for (int dim = 1; dim <= maxdimen; dim++) {
		SymmetrySet ss(dim, 4);
		for (symiterator si(ss); !si.isEnd(); ++si) {
			bigg->insert(bigg_m.deriv(*si));
			rcont->insert(r.deriv(*si));
			if ((*si)[2] == 0)
				g->insert(g_m.deriv(*si));
		}

		ts[dim-1] = f.deriv(dim);
	}
}

SparseDerivGenerator::~SparseDerivGenerator()
{
	delete bigg;
	delete g;
	delete rcont;
	for(int i = 0; i < maxdimen; i++)
		delete ts[i];
	delete [] ts;
}


DenseDerivGenerator::DenseDerivGenerator(int ng, int nx, int ny, int nu,
										 int mx, double prob, int maxdim)
	: maxdimen(maxdim), ts(new FGSTensor*[maxdimen]),
	  uts(new UGSTensor*[maxdimen])
{
	intgen.init(ng, nx, ny, nu, nu, mx, prob);
	Monom1Vector g(nx, ng);
	Monom2Vector x(ny, nu, nx);
	Monom2Vector r(g, x);
	xcont = x.deriv(maxdimen);
	rcont = r.deriv(maxdimen);
	uxcont = NULL;
	for (int d = 1; d <= maxdimen; d++) {
		ts[d-1] = g.deriv(d);
		uts[d-1] = NULL;
	}
}

void DenseDerivGenerator::unfold()
{
	uxcont = new UGSContainer(*xcont);
	for (int i = 0; i < maxdimen; i++) {
		uts[i] = new UGSTensor(*(ts[i]));
	}
}

DenseDerivGenerator::~DenseDerivGenerator()
{
	delete xcont;
	delete rcont;
	for (int i = 0; i < maxdimen; i++) {
		delete ts[i];
		if (uts[i])
			delete uts[i];
	}
	delete [] ts;	
	delete [] uts;	
}

// Local Variables:
// mode:C++
// End:
