/*
 * generator module program copyright (C) 2009 - 2010 H.Niwa
 */

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <setjmp.h>

#include <string>
#include <complex>

#include "syserr.h"

#include "bin_node.h"
#include "gc.h"
#include "var.h"
#include "pred.h"
#include "context.h"
#include "builtin.h"
#include "sysmodule.h"
#include "module.h"
#include "unify.h"
#include "func.h"
#include "generator.h"

extern void PushStack(Context* cx, Node* goals, Node* md, Node* env);
extern int PopStack(Context* cx, Node* &goals, Node* &md, Node* &env);

int generator_perm_rep(Context* cx, Node* goalscar, List* module);
int generator_combi_rep(Context* cx, Node* goalscar, List* module);
int generator_permutation(Context* cx, Node* goalscar, List* module);
int generator_combination(Context* cx, Node* goalscar, List* module);

int generator_permutationAll(Context* cx, Node* goalscar, List* module);
int generator_combinationAll(Context* cx, Node* goalscar, List* module);

extern int BreakFlag;
extern int ThrowFlag;

struct paramtype {
	Context* cx;
	List* module;
	Node* nvar;
	Node* gl;
	Node* val;
	Node* nlist;
};


int generatormodule(Context* cx, Node* goalscar, Node* goalscdr, 
				Node* goals, List* module, int& r)
{
	Node* retn;
	int	rn;

	std::string	s;

	if (goalscar->Val()->Car()->kind() == ATOM) {
		((Atom*)(goalscar->Val()->Car()))->toString(s);

		if (s == "perm_rep") {
			r = generator_perm_rep(cx, goalscar, module);
			return 1;
		} else if (s == "combi_rep ") {
			r = generator_combi_rep(cx, goalscar, module);
			return 1;
		} else if (s == "permutation") {
			r = generator_permutation(cx, goalscar, module);
			return 1;
		} else if (s == "combination") {
			r = generator_combination(cx, goalscar, module);
			return 1;
		} else if (s == "permutationAll") {
			r = generator_permutationAll(cx, goalscar, module);
			return 1;
		} else if (s == "combinationAll") {
			r = generator_combinationAll(cx, goalscar, module);
			return 1;
		}
	}

	r = -1;
	syserr("The predicate that did not exist in the generator module is used. \n");
	return 1;
}

		
int generator_perm_rep(Context* cx, Node* goalscar, List* module)
{
	int glen = ListLength(goalscar->Cdr());
	if ((glen != 2) && (glen != 3)) {
		syserr("usage: ::generator <perm_rep VAR LIST [LEN]>\n");
		return 0;
	}

	Node* g    = goalscar->Cdr()->Val();

	int rn;

	Node* nvar = g->Car()->Val();
	if ((rn = FuncArg(cx, nvar, goalscar, module)) <= 0) {
		syserr("perm_rep: failed in the evaluation of the argument. \n");
		return 0;
	}

	if (nvar->kind() != UNDEF) {
		syserr("usage: ::generator <perm_rep VAR LIST [LEN]>\n");
		return 0;
	}

	g = g->Cdr();
	Node* nlist = g->Car()->Val();
	if ((rn = FuncArg(cx, nlist, goalscar, module)) <= 0) {
		syserr("perm_rep: failed in the evaluation of the argument. \n");
		return 0;
	}

	if (nlist == Nil) {
		Node* env = Nil->Cons(Nil);

		SetEnv(env, nvar);
		((Undef*)(nvar->Val()))->Set(Nil);

		PushStack(cx, Nil, Nil, env);
		return 1;
	}
		
	if ((nlist->kind() != LIST) && (nlist != Nil)) {
		syserr("usage: ::generator <perm_rep VAR LIST [LEN]>\n");
		return 0;
	}

	long long len;
	if (glen == 2) {
		len = ListLength(nlist);
	} else {
 		g = g->Cdr();
		Node* nlen = g->Car()->Val();
		if ((rn = FuncArg(cx, nlen, goalscar, module)) <= 0) {
			syserr("perm_rep: failed in the evaluation of the argument. \n");
			return 0;
		}

		if (nlen->kind() != ATOM) {
			syserr("usage: ::generator <perm_rep VAR LIST [LEN]>\n");
			return 0;
		}

		if (!((Atom*)nlen)->toInt(len)) {
			syserr("usage: ::generator <perm_rep VAR LIST [LEN]>\n");
			return 0;
		}
	}
	
	if (len <= 0) {
		syserr("usage: ::generator <perm_rep VAR LIST [LEN]>\n");
		return 0;
	}		
	
	int	list_len = ListLength(nlist);
	int	i;
	Node*	nl=nlist;
	Node*	list[list_len];
	nl = nlist;
	for (i = 0; i < list_len; i++) {
		list[i] = nl->Car();
		nl = nl->Cdr();
	}

	Node*	nout=Nil;
	
	for (i=0; i < len; i++) {
		long long rd;
#ifndef __MINGW32__
		rd = (long long)random();
#else
		rd = (long long)rand();
#endif /* __MINGW32__ */

		rd = rd % list_len;
		
		nout = Cons(list[rd], nout);
	}			

	Node* env = Nil->Cons(Nil);

	SetEnv(env, nvar);
	((Undef*)(nvar->Val()))->Set(nout);

	PushStack(cx, Nil, Nil, env);
	return 1;
}

