var numb = '0123456789';
var lwr = 'abcdefghijklmnopqrstuvwxyz';
var upr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';

function isValid(parm,val) 
{
   if (parm == "") return true;
   for (i=0; i<parm.length; i++) 
   {
      if (val.indexOf(parm.charAt(i),0) == -1) 
         return false;
   }
   return true;
}
   
var atom_weight = new Array();
atom_weight['H'] = 1.0079;
atom_weight['He'] = 4.0026;
atom_weight['Li'] = 6.941;
atom_weight['Be'] = 9.0122;
atom_weight['B'] = 10.811;
atom_weight['C'] = 12.0107;
atom_weight['N'] = 14.0067;
atom_weight['O'] = 15.9994;
atom_weight['F'] = 18.9984;
atom_weight['Ne'] = 20.1797;
atom_weight['Na'] = 22.9897;
atom_weight['Mg'] = 24.305;
atom_weight['Al'] = 26.9815;
atom_weight['Si'] = 28.0855;
atom_weight['P'] = 30.9738;
atom_weight['S'] = 32.065;
atom_weight['Cl'] = 35.453;
atom_weight['K'] = 39.0983;
atom_weight['Ar'] = 39.948;
atom_weight['Ca'] = 40.078;
atom_weight['Sc'] = 44.9559;
atom_weight['Ti'] = 47.867;
atom_weight['V'] = 50.9415;
atom_weight['Cr'] = 51.9961;
atom_weight['Mn'] = 54.938;
atom_weight['Fe'] = 55.845;
atom_weight['Ni'] = 58.6934;
atom_weight['Co'] = 58.9332;
atom_weight['Cu'] = 63.546;
atom_weight['Zn'] = 65.39;
atom_weight['Ga'] = 69.723;
atom_weight['Ge'] = 72.64;
atom_weight['As'] = 74.9216;
atom_weight['Se'] = 78.96;
atom_weight['Br'] = 79.904;
atom_weight['Kr'] = 83.8;
atom_weight['Rb'] = 85.4678;
atom_weight['Sr'] = 87.62;
atom_weight['Y'] = 88.9059;
atom_weight['Zr'] = 91.224;
atom_weight['Nb'] = 92.9064;
atom_weight['Mo'] = 95.94;
atom_weight['Tc'] = 98;
atom_weight['Ru'] = 101.07;
atom_weight['Rh'] = 102.9055;
atom_weight['Pd'] = 106.42;
atom_weight['Ag'] = 107.8682;
atom_weight['Cd'] = 112.411;
atom_weight['In'] = 114.818;
atom_weight['Sn'] = 118.71;
atom_weight['Sb'] = 121.76;
atom_weight['I'] = 126.9045;
atom_weight['Te'] = 127.6;
atom_weight['Xe'] = 131.293;
atom_weight['Cs'] = 132.9055;
atom_weight['Ba'] = 137.327;
atom_weight['La'] = 138.9055;
atom_weight['Ce'] = 140.116;
atom_weight['Pr'] = 140.9077;
atom_weight['Nd'] = 144.24;
atom_weight['Pm'] = 145;
atom_weight['Sm'] = 150.36;
atom_weight['Eu'] = 151.964;
atom_weight['Gd'] = 157.25;
atom_weight['Tb'] = 158.9253;
atom_weight['Dy'] = 162.5;
atom_weight['Ho'] = 164.9303;
atom_weight['Er'] = 167.259;
atom_weight['Tm'] = 168.9342;
atom_weight['Yb'] = 173.04;
atom_weight['Lu'] = 174.967;
atom_weight['Hf'] = 178.49;
atom_weight['Ta'] = 180.9479;
atom_weight['W'] = 183.84;
atom_weight['Re'] = 186.207;
atom_weight['Os'] = 190.23;
atom_weight['Ir'] = 192.217;
atom_weight['Pt'] = 195.078;
atom_weight['Au'] = 196.9665;
atom_weight['Hg'] = 200.59;
atom_weight['Tl'] = 204.3833;
atom_weight['Pb'] = 207.2;
atom_weight['Bi'] = 208.9804;
atom_weight['Po'] = 209;
atom_weight['At'] = 210;
atom_weight['Rn'] = 222;
atom_weight['Fr'] = 223;
atom_weight['Ra'] = 226;

