// GroupLevel.code

#include <code\programmer\instruments.txt>



var arhivPriznakov(){
	R={};
	ap1={"typeof(x)","((set)x).size()","x.size()"};
	i=0;
	for(;i<ap1.size();++i)R.push(PROGRAM(ap1[i]));
	return R;
}



var indexesForGroups(var A){
	groups=((vector)((set)A));
	indexes={};
	i=0;
	for(;i<A.size();++i)indexes.push(groups.find(A[i]));
	return indexes;
}



bool sovpaloIndexesGroups(var A,var B,var C){
	groups=((vector)((set)A));
	i=0;
	for(;i<groups.size();++i){
		first=1;
		X=0;
		j=0;
		for(;j<B.size();++j)if(A[j]==groups[i]){
			if(first)X=B[j];
			first=0;
			if(X!=B[j])return 0;
			};
		}
	if(isset(C))return 1;
	return sovpaloIndexesGroups(B,A,1);
}



bool ProbaPriznakovFor1var(var sys){
	A=indexesForGroups(sys->outputs);
	priznaki=arhivPriznakov(1);
	priznakiOK={};
	Results={};
	i=0;
	for(;i<priznaki.size();++i){
		P=priznaki[i];
		name=P.getNames(1)[0];
		P.replace(name,"Q");
		Result={};
		j=0;
		for(;j<sys->inputs.size();++j){
			Q=(sys->inputs)[j];
			Result.push(eval(P));
			}
		B=indexesForGroups(Result);
		if(sovpaloIndexesGroups(A,B)){
			priznakiOK.push(P);
			Results.push(Result);
			}
		}
	if(priznakiOK.size()){
		i=0;
		if(priznakiOK.size()>1){
			vel={};
			i=0;
			for(;i<priznakiOK.size();++i)vel.push(priznakiOK[i].getPower(1));
			i=vel.find(vel.min());
			}
		P=priznakiOK[i];
		P.replace("Q",(sys->namesinput)[0]);
		Result=Results[i];
		addResultOKGroup(sys,Result,P);
		return 1;
		}
	return 0;
}



void addResultOKGroup(var sys,var Result,var P){
	S=((vector)((set)Result));
	if(Result==sys->outputs){
		P2=PROGRAM("return #");
		P2.setSub({0},P);
		P=P2;
		S={};
		}
	if(S.size()==2){
		X=S[0];
		i=Result.find(X);
		if(typeof(X)=="bool"){
			P2=PROGRAM("return !#?#:#");
			if(X)P2.setSub({0,0},P);else P2.setSub({0,0,0},P);
			}else {
			if({"string","vector"}.find(typeof(X))>=0)X=X.export();
			P2=PROGRAM("return #=="+X+"?#:#");
			P2.setSub({0,0,0},P);
			}
		X=(sys->outputs)[i];
		if({"string","vector"}.find(typeof(X))>=0)X=X.export();
		P2.setSub({0,1},PROGRAM(X));
		X=S[1];
		i=Result.find(X);
		X=(sys->outputs)[i];
		if({"string","vector"}.find(typeof(X))>=0)X=X.export();
		P2.setSub({0,2},PROGRAM(X));
		P=P2;
		}
	if(S.size()>2){
		str="i="+S.export()+".find("+((string)P)+");";
		R={};
		i=0;
		for(;i<S.size();++i)R.push((sys->outputs)[Result.find(S[i])]);
		str+="return "+R.export()+"[i];";
		P=PROGRAM(str);
		}
	Xfunct={inputnames:sys->namesinput,name:(*sys)["имя программы"],outputnames:(sys->namesoutput)[0],program:P};
	sys->functions.push(Xfunct);
}



bool ProbaDetailsContact(var sys){
	A=indexesForGroups(sys->outputs);
	sovpaloList={};
	i=0;
	for(;i<sys->newParams.size();++i){
		B=indexesForGroups((sys->newParams)[i]);
		if(sovpaloIndexesGroups(A,B))sovpaloList.push(i);
		}
	if(sovpaloList.empty())return 0;
	ROK=0;
	nametype="";
	i=0;
	for(;i<sys->outputs.size();++i){
		if(!i)nametype=typeof((sys->outputs)[i]);
		if(nametype!=typeof((sys->outputs)[i]))nametype="";
		}
	i=0;
	for(;i<sovpaloList.size();++i){
		P=(sys->newParamsF)[sovpaloList[i]];
		if(nametype!=""){
			P2=PROGRAM("("+nametype+")#");
			P2.setSub({0},P);
			P3=P2;
			names=P3.getNames();
			numbersInPF={};
			j=0;
			for(;j<names.size();++j){
				t=sys->newParamsF.find(PROGRAM(names[j]));
				if(t<0)break;
				numbersInPF.push(t);
				ReplaceVars(&P3,names[j],PROGRAM("V["+j+"]"));
				}
			if(j==names.size()){
				ok=1;
				j=0;
				for(;j<sys->outputs.size();++j){
					V={};
					t=0;
					for(;t<numbersInPF.size();++t)V.push((sys->newParams)[numbersInPF[t]][j]);
					ok=(sys->outputs)[j]==eval(P3);
					if(!ok)break;
					}
				if(ok){
					P3=PROGRAM("return #");
					P3.setSub({0},P2);
					Xfunct={inputnames:sys->namesinput,name:(*sys)["имя программы"],outputnames:(sys->namesoutput)[0],program:P3};
					sys->functions.push(Xfunct);
					ROK=1;
					continue;
					}
				}
			}
		addResultOKGroup(sys,(sys->newParams)[sovpaloList[i]],P);
		ROK=1;
		}
	return ROK;
}



bool GroupLevel(var sys){
	if(sys->namesinput.size()==1){if(ProbaPriznakovFor1var(sys))return 1;}
	if(ProbaDetailsContact(sys))return 1;
	return 0;
}


// GroupLevel.code	:-|