int generator_combi_rep(Context* cx, Node* goalscar, List* module)
{
	int glen = ListLength(goalscar->Cdr());
	if ((glen != 2) && (glen != 3)) {
		syserr("usage: ::generator <combi_rep VAR LIST [LEN]>\n");
		return 0;
	}

	Node* g    = goalscar->Cdr()->Val();

	int rn;

	Node* nvar = g->Car()->Val();
	if ((rn = FuncArg(cx, nvar, goalscar, module)) <= 0) {
		syserr("combi_rep: failed in the evaluation of the argument. \n");
		return 0;
	}

	if (nvar->kind() != UNDEF) {
		syserr("usage: ::generator <combi_rep VAR LIST [LEN]>\n");
		return 0;
	}

	g = g->Cdr();
	Node* nlist = g->Car()->Val();
	if ((rn = FuncArg(cx, nlist, goalscar, module)) <= 0) {
		syserr("combi_rep: failed in the evaluation of the argument. \n");
		return 0;
	}

	if (nlist == Nil) {
		Node* env = Nil->Cons(Nil);

		SetEnv(env, nvar);
		((Undef*)(nvar->Val()))->Set(Nil);

		PushStack(cx, Nil, Nil, env);
		return 1;
	}

	if ((nlist->kind() != LIST) && (nlist != Nil)) {
		syserr("usage: ::generator <combi_rep VAR LIST [LEN]>\n");
		return 0;
	}

	long long len;
	if (glen == 2) {
		len = ListLength(nlist);
	} else {
		g = g->Cdr();
		Node* nlen = g->Car()->Val();
		if ((rn = FuncArg(cx, nlen, goalscar, module)) <= 0) {
			syserr("combi_rep: failed in the evaluation of the argument. \n");
			return 0;
		}

		if (nlen->kind() != ATOM) {
			syserr("usage: ::generator <combi_rep VAR LIST [LEN]>\n");
			return 0;
		}

		if (!((Atom*)nlen)->toInt(len)) {
			syserr("usage: ::generator <combi_rep VAR LIST [LEN]>\n");
			return 0;
		}
	}
	
	if (len <= 0) {
		syserr("usage: ::generator <combi_rep VAR LIST [LEN]>\n");
		return 0;
	}		
	
	int	list_len = ListLength(nlist);
	int	i, j;
	Node*	nl=nlist;
	Node*	list[list_len];
	int	out[len];
	nl = nlist;
	for (i = 0; i < list_len; i++) {
		list[i] = nl->Car();
		nl = nl->Cdr();
	}

	Node*	nout=Nil;
	
	for (i=0; i < len; i++) {
		long long rd;
#ifndef __MINGW32__
		rd = (long long)random();
#else
		rd = (long long)rand();
#endif /* __MINGW32__ */

		rd = rd % list_len;

		out[i] = rd;		
	}			

	for (i = 0; i < len-1; i++) {
		for (j = i+1; j < len; j++) {
			if (out[i] > out[j]) {
				int tmp = out[i];
				out[i] = out[j];
				out[j] = tmp;
			}
		}
	}	

	for (i = len-1; i >= 0; i--) {
		nout = Cons(list[out[i]], nout);
	}

	Node* env = Nil->Cons(Nil);

	SetEnv(env, nvar);
	((Undef*)(nvar->Val()))->Set(nout);

	PushStack(cx, Nil, Nil, env);
	return 1;
}