function isNumber(parm) {return isValid(parm,numb);}
function isLower(parm) {return isValid(parm,lwr);}
function isUpper(parm) {return isValid(parm,upr);}
function isAlpha(parm) {return isValid(parm,lwr+upr);}
function isAlphanum(parm) {return isValid(parm,lwr+upr+numb);}
   
var contaminents;
var contaminentNames;

var atom_list;
var mol_list;
var mol_massp;

var resultsString;
var preferredResultsString;
var progressString;

var tolerance;

var exp_p_C;
var exp_p_H;
var exp_p_N;
var exp_p_S;

var last_deltaC;

var last_deltai;
var last_deltaj;
var last_deltak;

var converging;

/////////////
   var tot_mass;
  
   var C_tot;
   var H_tot;
   var N_tot;
   var S_tot;
   
   var C_percent;
   var H_percent;
   var N_percent;
   var S_percent;
    
   var old_tot;
   
   var deltaC;
   var deltaH;
   var deltaN;
   var deltaS;   
///////////////

function checkAtomType(atom)
{
   for(i = 0; i < atom_type.length; i++)
   {
      if(atom_type[i] == atom)
         return true;
   } 
   
   return false;
}
   
function getAtomIndex(atom)
{
   for(var i = 0; i < atom_list.length; i++)
   {
      if(atom_list[i] == atom)
      {
         return i;
      }
   }   
   
   return -1;
}
   
function addAtomToList(atom)
{
   if(!checkAtomType)
      return false;
   
   for(var i = 0; i < atom_list.length; i++)
   {
      if(atom_list[i] == atom)
      {
         return true;
      }
   }
   
   atom_list[atom_list.length] = atom;
         
   return true;
}
   
function addAtomToMol(atom, quantity, mol_num)
{
   var elemQty = parseInt(quantity,10);
   
   for(var i = 0; i < atom_list.length; i++)
   {
      if(atom_list[i] == atom)
      {
         mol_list[mol_num][i] += elemQty;
      }
   }
}
   
function showList()
{
   var displayString = "";
   
   for(i = 0; i < atom_list.length; i++)
      displayString += atom_list[i] + " ";
   
   
   //resultsString += "Atom list: " + displayString + "<br>";
}
   
function showMolList()
{
   var displayString;
   
   for(var i = 0; i < mol_list.length; i++)
   {
      displayString = "";
   
      for(var j = 0; j < mol_list[0].length; j++)
         displayString += mol_list[i][j] + " ";
         
      //resultsString += "Molecule " + i + " atom count: " + displayString + "<br>";
   }
}
   
function addFormula(formula, pass, mol_num)
{
   // trim
   formula = formula.replace(/^\s+|\s+$/g, '') ;
   
   var tempElement = "";
   var tempQuantity = "";
  
   var count = 0;

   while(count < formula.length)
   {
      if(isAlpha(formula.charAt(count)))
      {
         while(isAlpha(formula.charAt(count)))
         {
            tempElement += formula.charAt(count);
            count++;
            
            if(count == formula.length)
               break;
           
            if(isUpper(formula.charAt(count)))
               break;
         }
    
         if(isUpper(formula.charAt(count)))
         {
            if(tempQuantity == "")
               tempQuantity = "1";
            
            if(pass == 1)
            {
               if(!addAtomToList(tempElement))
                  return false;
            }
            if(pass == 2)
            {
               addAtomToMol(tempElement, tempQuantity, mol_num);
            }

            tempQuantity = "";
            tempElement = "";
            continue;
         }
      }

      if(count == formula.length)
      {
         if(tempQuantity == "")
            tempQuantity = "1";
            
         if(pass == 1)
         {
            if(!addAtomToList(tempElement))
               return false;
         }
         if(pass == 2)
         {
            addAtomToMol(tempElement, tempQuantity, mol_num);
         }

         tempQuantity = "";
         tempElement = "";
         break;
      }

      if(isNumber(formula.charAt(count)))
      {
         while(isNumber(formula.charAt(count)))
         {
            tempQuantity += formula.charAt(count);
            count++;
            
            if(count == formula.length)
               break;
         }
         
         if(tempQuantity == "")
            tempQuantity = "1";
            
         if(pass == 1)
         {
            if(!addAtomToList(tempElement))
               return false;
         }
         if(pass == 2)
         {
            addAtomToMol(tempElement, tempQuantity, mol_num);
         }

         tempQuantity = "";
         tempElement = "";
      }
   }
   
   return true;
}
   
   
function determineContaminents()
{
   var chk_count = 0;
   contaminents = [];
   contaminentNames = [];
   var myForm = document.getElementById("EAForm");

   for ( i = 0; i < myForm.elements.length; i++ )
      if (myForm.elements[i].id == 'check_contaminent' )
         if(myForm.elements[i].checked == true)
            chk_count++;
            
   if(chk_count > 3)
   {
      alert("Please choose no more than 3 contaminents.");
      return false;
   }
   
   contaminents = new Array(chk_count);
   contaminentNames = new Array(chk_count);
   
   chk_count = 0;
   
   for ( i = 0; i < myForm.elements.length; i++ )
      if (myForm.elements[i].id == 'check_contaminent' )
         if(myForm.elements[i].checked == true)
         {
           contaminents[chk_count] = myForm.elements[i].name;
           contaminentNames[chk_count] = myForm.elements[i].value;
           chk_count++; 
         }
   
   return true;
}
   
