// comparefunctions.code




bool CompareFunctions(var f1,var 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(var p_tree1,var p_tree2,var p_tablenamesa,var p_tablenamesb,var 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;
		i=0;
		for(;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;
	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"){
		i=1;
		for(;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;
	i=0;
	for(;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(var tree,var p_S,var 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(var command1,var command2,var p_tablenamesa,var p_tablenamesb,var 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(var p_tree1,var p_tree2,var p_tablenamesa,var p_tablenamesb,var p_EqualeFunctions){
	if(p_tree1->typeof()!="Sequence"||p_tree2->typeof()!="Sequence")return 0;
	S1={};
	n=p_tree1->getRowSize();
	i=0;
	for(;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();
	i=0;
	for(;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;
	perebroska={};
	i=0;
	for(;i<n;++i){
		j=0;
		for(;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);
		}
	i=0;
	for(;i<n;++i){
		pos=perebroska.find(i);
		if(i==pos)continue;
		useVars={};
		notuseVars={};
		j=i;
		for(;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;
		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];
		j=pos-1;
		for(;j>=i;--j){
			perebroska[j+1]=perebroska[j];
			S2[j+1]=S2[j];
			}
		perebroska[i]=i;
		S2[i]=P;
		}
	return 1;
}


// comparefunctions.code	:-|