Minifying math statements

18

1

The Challenge

You are the owner of an amazing service called Coyote Beta, which magically answers math questions its users send to it over the internet.

But it turns out, bandwidth is expensive. You have two choices, either create a "Coyote Beta Pro" or find some way to solve this. Just recently, someone queried (x + 2). Couldn't the client send x+2, and the user would see no difference?

The Task

Your task is to "minify" math expressions. Given an input expression, you must get rid of whitespace and parentheses until it gives a minimal representation of the same input. The parentheses around associative operations need not be preserved.

The only operators given here are +, -, *, /, and ^ (exponentiation), with standard mathematical associativity and precedence. The only whitespace given in the input will be actual space characters.

Sample Input/Output

Input       | Output
------------|--------------
(2+x) + 3   | 2+x+3
((4+5))*x   | (4+5)*x
z^(x+42)    | z^(x+42)
x - ((y)+2) | x-(y+2)
(z - y) - x | z-y-x
x^(y^2)     | x^y^2
x^2 / z     | x^2/z
- (x + 5)+3 | -(x+5)+3

Scoring

Input/output can use any preferred method. The smallest program in bytes wins.

Exact bits

Exponentiation is right associative and also follows standard math precedence (being the highest). A valid numeric literal is /[0-9]+/, and a valid variable literal is /[a-z]+/. A single variable literal represents a single value even when its character length is longer than 1.

What is meant by "the parentheses around associative operations need not be preserved" is that the output should consist of an expression that results in an identical parse tree, with the exception that associative operations can be rearranged.

TND

Posted 2015-10-17T17:47:47.010

Reputation: 563

The idea is to create a minimal equivalent statement that results in the same parse tree. This is so that Coyote Beta can display it visually when the user makes a query. – TND – 2015-10-17T17:55:59.447

If a valid variable is /[a-z]+/, that means multiplication by juxtaposition like ab is disallowed? – Joe Z. – 2015-10-17T18:01:41.713

Yeah, ab means the single value ab, not a*b. This is so people can focus on the core problem instead of implementing a bunch of features in their parser. – TND – 2015-10-17T18:03:15.643

Need unary plus be supported? – feersum – 2015-10-17T18:05:47.477

Or unary minus? – lirtosiast – 2015-10-17T18:09:07.083

No, that's not necessary. Unary minus is, however. – TND – 2015-10-17T18:09:13.397

1You do want 2+(3+4) to be changed to 2+3+4, right? This does change the parse tree. – feersum – 2015-10-17T18:26:14.850

Yeah, what I said in the first comment wasn't specific enough. I should say it results in an "equivalent" parse tree, where "equivalent" means "the same parse tree, plus any rearrangements to associative operations". – TND – 2015-10-17T18:30:02.490

Why not Rhenium Beta? – cole – 2015-10-17T18:31:46.643

2I take issue with the claim that x^(y/2)=x^y/2; exponentiation has a higher order precedence, ergo, x^y/2=(x^y)/2. – Conor O'Brien – 2015-10-17T18:50:14.973

As far as I'm aware, x^(y/2)=x^y/2 is never stated in the problem. If it is, it's an error an should be fixed, but I can't find it. – TND – 2015-10-17T18:55:06.930

Oh, was Conor's statement in response to a deleted comment? – TND – 2015-10-17T18:56:03.147

Are we permitted to simplify? – Addison Crump – 2015-10-17T20:07:40.143

What is the correct output for a+(-b+c)? Is it a-b+c or a+(-b)+c? – Egor Skriptunoff – 2015-10-17T20:23:26.573

You're not permitted to simplify beyond associative operator rearrangement. – TND – 2015-10-17T20:53:20.620

The correct output for a+(-b+c) is either a+(-b)+c or a+(-b+c). It's interesting that that input has two minimal solutions. Anyway, a-b+c can't be the correct answer because instead of using the unary -, it's using the binary -. – TND – 2015-10-17T20:56:09.233

@TND - a+(-b+c) cannot be the correct output as the parentheses around associative operations must be removed. – Egor Skriptunoff – 2015-10-17T21:22:28.430

Yes, why wouldn't the answer be a+-b+c ? – feersum – 2015-10-17T22:22:27.450

Yeah, it makes more sense to do it that way. I was worried if I said a string with two operators consecutively like that was a possible input, it would throw off the parsers of anyone who was already solving it. – TND – 2015-10-17T22:33:40.023

1Aww man, I was going to submit Prompt X:expr(X) in TI-BASIC but you can't simplify :( – DankMemes – 2015-10-17T23:49:33.620

Answers

1

C#, 523 519 504 bytes

Check the in-code comments to see how it works!


Golfed

using System;using System.Collections.Generic;namespace n{class p{static void Main(string[]a){foreach(String s in a){String r=s.Replace(" ","");List<int>l=new List<int>();for(int i=0;i<r.Length;i++){if(r[i]=='('){l.Add(i);continue;}if(r[i]==')'){switch(r[Math.Max(l[l.Count-1]-1,0)]){case'+':case'(':switch(r[Math.Min(i+1,r.Length-1)]){case'+':case'-':case')':r=r.Remove(Math.Max(l[l.Count-1],0),1);r=r.Remove(Math.Min(i,r.Length)-1,1);i-=2;break;}break;}l.RemoveAt(l.Count-1);}}Console.WriteLine(r);}}}}

