/****************************
 *  DrawFunction.js
 *
 *  This uses the Peter Jipsen's ASCIIsvg.js 1.0.
 *  ASCIIsvg.js is freely available under the GNU General Public License
 *
 *  Revison:  12
 *  Resource:  limit.ssu.ac.kr/ascii/DrawFunction.js
 *  License:   GNU General Public License
 *  Author:   Pilho Kim at Soongsil University,  phkim (AT) ssu.ac.kr   
 */
 
var pi = Math.PI
var e = Math.E

function changeMult(s) {
    var re = new RegExp("(([0-9|\\)|\\]|!])([a-z|A-Z|\\(|\\[])|([0-9|a-z|A-Z|\\)|\\]|!]+\\s+)" + "([0-9|a-z|A-Z|\\(|\\[]+)" + ")", "gi");
    var s1  = s.replace(re, function($0, $1, $2, $3, $4, $5) {
            var a, b
            a = ($2 == undefined) ? $4 : $2
            b = ($3 == undefined) ? $5 : $3
            return a + "*" + b;
          });
    while (s1 != s)
        return changeMult(s1)

    return s1
}

function changePower(s) {
    var re = new RegExp("(\\(" + "([^\|]+)" + "\\)|(\\w+\\s*))" + "(\\^|\\*\\*)(\\(" + "([^\|]+)" + "\\)|(\\s*\\w+))", "gi");
    return s.replace(re, function($0, $1, $2, $3, $4, $5) {
            var a, b
            a = ($2 == undefined) ? $1 : $2
            b = ($5 == undefined) ? $4 : $5
            return "pow(" + a + "," + b + ")";
          });
}

function changeFact(s) {
    var re = new RegExp("(\\(" + "([^\|]+)" + "\\)|(\\w+\\s*))" + "!");
    return s.replace(re, function($0, $1, $2) {
            var a
            a = ($2 == undefined) ? $1 : $2
            return "fact(" + a + ")"
          });
}


function changeLog(s) {
    var re = new RegExp("log([^a-zA-Z])", "gi");
    return s.replace(re, function($0, $1) {
            if ($1 != undefined)
                return "Log" + $1
          });
}

function changeXtoT(s) {
    var re = new RegExp("x([^a-zA-Z])|([^a-zA-Z])x|(^x)|(x$)", "gi");
    return s.replace(re, function($0, $1, $2, $3, $4) {
            if ($1 != undefined)
                return "t" + $1
            else if ($2 != undefined)
                return $2 + "t"
            else if ($3 != undefined)
                return "t"
            else if ($4 != undefined)
                return "t"
          });
}

function changeYtoT(s) {
    var re = new RegExp("y([^a-zA-Z])|([^a-zA-Z])y|(^y)|(y$)", "gi");
    return s.replace(re, function($0, $1, $2, $3, $4) {
            if ($1 != undefined)
                return "t" + $1
            else if ($2 != undefined)
                return $2 + "t"
            else if ($3 != undefined)
                return "t"
            else if ($4 != undefined)
                return "t"
          });
}

function changeYtoX(s) {
    var re = new RegExp("y([^a-zA-Z])|([^a-zA-Z])y|(^y)|(y$)", "gi");
    return s.replace(re, function($0, $1, $2, $3, $4) {
            if ($1 != undefined)
                return "x" + $1
            else if ($2 != undefined)
                return $2 + "x"
            else if ($3 != undefined)
                return "x"
            else if ($4 != undefined)
                return "x"
          });
}

function changeHyperbolic(s) {
    var re = new RegExp("(sinh|cosh|tanh|sech|csch|coth|asinh|acosh|atanh|asech|acsch|acoth|sinh\\^\\(-1\\)|cosh\\^\\(-1\\)|tanh\\^\\(-1\\)|sech\\^\\(-1\\)|csch\\^\\(-1\\)|coth\\^\\(-1\\))\\(" + "([^\|,]+)" + "\\)", "gi");
    return s.replace(re, function($0, $1, $2) {
              with(Math) {
                  var param0 = $0.toLowerCase();

                  /////////////////////////////////////////////////////////////
                  //    (cosh(x))**2
                  //    (cosh(x) )**2
                  //    (cosh(x)  )**2
                  //
                  // alert("$0 = " + $0 + "; $1 = " + $1 + ", $2 = " + $2);
                  var k = findFirstRightMatch($2)
                  var s2, s3
                  if (k >= 0) {
                      s2 = $2.substring(0, k)
                      s3 = $2.substring(k)
                  }
                  else {
                      s2 = $2
                      s3 = ""
                  }

                  if (param0.substring(0, 5) == "sinh(")
                      return "((exp(" + s2 + ")-exp(-(" + s2 + ")))/2)" + s3;
                  else if (param0.substring(0, 5) == "cosh(")
                      return "((exp(" + s2 + ")+exp(-(" + s2 + ")))/2)" + s3;
                  else if (param0.substring(0, 5) == "tanh(")
                      return "(1-2/((exp(2*(" + s2 + ")))+1))" + s3;
                  else if (param0.substring(0, 5) == "sech(")
                      return "(2/(exp(" + s2 + ")+exp(-(" + s2 + "))))" + s3;
                  else if (param0.substring(0, 5) == "csch(")
                      return "(2/(exp(" + s2 + ")-exp(-(" + s2 + "))))" + s3;
                  else if (param0.substring(0, 5) == "coth(")
                      return "(1+2/((exp(2*(" + s2 + ")))-1))" + s3;
                  else if (param0.substring(0, 10) == "sinh^(-1)(" || param0.substring(0, 6) == "asinh(")
                      return "ln((" + s2 + ")+sqrt(1+(" + s2 + ")*(" + s2 + ")))" + s3;
                  else if (param0.substring(0, 10) == "cosh^(-1)(" || param0.substring(0, 6) == "acosh(")
                      return "ln((" + s2 + ")+sqrt(-1+(" + s2 + ")*(" + s2 + ")))" + s3;
                  else if (param0.substring(0, 10) == "tanh^(-1)(" || param0.substring(0, 6) == "atanh(")
                      return "(ln((1+(" + s2 + "))/(1-(" + s2 + ")))/2)" + s3;
                  else if (param0.substring(0, 10) == "sech^(-1)(" || param0.substring(0, 6) == "asech(")
                      return "ln(1/(" + s2 + ")+sqrt(-1+1/((" + s2 + ")*(" + s2 + "))))" + s3;
                  else if (param0.substring(0, 10) == "csch^(-1)(" || param0.substring(0, 6) == "acsch(")
                      return "ln(1/(" + s2 + ")+sqrt(+1+1/((" + s2 + ")*(" + s2 + "))))" + s3;
                  else if (param0.substring(0, 10) == "coth^(-1)(" || param0.substring(0, 6) == "acoth(")
                      return "(ln((1+(" + s2 + "))/(-1+(" + s2 + ")))/2)" + s3;
                  else
                      return s;
                }
          });
}

