//CHECKJS  D:\imt\js\equiz\js\ccalc.js 2/11/2003 11:28:45 AM
/*
ccalc.js calculates the number of each element in a formula
bh 10:46 AM 2/11/2003
*/

//gifdir is defined in quiz.js
//and must be overridden if Hess is used from another directory

minussign="&#150"
O2CUTOFF=0.1  //fraction of time O2/H2/H2O
sdebug=""

     sdigits=" 0123456789"

Equation=new Array()
datatext=""

function getEqnCoef(Eqn,mol){
	return Eqn.list[mol].n
}

function getReactant(Eqn){
 var S=new Array()
 for(var mol in Equation.list){
	if(Equation.list[mol].n<0)S[S.length]=mol
 }
 return S[rand(S.length)]
}

function calcDeltaT(Eqn,dUorH_kJ,mol,ngrams,heatcap_JperK,idigits){
 var mw=MW(mol)
 var n=-Eqn.list[mol].n
 var q=dUorH_kJ*1000/n/mw*ngrams 
 var qsur=-q
 var dT=qsur/heatcap_JperK
 return calc_roundoff(dT,idigits)
}  

function calcDeltaVap(mol,swhat){
	//swhat: So, Ho, Go
	return calc_roundoff(getTValue(mol+"(g)",swhat)-getTValue(mol+"(l)",swhat),3)
}

function showEqnTCalc(Eqn,swhat){
	//swhat: So, Ho, Go
	var s=""
	var f=(swhat=="So"?swhat:"Delta_"+swhat+"_f")
	var units=(swhat=="So"?" [J/(mol-K)]":" [kJ/mol]")
	var bder=0
	var bdep=0
	var du=0
	var sout=""
	var s=calcT(Eqn,0,0,swhat)
	sout+=";"+f+"_reactants = "+s+units
	s=calcT(Eqn,1,0,swhat)
	sout+=";"+f+"_products = "+s+units
	sout+=";Delta_"+swhat+" = ("+f+"_products)-("+f+"_reactants)"+units
	return sout
}

function calcT(Eqn,isproducts,icalc,swhat){
	//or calcT(Eqn,swhat)
	//swhat: So, Ho, Go
	if(arguments.length==2){
		swhat=isproducts
		isproducts=0
		icalc=2
	}
	var d=0
	var sum=(icalc?0:"")
	var mol=""
	var tmol=""
	var factor=1
	var f=1
	factor=(icalc==2?-1:1)  //icalc==2 implies doboth
	for(var mol in Eqn.list){if(Eqn.list[mol].n){
		tmol=MolInfo[mol].tref
		if(TData[tmol]){
			f=(Eqn.list[mol].n>0 && (factor==-1||isproducts)
				?factor		//product and looking for products or all
				:Eqn.list[mol].n<0 && (!isproducts)  //reactant and looking for reactant
					?-1
					:0
			)
			if(f){
				if(icalc){
					sum+=f*Eqn.list[mol].n*TData[tmol][swhat]
				}else{
					d=f*Eqn.list[mol].n
					sum+=(sum.length?" + ":"")+(d==1?"":d)+"("+TData[tmol][swhat]+")"
				}
			}
		}else{
			alert("Thermodynamic data are not available for "+mol)
		}
	}}
 return (icalc==2?-sum:sum)
}

function getTValue(tmol,swhat){
	//swhat: So, Ho, Go
	if(!TData[tmol]){
		alert("No thermodynamic information for "+tmol)
		return 0
	}
	return TData[tmol][swhat]
}

function showHessH(Eqn,factor){
	if(arguments.length<2)factor=40
	var hfr=calcT(Eqn,0,1,"Ho")
	var hfp=calcT(Eqn,1,1,"Ho")
	var du=parseFloat(calc_roundoff(hfp-hfr,2))
	return showHess(Eqn.react,getStandardState(Eqn),Eqn.prod,-hfr,hfp,sym("D")+"H<sup>o</sup> = ",factor,du)
}

function showHessBDE(Eqn,factor){
	if(arguments.length<2)factor=40
	var bder=calcBDE(Eqn,0)
	var bdep=calcBDE(Eqn,1)
	var du=bder+(-bdep)	//or du=calcDeltaU(Eqn)
	return showHess(Eqn.react,Eqn.atomlist,Eqn.prod,bder,-bdep,sym("D")+"U = ",factor)
}