int generator_permutation(Context* cx, Node* goalscar, List* module)
{
	int glen = ListLength(goalscar->Cdr());
	if ((glen != 2) && (glen != 3)) {
		syserr("usage: ::generator <permutation VAR LIST [LEN]>\n");
		return 0;
	}

	Node* g    = goalscar->Cdr()->Val();

	int rn;

	Node* nvar = g->Car()->Val();
	if ((rn = FuncArg(cx, nvar, goalscar, module)) <= 0) {
		syserr("permutation: failed in the evaluation of the argument. \n");
		return 0;
	}

	if (nvar->kind() != UNDEF) {
		syserr("usage: ::generator <permutation VAR LIST [LEN]>\n");
		return 0;
	}

	g = g->Cdr();
	Node* nlist = g->Car()->Val();
	if ((rn = FuncArg(cx, nlist, goalscar, module)) <= 0) {
		syserr("permutation: failed in the evaluation of the argument. \n");
		return 0;
	}

	if (nlist == Nil) {
		Node* env = Nil->Cons(Nil);

		SetEnv(env, nvar);
		((Undef*)(nvar->Val()))->Set(Nil);

		PushStack(cx, Nil, Nil, env);
		return 1;
	}

	if ((nlist->kind() != LIST) && (nlist != Nil)) {
		syserr("usage: ::generator <permutation VAR LIST [LEN]>\n");
		return 0;
	}

	int		list_len = ListLength(nlist);
	long long	len;
	if (glen == 2) {
		len = list_len;
	} else {
		g = g->Cdr();
		Node* nlen = g->Car()->Val();
		if ((rn = FuncArg(cx, nlen, goalscar, module)) <= 0) {
			syserr("permutation: failed in the evaluation of the argument. \n");
			return 0;
		}

		if (nlen->kind() != ATOM) {
			syserr("usage: ::generator <permutation VAR LIST [LEN]>\n");
			return 0;
		}

		if (!((Atom*)nlen)->toInt(len)) {
			syserr("usage: ::generator <permutation VAR LIST [LEN]>\n");
			return 0;
		}
	}
	

	if ((len <= 0) || (len > list_len)) {
		syserr("usage: ::generator <permutation VAR LIST [LEN]>\n");
		return 0;
	}		
	
	int	i;
	Node*	nl=nlist;
	Node*	list[list_len];
	int	mask[list_len];
	nl = nlist;
	for (i = 0; i < list_len; i++) {
		list[i] = nl->Car();
		nl = nl->Cdr();
		mask[i] = 1;
	}

	Node*	nout=Nil;
	
	for (i=0; i < len; i++) {
		long long rd;
		do {
#ifndef __MINGW32__
			rd = (long long)random();
#else
			rd = (long long)rand();
#endif /* __MINGW32__ */

			rd = rd % list_len;
		} while (!mask[rd]);
		
		nout = Cons(list[rd], nout);
		mask[rd] = 0;
	}			

	Node* env = Nil->Cons(Nil);

	SetEnv(env, nvar);
	((Undef*)(nvar->Val()))->Set(nout);

	PushStack(cx, Nil, Nil, env);
	return 1;
}


