//-------------------------------------------------------------------------------------------------------------------------
var Subject(X){
	if(typeof(X)!="map")return #;
	Q={"table","unknovn","names"} - X.getKeys();
	if(Q.size())return #;
	X+={date:{},vars:(map)0,NamesDano:{}};
	//trace(X.export());

	X.NamesDano=X.names-{X.unknovn};
	//traceX(&X,X.NamesDano[0]);traceX(&X,X.unknovn);

	//controlPeregruzkaNaVxode(&X);

	for(i=0;i<X.NamesDano.size();++i)probaProcentaMod(&X,X.unknovn,X.NamesDano[i]);
	if(X.vars.isset(X.unknovn))return buildORDER(&X);

	posovpadaloPoisk(&X,X.unknovn);
	if(X.vars.isset(X.unknovn))return buildORDER(&X);

	return #;
}



string buildORDER(X){
	Need=(set)0;
	S={X->unknovn};
	while(S.size()){
		x=S.pop();
		Need.insert(x);
		if(!X->vars.isset(x))continue;
		Q=getListAllVars(PROGRAM(X->vars[x]));
		S+=Q;
		}
	Need=(vector)Need;
	S=Need-X->vars.getKeys();
	Need-=S;
	R="";
	while(Need.size()){
		for(i=0;i<Need.size();++i){
			x=Need[i];
			Q=getListAllVars(PROGRAM(X->vars[x]))-S;
			if(!Q.size())break;
			}
		S.push(x);
		s=x+"="+X->vars[x]+";";
		R+=s;
		Need.erase(i);
		}
	return R;
}



//Взять список всех переменных
vector getListAllVars(comand){
	S=(set){};
	acces={};
	accesf={};
	isf=0;
	while(1){
		p=comand.getRowSize(acces);
		x=comand.getSub(acces);
		if(!p){
			if(x.typeof()=="var"){
				S.insert((string)x);
				}
			}
		acces.push(p);
		isf=(x.typeof()=="function");
		accesf.push(isf);
		n=acces.size()-1;
		p=acces[n];
		isf=accesf[n];
		if(isf)if(p==1)--p;
		while(!p){
			acces.pop_back();
			accesf.pop_back();
			--n;
			if(n<0)break;
			p=acces[n];
			isf=accesf[n];
			if(isf)if(p==1)--p;
			}
		if(!p)break;
		--p;
		acces[n]=p;
		}
	return (vector)S;
}




//-------------------------------------------------------------------------------------------------------------------------
void probaProcentaMod(X,unknovn,knovn){
	traceX(X,knovn);
	traceX(X,unknovn);
	namep=X->names.newName();
	setrow(X,namep+"="+knovn+"-"+unknovn);
	//traceX(X,namep);//
	a=(vector)(set)getRowX(X,namep);
	deleteRow(X,namep);
	v={};
	for(i=1;i<a.size();++i)v.push(a[i]-a[i-1]);
	X->date=(vector)(set)(X->date+v);
	//trace(X->date.export());//
	namez=X->names.newName();
	for(i=0;i<X->date.size();++i){
		setrow(X,namez+"="+knovn+"%"+X->date[i]);
		traceX(X,namez);//
		if(compare2Rows(X,namez,unknovn)){
			deleteRow(X,namez);
			X->vars[unknovn]=knovn+"%"+X->date[i];
			return;
			}
		namea=X->names.newName();
		setrow(X,namea+"="+namez+"-"+unknovn);
		u=(getSortUniqueValue(X,namea)-{0})[0];
		deleteRow(X,namea);
		if(X->date[i]==(u<0?-u:u))continue;
		s="("+knovn+(u>0?("-"+u):("+"+(-u)))+")%"+X->date[i];
		setrow(X,namez+"="+s);
		traceX(X,namez);//
		if(compare2Rows(X,namez,unknovn)){
			deleteRow(X,namez);
			X->vars[unknovn]=s;
			return;
			}
		}
}


bool compare2Rows(X,A,BB){
	RowA=getRowX(X,A);
	RowBB=getRowX(X,BB);
	if(RowA==RowBB)return 1;
	if(X->NamesDano.size()!=1)return 0;
	si=RowA.size();
	nu=X->names.find(X->NamesDano[0]);
	Gipotizes=(set)0;
	ProblemV={};
	for(i=0;i<si;++i)if(RowA[i]!=RowBB[i]){
		for(t=0;t<si;++t)
		if(X->table[nu][t]==X->table[nu][i])
		if(RowA[i]==RowBB[t]){
			Gipotizes.insert({RowBB[i],RowBB[t]});
			break;
			}
		if(t>=si)ProblemV.push(i);
		}
	for(v=0;v<ProblemV.size();++v){
		u=RowBB[ProblemV[v]];
		ok=0;
		for(i=0;i<Gipotizes.size();++i)
			if(Gipotizes[i][0]==u)
			if(Gipotizes[i][1]==RowA[ProblemV[v]])ok=1;
		if(!ok)return 0;
		}
	return 1;
}



//-------------------------------------------------------------------------------------------------------------------------
var getSortUniqueValue(X,name){
	S=getRowX(X,name);
	m=(map)0;
	for(i=0;i<S.size();++i)if(m.isset(S[i]))++m[S[i]]; else m[S[i]]=1;
	S={};
	K=m.getKeys();
	for(i=0;i<K.size();++i)S.push(m[K[i]]);
	S.sort();
	R={};
	for(t=S.size()-1;t>=0;--t){
		k=0;
		for(i=0;i<K.size();++i)if(m[K[i]]==S[t]){k=i;break;}
		R.push(K[k]);
		m.erase(K[k]);
		K.erase(k);
		}
	return R;
}



