Page 1 of 1

Advanced Equations Parser tutorial: Part II - Coding the Standard Calc Rate Topic: -----

#1 pryogene  Icon User is offline

  • The Leafiest of the Leif's
  • member icon

Reputation: 42
  • View blog
  • Posts: 670
  • Joined: 30-June 09

Posted 24 February 2011 - 01:03 PM

Equatron: Advanded Equations parser for Windows Phone 7 tutorial
Part II: Coding the standard calculator and Introduction to the Advanced calculator

Hi again, and I humbly welcome you back to my series of tutorials on building an advanced equations parser, named ‘Equatron’, for Microsoft’s Windows Phone 7 platform. If you’re here, then I’m assuming you’ve read, followed and completed my first tutorial successfully; I would however seriously advise you to go back and check everything just to make sure, as if any one thing is wrong, you’ll have problems following this tutorial. As with all parts of this series, I don’t intend to post completed code at the end in some archive for some lazy-ass to download, that’d make this way to easy, and you wouldn’t learn anything; I’ve worked hard to make this, and I intend for you to work hard to get the end result I have – fair’s fair after-all!

So, to recap, last time I introduced you to our ‘Equatron’ project (the joke still stands… no laughs?), we built up our user interface for the standard calculator, and I gave a relatively small introduction to the two ways we can work with our interface – hard coded xaml, or runtime modification with C# - which we will work with depending on what the situation requires. If we need it to change, we use C#, if it’s static or animated, xaml. Today we’re going to be working with the Visual Studio IDE and C#, absolutely nothing else. So whenever you’re ready, by which I mean once you’ve gone back and checked your work so far (I cannot stress how important it is that you do that, if anything is wrong, some of the following really won’t work, I assure you), continue on reading and we’ll begin.

But what are we coding exactly?

This is easy enough to answer, when we designed our interface last time, we put in buttons that related to functions we want our calculator to perform: Addition, Subtraction, Multiplication, Division, Square rooting, Reciprocating, Negation (or inversion), Percentage finding, plus our memory functions – Clear, Recall, Save, Add to and Subtract from. This time round, we’re going to add the code for all of these.

I’m ready, where do I start?

We’ll start off easy. Naturally, we’ll be performing a lot of mathematical operations so it will help us to have a class of reusable code. So we’ll begin with a few helper functions which we’ll store in a class called NumericalHelper, which we’ll define below:

public static class NumericalHelper
{
}



Got that? Good! Inside here, let’s start simple with our standard functions:

        public static float Add(float a, float B)/>
        {
            return (a + B)/>;
        }
        public static float Subtract(float a, float B)/>
        {
            return (a - B)/>;
        }
        public static float Multiply(float a, float B)/>
        {
            return (a * B)/>;
        }
        public static float Divide(float a, float B)/>
        {
            return (a / B)/>;
        }
        public static float SquareRoot(float a)
        {
            return ((float)Math.Sqrt((double)a));
        }
        public static float Reciprocate(float a)
        {
            return (1 / a);
        }
        public static float Negate(float a)
        {
            return (a * -1);
        }
        public static float Percent(float a, float percent)
        {
            return ((a / 100) * percent);
        }



All those should be self-explanatory, but for those who are slightly dumber than others: Our first four methods are simple Add, Subtract, Multiply by and Divide by functions. After this, Math.Sqrt() is used to find the square root of a number, we’ll come back to the way this is written in a moment. A reciprocal is 1 over our value, i.e. 1 over ‘x’, and is found by performing 1 divided by a. Negation (or inversion) is simply taking our value and inverting the sign – positive becomes negative and vice versa – which we do by multiplying the value by ‘-1’, which will always result in our number inverted. And lastly, in the percentage method, we do a divided by 100 to get 1 per cent of the value, and then multiply the result by the percentage we want. In the case of Math.Sqrt(), we have written it oddly, this is because it’s input and return values are ‘double’. We want them to be float, so at the beginning of the line we ‘cast’ our variable type as ‘(float)’ – this converts the methods output automatically from double to float. The input must be converted to a double, so we cast the type as ‘(double)’ – see the likeness?