int generator_combination(Context* cx, Node* goalscar, List* module)
{
	int glen = ListLength(goalscar->Cdr());
	if ((glen != 2) && (glen != 3)) {
		syserr("usage: ::generator <combination VAR LIST [LEN]>\n");
		return 0;
	}

	Node* g    = goalscar->Cdr()->Val();

	int rn;

	Node* nvar = g->Car()->Val();
	if ((rn = FuncArg(cx, nvar, goalscar, module)) <= 0) {
		syserr("combination: failed in the evaluation of the argument. \n");
		return 0;
	}

	if (nvar->kind() != UNDEF) {
		syserr("usage: ::generator <combination VAR LIST [LEN]>\n");
		return 0;
	}

	g = g->Cdr();
	Node* nlist = g->Car()->Val();
	if ((rn = FuncArg(cx, nlist, goalscar, module)) <= 0) {
		syserr("combination: failed in the evaluation of the argument. \n");
		return 0;
	}

	if (nlist == Nil) {
		Node* env = Nil->Cons(Nil);

		SetEnv(env, nvar);
		((Undef*)(nvar->Val()))->Set(Nil);

		PushStack(cx, Nil, Nil, env);
		return 1;
	}

	if ((nlist->kind() != LIST) && (nlist != Nil)) {
		syserr("usage: ::generator <combination VAR LIST [LEN]>\n");
		return 0;
	}

	int		list_len = ListLength(nlist);
	long long	len;
	if (glen == 2) {
		len = list_len;

		Node* env = Nil->Cons(Nil);

		SetEnv(env, nvar);
		((Undef*)(nvar->Val()))->Set(nlist);

		PushStack(cx, Nil, Nil, env);
		return 1;

	} else {
		g = g->Cdr();
		Node* nlen = g->Car()->Val();
		if ((rn = FuncArg(cx, nlen, goalscar, module)) <= 0) {
			syserr("combination: failed in the evaluation of the argument. \n");
			return 0;
		}

		if (nlen->kind() != ATOM) {
			syserr("usage: ::generator <combination VAR LIST [LEN]>\n");
			return 0;
		}

		if (!((Atom*)nlen)->toInt(len)) {
			syserr("usage: ::generator <combination VAR LIST [LEN]>\n");
			return 0;
		}
	}
	

	if ((len <= 0) || (len > list_len)) {
		syserr("usage: ::generator <combination VAR LIST [LEN]>\n");
		return 0;
	}		
	
	int	i, j;
	Node*	nl=nlist;
	Node*	list[list_len];
	int	mask[list_len];
	int	out[list_len];
	nl = nlist;
	for (i = 0; i < list_len; i++) {
		list[i] = nl->Car();
		nl = nl->Cdr();
		mask[i] = 1;
	}

	Node*	nout=Nil;
	
	for (i=0; i < len; i++) {
		long long rd;
		do {
#ifndef __MINGW32__
			rd = (long long)random();
#else
			rd = (long long)rand();
#endif /* __MINGW32__ */

			rd = rd % list_len;
		} while (!mask[rd]);

		out[i] = rd;
		mask[rd] = 0;
	}			

	for (i = 0; i < len-1; i++) {
		for (j = i+1; j < len; j++) {
			if (out[i] > out[j]) {
				int tmp = out[i];
				out[i] = out[j];
				out[j] = tmp;
			}
		}
	}

	for (i = len-1; i >= 0; i--) {
		nout = Cons(list[out[i]], nout);
	}
	
	Node* env = Nil->Cons(Nil);

	SetEnv(env, nvar);
	((Undef*)(nvar->Val()))->Set(nout);

	PushStack(cx, Nil, Nil, env);
	return 1;
}

int callpermpred(Node* out[], int len2, paramtype &pt)
{
	int	i;
	int	rn = 1;
	Node*	n = Nil;
	
	for (i=len2-1; i >= 0; i--) {
		n = Cons(out[i], n);
	}

	// exec preds

	Context* cx = pt.cx;
	List* module = pt.module;
	Node* nvar = pt.nvar;
	Node* gl = pt.gl;
	Node* nlist = pt.nlist;
	
// PrintNode(n); //dummy for test

	Context *cx2 = new Context(module, cx->modulename);
	cx2->ioin = cx->ioin;
	cx2->ioout = cx->ioout;
	cx2->tokenflag = cx->tokenflag;
	cx2->token = cx->token;

	Node* env = Nil->Cons(Nil);
	SetEnv(env, nvar);
	((Undef*)nvar)->Set(n);

	cxpush(cx2, env);
	cxpush(cx2, gl);
	cxpush(cx2, nvar);
	cxpush(cx2, nlist);
	cxpush(cx2, n);
	cxpush(cx2, pt.val);
				
	rn = Unify(cx2, gl, module);

	if (rn == 1) {
		if (cx->tokenflag) cx->token = cx2->token;

		Node*	res;
		if (gl->Car()->Car()->Eq(mka("unify"))
				|| gl->Car()->Car()->Eq(mka("obj"))) {
			res = gl->Car()->Cdr()->Cdr()->Car()->Cdr()->Car()->Val();
		} else {
			res = gl->Car()->Cdr()->Car()->Val();
		}
		
		pt.val = Append(pt.val, MkList(res));
	}
		
	cx2->Clear();

	cxpop(cx2);
	cxpop(cx2);
	cxpop(cx2);
	cxpop(cx2);
	cxpop(cx2);
	cxpop(cx2);
		
	UnsetEnv(env);
		
	delete cx2;
	cx2 = 0;

	return rn;
}

