var log = function(x) { return ln(x)/ln(10) };
var pi = Math.PI, e = Math.E, ln = Math.log, sqrt = Math.sqrt, pow = Math.pow;
var floor = Math.floor, ceil = Math.ceil, abs = Math.abs, round = Math.round;
var sin = Math.sin, cos = Math.cos, tan = Math.tan, exp = Math.exp;
var arcsin = Math.asin, arccos = Math.acos, arctan = Math.atan;
var sec = function(x) { return 1/Math.cos(x) };
var csc = function(x) { return 1/Math.sin(x) };
var cot = function(x) { return 1/Math.tan(x) };
var arcsec = function(x) { return arccos(1/x) };
var arccsc = function(x) { return arcsin(1/x) };
var arccot = function(x) { return arctan(1/x) };
var sinh = function(x) { return (Math.exp(x)-Math.exp(-x))/2 };
var cosh = function(x) { return (Math.exp(x)+Math.exp(-x))/2 };
var tanh = function(x) { return (Math.exp(x)-Math.exp(-x))/(Math.exp(x)+Math.exp(-x)) };
var sech = function(x) { return 1/cosh(x) };
var csch = function(x) { return 1/sinh(x) };
var coth = function(x) { return 1/tanh(x) };
var arcsinh = function(x) { return ln(x+Math.sqrt(x*x+1)) };
var arccosh = function(x) { return ln(x+Math.sqrt(x*x-1)) };
var arctanh = function(x) { return ln((1+x)/(1-x))/2 };
var sech = function(x) { return 1/cosh(x) };
var csch = function(x) { return 1/sinh(x) };
var coth = function(x) { return 1/tanh(x) };
var arcsech = function(x) { return arccosh(1/x) };
var arccsch = function(x) { return arcsinh(1/x) };
var arccoth = function(x) { return arctanh(1/x) };
var sign = function(x) { return (x==0?0:(x<0?-1:1)) };
// power functions
function sq(x) {return Math.pow(x,2);}
function cb(x) {return Math.pow(x,3);}
// root functions
function cbrt(x) {return Math.pow(x,1/3);}
function root(index,x) {return Math.pow(x,1/index);}
// miscellaneous functions
function max(x,y) { return Math.max(x,y);}
function min(x,y) { return Math.min(x,y);}
function less(x,y) { return x < y }
function more(x,y) { return x > y }
function lessOrEqual(x,y) { return x <= y }
function moreOrEqual(x,y) { return x >= y }

function factorial(x,n) { // Factorial function
  if (n==null) n=1;
  if (Math.abs(x-Math.round(x*1000000)/1000000)<1e-15)
    x = Math.round(x*1000000)/1000000;
  if (x-Math.floor(x)!=0) return NaN;
  for (var i=x-n; i>0; i-=n) x*=i;
  return (x<0?NaN:(x==0?1:x));
}

function C(x,k) {  // Binomial coefficient function
  var res=1;
  for (var i=0; i<k; i++) res*=(x-i)/(k-i);
  return res;
}

function chop(x,n) { // Truncate decimal number to n places after decimal point
  if (n==null) n=0;
  return Math.floor(x*Math.pow(10,n))/Math.pow(10,n);
}

function roundn(x,n) { // Round decimal number to n places after decimal point
  if (n==null) n=0;
  return Math.round(x*Math.pow(10,n))/Math.pow(10,n);
}

function ran(a,b,n) { // Generate random number in [a,b] with n digits after .
  if (n==null) n=0;
  return chop((b+Math.pow(10,-n)-a)*Math.random()+a,n);
}

function rad(x) {
  return x*pi/180;
}

function evaluate(variables,input,decimals) {
  var str = variables+'\r'+input;
//  str = str.replace(/\r|\n|\r\n/g, "")
  var err = "";
//  alert(mathjs(str));
  try {
    var res = eval(mathjs(str));
  } catch(e) {
    err = "syntax incomplete";
  }
  if (!isNaN(res) && res!="Infinity")
  { 
    str = (Math.abs(res-Math.round(res*1000000)/1000000)<1e-15?Math.round(res*1000000)/1000000:res)+err; 
    str = Math.round(str*Math.pow(10,decimals))/Math.pow(10,decimals);
  } 
  else if (str!="") str = "undefined"; 
  var output = str;
  return output;
}