function changeInverseTrig(s) {
    var re = new RegExp("(sec|csc|cot|arctan|arcsec|arccsc|arccot|asin|acos|asec|acsc|acot|sin\\^\\(-1\\)|cos\\^\\(-1\\)|tan\\^\\(-1\\)|sec\\^\\(-1\\)|csc\\^\\(-1\\)|cot\\^\\(-1\\))\\(" + "([^\|]+)" + "\\)", "gi");
    return s.replace(re, function($0, $1, $2) {
            with(Math) {
                  var param0 = $0.toLowerCase();
                  if (param0.substring(0, 9) == "sin^(-1)(" || param0.substring(0, 5) == "asin(")
                      return "arcsin(" + $2 + ")";
                  else if (param0.substring(0, 9) == "cos^(-1)(" || param0.substring(0, 5) == "acos(")
                      return "arccos(" + $2 + ")";
                  else if (param0.substring(0, 9) == "tan^(-1)(")
                      return "arctan(" + $2 + ")";
                  else if (param0.substring(0, 9) == "sec^(-1)(" || param0.substring(0, 5) == "asec(" || param0.substring(0, 7) == "arcsec(")
                      return "arccos(1/(" + $2 + "))";
                  else if (param0.substring(0, 9) == "csc^(-1)(" || param0.substring(0, 5) == "acsc(" || param0.substring(0, 7) == "arccsc(")
                      return "arcsin(1/(" + $2 + "))";
                  else if (param0.substring(0, 7) == "arccot(" || param0.substring(0, 9) == "cot^(-1)(" || param0.substring(0, 5) == "acot(")
                      return "arccot(" + $2 + ")";
                  else if (param0.substring(0, 4) == "sec(")
                      return "(1/cos(" + $2 + "))";
                  else if (param0.substring(0, 4) == "csc(")
                      return "(1/sin(" + $2 + "))";
                  else if (param0.substring(0, 4) == "cot(")
                      return "(cos(" + $2 + ")/sin(" + $2 + "))";
                  else
                      return s;
                }
          });
}

function changeDelim(s, deli1, deli2, t1, t2) {
    var re = new RegExp(deli1+ "([^\|]+)" + deli2, "gi");
    return s.replace(re, function($0, $1) {
              return t1 + $1 + t2 
          });
}

function changeEqnDelimeters(s) {
    var t = changeDelim(s, "\\|", "\\|", "abs(", ")")
    t = changeDelim(t, "\\[", "\\]", "floor(", ")")
    return t
}

function pow(a, x) {
    var z = 1/x
    b = eval(a)
    if ((z == Math.floor(z)) && ((z % 2) == 1) && b < 0) {
        return -Math.pow(-b, x)
    }
    else {
        return Math.pow(b, x)
    }
}

function sqrt(x) {
    if (x < 0)
       return undefined
    else {
        return Math.sqrt(x)
    }
}

function fact(n) {
    if (n <= 0)
        return 1
    else if (n <= 1)
        return n

    var z = n
    for (var i = n - 1; i > 0; i--)
        z *= i
    return z
}

function perm(n, r) {
    if (n <= 0 || r > n)
        return undefined
    else if (r == 0)
        return 1

    var z = n
    for (var i = 2; i <= r; i++)
        z *= n - i + 1
    return z
}

function comb(n, r) {
    if (n <= 0 || r > n)
        return undefined
    else if (r == 0)
        return 1

    var z = n
    for (var i = 2; i <= r; i++) {
        z *= n - i + 1
        z /= i
    }
    return z
}

function Log(a, x) {
    if (a <= 0 || a == 1)
       return undefined
    if (x <= 0)
       return undefined
    return Math.log(x)/Math.log(a)
}

function ln(x) {
    if (x <= 0)
       return undefined
    return Math.log(x)
}

function Exp(x) {
   return Math.exp(x)
}

function exp(x) {
   return Math.exp(x)
}

function sin(x) {
   return Math.sin(x)
}

function cos(x) {
   return Math.cos(x)
}

function tan(x) {
   var u = Math.cos(x)
   if (u != 0)
       return Math.sin(x)/Math.cos(x)
   else
       return undefined
}

function cot(x) {
   var u = Math.sin(x)
   if (u != 0)
       return Math.cos(x)/Math.sin(x)
   else
       return undefined
}

function arcsin(x) {
   if (-1 <= x && x <= 1)
       return Math.asin(x)
   else
       return undefined
}

function arccos(x) {
   if (x == 1)
       return 0
   else if (x == -1)
       return Math.PI
   else if (x >= -1 && x <= 1)
       return Math.acos(x)
   else
       return undefined
}

function sec(x) {
   var u = Math.cos(x)
   if (u != 0)
       return 1/u
   else
       return Number.NaN
}

function csc(x) {
   var u = Math.sin(x)
   if (u != 0)
       return 1/u
   else
       return undefined
}

function arctan(x) {
   return Math.atan(x)
}

function arccot(x) {
    if (x > 0)
       return Math.atan(1/x)
    else if (x < 0)
       return Math.PI + Math.atan(1/x)
    else
       return Math.PI/2
}

function sinh(x) {
    return (Math.exp(x) - Math.exp(-x))/2
}

function cosh(x) {
    return (Math.exp(x) + Math.exp(-x))/2
}

function tanh(x) {
    return (Math.exp(2*x) - 1)/(Math.exp(2*x) + 1)
}

function sech(x) {
    return 2/(Math.exp(x) + Math.exp(-x))
}

function csch(x) {
    if (x != 0)
        return 2/(Math.exp(x) - Math.exp(-x))
   else
       return undefined
}

function coth(x) {
    if (x != 0)
       return (Math.exp(2*x) + 1)/(Math.exp(2*x) - 1)
   else
       return undefined
}

function showStack(arr) {
        var str = "["
        for (var i = 0; i < arr.length; i++) {
            if (i > 0)
                str += ", "
            str += arr[i]
        }
        str += "]"
}


function isWhite(c) {
        return (c == ' ' || c == '\t' || c == '\f' || c == '\n' || c == '\r')
}

function isDigit(c) {
        return (c == "." || (c >= '0' && c <= '9'))
}

function isComma(c) {
        return (c == ',')
}