function showHess(r,ref,p,dr,dp,label,factor,du){
	if(arguments.length<8)du=dr+dp
	var bar="__________________________________________".substring(0,ref.length>30?5:30-ref.length)
	var s="<table cellpadding=0 cellspacing=0><tr><td align=center colspan=5 nowrap>"+bar+ref+bar+"</td></tr><tr>"
	s+="<td valign=top>"+showHessArrow(dr,factor,"black",dr+" kJ","",r)+"</td>"
	s+="<td align=center valign=bottom>"+showHessArrow(du,factor,"","","",label+"<font color="+(du>0?"red":"blue")+">"+du+" kJ</font>")+"</td>"
	s+="<td align=right valign=top>"+showHessArrow(dp,factor,"black",dp+" kJ","",p)+"</td>"
	s+="</tr></table>"
	return s
}

function showHessArrow(du,factor,color,left,right,below){
	//if(left=="" && below=="")left="&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
	//if(right=="" && below=="")right="&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
	var d=Math.floor(Math.abs(du/factor)-10)
	if(d<1)d=1
	var s="<table cellspacing=0 cellpadding=0>"
	var sline="<tr><td width=75 align=right valign=center>"+left+"</td><td align=center><img src="+gifdir+color+(du>0?"up":"down")+"line.gif width=20 height="+d+"></td><td width=75 valign=center>"+right+"</td></tr>"
	if(du>0){
		s+="<tr><td></td><td align=center><img src="+gifdir+color+"up.gif width=20></td></tr>"
		+sline
	}else if(du<0){
		s+=sline
		+"<tr><td></td><td align=center><img src="+gifdir+color+"down.gif width=20></td></tr>"
	}
	if(below!="")s+="<tr><td width=170 colspan=3 align=center>"+below+"</td></tr>"
	s+="</table>"
	return s
}

function addMolecule(Eqn,mol,idir,msg){
	var n=1
	if(mol=="")return
	if(mol.indexOf(",")>=0){
		var M=mol.split(",")
		for(var i=0;i<M.length;i++)addMolecule(Eqn,M[i],idir,"xxx")
		return
	}
	if(!Eqn.list)Eqn.list=new Array()
	n=parseInt(mol)
	if(isNaN(n))n=1
	if(n>1)mol=mol.substring(n.toString().length,mol.length)
	if(!Eqn.list[mol]){
		Eqn.list[mol]=new Array()
		Eqn.list[mol].n=0
		getchem(MolInfo[mol].BDEref,Eqn.list[mol])
	}
	idir*=n
	for(var i in Eqn.list[mol].Alist){
		if(!Eqn.Alist[i])Eqn.Alist[i]=0
		Eqn.Alist[i]+=Eqn.list[mol].Alist[i]*idir
	}
	Eqn.list[mol].n+=idir
	
}