So now we have our basic mathematical IO functions, we want to start working on our actual calculator. We can start simple, for each of our 10 – yes, look, there’s 10 – number buttons, we’ll type the following:

            txtSum.Text += "x";
            _preParse += "x";



Where ‘x’ is in the code, replace it with the corresponding number. In addition, we also need to work with decimals. As you saw in our previous session, we work with a string variable before we convert and add each new number to the stack. Yes, you guessed it, this means our _decimal value is redundant – go ahead and remove it. In our decimal code block, we want to do the following:

            if (!_preParse.Contains('.'))
            {
                txtSum.Text += ".";
                _preParse += ".";
            }
            else
            {
                Zero(sender, e);
            }



In here, we first check if _preParse already contains a decimal – if it does, we just put zero (we add a ‘decimal place’), and if it doesn’t, we add the decimal to the text box and the _preParse value. This means that if we already have a decimal, we can’t add a new one, but it still allows us to have more than one decimal per sum – which is where _decimal would have come in, but proved it redundant.

Now we have our numbers, we want to program our functions, so again, we’ll start simple. In our Addition, Subtract, Multiplication and Division code blocks, we want to write four lines. The first will parse our _preParse variable into a number and add it to our value stack, the second will nullify _preParse, and the third will add the corresponding operator to the operators stack.

                _values.Add(float.Parse(_preParse));
                txtSum.Text += text;
                _preParse = string.Empty;
                _operators.Add(x);



Again, where ‘x’ is we need to change it out for the correct StandardOperator variable. One thing that should be apparent to you is that we cannot have more operators than numbers, this would result in our calculator attempting to perform functions on numbers we don’t have, which therefore get substituted for 0 and would completely mess up our equation. Nothing worse than having a good result multiplied by 0, huh! This is prevented by not allowing us to add an operator if _preParse is empty, as that means no number has been input.

The next 4 functions – Square root, Reciprocate, Negate and Per cent – work slightly differently, and each one is listed in the code definition below, explanation following:


private bool _inverse = false;

        private void Inverse(object sender, RoutedEventArgs e)
        {
            if (!_inverse)
            {
                string inverse = "-" + _preParse;
                _inverse = true;
                //txtSum.Text.Insert(txtSum.Text.IndexOf(_preParse, 0), "-"); // problem lies here
                txtSum.Text = txtSum.Text.Insert((txtSum.Text.Length - 1) - (_preParse.Length - 1), "neg(");
                _preParse = inverse;
            }
            else
            {
                string inverse = _preParse.Remove(0, 1);
                txtSum.Text = txtSum.Text.Replace("neg(", "");
                txtSum.Text = txtSum.Text.Replace(")", "");
            }
        }
        private void SquareRoot(object sender, RoutedEventArgs e)
        {
            float rec = float.Parse(txtSum.Text);
            txtSum.Text = "sqrt(" + rec + ")";
            lstHistory.Items.Add(txtSum.Text);
            lstHistory.SelectedIndex = lstHistory.Items.Count - 1;
            _totals = NumericalHelper.SquareRoot(rec);
            txtSum.Text = _totals.ToString();
        }
        private void Reciprocal(object sender, RoutedEventArgs e)
        {
            float rec = float.Parse(txtSum.Text);
            txtSum.Text = "rec(" + rec + ")";
            lstHistory.Items.Add(txtSum.Text);
            lstHistory.SelectedIndex = lstHistory.Items.Count - 1;
            _totals = NumericalHelper.Reciprocate(rec);
            txtSum.Text = _totals.ToString();
        }
        private void Percentage(object sender, RoutedEventArgs e)
        {
            if (_operators.Count > 0)
            {
                _values.Add(float.Parse(_preParse));
                txtSum.Text += "%";
                float t = NumericalHelper.Percent(_values[0], _values[1]);
                StandardOperator op = _operators[0];
                if (op == StandardOperator.Add)
                    _totals = NumericalHelper.Add(_values[0], t);
                else if (op == StandardOperator.Subtract)
                    _totals = NumericalHelper.Subtract(_values[0], t);
                else if (op == StandardOperator.Divide)
                    _totals = NumericalHelper.Divide(_values[0], t);
                else if (op == StandardOperator.Multiply)
                    _totals = NumericalHelper.Multiply(_values[0], t);
                lstHistory.Items.Add(txtSum.Text);
                lstHistory.SelectedIndex = lstHistory.Items.Count - 1;
                txtSum.Text = _totals.ToString();
            }
        }