void deleteRow(X,name){
	i=X->names.find(name);
	if(i>=0){
		X->table.erase(i);
		X->names.erase(i);
		X->vars.erase(name);
		}
}



void setrow(X,proga){
	if(typeof(proga)=="string")proga=PROGRAM(proga);
	if(typeof(proga)!="program")return;
	t=proga.typeof();
	if(t!="=" && t!="==")return;
	newname=(string)proga.getSub({0});
	deleteRow(X,newname);
	proga=proga.getSub({1});
	n=X->table[0].size();
	R={};
	for(i=0;i<n;++i){
		cp=proga;
		for(k=0;k<X->table.size();++k)ReplaceVars(&cp,X->names[k],X->table[k][i]);
		R.push(eval(cp));
		}
	X->table.push(R);
	X->names.push(newname);
	X->vars[newname]=proga;
}



// заменит в программе все переменные ho на выражение na
void ReplaceVars(var*proga,string ho,var na){
	if(typeof(na)!="program")na=PROGRAM((string)na);
	acces={};
	while(1){
		p=proga->getRowSize(acces);
		if(proga->typeof(acces,"name")==ho)proga->typeof(acces,"name",na);
		if(!p){
			x=proga->getSub(acces);
			if(ho==x)proga->setSub(acces,na);
			}
		acces.push(p);
		n=acces.size()-1;
		p=acces[n];
		while(!p){
			acces.pop_back();
			--n;
			if(n<0)break;
			p=acces[n];
			}
		if(!p)break;
		--p;
		acces[n]=p;
		}
}


var getRowX(X,name){
	t=X->names.find(name);
	if(t<0)return;
	return X->table[t];
}


void traceX(X,name){
	s=name+": ";
	p=getRowX(X,name);
	if(typeof(p)=="vector")s+=p.export();
	if(X->vars.isset(name))s+="   "+X->vars[name];
	trace(s);
}


//-------------------------------------------------------------------------------------------------------------------------
void posovpadaloPoisk(X,unknovn){
	nu=X->names.find(unknovn);
	mi=mt=0;
	for(i=0;i<X->names.size();++i)if(i!=nu){
		t=sovpadaetV(X->table[i],X->table[nu]);
		if(t>mt){mt=t;mi=i;}
		}
	if(!mt)return;
	trace("посовпадало елементов в двух рядах: "+mt);
	S=W=(set)0;
	for(i=0;i<X->table[nu].size();++i){
		S.insert({X->table[mi][i],X->table[nu][i]});
		W.insert(X->table[mi][i]);
		}
	S=(vector)S;
	W=(vector)W;
	//trace(S);//
	IDOK=IDnotOK={};
	for(i=0;i<W.size();++i){
		n=0;
		for(t=0;t<S.size();++t)if(S[t][0]==W[i])++n;
		if(n==1)IDOK.push(W[i]); else IDnotOK.push(W[i]);
		}
	trace(IDOK);trace(IDnotOK);//
	nok=1;
	if(IDOK.size()!=IDnotOK.size()){
		nok=2;
		if(IDOK.size()<IDnotOK.size())nok=1;
		}
	Nam=X->names-{unknovn,X->names[mi]};
	progaNOK="";
	for(i=0;i<IDnotOK.size();++i){
		x=IDnotOK[i];
		Q={};
		for(t=0;t<S.size();++t)if(S[t][0]==x)Q.push(S[t][1]);
		trace(Q);//
		for(t=0;t<Nam.size();++t){
			R=getRowX(X,Nam[t]);
			table={};
			for(p=0;p<Q.size();++p){
				U={};
				for(ii=0;ii<X->table[nu].size();++ii)if(X->table[nu][ii]==Q[p])U.push(R[ii]);
				table.push((vector)(set)U);
				}
			trace(table.export());//
			ce=0;
			if(table.size()==2){
				if(table[0].size()==1)ce=1;
				if(table[1].size()==1)ce=2;
				}
			if(ce)if(table[2-ce].find(table[ce-1][0])<0){
				s=Nam[t]+"=="+table[ce-1][0];
				s+="?"+Q[ce-1]+":"+Q[2-ce];
				if(progaNOK=="")progaNOK=s;
				if(progaNOK.size()>s.size())progaNOK=s;
				continue;
				}
			if(table.size()==2)
			if( ((set)(table[0]+table[1])).size()==table[0].size()+table[1].size() ){
				ce=(table[0].size()>table[1].size());
				s=table[ce].export()+".find("+Nam[t]+")>=0";
				s+="?"+Q[ce]+":"+Q[!ce];
				if(progaNOK=="")progaNOK=s;
				if(progaNOK.size()>s.size())progaNOK=s;
				}

			}
		}
	if(progaNOK=="")return;
	if((nok==1?IDOK:IDnotOK).size()==1){
		n=(nok==1?IDOK:IDnotOK)[0];
		s=X->names[mi]+"=="+n;
		for(t=0;t<S.size();++t)if(S[t][0]==n)break;
		s+="?"+S[t][1]+":("+progaNOK+")";
		namea=X->names.newName();
		setrow(X,namea+"="+s);
		//traceX(X,namea);//
		if(getRowX(X,namea)==getRowX(X,unknovn)){
			deleteRow(X,namea);
			X->vars[unknovn]=s;
			return;
			}
		}

}



int sovpadaetV(A,B){
	n=A.size();
	if(B.size()<n)n=B.size();
	r=0;
	for(i=0;i<n;++i)if(A[i]==B[i])++r;
	return r;
}

