// comparefunctions.txt


bool CompareFunctions(f1,f2){
	if(typeof(f1)=="string")f1=getFunction(f1);
	if(typeof(f2)=="string")f2=getFunction(f2);
	if(typeof(f1)!="function" || typeof(f2)!="function")return 0;
	if(f1==f2)return 1;
	tablenamesa=(map)0;
	tablenamesb=(map)0;
	f1.Root();
	f2.Root();
	tree1=f1.export();
	tree2=f2.export();
	EqualeFunctions={f1.getName(),f2.getName()};
	return CompareFunctionsTree(&tree1,&tree2,&tablenamesa,&tablenamesb,&EqualeFunctions);
}


bool CompareFunctionsTree(p_tree1,p_tree2,p_tablenamesa,p_tablenamesb,p_EqualeFunctions){
	if(typeof(*p_tree1)!="program" || typeof(*p_tree2)!="program")return (typeof(*p_tree1)==typeof(*p_tree2));
	type=p_tree1->typeof();
	if(type!=p_tree2->typeof()){
		if(p_tree1->getNames(1).empty() && p_tree2->getNames(1).empty()){
			return eval(*p_tree1)==eval(*p_tree2);
			}
		n1={"<",">","<=",">="}.find(type);
		n2={">","<",">=","<="}.find(p_tree2->typeof());
		if(n1<0 || n1!=n2){
			ok=0;
			command1=*p_tree1;
			command2=*p_tree2;
			if(command1.typeof()=="!#"){ok=1;command1=command1.getSub({0});} else
			if(command2.typeof()=="!#"){ok=1;command2=command2.getSub({0});}
			if(ok)return CompareAntiConditions(command1,command2,p_tablenamesa,p_tablenamesb,p_EqualeFunctions);
			return 0;
			}
		S1=getListTransformVarsInTree(p_tree1->getSub({0}));
		S2=p_tree1->getSub({1}).getNames(1);
		S1.notU(S2);
		if(S1.size())return 0;
		for(i=0;i<2;++i){
			command1=p_tree1->getSub({1-i});
			command2=p_tree2->getSub({i});
			ok=CompareFunctionsTree(&command1,&command2,p_tablenamesa,p_tablenamesb,p_EqualeFunctions);
			if(!ok)return 0;
			}
		return 1;
		}
	if(type=="Sequence"){
		n1=p_tree1->getRowSize();
		n2=p_tree2->getRowSize();
		pos1=pos2=0;
		ok=1;
		tablenamesa=*p_tablenamesa;
		tablenamesb=*p_tablenamesb;
		while(pos1<n1 && pos2<n2){
			command1=p_tree1->getSub({pos1});
			command2=p_tree2->getSub({pos2});
			if(typeof(command1)!="program"){
				++pos1;
				continue;
				}
			if(typeof(command2)!="program"){
				++pos2;
				continue;
				}
			if({"var","digit","#","Label"}.in(command1.typeof())){
				++pos1;
				continue;
				}
			if({"var","digit","#","Label"}.in(command2.typeof())){
				++pos2;
				continue;
				}
			if(!CompareFunctionsTree(&command1,&command2,p_tablenamesa,p_tablenamesb,p_EqualeFunctions)){
				ok=0;
				break;
				}
			++pos1;
			++pos2;
			}
		if(pos1<n1 || pos2<n2)ok=0;
		if(!ok){
			*p_tablenamesa=tablenamesa;
			*p_tablenamesb=tablenamesb;
			return CompareSequences(p_tree1,p_tree2,p_tablenamesa,p_tablenamesb,p_EqualeFunctions);
			}
		return 1;
		}
	n1=p_tree1->getRowSize();
	n2=p_tree2->getRowSize();
	if(n1!=n2)return 0;
	//trace(*p_tree1);trace(*p_tree2);trace(type);//
	if(n1==0){
		if(type=="var"){
			s1=(string)(*p_tree1);
			s2=(string)(*p_tree2);
			ok=2;
			if(p_tablenamesa->find(s1))ok=(*p_tablenamesa)[s1]==s2; else
			if(p_tablenamesb->find(s2))ok=(*p_tablenamesb)[s2]==s1;
			if(ok<2)return ok;
			(*p_tablenamesa)[s1]=s2;
			(*p_tablenamesb)[s2]=s1;
			return 1;
			}
		return *p_tree1==*p_tree2;
		}
	good=1;
	i=0;
	if(type=="function"){
		i=1;
		command1=p_tree1->getSub({0});
		command2=p_tree2->getSub({0});
		xtype=command1.typeof();
		if(xtype!=command2.typeof())return 0;
		ok=0;
		if(xtype=="var"){
			ok=(command1==command2);
			if(!ok){
				a=p_EqualeFunctions->in((string)command1);
				b=p_EqualeFunctions->in((string)command2);
				if(a&&b)ok=1;
				}
			}
		if(xtype=="." || xtype=="->"){
			if(command1.getSub({1})!=command2.getSub({1}))return 0;
			command1=command1.getSub({0});
			command2=command2.getSub({0});
			ok=CompareFunctionsTree(&command1,&command2,p_tablenamesa,p_tablenamesb,p_EqualeFunctions);
			}
		if(!ok)return 0;
		}
	for(;i<n1 && good;++i){
		command1=p_tree1->getSub({i});
		command2=p_tree2->getSub({i});
		ok=CompareFunctionsTree(&command1,&command2,p_tablenamesa,p_tablenamesb,p_EqualeFunctions);
		if(!ok)good=0;
		}
	if(good)return 1;
	if(type=="if"){
		for(i=1;i<3;++i){
			command1=p_tree1->getSub({3-i});
			command2=p_tree2->getSub({i});
			ok=CompareFunctionsTree(&command1,&command2,p_tablenamesa,p_tablenamesb,p_EqualeFunctions);
			if(!ok)return 0;
			}
		command1=p_tree1->getSub({0});
		command2=p_tree2->getSub({0});
		return CompareAntiConditions(command1,command2,p_tablenamesa,p_tablenamesb,p_EqualeFunctions);
		}
	isRevers={"+","*","&&","||","==","!=","&","|"}.in(type);
	if(!isRevers)return 0;
	S1=getListTransformVarsInTree(p_tree1->getSub({0}));
	S2=p_tree1->getSub({1}).getNames(1);
	S1.notU(S2);
	if(S1.size())return 0;
	for(i=0;i<2;++i){
		command1=p_tree1->getSub({1-i});
		command2=p_tree2->getSub({i});
		ok=CompareFunctionsTree(&command1,&command2,p_tablenamesa,p_tablenamesb,p_EqualeFunctions);
		if(!ok)return 0;
		}
	return 1;
}