int setpermdata(Node* data[], Node* out[], int mask[], 
				int n, int len1, int len2, paramtype &pt)
{
	int	i;
	int 	rn;
	
	if (n <= 0) {
		rn = callpermpred(out, len2, pt);

		if (BreakFlag) {
			BreakFlag = 0;
			rn = -1;
		}
		if (ThrowFlag) {
			rn = 1;
		}

		return rn;
	}

	rn = 1;
	for (i=0; i<len1; i++) {
		if (mask[i]) {
			out[len2-n] = data[i];
			mask[i] = 0;
			rn = setpermdata(data, out, mask, n-1, len1, len2, pt);
			if (rn != 1) {
				break;
			}
			mask[i] = 1;
		}
	}
	return rn;
}


int permutation(Node* data[], Node* out[], int len1, int len2, paramtype &pt)
{
	int i;
	int rn;
	int mask[len1];
	
	for (i=0; i<len1; i++) {
		mask[i] = 1;
	}

	rn = 1;
	for (i=0; i<len1; i++) {
		out[0] = data[i];
		mask[i] = 0;
		rn = setpermdata(data, out, mask, len2-1, len1, len2, pt);
		if (rn != 1) {
			break;
		}
		if (BreakFlag) {
			BreakFlag = 0;
			break;
		}
		if (ThrowFlag) {
			break;
		}
		mask[i] = 1;
	}
	return rn;
}

int generator_permutationAll(Context* cx, Node* goalscar, List* module)
{
	int i;
	
	int glen = ListLength(goalscar->Cdr());
	if (glen < 2) {
		syserr("usage: ::generator <permutationAll [RESULT_VAR] (VAR LIST [LEN]) PRED ... >\n");
		return 0;
	}

	Node* g    = goalscar->Cdr()->Val();

	int rn;

	Node* nresult = g->Car()->Val();
 	if ((rn = FuncArg(cx, nresult, goalscar, module)) <= 0) {
		syserr("permutationAll: failed in the evaluation of the argument. \n");
		return 0;
	}

	if (nresult->kind() != UNDEF) {
		nresult = Nil;
	} else {
		g = g->Cdr();
	}

	Node* nglist = g->Car()->Val();
 	if ((rn = FuncArg(cx, nglist, goalscar, module)) <= 0) {
		syserr("permutationAll: failed in the evaluation of the argument. \n");
		return 0;
	}

	if ((nglist->kind() != LIST) && (nglist != Nil)) {
		syserr("usage: ::generator <permutationAll [RESULT_VAR] (VAR LIST [LEN]) PRED ... >\n");
		return 0;
	}

	int glistlen = ListLength(nglist);
	if ((glistlen != 2) && (glistlen != 3)) {
		syserr("usage: ::generator <permutationAll [RESULT_VAR] (VAR LIST [LEN]) PRED ... >\n");
		return 0;
	}

	Node* nvar = nglist->Car()->Val();
	if (nvar->kind() != UNDEF) {
		syserr("usage: ::generator <permutationAll [RESULT_VAR] (VAR LIST [LEN]) PRED ... >\n");
		return 0;
	}
		
	nglist = nglist->Cdr();
	Node* nlist = nglist->Car()->Val();
	if ((nlist->kind() != LIST) && (nlist != Nil)) {
		syserr("usage: ::generator <permutationAll [RESULT_VAR] (VAR LIST [LEN]) PRED ... >\n");
		return 0;
	}

	long long len;
	int len1 = ListLength(nlist);
	if (glistlen != 3) {
		len = len1;
	} else {
		nglist = nglist->Cdr();
		Node* nlen = nglist->Car()->Val();
		if (nlen->kind() != ATOM) {
			syserr("usage: ::generator <permutationAll [RESULT_VAR] (VAR LIST [LEN]) PRED ... >\n");
			return 0;
		}

		if (!((Atom*)nlen)->toInt(len)) {
			syserr("usage: ::generator <permutationAll [RESULT_VAR] (VAR LIST [LEN]) PRED ... >\n");
			return 0;
		}
	}
	
	if (len <= 0) {
		syserr("usage: ::generator permutationAll : LEN is 0 or less.\n");
		return 0;
	}		


	if (len > len1) {
		syserr("usage: ::generator permutationAll : LEN is longer than the length of LIST.\n");
		return 0;
	}
	
	Node*	data[len1];
	Node*	out[len];
	for (i = 0; i < len1; i++) {
		data[i] = nlist->Car();
		nlist = nlist->Cdr();
	}

	struct paramtype pt;
	pt.cx = cx;
	pt.module = module;
	pt.nvar = nvar;
	pt.gl = g->Cdr();
	pt.val = Nil;
	pt.nlist = nlist;
		
	rn = permutation(data, out, len1, len, pt);

	if (nresult->kind() == UNDEF) {
		Node* env = Nil->Cons(Nil);

		SetEnv(env, nresult);
		((Undef*)(nresult->Val()))->Set(pt.val);

		PushStack(cx, Nil, Nil, env);
	}
		
	return rn;
}