function balanceEquation(Eqn){
	var x=""
	var h=""
	var o=""
	var n=0
	var ihavex=false
	var C=new Array()
	for(i=0;i<3;i++){
		x=findUnbalanced(Eqn,"","O;H;")
		if(x){
			ihavex=true
			n=findUnbalanced(Eqn,x)
			if(n){
				C=new Array([],[])
				findComponents(Eqn,C)
				if(n>0)addMolecule(Eqn,C[0][0],-n)
				if(n<0)addMolecule(Eqn,C[1][0],-n)
			}
		}
	}
	h=findUnbalanced(Eqn,"H")
	o=findUnbalanced(Eqn,"O")
	//two ways to proceed.
	//if h and o
	var h2oref=0
        var o2ref=0
	var h2ref=0
        for (var i in Eqn.list){
		if(i=="H2"||i=="H2(g)")h2ref=i
		else if(i=="H2O"||i=="H2O(g)"||i=="H2O(l)"||i=="H2O(s)")h2oref=i
		else if(i=="O2"||i=="O2(g)")o2ref=i
	}
	if(h2ref && h2oref && (o || h)) {
		if(o){
			addMolecule(Eqn,h2oref ? h2oref : "H2O",-o,"needs h2o")
		}
		if(h){
			addMolecule(Eqn,h2ref ? h2ref : "H2",-h/2,"needs h2")
		}
		h=findUnbalanced(Eqn,"H")
		o=findUnbalanced(Eqn,"O")
	}
	if(o2ref && h2oref && (o || h)) {
		if(h){
			addMolecule(Eqn,h2oref ? h2oref : "H2O",-h/2,"needs h2o")
			if (Eqn.Alist["H"] == 0)
				addMolecule(Eqn,h2oref ? h2oref : "H2O", h/2,"needs h2o")
		}
		if(o){
			addMolecule(Eqn,o2ref ? o2ref : "O2",-o/2,"needs h2o")
		}
		h=findUnbalanced(Eqn,"H")
		o=findUnbalanced(Eqn,"O")
		if(h){
			addMolecule(Eqn,h2ref ? h2ref : "H2",-h/2,"needs h2")
			h=0
		}
	}

	if(h) {
		addMolecule(Eqn,h2oref ? h2oref : "H2O",-h/2,"needs h2o")
		o=findUnbalanced(Eqn,"O")
	}
	if(o)addMolecule(Eqn,o2ref ? o2ref : "O2",-o/2, "needs o2")
	for(var i=0;i<3;i++){
		for(var mol in Eqn.list){
			n=Eqn.list[mol].n
			if(n!=Math.floor(n)){
				n=Math.abs(n)
				n=n-Math.floor(n)
				for(var mol in Eqn.list)addMolecule(Eqn,mol,Eqn.list[mol].n*(1/n-1))
				break
			}
		}
	}
}

function checkMolecules(slist,isthermo,isRandom,allowNoMolInfo){
	var S=slist.split(",") //mol1 may be x,y
	var n=0
	var mol=""
	var isOK=true
	for(var i=0;i<S.length && isOK;i++)if(S[i]!=""){
		mol=S[i]
		n=parseInt(mol)
		if(isNaN(n))n=1
		if(n>1)mol=mol.substring(n.toString().length,mol.length)
		if(!MolInfo[mol] && allowNoMolInfo) {
			for (var x in TData) if(x == mol || TData[x].formula == mol) {
				MolInfo[mol] = new Array();
				MolInfo[mol].tref = x;
				MolInfo[mol].BDEref = TData[x].formula
				mol = x//.replace(/\,/g,";")
				MolInfo[mol] = new Array();
				MolInfo[mol].tref = x;
				MolInfo[mol].BDEref = TData[x].formula
				break;
			}
		}
		if(!MolInfo[mol] && isthermo && mol.charAt(mol.length-1) ==")") {
			var mol2 = mol.substring(0, mol.lastIndexOf("("))
			if(MolInfo[mol2])copyMolInfo(mol2,mol)
		}
		if(MolInfo[mol]){
			isOK=(!isthermo || MolInfo[mol].tref!="")
		}else{
			alert("MolInfo for "+mol+" was not found. This compound is in the EqnData array, but there is no addMolInfo() entry for it. See bde.xls and bde.js")
			isOK=false
		}
	}
	return isOK
}