vector getListTransformVarsInTree(tree,p_S,isR){
	if(!isset(p_S)){
		S={};
		getListTransformVarsInTree(tree,&S,0);
		return S;
		}
	if(typeof(tree)!="program")return;
	n=tree.getRowSize();
	type=tree.typeof();
	if((type=="." || type=="->") && isR){
		getListTransformVarsInTree(tree.getSub({0}),p_S,1);
		return;
		}
	if(type=="var" && isR==1)p_S->push((string)tree);
	i=0;
	if(type=="AccessArray" && isR){
		getListTransformVarsInTree(tree.getSub({0}),p_S,1);
		i=1;
		isR=0;
		}
	if(type=="function"){
		getListTransformVarsInTree(tree.getSub({i}),p_S,2);
		i=1;
		}
	if({"=","+=","-=","*=","/=","++#","--#","#++","#--","*#","&#"}.in(type)){
		getListTransformVarsInTree(tree.getSub({i}),p_S,1);
		i=1;
		}
	if(type=="?"){
		getListTransformVarsInTree(tree.getSub({i}),p_S,0);
		i=1;
		}
	for(;i<n;++i)getListTransformVarsInTree(tree.getSub({i}),p_S,isR);
}



bool CompareAntiConditions(command1,command2,p_tablenamesa,p_tablenamesb,p_EqualeFunctions){
	ok=0;
	if(command1.typeof()=="!#"){ok=1;command1=command1.getSub({0});} else
	if(command2.typeof()=="!#"){ok=1;command2=command2.getSub({0});}
	if(ok)return CompareFunctionsTree(&command1,&command2,p_tablenamesa,p_tablenamesb,p_EqualeFunctions);
	a={"&&","||"}.find(command1.typeof());
	b={"||","&&"}.find(command2.typeof());
	if(a>=0){
		if(a==b){
			ok=CompareAntiConditions(command1.getSub({0}),command2.getSub({0}),p_tablenamesa,p_tablenamesb,p_EqualeFunctions);
			if(ok)ok=CompareAntiConditions(command1.getSub({1}),command2.getSub({1}),p_tablenamesa,p_tablenamesb,p_EqualeFunctions);
			if(ok)return 1;
			S1=getListTransformVarsInTree(command1.getSub({0}));
			S2=command1.getSub({1}).getNames(1);
			S1.notU(S2);
			if(S1.size())return 0;
			ok=CompareAntiConditions(command1.getSub({1}),command2.getSub({0}),p_tablenamesa,p_tablenamesb,p_EqualeFunctions);
			if(ok)ok=CompareAntiConditions(command1.getSub({0}),command2.getSub({1}),p_tablenamesa,p_tablenamesb,p_EqualeFunctions);
			return ok;
			}
		return 0;
		}
	a={"<",">","==","!=","<=",">="}.find(command1.typeof());
	if(a<0)return 0;
	z={">=","<=","!=","==",">","<"}[a];
	P=PROGRAM("#"+z+"#");
	P.setSub({0},command1.getSub({0}));
	P.setSub({1},command1.getSub({1}));
	return CompareFunctionsTree(&P,&command2,p_tablenamesa,p_tablenamesb);
}