Firstly, in our Square root function, we want to parse the number, and then we change our onscreen input to show that we are square rooting it with the notation ‘sqrt(x)’. As with all our completed equations, we next add a listing to our history box, and set it as the most recent. Then, we perform the operator listed in NumericalHelper, and then update the onscreen input with our total. Our Reciprocal function is similar, except we notate with ‘rec(x)’, and use the Reciprocate function of NumericalHelper. Our inverse is a little different. We have defined a Boolean variable _inverse, this tells us if we have already inversed our number, and will allow us to re inverse it if need be. If we are inversed, we add a minus sign to our _preParse variable, and add ‘neg(x)’ to our onscreen display. In the case of the opposite, we do the reverse – remove the minus, and the ‘neg(x)’ notation. Lastly, Percentages; this one is very different, as when we press the percentage button we want to actually find the percentage and then equate the equation. So to start, we must check for at least one operator – if there is, continue. Add our value, as we do, add the percentage sign to the onscreen input, as normal, apply the percentage operator from NumericalHelper to our ONLY TWO NUMBERS (values 0 and 1 in the _values list, if there’s more then something’s gone wrong), and then apply the resulting variable against our first number in a series of if statements determining which operator from NumericalHelper to apply. Lastly, again as usual, we add our history item and update our onscreen input.

You will have noticed the _totals variable, yes? This variable is what we apply everything to, and it is maintained through all calculations until we press clear everything.

Problem? Should be, as in our first 4 functions (add, subtract etc.) we are not checking to see if we actually have a number to work with, so let’s adapt our code a little:

        private void AddValueToStack(StandardOperator operation, string text)
        {
            if (!string.IsNullOrEmpty(_preParse))
            {
                _values.Add(float.Parse(_preParse));
                if (_inverse)
                    txtSum.Text += ")";
                _inverse = false;
                txtSum.Text += text;
                _preParse = string.Empty;
                _operators.Add(operation);
            }
        }



Now, we use a separate function to minimize code space, and check that we are indeed working with a number before actually working on it – can’t work on non-existent information. In our 4 basic operator methods, we just do this instead:

            AddValueToStack(x, "z");



Replace 'x' with the StandardOperator, and Z with the equivalent string symbol (+, -, /, *).

Okay, so memory. Our calculator needs to remember simple numbers, so this is where we begin to use our _memory function. We’ll again start simple, with the ‘save’ function:

        private void SaveToMemory(object sender, RoutedEventArgs e)
        {
            //saves to or overwrites
            try
            {
                _memory = float.Parse(txtSum.Text);
            }
            catch
            {
                Equals(sender, e);
                _memory = float.Parse(txtSum.Text);
            }
        }



We have used a try...catch loop here because our textbox could contain an equation. If it doesn’t, our memory is equal to the number in the textbox, if it is an equation though, it will throw an error, to which we respond by first equating the equation and THEN saving it as memory. Next, Clear and recall:

        private void ClearMemory(object sender, RoutedEventArgs e)
        {
            _memory = 0;
        }
        private void RecallMemory(object sender, RoutedEventArgs e)
        {
                txtSum.Text = _memory.ToString();
                _preParse = _memory.ToString();
        }