Ungolfed

using System;
using System.Collections.Generic;

namespace n {
    class p {
        static void Main( string[] a ) {
            // Loop every String given for the program
            foreach (String s in a) {
                // Get rid of the spaces
                String r = s.Replace( " ", "" );

                // A little helper that will have the indexes of the '('
                List<int> l = new List<int>();

                // Begin the optimizatio process
                for (int i = 0; i < r.Length; i++) {
                    // If char is an '(', add the index to the helper list and continue
                    if (r[ i ] == '(') {
                        l.Add( i );
                        continue;
                    }

                    // If the char is an ')', validate the group
                    if (r[ i ] == ')') {
                        // If the char before the last '(' is an '+' or '(' ...
                        switch (r[ Math.Max( l[ l.Count - 1 ] - 1, 0 ) ]) {
                            case '+':
                            case '(':
                                // ... and the char after the ')' we're checking now is an '+', '-' or ')' ...
                                switch (r[ Math.Min( i + 1, r.Length - 1 ) ]) {
                                    case '+':
                                    case '-':
                                    case ')':
                                        // Remove the '()' since they're most likely desnecessary.
                                        r = r.Remove( Math.Max( l[ l.Count - 1 ], 0 ), 1 );
                                        r = r.Remove( Math.Min( i, r.Length ) - 1, 1 );

                                        // Go two steps back in the loop since we removed 2 chars from the String,
                                        //   otherwise we would miss some invalid inputs
                                        i -= 2;
                                        break;
                                }

                                break;
                        }

                        // Remove the last inserted index of '(' from the list,
                        //   since we matched an ')' for it.
                        l.RemoveAt( l.Count - 1 );
                    }
                }

                // Print the result
                Console.WriteLine( r );
            }
        }
    }
}

Side notes

  1. Fixed some typos and renamed some vars.
  2. Nested an switch to get rid of an unnecessary variable. Also, fixed a bug that would render some solutions invalid, reported by Anders Kaseorg.

P.S.: If you have a tip or found a bug, please let me know in the comments and I'll try to fix it ( I'll then add a note about the bug fix with your name ;) )

auhmaan

Posted 2015-10-17T17:47:47.010

Reputation: 906

Nice answer! :D substantial answers here are generally better recieved if you include an explanation :P – cat – 2016-04-15T14:04:15.520

Can I do it in the form of code comments? – auhmaan – 2016-04-15T14:24:47.807

Sure, whatever works c: – cat – 2016-04-15T14:25:16.067

Then I'll do that! I'll also try to add a summary somewhere. – auhmaan – 2016-04-15T14:28:19.450

Welcome to Programming Puzzles and Code Golf, by the way! (even though it's not your first answer) – cat – 2016-04-15T14:30:56.843

You can’t return r; from void Main(). Also, with that fixed, ((4+5))*x gives the wrong answer 4+5*x. – Anders Kaseorg – 2016-04-15T18:03:58.820

@AndersKaseorg Thanks for finding out the bug!If I may ask you a question, do you know why the code hasn't is methods and classes coloured as it has in SO? Is it an error I made or that doesn't happen here? EDIT! Nevermind, found out how to do it. – auhmaan – 2016-04-15T22:13:58.307

Test your answer on at least the provided cases; it still doesn’t pass them all. – Anders Kaseorg – 2016-04-15T22:22:34.293

The only test from those that have been supplied that fails is the x^(y^2) that doesn't parse the (). I'm still figuring out how to do it without expanding too much the code. – auhmaan – 2016-04-15T22:25:22.777

0

C++, 284 bytes

Golfed

#include<iostream>
#include<algorithm>
int main(){std::string e;std::getline(std::cin,e);e.erase(std::remove_if(e.begin(),e.end(),isspace),e.end());for(int x=0;x<e.length();x++){if(e[x]=='('&&e[x+1]=='('){e.erase(x,1);}if(e[x]==')'&&e[x+1]==')'){e.erase(x,1);}}std::cout<<e;return 0;}

Ungolfed

#include<iostream>
#include<algorithm>

int main()
{
    std::string e;
    std::getline(std::cin, e);
    e.erase(std::remove_if(e.begin(), e.end(), isspace), e.end());
    for(int x = 0; x < e.length(); x++) {
        if (e[x] == '(' && e[x+1] == '('){
            e.erase(x, 1);
        }
        if (e[x] == ')' && e[x+1] == ')'){
            e.erase(x, 1);
        }
    }
    std::cout<<e;
    return 0;
}

Michelfrancis Bustillos

Posted 2015-10-17T17:47:47.010

Reputation: 695

This doesn’t have any precedence logic and fails many of the given test cases. – Anders Kaseorg – 2016-04-15T18:01:34.870