function MultiDimensionalArray(iRows,iCols) 
{ 
   var a = new Array(iRows); 
   
   for (var i=0; i < iRows; i++) 
   {
      a[i] = new Array(iCols);
      for (var j=0; j < iCols; j++) 
      { 
         a[i][j] = 0; 
      }  
   }

   return(a); 
}
   
function percentComposition2(mult1, mult2, mult3)
{
   C_tot = H_tot = N_tot = C_percent = H_percent = N_percent = old_tot = 0;
   
   tot_mass = mol_massp[0][0];
   C_tot = mol_massp[0][1];
   
   if(mol_list.length > 1)
   {
      tot_mass += mol_massp[1][0] * mult1;
      C_tot += mol_massp[1][1] * mult1;
      
      if(mol_list.length > 2)
      {
         tot_mass += mol_massp[2][0] * mult2;
         C_tot += mol_massp[2][1] * mult2;         
         
         if(mol_list.length > 3)
         {
            tot_mass += mol_massp[3][0] * mult3;
            C_tot += mol_massp[3][1] * mult3;            
         }
      }
   } 
   
   C_percent = (C_tot * 100) / tot_mass;
   deltaC = Math.abs((C_percent*100 - exp_p_C*100)/100);
   
   converging = false;
   if(deltaC < last_deltaC)
      converging = true;
   

   last_deltaC = deltaC;
   
   if( deltaC <= tolerance )
   {
      H_tot = mol_massp[0][2];
      if(mol_list.length > 1)
      {
         H_tot += mol_massp[1][2] * mult1;
      
         if(mol_list.length > 2)
         {
            H_tot += mol_massp[2][2] * mult2;        
         
            if(mol_list.length > 3)
            {
               H_tot += mol_massp[3][2] * mult3;            
            }
         }
      }
      
      H_percent = (H_tot * 100) / tot_mass;
      deltaH = Math.abs((H_percent*100 - exp_p_H*100)/100);
      
      if( (deltaH <= tolerance) )
      {
         N_tot = mol_massp[0][3];
         if(mol_list.length > 1)
         {
            N_tot += mol_massp[1][3] * mult1;
         
            if(mol_list.length > 2)
            {
               N_tot += mol_massp[2][3] * mult2;        
            
               if(mol_list.length > 3)
               {
                  N_tot += mol_massp[3][3] * mult3;            
               }
            }
         }
         
         N_percent = (N_tot * 100) / tot_mass;
         deltaN = Math.abs((N_percent*100 - exp_p_N*100)/100);
         
         if( (deltaN <= tolerance) )
         {

               if(mol_list.length == 4)
               {
                  resultsString += parentMolFormula + " + " + mult1.toFixed(2) + " " + contaminentNames[0] + " + " + mult2.toFixed(2) + " " + contaminentNames[1] + " + " + mult3.toFixed(2) + " " + contaminentNames[2] + "  (%C: " + C_percent.toFixed(2) + ", %H: " + H_percent.toFixed(2) + ", %N: " + N_percent.toFixed(2) + ")<br>";
                  if(mult1.toFixed(2) * 100 % 25 == 0)
                     if(mult2.toFixed(2) * 100 % 25 == 0) 
                        if(mult3.toFixed(2) * 100 % 25 == 0)
                        {
                           preferredResultsString += parentMolFormula + " + " + mult1.toFixed(2) + " " + contaminentNames[0] + " + " + mult2.toFixed(2) + " " + contaminentNames[1] + " + " + mult3.toFixed(2) + " " + contaminentNames[2] + "  (%C: " + C_percent.toFixed(2) + ", %H: " + H_percent.toFixed(2) + ", %N: " + N_percent.toFixed(2) + ")<br>";  
                        }
               }
               else if(mol_list.length == 3)
               {
                  resultsString += parentMolFormula + " + " + mult1.toFixed(2) + " " + contaminentNames[0] + " + " + mult2.toFixed(2) + " " + contaminentNames[1] + "  (%C: " + C_percent.toFixed(2) + ", %H: " + H_percent.toFixed(2) + ", %N: " + N_percent.toFixed(2) + ")<br>";
                  if(mult1.toFixed(2) * 100 % 25 == 0)
                     if(mult2.toFixed(2) * 100 % 25 == 0)  
                     {
                        preferredResultsString += parentMolFormula + " + " + mult1.toFixed(2) + " " + contaminentNames[0] + " + " + mult2.toFixed(2) + " " + contaminentNames[1] + "  (%C: " + C_percent.toFixed(2) + ", %H: " + H_percent.toFixed(2) + ", %N: " + N_percent.toFixed(2) + ")<br>";
                     }
               }
               else if(mol_list.length == 2)
               {
                  resultsString += parentMolFormula + " + " + mult1.toFixed(2) + " " + contaminentNames[0] + " (%C: " + C_percent.toFixed(2) + ", %H: " + H_percent.toFixed(2) + ", %N: " + N_percent.toFixed(2) + ")<br>";
                  if(mult1.toFixed(2) * 100 % 25 == 0)
                     preferredResultsString += parentMolFormula + " + " + mult1.toFixed(2) + " " + contaminentNames[0] + " + " + mult2.toFixed(2) + " " + contaminentNames[1] + "  (%C: " + C_percent.toFixed(2) + ", %H: " + H_percent.toFixed(2) + ", %N: " + N_percent.toFixed(2) + ")<br>";
                  
               }
               else if(mol_list.length == 1)
                  resultsString += parentMolFormula + " (%C: " + C_percent.toFixed(2) + ", %H: " + H_percent.toFixed(2) + ", %N: " + N_percent.toFixed(2) + ")<br>";
         }
      }
   } 
}