int callcombipred(Node* out[], int len2, paramtype &pt)
{
	int	i;
	int	rn = 1;
	Node*	n = Nil;

	for (i=len2-1; i >= 0; i--) {
		n = Cons(out[i], n);
	}

	Context* cx = pt.cx;
	List* module = pt.module;
	Node* nvar = pt.nvar;
	Node* gl = pt.gl;
	Node* nlist = pt.nlist;
	
// PrintNode(n); //dummy for test

	Context *cx2 = new Context(module, cx->modulename);
	cx2->ioin = cx->ioin;
	cx2->ioout = cx->ioout;
	cx2->tokenflag = cx->tokenflag;
	cx2->token = cx->token;

	Node* env = Nil->Cons(Nil);
	SetEnv(env, nvar);
	((Undef*)nvar)->Set(n);

	cxpush(cx2, env);
	cxpush(cx2, gl);
	cxpush(cx2, nvar);
	cxpush(cx2, nlist);
	cxpush(cx2, n);
	cxpush(cx2, pt.val);
				
	rn = Unify(cx2, gl, module);

	if (rn == 1) {
		if (cx->tokenflag) cx->token = cx2->token;

		Node*	res;
		if (gl->Car()->Car()->Eq(mka("unify"))
				|| gl->Car()->Car()->Eq(mka("obj"))) {
			res = gl->Car()->Cdr()->Cdr()->Car()->Cdr()->Car()->Val();
		} else {
			res = gl->Car()->Cdr()->Car()->Val();
		}
		
		pt.val = Append(pt.val, MkList(res));
	}
		
	cx2->Clear();

	cxpop(cx2);
	cxpop(cx2);
	cxpop(cx2);
	cxpop(cx2);
	cxpop(cx2);
	cxpop(cx2);
		
	UnsetEnv(env);
		
	delete cx2;
	cx2 = 0;

	return rn;
}

int setcombidata(Node* data[], Node* out[], 
			int p, int n, int len1, int len2, paramtype &pt)
{
	int	i;
	int	rn = 1;

	if (n <= 0) {
		rn = callcombipred(out, len2, pt);

		if (BreakFlag) {
			BreakFlag = 0;
			rn = -1;
		}
		if (ThrowFlag) {
			rn = 1;
		}

		return rn;
	}

	rn = 1;
	for (i=p; i<len1; i++) {
		out[len2-n] = data[i];
		rn = setcombidata(data, out, i+1, n-1, len1, len2, pt);
		if (rn != 1) {
			break;
		}
	}
	return rn;
}


