37
\$\begingroup\$

You are transported in a parallel universe where people write mathematical equations on computers as ASCII art by hand. As a LaTeX addict, this is totally unacceptable, and you ought to automate this process somewhat.

Your goal is to write a program that outputs an ASCII version of an equation inputed as a LaTeX math command.

Mandatory LaTeX commands to support

  • Sum: the LaTeX command for a sum is \sum_{lower bound}^{upper bound}

    The ASCII figure you have to use for sums is:

    upper bound
        ___ 
        \  `
        /__,
    lower bound
    
  • Product: the LaTeX command for a product is \prod_{lower bound}^{upper bound}

    The ASCII figure you have to use for products is:

    upper bound
        ____
        |  |
        |  |
    lower bound
    
  • Fraction: the LaTeX command for fractions is \frac{numerator}{denominator}

    The ASCII figure you have to use for fractions is:

     numerator
    -----------
    denominator
    

Anything that is not one of those three commands is displayed as is. For example, \sum{i=3}^{e^10}\frac{3x+5}{2}should be displayed as

e^10
___  3x+5
\  ` ----
/__,  2
i=3

Inputs

The input is a LaTeX command passed as a string (or your language's equivalent to strings). LaTeX commands can be nested, for instance \frac{\frac{1}{2}}{3} is a valid input. Inputs are supposed to be always correct (no need to check LaTeX's syntax in your code). Inputs will only consist of the three LaTeX commands presented above and 'text' that you won't need to format.

LaTeX commands will always come with the syntax presented above, i.e. sums and products will always have upper and lower bounds (although they can be empty) and there will always be a numerator and denominator for fractions.

We assume that the bounds of sums and products are at most 4 characters long (= the width of the sum and product symbols), so that you don't have to worry about possible overlap issues. For similar reasons, we assume that the bounds are just 'text' and will never be LaTeX commands, e.g. \sum_{\sum_{1}^{2}}^{1} is not a valid input.

Outputs

Your program's output is the ASCII representation of the LaTeX command you were given as input.

Your program has to take horizontal alignment into account: for instance, the bounds of the sum or the product have to be horizontally aligned with the sum or product symbol (which are both 4 characters wide). If the bound has an odd number of characters, it does not matter whether it is one character off to the right or to left of the center, whichever is fine. The fraction's line has to be as long as the numerator or the denominator, whichever is the longest.

Your program has to take vertical alignment into account: for instance, \frac{\frac{1}{2}}{3} = \frac{1}{6} should be displayed as

1
-
2   1
- = -
3   6

For sums and products, since the symbols are 4 characters high, the vertical center is assumed to be the second line from the top.

Horizontal spacing is assumed to be correct in the given input, i.e. the spaces in the input should be displayed in the output.

