using System;
using System.Collections.Generic;

namespace Knoks.Sandbox
{
    public static class StringEvaluation
    {
        //StringEvaluation.Eval("(12+22)");
        //StringEvaluation.Eval("5*4/2");
        //StringEvaluation.Eval("((3+5)-6)");
        //StringEvaluation.Eval("pow(3,5)");
        //StringEvaluation.Eval("sqrt(4)");
        //StringEvaluation.Eval("2.5*2");
        //StringEvaluation.Eval("2+3* 3");
        //StringEvaluation.Eval("sqrt(sqrt(pow(2,2)))");

        public static double Eval(string input)
        {
            var lambdaParser = new NReco.Linq.LambdaParser();
            //var ans = Evaluate(input);
            IDictionary<string, object> varContext = new Dictionary<string, object>();
            varContext["pow"] = (Func<string, string, double>)((num, power) => Math.Pow(Eval(num), Eval(power)));
            varContext["sqrt"] = (Func<string, double>)((num) => Math.Sqrt(Eval(num)));
            var ans = lambdaParser.Eval(input, varContext);
            Console.WriteLine(input + " = " + ans);
            return Convert.ToDouble(ans);
        }

        public static double Evaluate(string input)
        {
            string expr = "(" + input + ")";
            Stack<string> ops = new Stack<string>();
            Stack<double> vals = new Stack<double>();

            for (int i = 0; i < expr.Length; i++)
            {
                string s = expr.Substring(i, 1);
                if (s.Equals("(")) { }
                else if (s.Equals("+")) ops.Push(s);
                else if (s.Equals("-")) ops.Push(s);
                else if (s.Equals("*")) ops.Push(s);
                else if (s.Equals("/")) ops.Push(s);
                else if (s.Equals("^")) ops.Push(s);
                else if (s.Equals(")"))
                {
                    int count = ops.Count;
                    while (count > 0)
                    {
                        string op = ops.Pop();
                        double v = vals.Pop();
                        if (op.Equals("+")) v = vals.Pop() + v;
                        else if (op.Equals("-")) v = vals.Pop() - v;
                        else if (op.Equals("*")) v = vals.Pop() * v;
                        else if (op.Equals("/")) v = vals.Pop() / v;
                        else if (op.Equals("^")) v = Math.Pow(v, vals.Pop());
                        vals.Push(v);

                        count--;
                    }
                }
                else vals.Push(double.Parse(s));
            }
            return vals.Pop();
        }
    }
}