function fitData2()
{
   var max1;
   var max2;
   var max3;
   
   var step1;
   var step2;
   var step3;
   
   if(contaminents.length == 1)
   {
      max1 = 10;

      step1 = 0.01;
   }
   if(contaminents.length == 2)
   {
      max1 = 10;
      max2 = 10;

   
      step1 = 0.01;
      step2 = 0.01;
   }
   if(contaminents.length == 3)
   {
      max1 = 5;
      max2 = 5;
      max3 = 5;
   
      step1 = 0.05;
      step2 = 0.05;
      step3 = 0.05; 
   }
    
   
   if(contaminents.length == 0)
      percentComposition2(0, 0, 0);
   
   if(contaminents.length == 1)
   {
      for(var i = 0; i <= max1; i = ((i * 100) + (step1 * 100))/100)
         percentComposition2(i, 0, 0);
   }

   if(contaminents.length == 2)
   {
      for(var i = 0; i <= max1; i = ((i * 100) + (step1 * 100))/100)
      {

         last_deltaC = 101;
         for(var j = 0; j <= max2; j = ((j * 100) + (step2 * 100))/100)
         {
            
            percentComposition2(i, j, 0); 
            if(!converging)
               break;
         }
      }
   }  
   
   if(contaminents.length == 3)
   {  

      var breakCount = 0;
      
      for(var i = 0; i <= max1; i = ((i * 100) + (step1 * 100))/100)
      {

            
         for(var j = 0; j <= max2; j = ((j * 100) + (step2 * 100))/100)
         {
            last_deltaC = 101;
            
            for(var k = 0; k <= max3; k = ((k * 100) + (step3 * 100))/100)
            {  
               percentComposition2(i, j, k);
               
               if(!converging)
               {
                 breakCount +=  (max3 - k) * 1/step3; 
                  break;
               }
            }
         }

      }
      
      alert(breakCount);
   }  
}