Test cases

  • Input abc = 2

    Output abc = 2

  • Input e = \sum_{n=0}^{+inf} \frac{1}{n!}

    Output

        +inf
        ___  1
    e = \  ` --
        /__, n!
        n=0
    
  • Input e^x = 1 + \frac{x}{1 - \frac{x}{2 + x - ...}}

    Output

                     x
    e^x = 1 + ---------------
                       x
              1 - -----------
                  2 + x - ...
    
  • Input \prod_{i=1}^{n} \frac{\sum_{j=0}^{m} 2j}{i + 1}

    Output

           m
          ___
          \  ` 2j
     n    /__,
    ____  j=0
    |  |  -------
    |  |   i + 1
    i=1
    
  • Input \frac{sum}{prod} = \sum_{frac}^{prod} sum

    Output

           prod
    sum    ___
    ---- = \  ` sum
    prod   /__,
           frac
    

Scoring

This is , so the shortest code wins.

\$\endgroup\$
17
  • 11
    \$\begingroup\$ Nice first challenge. It looks pretty difficult; I'm excited to see some solutions. \$\endgroup\$
    – Alex A.
    Jun 26, 2015 at 16:58
  • 1
    \$\begingroup\$ @Alex A. I originally intended to also have integrals, square roots and expandable parenthesis, but that seemed a bit too much... \$\endgroup\$
    – Fatalize
    Jun 26, 2015 at 17:06
  • 2
    \$\begingroup\$ I believe there will be cases where you get overlap. For example, if you have a sum where the term becomes higher than 4 (e.g. multiple fractions, fractions of sums), and the sum has a long upper/lower bound, the upper/lower bound string could overlap with the term. How would that be resolved? Does the term have to be spaced from the sum to avoid overlap with the bounds? \$\endgroup\$ Jun 26, 2015 at 18:06
  • 4
    \$\begingroup\$ Related challenge. \$\endgroup\$
    – Zgarb
    Jun 26, 2015 at 20:23
  • 8
    \$\begingroup\$ I really hope someone comes up with a solution in LaTeX \$\endgroup\$ Jun 27, 2015 at 1:23

2 Answers 2

23
\$\begingroup\$

Python 2, 656 627 618 bytes

M=max
O=lambda l,o=2:[(p+o,c)for p,c in l]
def C(s,m=0):
 if''<s<'}'[m:]:f,w,h,d,s=C(s,1);F,W,H,D,s=C(s);e=M(d,D);return[O(f,e-d)+O(F,w*1j+e-D),w+W,M(h-d,H-D)+e,e,s]
 if'\\'!=s[:1]:return[[(0,s[:1])]*m,m,m,0,s[1:]]
 t=s[1]<'s';e=s[1]>'f';f,w,h,d,s=C(s[5+t+e:]);F,W,H,D,s=C(s[1+e:]);g=M(w,W);G=C('-'*g)[0]
 if e:f,w,h,F,W,H=F,W,H,f,w,h;g=4;p=C('|  |')[0];G=C('_'*(3+t))[0]+[O(C('/__,')[0])+[(1,'\\'),(1+3j,'`')],O(p,1)+O(p)][t]
 x=M(w,W,g);return[O(f,(x-w)/2*1j)+O(F,(x-W)/2*1j+h+3**e)+O(G,(x-g)/2*1j+h),x,h+3**e+H,h+e,s]
f,w,h,d,s=C(raw_input())
for y in range(h):print"".join(dict(f).get(y+x*1j,' ')for x in range(w))

Takes input on STDIN and writes output to STDOUT.