function createEquation(Eqn,isthermo,isQcalc,seqn,allowNoMolInfo){
	var isOK=false
	var imol1=0
	var imol2=0
	var mol=""
	var mol1=""
	var mol2=""
	var sout=""
	var ntry=0
	var S=new Array()
	var n=0
	if(arguments.length<2)isthermo=0
	if(arguments.length<3)isQcalc=0
	if(arguments.length<4)seqn=""
	Eqn.list=new Array()
	Eqn.Alist=new Array()
	Eqn.Alist.n=0
	Eqn.PInfo=new Array()

while(!isOK){

if(seqn==""){


	Eqn.list=new Array()
	Eqn.Alist=new Array()
	Eqn.Alist.n=0
	//pick a group row in EqnData
	while(!isOK && (++ntry)<100){

	while(!isOK){
		igroup=randx_y(2,EqnData.length-1)
		isOK=true
	}
	igroup=(Math.random()<O2CUTOFF?1:igroup)//sometimes O2/H2O
	
	//pick two molecules
	imol1=randx_y(1,EqnData[igroup].length-2)
	imol2=randx_y(imol1+1,EqnData[igroup].length-1)
	mol1=getMolecule(igroup,imol1)
	mol2=getMolecule(igroup,imol2)
        isOK=checkMolecules(mol1+","+mol2,isthermo,true,allowNoMolInfo)
	}
	//pick a random direction
	idir=(Math.random()>0.5?1:-1)

}else{
	S=(seqn+">").replace(/ /g,"").replace(/\|/g,">").replace(/\-/g,"").replace(/\+/g,",").split(">")
	if(!checkMolecules(S.join(","),isthermo,false,allowNoMolInfo)){
		alert("Cannot process this equation "+seqn)
		return ""
	}
	mol1=S[0]
	mol2=S[1]
	idir=1
	isOK=true
}
	//add the molecules to the equation
	addMolecule(Eqn,mol1,-idir)
	addMolecule(Eqn,mol2,idir)
	balanceEquation(Eqn)
	//document.write( test(Eqn,"createEquation"))
	isOK=getAtoms(Eqn)
	Eqn.show=showEquation(Eqn,3,isthermo)
	if(isOK && isQcalc)isOK=(Eqn.show.indexOf("(g)")>=0)
}
	Eqn.react=showEquation(Eqn,1,isthermo)
	Eqn.prod=showEquation(Eqn,2,isthermo)
	Eqn.Qcalc=(isQcalc?getQcalc(Eqn):0)
	return Eqn.show
}

function createQEquation(Eqn,pmin,pmax){
 var s=createEquation(Eqn,1,1)
 setRandomQ(Eqn,pmin,pmax)
 return s
}

function createEquationKTDataSet(Eq,t0,t1,npoints){
	var sout="\nT [Kelvin], Keq"
	var dho=calcT(Eq,"Ho")*1000
	var dso=calcT(Eq,"So")
	var dt=Math.floor((t1-t0)/npoints)
	var t=0
	for(var i=0;i<npoints;i++){
		t=t0+i*dt
		sout+="\n"+t+" , "+calc_roundoff(Math.pow(Math.E,-(dho-t*dso)/(8.3145*t)),-3)		 
	}
	//alert(sout)
	datatext=Eq.datatext=sout
	return sout
}

function createKTDataSet(dho,dso,t0,t1,npoints){
	var sout="\nT [Kelvin], Keq"
	dho=dho*1000
	var dt=Math.floor((t1-t0)/npoints)
	var t=0
	for(var i=0;i<npoints;i++){
		t=t0+i*dt
		sout+="\n"+t+" , "+calc_roundoff(Math.pow(Math.E,-(dho-t*dso)/(8.3145*t)),-3)		 
	}
	//alert(sout)
	datatext=sout
	return 1
}


function findComponents(Eqn,C){
	for(var mol in Eqn.list){if(Eqn.list[mol].n){
			if(Eqn.list[mol].n<0)C[0][C[0].length]=mol
			if(Eqn.list[mol].n>0)C[1][C[1].length]=mol
	}}
}


function findUnbalanced(Eqn,swhat,snotwhat){
	var Alist=Eqn.Alist
	if(swhat){
		return(Alist[swhat]?Alist[swhat]:0)
	}
	for(var mol in Alist){
		if(Alist[mol] && (snotwhat=="" || snotwhat.indexOf(mol+";")<0))return mol
	}
	return ""
}

function getMolecule(igroup,imol){
	var M=EqnData[igroup][imol]
	while(typeof(M)=="object")M=M[rand(M.length)]
	return M
}

function showPinfo(Eqn){
	var sout=""
	var n=0
	var i=0
	for(var mol in Eqn.list){if(Eqn.list[mol].n!=0 && isgas(mol)){
			n++
	}}
	if(n==2)n=-2
	for(var mol in Eqn.list){if(Eqn.list[mol].n!=0 && isgas(mol)){
			i++
			sout+=(sout==""?"":n==i?", and ":n==-i?" and ":", ")+"P_"+mol+"="+Eqn.PInfo[mol]+"&nbsp;atm"
	}}
	sout=sout.replace(/\=\./g,"=0.")
	return sout
}