int combination(Node* data[], Node* out[], int len1, int len2, paramtype &pt)
{
	int i;
	int rn = 1;

	for (i=0; i<len1; i++) {
		out[0] = data[i];
		rn = setcombidata(data, out, i+1, len2-1, len1, len2, pt);

		if (rn != 1) {
			break;
		}
		if (BreakFlag) {
			BreakFlag = 0;
			break;
		}
		if (ThrowFlag) {
			break;
		}
	}
	return rn;
}

int generator_combinationAll(Context* cx, Node* goalscar, List* module)
{

	int i;
	
	int glen = ListLength(goalscar->Cdr());
	if (glen < 2) {
		syserr("usage: ::generator <combinationAll [RESULT_VAR] (VAR LIST [LEN]) PRED ... >\n");
		return 0;
	}

	Node* g    = goalscar->Cdr()->Val();

	int rn;

	Node* nresult = g->Car()->Val();
 	if ((rn = FuncArg(cx, nresult, goalscar, module)) <= 0) {
		syserr("combinationAll: failed in the evaluation of the argument. \n");
		return 0;
	}

	if (nresult->kind() != UNDEF) {
		nresult = Nil;
	} else {
		g = g->Cdr();
	}

	Node* nglist = g->Car()->Val();
 	if ((rn = FuncArg(cx, nglist, goalscar, module)) <= 0) {
		syserr("combinationAll: failed in the evaluation of the argument. \n");
		return 0;
	}

	if ((nglist->kind() != LIST) && (nglist != Nil)) {
		syserr("usage: ::generator <combinationAll [RESULT_VAR] (VAR LIST [LEN]) PRED ... >\n");
		return 0;
	}

	int glistlen = ListLength(nglist);
	if ((glistlen != 2) && (glistlen != 3)) {
		syserr("usage: ::generator <combinationAll [RESULT_VAR] (VAR LIST [LEN]) PRED ... >\n");
		return 0;
	}

	Node* nvar = nglist->Car()->Val();
	if (nvar->kind() != UNDEF) {
		syserr("usage: ::generator <combinationAll [RESULT_VAR] (VAR LIST [LEN]) PRED ... >\n");
		return 0;
	}
		
	nglist = nglist->Cdr();
	Node* nlist = nglist->Car()->Val();
	if ((nlist->kind() != LIST) && (nlist != Nil)) {
		syserr("usage: ::generator <combinationAll [RESULT_VAR] (VAR LIST [LEN]) PRED ... >\n");
		return 0;
	}

	long long len;
	int len1 = ListLength(nlist);
	if (glistlen != 3) {
		len = len1;
	} else {
		nglist = nglist->Cdr();
		Node* nlen = nglist->Car()->Val();
		if (nlen->kind() != ATOM) {
			syserr("usage: ::generator <combinationAll [RESULT_VAR] (VAR LIST [LEN]) PRED ... >\n");
			return 0;
		}

		if (!((Atom*)nlen)->toInt(len)) {
			syserr("usage: ::generator <combinationAll [RESULT_VAR] (VAR LIST [LEN]) PRED ... >\n");
			return 0;
		}
	}
	
	if (len <= 0) {
		syserr("usage: ::generator combinationAll : LEN is 0 or less.\n");
		return 0;
	}		


	if (len > len1) {
		syserr("usage: ::generator combinationAll : LEN is longer than the length of LIST.\n");
		return 0;
	}

	Node*	data[len1];
	Node*	out[len];
	for (i = 0; i < len1; i++) {
		data[i] = nlist->Car();
		nlist = nlist->Cdr();
	}

	struct paramtype pt;
	pt.cx = cx;
	pt.module = module;
	pt.nvar = nvar;
	pt.gl = g->Cdr();
	pt.val = Nil;
	pt.nlist = nlist;
	
	rn = combination(data, out, len1, len, pt);

	if (nresult->kind() == UNDEF) {
		Node* env = Nil->Cons(Nil);

		SetEnv(env, nresult);
		((Undef*)(nresult->Val()))->Set(pt.val);

		PushStack(cx, Nil, Nil, env);
	}
		
	return rn;
}