function evaluatemulti(variables,input) {
  var str = variables+'\r'+input;
//  str = str.replace(/\r|\n|\r\n/g, "")
  var err = "";
  //alert(mathjs(str));
  try {
    var res = eval(mathjs(str));
  } catch(e) {
    err = "syntax incomplete";
  }
  var output = res;
  return output;
}

function mathjs(st) {
  //translate a math formula to js function notation
  // a^b --> pow(a,b)
  // na --> n*a
  // (...)d --> (...)*d
  // n! --> factorial(n)
  // sin^-1 --> arcsin etc.
  //while ^ in string, find term on left and right
  //slice and concat new formula string
//  st = st.replace(/\s/g,"");
  st=" "+st
  if (st.indexOf("^-1")!=-1) {
    st = st.replace(/sin\^-1/g,"arcsin");
    st = st.replace(/cos\^-1/g,"arccos");
    st = st.replace(/tan\^-1/g,"arctan");
    st = st.replace(/sec\^-1/g,"arcsec");
    st = st.replace(/csc\^-1/g,"arccsc");
    st = st.replace(/cot\^-1/g,"arccot");
    st = st.replace(/sinh\^-1/g,"arcsinh");
    st = st.replace(/cosh\^-1/g,"arccosh");
    st = st.replace(/tanh\^-1/g,"arctanh");
    st = st.replace(/sech\^-1/g,"arcsech");
    st = st.replace(/csch\^-1/g,"arccsch");
    st = st.replace(/coth\^-1/g,"arccoth");
  }
//  st = st.replace(/^e$/g,"(Math.E)");
//  st = st.replace(/^e([^a-zA-Z])/g,"(Math.E)$1");
//  st = st.replace(/([^a-zA-Z])e/g,"$1(Math.E)");
// st = st.replace(/([^a-zA-Z])e([^a-zA-Z])/g,"$1(Math.E)$2");
  st = st.replace(/([0-9])([\(a-zA-Z])/g,"$1*$2");
  st = st.replace(/\)([\(0-9a-zA-Z])/g,"\)*$1");
  var i,j,k, ch, nested;
  while ((i=st.indexOf("^"))!=-1) {
    //find left argument
    if (i==0) return "Error: missing argument";
    j = i-1;
    ch = st.charAt(j);
    if (ch>="0" && ch<="9" || ch=="_" || ch>="a" && ch<="z" || ch>="A" && ch<="Z") {// look for (decimal) number
      j--;
      while (j>=0 && (ch=st.charAt(j))>="0" && ch<="9" || ch=="_" || ch>="a" && ch<="z" || ch>="A" && ch<="Z") j--;
      if (ch==".") {
        j--;
        while (j>=0 && (ch=st.charAt(j))>="0" && ch<="9" || ch=="_" || ch>="a" && ch<="z" || ch>="A" && ch<="Z") j--;
      }
    } else if (ch==")") {// look for matching opening bracket and function name
      nested = 1;
      j--;
      while (j>=0 && nested>0) {
        ch = st.charAt(j);
        if (ch=="(") nested--;
        else if (ch==")") nested++;
        j--;
      }
      while (j>=0 && (ch=st.charAt(j))>="a" && ch<="z" || ch>="A" && ch<="Z" || ch=="_")
        j--;
    } else if (ch>="a" && ch<="z" || ch>="A" && ch<="Z" || ch=="_") {// look for variable
      j--;
      while (j>=0 && (ch=st.charAt(j))>="a" && ch<="z" || ch>="A" && ch<="Z" || ch=="_")
        j--;
    } else { 
      return "Error: incorrect syntax in "+st+" at position "+j;
    }
    //find right argument
    if (i==st.length-1) return "Error: missing argument";
    k = i+1;
    ch = st.charAt(k);
    if (ch>="0" && ch<="9" || ch=="-" ) {// look for signed (decimal) number
      k++;
      while (k<st.length && (ch=st.charAt(k))>="0" && ch<="9" ) k++;
      if (ch==".") {
        k++;
        while (k<st.length && (ch=st.charAt(k))>="0" && ch<="9") k++;
      }
    } else if (ch=="(") {// look for matching closing bracket and function name
      nested = 1;
      k++;
      while (k<st.length && nested>0) {
        ch = st.charAt(k);
        if (ch=="(") nested++;
        else if (ch==")") nested--;
        k++;
      }
    } else if (ch>="a" && ch<="z" || ch>="A" && ch<="Z" || ch=="_") {// look for variable
      k++;
      while (k<st.length && (ch=st.charAt(k))>="a" && ch<="z" || ch>="A" && ch<="Z" || ch=="_") k++;
    } else { 
      return "Error: incorrect syntax in "+st+" at position "+k;
    }
    st = st.slice(0,j+1)+"Math.pow("+st.slice(j+1,i)+","+st.slice(i+1,k)+")"+
           st.slice(k);
  }

  while ((i=st.indexOf("!"))!=-1) {
    //find left argument
    if (i==0) return "Error: missing argument";
    j = i-1;
    ch = st.charAt(j);
    if (ch>="0" && ch<="9") {// look for (decimal) number
      j--;
      while (j>=0 && (ch=st.charAt(j))>="0" && ch<="9") j--;
      if (ch==".") {
        j--;
        while (j>=0 && (ch=st.charAt(j))>="0" && ch<="9") j--;
      }
    } else if (ch==")") {// look for matching opening bracket and function name
      nested = 1;
      j--;
      while (j>=0 && nested>0) {
        ch = st.charAt(j);
        if (ch=="(") nested--;
        else if (ch==")") nested++;
        j--;
      }
      while (j>=0 && (ch=st.charAt(j))>="a" && ch<="z" || ch>="A" && ch<="Z" || ch=="_")
        j--;
    } else if (ch>="a" && ch<="z" || ch>="A" && ch<="Z" || ch=="_") {// look for variable
      j--;
      while (j>=0 && (ch=st.charAt(j))>="a" && ch<="z" || ch>="A" && ch<="Z" || ch=="_")
        j--;
    } else { 
      return "Error: incorrect syntax in "+st+" at position "+j;
    }
    st = st.slice(0,j+1)+"factorial("+st.slice(j+1,i)+")"+st.slice(i+1);
  }
  return st;
}

function showequation(inputId,outputId) {
  var str = document.getElementById(inputId).value;
  str = str.replace(/\r|\n|\r\n/g, "")
  var outnode = document.getElementById(outputId);
  var n = outnode.childNodes.length;
  for (var i=0; i<n; i++)
    outnode.removeChild(outnode.firstChild);
  str = "`"+str+"`";  
  outnode.appendChild(document.createTextNode(str));
  str = outnode.innerHTML;
  str = str.replace(/;/g,"`<br>&nbsp;<br>`");
  outnode.innerHTML = str;
  AMprocessNode(outnode);
}

function calculatenumber(inputId,outputId,decimals) {
  var str = document.getElementById(inputId).value;
  str = str.replace(/\r|\n|\r\n/g, "")
  var err = "";
  var ind = str.lastIndexOf("\n");
  if (ind==str.length-1) str = str.slice(0,ind);
  str = str.slice(str.lastIndexOf("\n")+1);
  try {
    var res = eval(mathjs(str));
  } catch(e) {
    err = "syntax incomplete";
  }
  if (!isNaN(res) && res!="Infinity")
  { 
    str = (Math.abs(res-Math.round(res*1000000)/1000000)<1e-15?Math.round(res*1000000)/1000000:res)+err; 
    str = Math.round(str*Math.pow(10,decimals))/Math.pow(10,decimals);
  } 
  else if (str!="") str = "undefined"; //debug:+mathjs(str);
  var outnode = document.getElementById(outputId);
  var n = outnode.childNodes.length;
  for (var i=0; i<n; i++)
    outnode.removeChild(outnode.firstChild);
  outnode.appendChild(document.createTextNode(str));
  AMprocessNode(outnode);
}