function isChar(c) {
        return (c == "_" || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
}

function isCharOrDigit(c) {
        return (isChar(c) || isDigit(c))
}

function isLParen(c) {
        return (c == "(")
}

function isRParen(c) {
        return (c == ")")
}

    //////////////////////////////////////////////
    // Test Parsing:
    //   1+(1+2)^(3+(2)-1)
    //   1+(1+(2))^(3+(2)-1)
    //   1+(1+(sin(2))^(3+(2)-1))
    //   1+4^((-1-(2)))+3
    //   
    //   1+(2)^3
    //   1+2^3
    //   10 -(1+2)^3
    //   1+2+2^((-1-(2)))+3
    //   1+((2))!
    //   1+((2)!)
    //   1+((2))^3
    //   1+((2)^3)
    //   
    //   1+(3!)
    //   1+(2^3)
    //   1+(2)^3
    //   1+(-2)^3
    //   1+2^3

function findFirstRightMatch(s) {
        var len = s.length
        var depth = 0
        var a
        var i = 0
        for (i = 0; i < len; i++) {
            a = s.charAt(i)
            if (isLParen(a)) {
                 depth++
            }
            else if (isRParen(a)) {
                 if (depth <= 0)
                     return i;
                 else
                     depth--
            }
        }
        return -1
}

function changeLeftRightToken(s, op, fnName) {
        var pos = s.indexOf(op)
        if (pos < 0)
           return s;
        var len = s.length
        var s1 = s.substring(pos)
        var s2 = s.substring(pos + 1)
        
        var START_STATE = 0
        var WHITE_STATE = 1
        var COMMA_STATE = 2
        var CHAR_STATE = 3
        var DIGIT_STATE = 4
        var LPAREN_STATE = 5
        var RPAREN_STATE = 6
        var SYMBOL_STATE = 7
        var FINAL_STATE = 99
        var state = START_STATE
        var pos1 = pos, pos2 = pos
        var hasToken = false
        var depth = 0
        var a
        var i = 0
        for (i = pos + op.length; i < len; i++) {
            a = s.charAt(i)
            switch (state) {
                case START_STATE:
                    if (isWhite(a))
                        state = WHITE_STATE
                    else if (isDigit(a)) {
                        hasToken = true
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        hasToken = true
                        state = CHAR_STATE
                    }
                    else if (isLParen(a)) {
                        depth++
                        hasToken = true
                        state = LPAREN_STATE
                    }
                    else if (!hasToken && isRParen(a)) {
                        alert("The right parenthesis ')' is in bad position!")
                        return s.substring(0, pos)
                    }
                    else {
                        state = SYMBOL_STATE
                    }
                    break
                case DIGIT_STATE:
                case CHAR_STATE:
                    if (isWhite(a))
                        state = WHITE_STATE
                    else if (isComma(a)) {
                        state = COMMA_STATE
                    }
                    else if (isDigit(a)) {
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        state = CHAR_STATE
                    }
                    else if (isLParen(a)) {
                        depth++
                        state = LPAREN_STATE
                    }
                    else if (isRParen(a)) {
                        if (!hasToken && depth <= 0) {
                            alert("The right parenthesis ')' is in bad position!")
                            return s.substring(0, pos)
                        }
                        depth--
                        state = RPAREN_STATE
                    }
                    else {
                        state = SYMBOL_STATE
                    }
                    break
                case LPAREN_STATE:
                    if (isWhite(a))
                        state = WHITE_STATE
                    else if (isDigit(a)) {
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        state = CHAR_STATE
                    }
                    else if (isLParen(a)) {
                        depth++
                        state = LPAREN_STATE
                    }
                    else if (isRParen(a)) {
                        depth--
                        state = RPAREN_STATE
                    }
                    else {
                        state = SYMBOL_STATE
                    }
                    break
                case RPAREN_STATE:
                    if (isWhite(a))
                        state = WHITE_STATE
                    else if (isDigit(a)) {
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        state = CHAR_STATE
                    }
                    else if (isLParen(a)) {
                        depth++
                        state = LPAREN_STATE
                    }
                    else if (isRParen(a)) {
                        depth--
                        state = RPAREN_STATE
                    }
                    else {
                        state = SYMBOL_STATE
                    }
                    break
                case WHITE_STATE:
                    if (isDigit(a)) {
                        hasToken = true
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        hasToken = true
                        state = CHAR_STATE
                    }
                    else if (isComma(a)) {
                        hasToken = true
                        state = COMMA_STATE
                    }
                    else if (isLParen(a)) {
                        depth++
                        hasToken = true
                        state = LPAREN_STATE
                    }
                    else if (isRParen(a)) {
                        if (depth <= 0) {
                            alert("The right parenthesis ')' is in bad position!")
                            return s.substring(0, i+1)
                        }
                        depth--
                        state = RPAREN_STATE
                    }
                    break
                case COMMA_STATE:
                    if (isDigit(a)) {
                        hasToken = true
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        hasToken = true
                        state = CHAR_STATE
                    }
                    else if (isLParen(a)) {
                        depth++
                        hasToken = true
                        state = LPAREN_STATE
                    }
                    else if (isRParen(a)) {
                        if (depth <= 0) {
                            alert("The right parenthesis ')' is in bad position!")
                            return s.substring(0, i+1)
                        }
                        depth--
                        state = RPAREN_STATE
                    }
                    break
                case SYMBOL_STATE:
                    if (isWhite(a)) {
                        hasToken = true
                        state = WHITE_STATE
                    }
                    else if (isDigit(a)) {
                        hasToken = true
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        hasToken = true
                        state = CHAR_STATE
                    }
                    else if (isLParen(a)) {
                        depth++
                        hasToken = true
                        state = LPAREN_STATE
                    }
                    else if (isRParen(a)) {
                        depth--
                        state = RPAREN_STATE
                    }
                    break
            }

            if (hasToken && depth <= 0 && (state == WHITE_STATE || state == SYMBOL_STATE || state == RPAREN_STATE)) {
                if (state == RPAREN_STATE)
                    i++
                break
            }
        }
        pos2 = i


        state = START_STATE
        hasToken = false
        depth = 0
        i = 0
        for (i = pos - 1; i >= 0; i--) {
            a = s.charAt(i)
            switch (state) {
                case START_STATE:
                    if (isWhite(a))
                        state = WHITE_STATE
                    else if (isDigit(a)) {
                        hasToken = true
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        hasToken = true
                        state = CHAR_STATE
                    }
                    else if (isComma(a)) {
                        hasToken = true
                        state = COMMA_STATE
                    }
                    else if (!hasToken && isLParen(a)) {
                        alert("The left parenthesis '(' is in bad position!")
                        return s.substring(0, pos)
                    }
                    else if (isRParen(a)) {
                        depth++
                        hasToken = true
                        state = RPAREN_STATE
                    }
                    else {
                        state = SYMBOL_STATE
                    }
                    break
                case DIGIT_STATE:
                case CHAR_STATE:
                    if (isWhite(a))
                        state = WHITE_STATE
                    else if (isDigit(a)) {
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        state = CHAR_STATE
                    }
                    else if (isLParen(a)) {
                        if (!hasToken && depth <= 0) {
                            alert("The left parenthesis '(' is in bad position!")
                            return s.substring(0, pos)
                        }
                        depth--
                        state = LPAREN_STATE
                    }
                    else if (isRParen(a)) {
                        depth++
                        state = RPAREN_STATE
                    }
                    else {
                        state = SYMBOL_STATE
                    }
                    break
                case LPAREN_STATE:
                    if (isWhite(a))
                        state = WHITE_STATE
                    else if (isDigit(a)) {
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        state = CHAR_STATE
                    }
                    else if (isLParen(a)) {
                        depth--
                        state = LPAREN_STATE
                    }
                    else if (isRParen(a)) {
                        depth++
                        state = RPAREN_STATE
                    }
                    else {
                        state = SYMBOL_STATE
                    }
                    break
                case RPAREN_STATE:
                    if (isWhite(a))
                        state = WHITE_STATE
                    else if (isDigit(a)) {
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        state = CHAR_STATE
                    }
                    else if (isLParen(a)) {
                        depth--
                        state = LPAREN_STATE
                    }
                    else if (isRParen(a)) {
                        depth++
                        state = RPAREN_STATE
                    }
                    else {
                        state = SYMBOL_STATE
                    }
                    break
                case WHITE_STATE:
                    if (isDigit(a)) {
                        hasToken = true
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        hasToken = true
                        state = CHAR_STATE
                    }
                    else if (isLParen(a)) {
                        depth--
                        hasToken = true
                        state = LPAREN_STATE
                    }
                    else if (isRParen(a)) {
                        if (!hasToken && depth <= 0) {
                            alert("The right parenthesis ')' is in bad position!")
                            return s.substring(0, i+1)
                        }
                        depth++
                        state = RPAREN_STATE
                    }
                    break
                case COMMA_STATE:
                    if (isDigit(a)) {
                        hasToken = true
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        hasToken = true
                        state = CHAR_STATE
                    }
                    else if (isLParen(a)) {
                        depth--
                        hasToken = true
                        state = LPAREN_STATE
                    }
                    else if (isRParen(a)) {
                        if (!hasToken && depth <= 0) {
                            alert("The right parenthesis ')' is in bad position!")
                            return s.substring(0, i+1)
                        }
                        depth++
                        state = RPAREN_STATE
                    }
                    break
                case SYMBOL_STATE:
                    if (isWhite(a)) {
                        hasToken = true
                        state = WHITE_STATE
                    }
                    else if (isDigit(a)) {
                        hasToken = true
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        hasToken = true
                        state = CHAR_STATE
                    }
                    else if (isLParen(a)) {
                        depth--
                        state = LPAREN_STATE
                    }
                    else if (isRParen(a)) {
                        depth++
                        hasToken = true
                        state = RPAREN_STATE
                    }
                    break
            }

            if (hasToken && depth <= 0 && (state == WHITE_STATE || state == SYMBOL_STATE || state == LPAREN_STATE)) {
                if (depth == 0 && state == LPAREN_STATE)
                    i--
                break
            }
        }
        pos1 = i

        var s3 = s.substring(0, pos1 + 1) + fnName + "(" + s.substring(pos1 + 1, pos) + "," + s.substring(pos+op.length, pos2) + ")" + s.substring(pos2)
        return s3
}

    //////////////////////////////////////////////
    // Test Parsing:
    //   1+(1+3)!
    //   1+((1+3)!)
    //   1+sin((1+3)!)
    //   
    //   1+(3!)

function changeLeftToken(s, op, fnName) {
        var pos = s.indexOf(op)
        if (pos < 0)
           return s;
        var len = s.length
        
        var START_STATE = 0
        var WHITE_STATE = 1
        var CHAR_STATE = 2
        var DIGIT_STATE = 3
        var LPAREN_STATE = 4
        var RPAREN_STATE = 5
        var SYMBOL_STATE = 6
        var FINAL_STATE = 99
        var state = START_STATE
        var pos1 = pos
        var hasToken = false
        var depth = 0
        var a
        var i = 0
        for (i = pos - 1; i >= 0; i--) {
            a = s.charAt(i)
            switch (state) {
                case START_STATE:
                    if (isWhite(a))
                        state = WHITE_STATE
                    else if (isDigit(a)) {
                        hasToken = true
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        hasToken = true
                        state = CHAR_STATE
                    }
                    else if (!hasToken && isLParen(a)) {
                        alert("The left parenthesis '(' is in bad position!")
                        return s.substring(0, i+1)
                    }
                    else if (isRParen(a)) {
                        depth++
                        hasToken = true
                        state = RPAREN_STATE
                    }
                    else {
                        state = SYMBOL_STATE
                    }
                    break
                case DIGIT_STATE:
                case CHAR_STATE:
                    if (isWhite(a))
                        state = WHITE_STATE
                    else if (isDigit(a)) {
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        state = CHAR_STATE
                    }
                    else if (isLParen(a)) {
                        if (!hasToken && depth <= 0) {
                            alert("The left parenthesis '(' is in bad position!")
                            return s.substring(0, i+1)
                        }
                        depth--
                        state = LPAREN_STATE
                    }
                    else if (isRParen(a)) {
                        depth++
                        state = RPAREN_STATE
                    }
                    else {
                        state = SYMBOL_STATE
                    }
                    break
                case LPAREN_STATE:
                    if (isWhite(a))
                        state = WHITE_STATE
                    else if (isDigit(a)) {
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        state = CHAR_STATE
                    }
                    else if (isLParen(a)) {
                        depth--
                        state = LPAREN_STATE
                    }
                    else if (isRParen(a)) {
                        depth++
                        state = RPAREN_STATE
                    }
                    else {
                        state = SYMBOL_STATE
                    }
                    break
                case RPAREN_STATE:
                    if (isWhite(a))
                        state = WHITE_STATE
                    else if (isDigit(a)) {
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        state = CHAR_STATE
                    }
                    else if (isLParen(a)) {
                        depth--
                        state = LPAREN_STATE
                    }
                    else if (isRParen(a)) {
                        depth++
                        state = RPAREN_STATE
                    }
                    else {
                        state = SYMBOL_STATE
                    }
                    break
                case WHITE_STATE:
                    if (isDigit(a)) {
                        hasToken = true
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        hasToken = true
                        state = CHAR_STATE
                    }
                    else if (isLParen(a)) {
                        depth--
                        hasToken = true
                        state = LPAREN_STATE
                    }
                    else if (isRParen(a)) {
                        if (!hasToken && depth <= 0) {
                            alert("The right parenthesis ')' is in bad position!")
                            return s.substring(0, i+1)
                        }
                        depth++
                        state = RPAREN_STATE
                    }
                    break
                case SYMBOL_STATE:
                    if (isWhite(a)) {
                        hasToken = true
                        state = WHITE_STATE
                    }
                    else if (isDigit(a)) {
                        hasToken = true
                        state = DIGIT_STATE
                    }
                    else if (isChar(a)) {
                        hasToken = true
                        state = CHAR_STATE
                    }
                    else if (isLParen(a)) {
                        depth--
                        state = LPAREN_STATE
                    }
                    else if (isRParen(a)) {
                        depth++
                        hasToken = true
                        state = RPAREN_STATE
                    }
                    break
            }

            if (hasToken && depth <= 0 && (state == WHITE_STATE || state == SYMBOL_STATE || state == LPAREN_STATE)) {
                break
            }
        }
        pos1 = i

        var s3 = s.substring(0, pos1 + 1) + fnName + "(" + s.substring(pos1 + 1, pos) + ")" + s.substring(pos + op.length)
        return s3
}

function parseEqn(s) {
        var t = changeXtoT(s)
        t = changeLog(t)

        t = changeMult(t);
        t = changeInverseTrig(t)
        while (t.indexOf("^") >= 0) {
             t = changeLeftRightToken(t, "^", "pow")
        }
        while (t.indexOf("**") >= 0) {
            t = changeLeftRightToken(t, "**", "pow")
        }
        while (t.indexOf("!") >= 0) {
            t = changeLeftToken(t, "!", "fact")
        }

        t = changeEqnDelimeters(t)
        t = changeHyperbolic(t)

        t = t.replace(/\s/gi, "")

        return t
}

function evaluateEqn(s) {
        var value = eval(parseEqn(s))
        return value
}

function drawFunction(figName, s, sx1, sx2, sy1, sy2, color) {
    var x1, x2, y1, y2
    with (Math) {
        x1 = eval(parseEqn(sx1))
        x2 = eval(parseEqn(sx2))
        y1 = eval(parseEqn(sy1))
        y2 = eval(parseEqn(sy2))
    }
    if (x1 < x2 && y1 < y2) {
        initPicture(figName, x1, x2, y1, y2)
        axes()
        stroke = "silver"
        text([x2,0],"x","belowleft","x-axis");
        text([0, y2],"y","belowleft","y-axis");
        text([0, 0],"O","belowleft","origin-mark");
        for (var t = 1; less(t, x2); t += 1) {
            dashline([t, y1], [t, y2], "3 5", "");
            text2([t, 0], ""+t, "Arial", "", 12,"below","");
        }
        for (var t = -1; less(x1, t); t -= 1) {
            dashline([t, y1], [t, y2], "3 5", "");
            text2([t, 0],""+t, "Arial", "", 12,"below","");
        }
        for (var t = 1; less(t, y2); t += 1) {
            dashline([x1, t], [x2, t], "3 5", "");
            text2([0, t],""+t, "Arial", "", 12,"left","");
        }
        for (var t = -1; less(y1, t); t -= 1) {
            dashline([x1, t], [x2, t], "3 5", "");
            text2([0, t],""+t, "Arial", "", 12,"left","");
        }

        //  marker = "blue"

        stroke = color
        var a = []
        var sy = parseEqn(s)
        var y;
        var delta = (eval(x2) - eval(x1))/200;
        with (Math) {
            for (var t = x1; less(t, x2+delta); t += delta) {
                y = eval(sy);
                if (y == undefined || y == Number.NaN) {
                    if (a.length > 0)
                        path(a)
                    a = []
                    continue
                }
                else if (a.length > 0 && abs(y - a[a.length - 1][1])/delta > 200 ) {
                    if (a.length > 0)
                        path(a)
                    a = []
                    continue
                }
                else
                    a[a.length] = [t, y]
            }
        }

        path(a)
    }
}



function drawManyFunctions(figName, str, sx1, sx2, sy1, sy2) {
    var colors = ["blue", "green", "red", "navy", "seagreen", "darkgrey", "goldenrod", "magenta"]
    var x1, x2, y1, y2
    with (Math) {
        x1 = eval(parseEqn(sx1))
        x2 = eval(parseEqn(sx2))
        y1 = eval(parseEqn(sy1))
        y2 = eval(parseEqn(sy2))
    }
    var ss = str.split(";")
    if (x1 < x2 && y1 < y2) {
        initPicture(figName, x1, x2, y1, y2)
        axes()
        stroke = "silver"
        text([x2,0],"x","belowleft","x-axis");
        text([0, y2],"y","belowleft","y-axis");
        text([0, 0],"O","belowleft","origin-mark");
        for (var t = 1; less(t, x2); t += 1) {
            dashline([t, y1], [t, y2], "3 5", "");
            text2([t, 0], ""+t, "Arial", "", 12,"below","");
        }
        for (var t = -1; less(x1, t); t -= 1) {
            dashline([t, y1], [t, y2], "3 5", "");
            text2([t, 0],""+t, "Arial", "", 12,"below","");
        }
        for (var t = 1; less(t, y2); t += 1) {
            dashline([x1, t], [x2, t], "3 5", "");
            text2([0, t],""+t, "Arial", "", 12,"left","");
        }
        for (var t = -1; less(y1, t); t -= 1) {
            dashline([x1, t], [x2, t], "3 5", "");
            text2([0, t],""+t, "Arial", "", 12,"left","");
        }

        //  marker = "blue"

        var s
        var sy
        var y
        var delta

        for (var i = 0; i < ss.length; i++) {
            s = ss[i]
            stroke = colors[i % colors.length]
            sy = parseEqn(s)
            delta = (eval(x2) - eval(x1))/200;
            var a = []
            with (Math) 
                for (var t = x1; less(t, x2+delta); t += delta) {
                    y = eval(sy);
                    if (y == undefined || y == Number.NaN) {
                        if (a.length > 0)
                            path(a)
                            a = []
                        continue
                    }
                    else if (a.length > 0 && abs(y - a[a.length - 1][1])/delta > 200 ) {
                        if (a.length > 0)
                            path(a)
                        a = []
                        continue
                    }
                    else
                        a[a.length] = [t, y]
                }

            path(a)
        }
    }
}


function drawFunctionWithAsymptotic(figName, s, asyms, sx1, sx2, sy1, sy2, color) {
    var x1, x2, y1, y2
    with (Math) {
        x1 = eval(parseEqn(sx1))
        x2 = eval(parseEqn(sx2))
        y1 = eval(parseEqn(sy1))
        y2 = eval(parseEqn(sy2))
    }
    if (x1 < x2 && y1 < y2) {
        initPicture(figName, x1, x2, y1, y2)
        axes()
        stroke = "silver"
        text([x2,0],"x","belowleft","x-axis");
        text([0, y2],"y","belowleft","y-axis");
        text([0, 0],"O","belowleft","origin-mark");
        for (var t = 1; less(t, x2); t += 1) {
            dashline([t, y1], [t, y2], "3 5", "");
            text2([t, 0], ""+t, "Arial", "", 12,"below","");
        }
        for (var t = -1; less(x1, t); t -= 1) {
            dashline([t, y1], [t, y2], "3 5", "");
            text2([t, 0],""+t, "Arial", "", 12,"below","");
        }
        for (var t = 1; less(t, y2); t += 1) {
            dashline([x1, t], [x2, t], "3 5", "");
            text2([0, t],""+t, "Arial", "", 12,"left","");
        }
        for (var t = -1; less(y1, t); t -= 1) {
            dashline([x1, t], [x2, t], "3 5", "");
            text2([0, t],""+t, "Arial", "", 12,"left","");
        }

        //  marker = "blue"

        var s1
        var sy, sx
        var y, x
        var delta

        for (var i = 0; i < asyms.length; i++) {
            s1 = asyms[i]
            s1 = s1.replace(/^\s*/,"")
            stroke = "gray"
            if (s1.substring(0, 1) == "y" && s1.indexOf("=") > 0) {
                s1 = s1.substring(s1.indexOf("=") + 1)
                sy = parseEqn(s1)
                delta = (x2 - x1)/200;
                var a = []
                with (Math) 
                    for (var t = x1; less(t, x2+delta); t += delta) {
                        y = eval(sy);
                        if (y == undefined || y == Number.NaN) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else if (a.length > 0 && abs(y - a[a.length - 1][1])/delta > 200 ) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else
                            a[a.length] = [t, y]
                    }

                dashpath(a, null, null, "5 5")
            }
            else if (s1.substring(0, 1) == "x" && s1.indexOf("=") > 0) {
                s1 = s1.substring(s1.indexOf("=") + 1)
                s1 = s1.replace(/y/g, "x")

                sx = parseEqn(s1)
                delta = (y2 - y1)/200;
                var a = []
                with (Math) 
                    for (var t = y1; less(t, y2+delta); t += delta) {
                        x = eval(sx);
                        if (x == undefined || x == Number.NaN) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else if (a.length > 0 && abs(x - a[a.length - 1][1])/delta > 200 ) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else
                            a[a.length] = [x, t]
                    }

                dashpath(a, null, null, "5 5")
            }
        }


        stroke = color
        var a = []
        sy = parseEqn(s)
        delta = (eval(x2) - eval(x1))/200;
        with (Math) {
            var t
            for (t = x1; less(t, x2); t += delta) {
                y = eval(sy);
                if (y == undefined || y == Number.NaN) {
                    if (a.length > 0)
                        path(a)
                    a = []
                    continue
                }
                else if (a.length > 0 && abs(y - a[a.length - 1][1])/delta > 200 ) {
                    if (a.length > 0)
                        path(a)
                    a = []
                    continue
                }
                else
                    a[a.length] = [t, y]
            }

            t = x2
            y = eval(sy);
            if (y == undefined || y == Number.NaN)
                a[a.length] = [t, y]
        }

        path(a)
    }
}


function drawFunctionWithAsymptotics(figName, s, asyms, sx1, sx2, sy1, sy2, dx1, dx2, dy1, dy2, color) {
    var wx1, wx2, wy1, wy2
    var x1, x2, y1, y2
    with (Math) {
        wx1 = eval(parseEqn(sx1))
        wx2 = eval(parseEqn(sx2))
        wy1 = eval(parseEqn(sy1))
        wy2 = eval(parseEqn(sy2))
        x1 = eval(parseEqn(dx1))
        x2 = eval(parseEqn(dx2))
        y1 = eval(parseEqn(dy1))
        y2 = eval(parseEqn(dy2))
    }
    if (wx1 < wx2 && wy1 < wy2) {
        initPicture(figName, wx1, wx2, wy1, wy2)
        axes()
        stroke = "silver"
        text([wx2,0],"x","belowleft","x-axis");
        text([0, wy2],"y","belowleft","y-axis");
        text([0, 0],"O","belowleft","origin-mark");
        for (var t = 1; less(t, wx2); t += 1) {
            dashline([t, wy1], [t, wy2], "3 5", "");
            text2([t, 0], ""+t, "Arial", "", 12,"below","");
        }
        for (var t = -1; less(wx1, t); t -= 1) {
            dashline([t, wy1], [t, wy2], "3 5", "");
            text2([t, 0],""+t, "Arial", "", 12,"below","");
        }
        for (var t = 1; less(t, wy2); t += 1) {
            dashline([wx1, t], [wx2, t], "3 5", "");
            text2([0, t],""+t, "Arial", "", 12,"left","");
        }
        for (var t = -1; less(wy1, t); t -= 1) {
            dashline([wx1, t], [wx2, t], "3 5", "");
            text2([0, t],""+t, "Arial", "", 12,"left","");
        }

        //  marker = "blue"

        var s1
        var sy, sx
        var y, x
        var delta

        for (var i = 0; i < asyms.length; i++) {
            s1 = asyms[i]
            s1 = s1.replace(/^\s*/,"")
            stroke = "gray"
            if (s1.substring(0, 1) == "y" && s1.indexOf("=") > 0) {
                s1 = s1.substring(s1.indexOf("=") + 1)
                sy = parseEqn(s1)
                delta = (x2 - x1)/200;
                var a = []
                with (Math) 
                    for (var t = x1; less(t, x2+delta); t += delta) {
                        y = eval(sy);
                        if (y == undefined || y == Number.NaN) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else if (a.length > 0 && abs(y - a[a.length - 1][1])/delta > 200 ) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else
                            a[a.length] = [t, y]
                    }

                dashpath(a, null, null, "5 5")
            }
            else if (s1.substring(0, 1) == "x" && s1.indexOf("=") > 0) {
                s1 = s1.substring(s1.indexOf("=") + 1)
                s1 = s1.replace(/y/g, "x")

                sx = parseEqn(s1)
                delta = (y2 - y1)/200;
                var a = []
                with (Math) 
                    for (var t = y1; less(t, y2+delta); t += delta) {
                        x = eval(sx);
                        if (x == undefined || x == Number.NaN) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else if (a.length > 0 && abs(x - a[a.length - 1][1])/delta > 200 ) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else
                            a[a.length] = [x, t]
                    }

                dashpath(a, null, null, "5 5")
            }
        }


        stroke = color
        var a = []
        s = s.replace(/^\s*/,"")
        if (s.substring(0, 1) == "y" && s.indexOf("=") > 0) {
            s = s.substring(s.indexOf("=") + 1)
            sy = parseEqn(s)
            delta = (eval(x2) - eval(x1))/200;
            with (Math) {
                var t
                for (t = x1; less(t, x2); t += delta) {
                    y = eval(sy)
                    if (y == undefined || y == Number.NaN) {
                        if (a.length > 0)
                            path(a)
                        a = []
                        continue
                    }
                    else if (a.length > 0 && abs(y - a[a.length - 1][1])/delta > 200 ) {
                        if (a.length > 0)
                            path(a)
                        a = []
                        continue
                    }
                    else
                        a[a.length] = [t, y]
                }

                t = x2
                y = eval(sy);
                if (y == undefined || y == Number.NaN)
                    a[a.length] = [t, y]
            }

            path(a)
        }
        else if (s.substring(0, 1) == "x" && s.indexOf("=") > 0) {
            s = s.substring(s.indexOf("=") + 1)
            s = changeYtoT(s)
            sy = parseEqn(s)
            delta = (eval(y2) - eval(y1))/200;
            with (Math) {
                var t
                for (t = y1; less(t, y2); t += delta) {
                    y = eval(sy);
                    if (y == undefined || y == Number.NaN) {
                        if (a.length > 0)
                            path(a)
                        a = []
                        continue
                    }
                    else if (a.length > 0 && abs(y - a[a.length - 1][0])/delta > 200 ) {
                        if (a.length > 0)
                            path(a)
                        a = []
                        continue
                    }
                    else
                        a[a.length] = [y, t]
                }

                t = y2
                y = eval(sy);
                if (y == undefined || y == Number.NaN)
                    a[a.length] = [y, t]
            }

            path(a)
        }
    }
}


function drawParamCurveWithAsymptotics(figName, fnF, fnG, asyms, sx1, sx2, sy1, sy2, dt1, dt2, dx1, dx2, dy1, dy2, color) {
    var wx1, wx2, wy1, wy2
    var t1, t2, x1, x2, y1, y2
    with (Math) {
        wx1 = eval(parseEqn(sx1))
        wx2 = eval(parseEqn(sx2))
        wy1 = eval(parseEqn(sy1))
        wy2 = eval(parseEqn(sy2))
        t1 = eval(parseEqn(dt1))
        t2 = eval(parseEqn(dt2))
        x1 = eval(parseEqn(dx1))
        x2 = eval(parseEqn(dx2))
        y1 = eval(parseEqn(dy1))
        y2 = eval(parseEqn(dy2))
    }
    if (wx1 < wx2 && wy1 < wy2) {
        initPicture(figName, wx1, wx2, wy1, wy2)
        axes()
        stroke = "silver"
        text([wx2,0],"x","belowleft","x-axis");
        text([0, wy2],"y","belowleft","y-axis");
        text([0, 0],"O","belowleft","origin-mark");
        for (var t = 1; less(t, wx2); t += 1) {
            dashline([t, wy1], [t, wy2], "3 5", "");
            text2([t, 0], ""+t, "Arial", "", 12,"below","");
        }
        for (var t = -1; less(wx1, t); t -= 1) {
            dashline([t, wy1], [t, wy2], "3 5", "");
            text2([t, 0],""+t, "Arial", "", 12,"below","");
        }
        for (var t = 1; less(t, wy2); t += 1) {
            dashline([wx1, t], [wx2, t], "3 5", "");
            text2([0, t],""+t, "Arial", "", 12,"left","");
        }
        for (var t = -1; less(wy1, t); t -= 1) {
            dashline([wx1, t], [wx2, t], "3 5", "");
            text2([0, t],""+t, "Arial", "", 12,"left","");
        }

        //  marker = "blue"

        var s1
        var sy, sx
        var y, x
        var delta

        for (var i = 0; i < asyms.length; i++) {
            s1 = asyms[i]
            s1 = s1.replace(/^\s*/,"")
            stroke = "gray"
            if (s1.substring(0, 1) == "y" && s1.indexOf("=") > 0) {
                s1 = s1.substring(s1.indexOf("=") + 1)
                sy = parseEqn(s1)
                delta = (x2 - x1)/200;
                var a = []
                with (Math) 
                    for (var t = x1; less(t, x2+delta); t += delta) {
                        y = eval(sy);
                        if (y == undefined || y == Number.NaN) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else if (a.length > 0 && abs(y - a[a.length - 1][1])/delta > 200 ) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else
                            a[a.length] = [t, y]
                    }

                dashpath(a, null, null, "5 5")
            }
            else if (s1.substring(0, 1) == "x" && s1.indexOf("=") > 0) {
                s1 = s1.substring(s1.indexOf("=") + 1)
                s1 = s1.replace(/y/g, "x")

                sx = parseEqn(s1)
                delta = (y2 - y1)/200;
                var a = []
                with (Math) 
                    for (var t = y1; less(t, y2+delta); t += delta) {
                        x = eval(sx);
                        if (x == undefined || x == Number.NaN) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else if (a.length > 0 && abs(x - a[a.length - 1][1])/delta > 200 ) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else
                            a[a.length] = [x, t]
                    }

                dashpath(a, null, null, "5 5")
            }
        }


        stroke = color
        var a = []
        var sf = fnF.replace(/^\s*/,"")
        var sg = fnG.replace(/^\s*/,"")

        var f = parseEqn(sf)
        var g = parseEqn(sg)

        delta = (eval(t2) - eval(t1))/200;
        with (Math) {
            var t
            for (t = t1; less(t, t2); t += delta) {
                x = eval(f)
                y = eval(g)
                if (y == undefined || y == Number.NaN || x == undefined || x == Number.NaN) {
                    if (a.length > 0)
                        path(a)
                    a = []
                    continue
                }
                else if (a.length > 0 && abs(y - a[a.length - 1][1])/delta > 200 ) {
                    if (a.length > 0)
                        path(a)
                    a = []
                    continue
                }
                else
                    a[a.length] = [x, y]
            }

            t = t2
            x = eval(f);
            y = eval(g);
            if (y == undefined || y == Number.NaN || x == undefined || x == Number.NaN)
                a[a.length] = [x, y]
        }

        path(a)
    }
}


function drawPolarCurveWithAsymptotics(figName, fnF, asyms, sx1, sx2, sy1, sy2, dt1, dt2, dx1, dx2, dy1, dy2, color) {
    var wx1, wx2, wy1, wy2
    var t1, t2, x1, x2, y1, y2
    with (Math) {
        wx1 = eval(parseEqn(sx1))
        wx2 = eval(parseEqn(sx2))
        wy1 = eval(parseEqn(sy1))
        wy2 = eval(parseEqn(sy2))
        t1 = eval(parseEqn(dt1))
        t2 = eval(parseEqn(dt2))
        x1 = eval(parseEqn(dx1))
        x2 = eval(parseEqn(dx2))
        y1 = eval(parseEqn(dy1))
        y2 = eval(parseEqn(dy2))
    }
    if (wx1 < wx2 && wy1 < wy2) {
        initPicture(figName, wx1, wx2, wy1, wy2)
        axes()
        stroke = "silver"
        text([wx2,0],"x","belowleft","x-axis");
        text([0, wy2],"y","belowleft","y-axis");
        text([0, 0],"O","belowleft","origin-mark");
        with (Math) {
            for (var t = 0.5; less(t, sqrt(wx2*wx2 + wy2*wy2)); t += 0.5) {
                dashcircle([0,0], t, "3 5", "");
                text2([t, 0], ""+t, "Arial", "", 12,"below","");
                text2([-t, 0], ""+(-t), "Arial", "", 12,"below","");
                text2([0, t], ""+t, "Arial", "", 12,"left","");
                text2([0, -t], ""+(-t), "Arial", "", 12,"left","");
            }
            for (var t = pi/12; less(t, pi/4); t += pi/12) {
                dashline([0,0], [wx2, tan(t)*wx2], "3 5", "");
                dashline([0,0], [wx2, -tan(t)*wx2], "3 5", "");
                dashline([0,0], [wx1, tan(t)*wx1], "3 5", "");
                dashline([0,0], [wx1, -tan(t)*wx1], "3 5", "");
            }
            for (var t = pi/4; less(t, pi/2); t += pi/12) {
                dashline([0,0], [cot(t)*wx2, wy2], "3 5", "");
                dashline([0,0], [cot(t)*wx2, -wy2], "3 5", "");
                dashline([0,0], [cot(t)*wx1, wy1], "3 5", "");
                dashline([0,0], [cot(t)*wx1, -wy1], "3 5", "");
            }
        }

        //  marker = "blue"

        var s1
        var sy, sx
        var y, x
        var delta

        for (var i = 0; i < asyms.length; i++) {
            s1 = asyms[i]
            s1 = s1.replace(/^\s*/,"")
            stroke = "gray"
            if (s1.substring(0, 1) == "y" && s1.indexOf("=") > 0) {
                s1 = s1.substring(s1.indexOf("=") + 1)
                sy = parseEqn(s1)
                delta = (x2 - x1)/200;
                var a = []
                with (Math) 
                    for (var t = x1; less(t, x2+delta); t += delta) {
                        y = eval(sy);
                        if (y == undefined || y == Number.NaN) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else if (a.length > 0 && abs(y - a[a.length - 1][1])/delta > 200 ) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else
                            a[a.length] = [t, y]
                    }

                dashpath(a, null, null, "5 5")
            }
            else if (s1.substring(0, 1) == "x" && s1.indexOf("=") > 0) {
                s1 = s1.substring(s1.indexOf("=") + 1)
                s1 = s1.replace(/y/g, "x")

                sx = parseEqn(s1)
                delta = (y2 - y1)/200;
                var a = []
                with (Math) 
                    for (var t = y1; less(t, y2+delta); t += delta) {
                        x = eval(sx);
                        if (x == undefined || x == Number.NaN) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else if (a.length > 0 && abs(x - a[a.length - 1][1])/delta > 200 ) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else
                            a[a.length] = [x, t]
                    }

                dashpath(a, null, null, "5 5")
            }
        }


        stroke = color
        var a = []
        var sf = fnF.replace(/^\s*/,"")
        while (sf.indexOf("theta") >= 0)
            sf = sf.replace(/theta/,"t")
        var sx = parseEqn("(" + sf + ")cos(t)")
        var sy = parseEqn("(" + sf + ")sin(t)")

        delta = (eval(t2) - eval(t1))/200;
        with (Math) {
            var t
            for (t = t1; less(t, t2); t += delta) {
                x = eval(sx)
                y = eval(sy)
                if (y == undefined || y == Number.NaN || x == undefined || x == Number.NaN) {
                    if (a.length > 0)
                        path(a)
                    a = []
                    continue
                }
                else if (a.length > 0 && abs(y - a[a.length - 1][1])/delta > 200 ) {
                    if (a.length > 0)
                        path(a)
                    a = []
                    continue
                }
                else
                    a[a.length] = [x, y]
            }

            t = t2
            x = eval(sx);
            y = eval(sy);
            if (y == undefined || y == Number.NaN || x == undefined || x == Number.NaN)
                a[a.length] = [x, y]
        }

        path(a)
    }
}


function drawPolarEqnCurveWithAsymptotics(figName, eqnF, asyms, sx1, sx2, sy1, sy2, dr1, dr2, dt1, dt2, dx1, dx2, dy1, dy2, xstep, ystep, color) {
    var wx1, wx2, wy1, wy2
    var r1, r2, t1, t2, x1, x2, y1, y2
    var xs, ys
    with (Math) {
        wx1 = eval(parseEqn(sx1))
        wx2 = eval(parseEqn(sx2))
        wy1 = eval(parseEqn(sy1))
        wy2 = eval(parseEqn(sy2))
        r1 = eval(parseEqn(dr1))
        r2 = eval(parseEqn(dr2))
        t1 = eval(parseEqn(dt1))
        t2 = eval(parseEqn(dt2))
        x1 = eval(parseEqn(dx1))
        x2 = eval(parseEqn(dx2))
        y1 = eval(parseEqn(dy1))
        y2 = eval(parseEqn(dy2))
        xs = eval(parseEqn(xstep))
        ys = eval(parseEqn(ystep))
    }
 
    if (wx1 < wx2 && wy1 < wy2) {
        initPicture(figName, wx1, wx2, wy1, wy2)
        axes()
        stroke = "silver"
        text([wx2 - (wx2 - wx1)/100.0, 0],"x","below","x-axis");
        text([0, wy2 - (wy2 - wy1)/100.0],"y","left","y-axis");
        text([0, 0],"O","belowleft","origin-mark");
        with (Math) {
            var deltaX = 0.5
            var deltaY = 0.5
            if (xs > 0)
                deltaX = xs
            if (ys > 0)
                deltaY = ys
            var bound = sqrt(wx2*wx2 + wy2*wy2)
            // bound = sqrt(wx1*wx1 + wy1*wy1) > bound ? sqrt(wx1*wx1 + wy1*wy1) : bound
            // bound = sqrt(wx1*wx1 + wy2*wy2) > bound ? sqrt(wx1*wx1 + wy2*wy2) : bound
            // bound = sqrt(wx2*wx2 + wy1*wy1) > bound ? sqrt(wx2*wx2 + wy1*wy1) : bound
            // alert(floor(bound/6)/2)
            for (var t = deltaX; less(t, bound + deltaX); t += deltaX) {
                dashcircle([0,0], t, "3 5", "");
                text2([t, 0], ""+t, "Arial", "", 12,"below","");
                text2([-t, 0], ""+(-t), "Arial", "", 12,"below","");
            }
            for (var t = deltaY; less(t, bound + deltaY); t += deltaY) {
                text2([0, t], ""+t, "Arial", "", 12,"left","");
                text2([0, -t], ""+(-t), "Arial", "", 12,"left","");
            }
            for (var t = pi/12; less(t, pi/4); t += pi/12) {
                dashline([0,0], [wx2, tan(t)*wx2], "3 5", "");
                dashline([0,0], [wx2, -tan(t)*wx2], "3 5", "");
                dashline([0,0], [wx1, tan(t)*wx1], "3 5", "");
                dashline([0,0], [wx1, -tan(t)*wx1], "3 5", "");
            }
            for (var t = pi/4; less(t, pi/2); t += pi/12) {
                dashline([0,0], [cot(t)*wx2, wy2], "3 5", "");
                dashline([0,0], [cot(t)*wx2, -wy2], "3 5", "");
                dashline([0,0], [cot(t)*wx1, wy1], "3 5", "");
                dashline([0,0], [cot(t)*wx1, -wy1], "3 5", "");
            }
        }

        //  marker = "blue"

        var s1
        var sy, sx
        var y, x
        var delta

        for (var i = 0; i < asyms.length; i++) {
            s1 = asyms[i]
            s1 = s1.replace(/^\s+/,"")
            stroke = "gray"
            if (s1.substring(0, 1) == "y" && s1.indexOf("=") > 0) {
                s1 = s1.substring(s1.indexOf("=") + 1)
                sy = parseEqn(s1)
                delta = (x2 - x1)/200;
                var a = []
                with (Math) 
                    for (var t = x1; less(t, x2+delta); t += delta) {
                        y = eval(sy);
                        if (y == undefined || y == Number.NaN) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else if (a.length > 0 && abs(y - a[a.length - 1][1])/delta > 200 ) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else
                            a[a.length] = [t, y]
                    }

                dashpath(a, null, null, "5 5")
            }
            else if (s1.substring(0, 1) == "x" && s1.indexOf("=") > 0) {
                s1 = s1.substring(s1.indexOf("=") + 1)
                s1 = s1.replace(/y/g, "x")

                sx = parseEqn(s1)
                delta = (y2 - y1)/200;
                var a = []
                with (Math) 
                    for (var t = y1; less(t, y2+delta); t += delta) {
                        x = eval(sx);
                        if (x == undefined || x == Number.NaN) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else if (a.length > 0 && abs(x - a[a.length - 1][1])/delta > 200 ) {
                            if (a.length > 0)
                                dashpath(a, null, null, "5 5")
                            a = []
                            continue
                        }
                        else
                            a[a.length] = [x, t]
                    }

                dashpath(a, null, null, "5 5")
            }
        }


        stroke = color
        var a = []
        var sf = eqnF.replace(/^\s+/,"")
        if (sf.substring(0, 1) == "r" && sf.indexOf("=") > 0) {

            sf = sf.substring(sf.indexOf("=") + 1)
            sy = parseEqn(sf)
            while (sf.indexOf("theta") >= 0)
                sf = sf.replace(/theta/,"t")

            var sx = parseEqn("(" + sf + ")cos(t)")
            var sy = parseEqn("(" + sf + ")sin(t)")

            delta = (eval(t2) - eval(t1))/200;
            with (Math) {
                var t
                for (t = t1; less(t, t2); t += delta) {
                    x = eval(sx)
                    y = eval(sy)
                    if (y == undefined || y == Number.NaN || x == undefined || x == Number.NaN) {
                        if (a.length > 0)
                            path(a)
                        a = []
                        continue
                    }
                    else if (a.length > 0 && abs(y - a[a.length - 1][1])/delta > 200 ) {
                        if (a.length > 0)
                            path(a)
                        a = []
                        continue
                   }
                   else
                       a[a.length] = [x, y]
                }

                t = t2
                x = eval(sx);
                y = eval(sy);
                if (y == undefined || y == Number.NaN || x == undefined || x == Number.NaN)
                    a[a.length] = [x, y]
            }

            path(a)
        }
        else if (sf.substring(0, 5) == "theta" && sf.indexOf("=") > 0) {
            sf = sf.substring(sf.indexOf("=") + 1)
            sf = parseEqn(sf)
            while (sf.indexOf("r") >= 0)
                sf = sf.replace(/r/,"t")


            var sx = parseEqn("t*cos(" + sf + ")")
            var sy = parseEqn("t*sin(" + sf + ")")

            delta = (eval(t2) - eval(t1))/200;
            with (Math) {
                var t
                for (t = r1; less(t, r2); t += delta) {
                    x = eval(sx)
                    y = eval(sy)
                    if (y == undefined || y == Number.NaN || x == undefined || x == Number.NaN) {
                        if (a.length > 0)
                            path(a)
                        a = []
                        continue
                    }
                    else if (a.length > 0 && abs(y - a[a.length - 1][1])/delta > 200 ) {
                        if (a.length > 0)
                            path(a)
                        a = []
                        continue
                   }
                   else
                       a[a.length] = [x, y]
                }

                t = t2
                x = eval(sx);
                y = eval(sy);
                if (y == undefined || y == Number.NaN || x == undefined || x == Number.NaN)
                    a[a.length] = [x, y]
            }

            path(a)
        }
    }
}