In clear, we just set it to 0, no need to explain. In recall, we take our memory value and apply it to the textbox and our _preParse variable. Adding and subtracting from memory are similar to our ‘save’ function:

        private void AddToMemory(object sender, RoutedEventArgs e)
        {
            try
            {
                _memory += float.Parse(txtSum.Text);
            }
            catch
            {
                Equals(sender, e);
                _memory += float.Parse(txtSum.Text);
            }
        }
        private void SubtractFromMemory(object sender, RoutedEventArgs e)
        {
            try
            {
                _memory -= float.Parse(txtSum.Text);
            }
            catch
            {
                Equals(sender, e);
                _memory -= float.Parse(txtSum.Text);
            }
        }



We again use the try...catch loop in case our textbox contains an equation – again, if it does, equate it and add to / subtract from memory, if it doesn’t, just add to / subtract from memory. Simples!

Earlier, I spoke of ‘clear’ and ‘clear everything’ (and if you’re now checking, I suggest you leave), so now we’ll implement these, as well as one small function I missed – backspace (who noticed?):

        private void ClearEverything(object sender, RoutedEventArgs e)
        {
            //set all of our values to nothing
            _totals = 0;
            _values.Clear();
            _operators.Clear();
            _preParse = string.Empty;
            _memory = 0;
            lstHistory.Items.Clear();
            txtSum.Text = string.Empty;
        }
        private void ClearThis(object sender, RoutedEventArgs e)
        {
            /* set only the values associated with this specific sum to nothing, 
               this means we clear the current values, preparser string and set the decimal to false
               but leave the total and memory intact. */
            _values.Clear();
            _operators.Clear();
            _preParse = string.Empty;
            txtSum.Text = "0";
        }
        private void BackSpace(object sender, RoutedEventArgs e)
        {
            if (txtSum.Text.Length > 0)
            {
                txtSum.Text = txtSum.Text.Remove(txtSum.Text.Length - 1);
                _preParse = _preParse.Remove(_preParse.Length - 1);
            }
        }



In clear everything, we’re just setting all our values to null, empty, or clear. In clear THIS, we clear everything BUT our memory and totals. That’s the difference. In backspace, we are finding the character at the end of our textbox and _preParse variables and removing it – this removes it from the equation and the number that will be parsed.

So far, most of this has been relatively simple. NOW, it’s about to get tougher. Welcome to the equals function. We start this function by checking that we have at least one operator:

            if (_operators.Count > 0)
            {
}



And then we parse the last number, adding it to the stack. Once this is done, we take the first two values from our _Values list, and perform the operator that applies to them. Because this bit doesn’t rely on there being more than one operator, we can just use 0 and 1 as the identifiers in the list.

                //add the last value, 'cause it won't have been already
                _values.Add(float.Parse(_preParse));
                //we start by performing the first operator on the first TWO values
                if (_operators[0] == StandardOperator.Add)
                    _totals = NumericalHelper.Add(_values[0], _values[1]);
                else if (_operators[0] == StandardOperator.Subtract)
                    _totals = NumericalHelper.Subtract(_values[0], _values[1]);
                else if (_operators[0] == StandardOperator.Multiply)
                    _totals = NumericalHelper.Multiply(_values[0], _values[1]);
                else if (_operators[0] == StandardOperator.Divide)
                    _totals = NumericalHelper.Divide(_values[0], _values[1]);



Next, however, it gets a little trickier. For the function to continue working, we must have at least 2 operators and 3 values – remember, we must have 1 more value than operators at all times, and otherwise we essentially corrupt our method.

if (_operators.Count > 1 &&
                    _values.Count > 2)
                {
                    for (int i = 1; i < _operators.Count; i++)
                    {
                        if (_operators[i] == StandardOperator.Add)
                            _totals = NumericalHelper.Add(_totals, _values[i + 1]);
                        else if (_operators[i] == StandardOperator.Subtract)
                            _totals = NumericalHelper.Subtract(_totals, _values[i + 1]);
                        else if (_operators[i] == StandardOperator.Multiply)
                            _totals = NumericalHelper.Multiply(_totals, _values[i + 1]);
                        else if (_operators[i] == StandardOperator.Divide)
                            _totals = NumericalHelper.Divide(_totals, _values[i + 1]);
                    }
                }