function setRandomQ(Eqn,pmin,pmax){
	var s=""
	var sout=""
	var q=1
	for(var mol in Eqn.list){if(Eqn.list[mol].n!=0){
		if(isgas(mol)){
			s=rand2(pmin,pmax)
			s=calc_roundoff(s,2)
			Eqn.PInfo[mol]=parseFloat(s)
			ThisCalc[thisiq].js+="P_"+mol+"="+s+";"
			q*=Math.pow(Eqn.PInfo[mol],Eqn.list[mol].n)
		}else{
//			Eqn.PInfo[mol]=1
//			eval("P_"+mol+"=''")
		}
	}}
	return q
}

function setsubs(sform,icharge){
	var s=""
	var sf=""
	if(arguments.length==1)icharge=0
	var iabs=Math.abs(icharge)
	for (var i=0;i<sform.length;i++)
	{
		s=sform.charAt(i)
		sf=sf + (sdigits.indexOf(s)>0?"<sub>"+s+"</sub>":s)
	}
	if(icharge)sf+="<sup>"+(iabs==1?"":iabs)+(icharge>0?"+":minussign)+"</sup>"
	return sf
}

function showEquation(Eqn,iwhich,isthermo){
	var s=""
	var sout=""

	if(arguments.length<2||iwhich==0)iwhich=3
	if(iwhich & 1){
		for(var mol in Eqn.list){if(Eqn.list[mol].n<0){
			s+=(s.length?"+ ":"")+(Eqn.list[mol].n==-1?"":-Eqn.list[mol].n)+setsubs(isthermo?MolInfo[mol].tref:mol)+" "
		}}
		sout+=s
	}
	if(iwhich==3)sout+=" --->  "
	if(iwhich & 2){
		s=""
		for(var mol in Eqn.list){if(Eqn.list[mol].n>0){
			s+=(s.length?"+ ":"")+(Eqn.list[mol].n==1?"":Eqn.list[mol].n)+setsubs(isthermo?MolInfo[mol].tref:mol)+" "
		}}
		sout+=s
	}
	return sout
}

function getQcalc(Eqn){
	var s=""
	var sout=""
	var slist=""
	for(var mol in Eqn.list){if(Eqn.list[mol].n<0 && isgas(mol)){
		slist+="P_"+mol+" [in atm];"
		s+=(s.length?" * ":"")+"P_"+mol+(Eqn.list[mol].n==-1?"":"^"+(-Eqn.list[mol].n))
	}}
	if(s=="")s="1"
	sout+=s
	s=""
	for(var mol in Eqn.list){if(Eqn.list[mol].n>0 && isgas(mol)){
		slist+="P_"+mol+" [in atm];"
		s+=(s.length?" * ":"")+"P_"+mol+(Eqn.list[mol].n==1?"":"^"+(Eqn.list[mol].n))
	}}
	if(s=="")s="1"
	sout=slist+"Q = ( "+s+" )/( "+sout+" ) [unitless]"
	return sout
}

function strclean(sclean){
	//tabs and spaces removed; {} to ()
	var s=sclean
	s=strsub(s,"{","(")
	s=strsub(s,"}",")")
	s=strsub(s,"\n","")
	s=strsub(s,"\x22","") //double quote
	s=strsub(s,"\r","")
	s=strsub(s,"\f","")
	s=strsub(s,"\t","")
	s=strsub(s," ","")
	return s
}

function strsub(ssub,ch1,ch2){
	if(ssub.length==0) return ""
	if(ch2.length>0){if(ch2.indexOf(ch1)>=0) return ssub}
	var s=ssub
	var i=s.indexOf(ch1)
	while (i>=0){
		s=s.substring(0,i)+ch2+s.substring(i+ch1.length,s.length)
		i=s.indexOf(ch1)
	}
	return s
}


function getAtoms(Eqn){
	Eqn.atoms=new Array()
	for(var mol in Eqn.list){if(Eqn.list[mol].n>0){
		for(a in Eqn.list[mol].Alist){
			if(!Eqn.atoms[a])Eqn.atoms[a]=0
			Eqn.atoms[a]+=Eqn.list[mol].Alist[a]*Eqn.list[mol].n
		}
	}}
	var sout=""
	for(var a in Eqn.atoms)sout+=(sout==""?"":" + ")+(Eqn.atoms[a]==1?"":Eqn.atoms[a])+a
	Eqn.atomlist=sout
	return sout
}