bool CompareSequences(p_tree1,p_tree2,p_tablenamesa,p_tablenamesb,p_EqualeFunctions){
	if(p_tree1->typeof()!="Sequence" || p_tree2->typeof()!="Sequence")return 0;
	S1={};
	n=p_tree1->getRowSize();
	for(i=0;i<n;++i){
		command=p_tree1->getSub({i});
		ok={"return","OFF"}.in(command.typeof());
		if(getListTransformVarsInTree(command).size() || ok)S1.push(command);
		}
	S2={};
	n=p_tree2->getRowSize();
	for(i=0;i<n;++i){
		command=p_tree2->getSub({i});
		ok={"return","OFF"}.in(command.typeof());
		if(getListTransformVarsInTree(command).size() || ok)S2.push(command);
		}
	n=S1.size();
	if(n!=S2.size())return 0;
	//trace(S1.export());//
	perebroska={};
	for(i=0;i<n;++i){
		for(j=0;j<n;++j)if(!perebroska.in(j)){
			tablenamesa=*p_tablenamesa;
			tablenamesb=*p_tablenamesb;
			ok=CompareFunctionsTree(&S1[j],&S2[i],&tablenamesa,&tablenamesb,p_EqualeFunctions);
			if(ok)break;
			}
		if(j>=n)return 0;
		*p_tablenamesa=tablenamesa;
		*p_tablenamesb=tablenamesb;
		perebroska.push(j);
		}
	for(i=0;i<n;++i){
		pos=perebroska.find(i);
		if(i==pos)continue;
		useVars={};
		notuseVars={};
		for(j=i;j<pos;++j){
			UseS=getListTransformVarsInTree(S2[j]);
			notUseS=(vector)(set)S2[j].getNames(1)-UseS;
			useVars+=UseS;
			notuseVars+=notUseS;
			}
		UseS=getListTransformVarsInTree(S2[pos]);
		notUseS=(vector)(set)S2[pos].getNames(1)-UseS;
		/*
		trace("-----------------");
		trace(UseS.export(),notUseS.export());
		trace(useVars.export(),notuseVars.export());
		*/
		R=UseS;
		R.notU(useVars);
		RA=UseS;
		RA.notU(notuseVars);
		RB=notUseS;
		RB.notU(useVars);
		ok=RA.empty() && RB.empty() && R.empty();
		if(!ok)return 0;
		P=S2[pos];
		for(j=pos-1;j>=i;--j){
			perebroska[j+1]=perebroska[j];
			S2[j+1]=S2[j];
			}
		perebroska[i]=i;
		S2[i]=P;
		}
	return 1;
}


// comparefunctions.txt	:-|