This loop just does what our original code did with numbers 0 and 1, but this time relying on our ‘I’ variable to find the correct number to work on. Sure, there’s probably a better way to do this, but for the purpose of education this is pretty damn good.
Lastly, the usual history item, textbox change and _preParse change, value clearing and operator clearing:

                lstHistory.Items.Add(txtSum.Text);
                lstHistory.SelectedIndex = lstHistory.Items.Count - 1;
                txtSum.Text = _totals.ToString();
                _preParse = _totals.ToString();
                _values.Clear();
                _operators.Clear();



The above should be pretty first nature by now, and you should instinctively know where I’m going to put this.
Believe it or not, all of this creates our entire standard calculator, go ahead and hit F5 to debug (or CTRL + F5… you know, if you’re badass) and see if any errors arise. Assuming you’ve followed this correctly, you shouldn’t find any errors at all – I sat and tested the above code for hours and ironed it out completely, if anything is wrong, you’ve done it wrong. If any errors do come up, I’m leaving it to you to fix them.

Now that we have our standard calculator done, I’ll introduce the concept of our ‘advanced’ calculator.

‘Advanced’? Doesn’t that really just mean ‘Scientific’?

Both yes and no. In essence what we will be making is a scientific calculator, however doing so is not as simple as what you will have read through and learned above. A lot of parsing, equation splitting, decisions and general computation are required to correctly solve a ‘scientific’ equation. I.e., if I present you with 2 + 2, the order it must be done in is explicit, however if I present you with 2x^2 + 5x – 3, the equation needs to be parsed to find the correct order it must be computed in, which would be ((x^2)*2) + (5*x) – 3. See, it’s not as straight forward, and considering we will eventually be working with things like differentials and integrals, things are going to get a whole lot messier. This is why I gave it the name ‘advanced’, because the programming behind it is a whole lot more complex, and eventually, we’ll be making this thing able to solve algebraic equations without input via trial and error – again, another reason it will be advanced. Naturally, we’ll make it take input before we learn how to make it solve them by itself.

On top of this, our advanced calculator will do what most will, like find the value of logarithms and calculate surds, and also provide functions for sine, cosine and tangent, among others.

I look forward to seeing you next time, when we’ll go right ahead and build the user interface for the advanced calculator.

See you next time, and happy coding!

Is This A Good Question/Topic? 0
  • +

Replies To: Advanced Equations Parser tutorial: Part II - Coding the Standard Calc

#2 ema2530  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 07-September 11

Posted 08 September 2011 - 03:49 PM

Hi I have been reading this tutorial but no where in put

txtSum.Text + = "x";
_preParse + = "x";


Where do I can put it?
Was This Post Helpful? 0
  • +
  • -

#3 pryogene  Icon User is offline

  • The Leafiest of the Leif's
  • member icon

Reputation: 42
  • View blog
  • Posts: 670
  • Joined: 30-June 09

Posted 04 October 2011 - 02:23 AM

View Postema2530, on 08 September 2011 - 04:49 PM, said:

Hi I have been reading this tutorial but no where in put

txtSum.Text + = "x";
_preParse + = "x";


Where do I can put it?


Please, don't come here trying to steal my code for some college project ¬¬

At DIC, We're about sharing and helping and all that jazz - not coming on here, going "gimme-teh-codez" and then running off to your professor going "look what someone else made but I'm claiming it as my own because the internet affords me that level of super awesome untracable theivery!".

txtSum.Text += "x"; and _preParse += "x"; would have been in there if I'd have wanted that to happen. I've deliberately left parts out so there's still an element of learning and reading that actually HAS to be done.

Alternatively, you could wait for part III. I'll deliberately convolute it - just for you :D
Was This Post Helpful? 0
  • +
  • -

#4 nicks707  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 12
  • Joined: 01-January 11

Posted 11 October 2011 - 10:08 AM