The program assumes that no other control sequence than \frac, \sum or \prod appears in the input (i.e., it won't show as normal text,) and that ~ doesn't appear as well (it has a special meaning in math mode anyway.) On the other hand, the program does support arbitrary formulas as limits for \sum and \prod.

Explanation

It works just like TeX! (well, sort of...) Each subformula (starting from single characters and building up to more complex formulas) is turned into a box, with an associated width, height and depth (baseline). Boxes of simpler formulas are combined into bigger boxes to form complex formulas, and so on. The contents of each box are represented as a list of position/character pairs, relative to the top-left corner of the box; when boxes are combined into a bigger box, the positions are offset according to the relative positions of the smaller boxes inside the bigger one, and the lists are concatenated.

Eventually, we end up with a top-level box, which is converted to a printable form.


To spice it up a little, the following version also supports square roots:

M=max;R=range
O=lambda l,o=2:[(p+o,c)for p,c in l]
def C(s,m=0):
 if''<s<'}'[m:]:f,w,h,d,s=C(s,1);F,W,H,D,s=C(s);e=M(d,D);return[O(f,e-d)+O(F,w*1j+e-D),w+W,M(h-d,H-D)+e,e,s]
 if'\\'!=s[:1]:return[[(0,s[:1])]*m,m,m,0,s[1:]]
 t=s[1]<'s';e=s[1]>'f';f,w,h,d,z=C(s[5+t+e:])
 if'r'>s[2]:return[O(f,1j+h*1j+1)+O(C('_'*w)[0],1j+h*1j)+[(h,'\\')]+[(h-y+y*1j+1j,'/')for y in R(h)],w+1+h,h+1,d+1,z]
 F,W,H,D,s=C(z[1+e:]);g=M(w,W);G=C('-'*g)[0]
 if e:f,w,h,F,W,H=F,W,H,f,w,h;g=4;p=C('|  |')[0];G=C('_'*(3+t))[0]+[O(C('/__,')[0])+[(1,'\\'),(1+3j,'`')],O(p,1)+O(p)][t]
 x=M(w,W,g);return[O(f,(x-w)/2*1j)+O(F,(x-W)/2*1j+h+3**e)+O(G,(x-g)/2*1j+h),x,h+3**e+H,h+e,s]
f,w,h,d,s=C(raw_input())
for y in R(h):print"".join(dict(f).get(y+x*1j,' ')for x in R(w))

Examples:

  • \frac{-b +- \sqrt{b^2 - 4ac}}{2a}

            _________
    -b +- \/b^2 - 4ac
    -----------------
           2a
    
  • |v| = \sqrt{ \sum_{i}^{} v[i]^2 }

               _____________
              / ___
    |v| =    /  \  ` v[i]^2
            /   /__,
          \/     i
    
\$\endgroup\$
3
  • 9
    \$\begingroup\$ I have to say I'm thoroughly impressed! Tried to run \prod_{i=1}^{\sum_{azededzeda}^{k}} \frac{\sum_{j=0}^{m} 2j}{i + 1} and it outputed everything correctly with no overlap, even though it wasn't required. Nice! \$\endgroup\$
    – Fatalize
    Jun 28, 2015 at 15:10
  • 4
    \$\begingroup\$ And you support square roots with only ~18% more bytes. Someone stop this man! \$\endgroup\$
    – Fatalize
    Jun 29, 2015 at 15:05
  • 1
    \$\begingroup\$ @Ell That makes sense! Nice work :) \$\endgroup\$
    – Kade
    Jun 29, 2015 at 15:45
23
\$\begingroup\$

LaTeX, 540 532 characters

Disclaimer: This is not perfect and arguably does not count as a valid answer.

\usepackage[LGRgreek]{mathastext}
\renewcommand{\sum}{\kern-1ex\displaystyle\mathop{\vphantom{\int}\begin{array}{l}\mbox{\underline{\hspace{12pt}}}\\ \mbox{\textbackslash}\hspace{8pt}`\\\mbox{/\underline{\hspace{8pt}},}\end{array}}\displaylimits}
\renewcommand{\prod}{\kern-1ex\displaystyle\mathop{\vphantom{\int}\begin{array}{c}\mbox{\underline{\hspace{16pt}}}\\|\ \ \ \ | \\| \ \ \ \ |\end{array}}\displaylimits}
\renewcommand{\frac}[2]{\mathop{\xleaders\hbox{-}\hfill\kern0pt}\limits^{#1}_{#2}}
\DeclareMathSizes{10}{10}{10}{10}

Some help from @Fatalize, see comments for details.

Test:

Input: \prod_{i=1}^{n} \frac{\sum_{j=0}^{m} 2j}{i + 1}

Output:

enter image description here

As you can see, the output does not exactly follow the spec. This may disqualify my answer, but I still think it's worth posting.

I wrote this on sharelatex.com. You can play with it here.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Nice! I played with your code a bit and I think you can fix everything by changing your fraction to \newcommand{\frac}[2]{\mathop{\xleaders\hbox{-}\hfill\kern0pt}\limits^{#1}_{#2}}, adding \DeclareMathSizes{10}{10}{10}{10} after that (to prevent LaTeX from shrinking numerators and denominators), and by adding \kern-1ex before \displaystyle in your sum and product definition. \$\endgroup\$
    – Fatalize
    Jun 28, 2015 at 6:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.