function populate_mol_massp(mol_count)
{
   var temp_mass;
   var temp_pC;
   var temp_pH;
   var temp_pN;
//   var temp_pS;
   
   for(var mol_num = 0; mol_num < mol_list.length; mol_num++)
   {
      temp_mass = 0;
      temp_mC = 0;
      temp_mH = 0;
      temp_mN = 0;
//      temp_mS = 0;      
      
      for(var j = 0; j < atom_list.length; j++)
      {
         temp_mass += mol_list[mol_num][j] * atom_weight[atom_list[j]];
         
         if(atom_list[j] == 'C')
            temp_mC = mol_list[mol_num][j] * atom_weight[atom_list[j]];
         
         if(atom_list[j] == 'H')
            temp_mH = mol_list[mol_num][j] * atom_weight[atom_list[j]];
            
         if(atom_list[j] == 'N')
            temp_mN = mol_list[mol_num][j] * atom_weight[atom_list[j]];
                    
      }

      mol_massp[mol_num][0] = temp_mass; // molecule's mass
      mol_massp[mol_num][1] = temp_mC; // mass C
      mol_massp[mol_num][2] = temp_mH; // mass H
      mol_massp[mol_num][3] = temp_mN; // mass N

   } 
} 
      
      
function go()
{
  
   atom_list = new Array();
   mol_list = new Array();
   mol_massp = new Array(); 
   
   exp_p_C = document.getElementById("p_C").value;
   exp_p_H = document.getElementById("p_H").value;
   exp_p_N = document.getElementById("p_N").value;
 
   tolerance = document.getElementById("tol").value;
   
   resultsDiv = document.getElementById("result_div");
   resultsDiv.innerHTML = "";
   resultsString = "";
   preferredResultsString = "";
         
   parentMolFormula = document.getElementById("parentFormula").value;
   addFormula(parentMolFormula, 1, 0);
   
   determineContaminents();
   
   // build atom list (pass 1)
   for(var i = 0; i < contaminents.length; i++)
      addFormula(contaminents[i], 1, 0);
   
   mol_list = MultiDimensionalArray(contaminents.length + 1,atom_list.length);

   // add atom counts (pass 2)
   addFormula(parentMolFormula, 2, 0);
   for(var i = 0; i < contaminents.length; i++)
      addFormula(contaminents[i], 2, i + 1);
      
   // the second argument in the following array declaration is the number of atoms that may be fitted (C, H, N = 3) + 1   
   // mol_massp consists of multiple rows containing the following columns "molecular_mass, mass C, mass H, mass N" for each molecule (contaminents + parent)
   mol_massp = MultiDimensionalArray(contaminents.length + 1, 3 + 1);
   populate_mol_massp();
   
   fitData2();
   
   if(resultsString == "")
   {
      resultsString = "No matching fit possible.";
      alert("No matching fit possible.");
   }
   
   var outputString = ""; 
   outputString = "<b>All possible fits:</b><br>" + resultsString;  
   
   if(preferredResultsString != "")
      outputString = "<b>Best fit:</b><br>" + preferredResultsString + "<br><b>All possible fits:</b><br>" + resultsString;

   
   resultsDiv.innerHTML = outputString;
}


function showIndicator()
{
   var indDiv = document.getElementById("indicatorDiv");
   
   indDiv.style.display = 'none';
   indDiv.style.display = 'block';
   
   alert("This may take a few seconds.  Click OK continue.");
}

function hideIndicator()
{
   var indDiv = document.getElementById("indicatorDiv");
   indDiv.style.display = 'block';
   indDiv.style.display = 'none';
}