hi there , thanks for the great tut dude .. i too made a calculator for wp7 sometym back .. and i ran into some trouble ... it seems maybe u can help me .

So my app has two pages one is mainpage which has UI of calculator and other page for constants i am having following problems :-->>
on mainpage i have a hyperlink button to navigate to the constats page with the following code
 
private void hyperlinkButton1_Click(object sender, RoutedEventArgs e)
        {
            textBox1.Tag = textBox1.Text;
            s = textBox1.Tag.ToString();
            NavigationService.Navigate(new Uri("/Page1.xaml", UriKind.Relative));
        }



and at constants page i have various constants and a textbox ,cance btn and okay button{which navigates back to the mainpage}
//any const button 
private void button2_Click(object sender, RoutedEventArgs e)
        {
            textBox1.Text = "5";
        }



//okay button
private void button1_Click(object sender, RoutedEventArgs e)
        {
            NavigationService.Navigate(new Uri("/MainPage.xaml?text=" + this.textBox1.Text, UriKind.Relative));
        }
// it takes the value of textbox of const page with it




and on mainpage i have used onnavigatedto event to get the value of the textbox of cont page into textbox of mainpage
 protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            if (this.NavigationContext.QueryString.ContainsKey("text"))
            {
                this.textBox1.Text = this.NavigationContext.QueryString["text"];
                
            }

            base.OnNavigatedTo(e);
            
        }



but what it does is that it clears all the content previously in the textbox of the mainpage ... for example i have 1+2*3+ in the textbox and i press cont button select a cont and hit ok it will show only the value of cont in the textbox .. not as 1+2*3+const i even tried to save the value of the textbox in the tag but in vain i.e.
even using
String s = this.NavigationContext.QueryString["text"];
textbox1.text = textbox1.tag +s; // where tag is the value in the textbox before navigation


... please help
Was This Post Helpful? 0
  • +
  • -

#5 pryogene  Icon User is offline

  • The Leafiest of the Leif's
  • member icon

Reputation: 42
  • View blog
  • Posts: 670
  • Joined: 30-June 09

Posted 06 December 2011 - 04:36 AM

View Postnicks707, on 11 October 2011 - 10:08 AM, said:

 protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            if (this.NavigationContext.QueryString.ContainsKey("text"))
            {
                this.textBox1.Text = this.NavigationContext.QueryString["text"];
                
            }

            base.OnNavigatedTo(e);
            
        }



              this.textBox1.Text += this.NavigationContext.QueryString["text"];


Simple mistake
Was This Post Helpful? 0
  • +
  • -

#6 pryogene  Icon User is offline

  • The Leafiest of the Leif's
  • member icon

Reputation: 42
  • View blog
  • Posts: 670
  • Joined: 30-June 09

Posted 06 December 2011 - 04:51 AM

View Postnicks707, on 11 October 2011 - 10:08 AM, said:

 protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            if (this.NavigationContext.QueryString.ContainsKey("text"))
            {
                this.textBox1.Text = this.NavigationContext.QueryString["text"];
                
            }

            base.OnNavigatedTo(e);
            
        }



              this.textBox1.Text += this.NavigationContext.QueryString["text"];


Simple mistake
Was This Post Helpful? 0
  • +
  • -

#7 EightyFive  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 2
  • Joined: 10-July 12

Posted 10 July 2012 - 05:23 PM

I just wanted to say that I've enjoyed the tut so far. I was also wondering if/when a part III was coming? Good work.
Was This Post Helpful? 0
  • +
  • -

#8 pryogene  Icon User is offline

  • The Leafiest of the Leif's
  • member icon

Reputation: 42
  • View blog
  • Posts: 670
  • Joined: 30-June 09

Posted 03 October 2012 - 04:44 PM

View PostEightyFive, on 10 July 2012 - 06:23 PM, said:

I just wanted to say that I've enjoyed the tut so far. I was also wondering if/when a part III was coming? Good work.


Soon, my dog of war. Soon.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1