[Question]
  [
In this challenge, your task is to take an anion and a cation, and output the chemical formula of the compound.
## Input rules
* Take in 2 strings (in any order) representing the anion and cation, e.g. `F`, `NH_4`, or `Al`.
* To take in the charge of each ion, you can either have it as part of the string separated by a caret (e.g. `F^-1`) or take in additional numerical arguments.
* **Note:** As long as your numeric input type is signed, then the anion's charge will be passed in as a negative number.
* The symbols will always be real, and charges accurate.
## Output rules
* Use `_` for subscripts: Fe2O3 would be `Fe_2O_3`.
* Cation first: NaCl, not ClNa.
* Neutral molecule: Li2O, not LiO or LiO-.
* Lowest possible coefficients: Fe2O3, not Fe4O6.
* No subscript ones: NaCl, not Na1Cl1.
* No shifting: NH4OH, not NH5O.
* Conditional parentheses:
* Do not use parentheses on a single-atom ion: MgCl2, not Mg(Cl)2.
* Do not use parentheses if there is only one of the ion per molecule: KClO3, not K(ClO3).
* DO use parentheses if there are two or more of a polyatomic ion: Be3(PO4)2, not Be3PO42 or Be3P2O8.
## Testcases
```
Input               Output
Fe^+3, O^-2         Fe_2O_3
Fe^+2, O^-2         FeO
H^+1, SO_4^-2       H_2SO_4
Al^+3, SO_4^-2      Al_2(SO_4)_3
NH_4^+1, SO_4^-2    (NH_4)_2SO_4
Hg_2^+2, PO_4^-3    (Hg_2)_3(PO_4)_2
NH_4^+1, OH^-1      NH_4OH
Hg_2^+2, O_2^-2     Hg_2O_2
```
This is [code-golf](/questions/tagged/code-golf "show questions tagged 'code-golf'"), the shortest answer in bytes wins!
  
[Answer]
# C, ~~208~~ ~~205~~ ~~175~~ 169 bytes
`argv[1]` : cation  
`argv[2]` : anion  
Charges of ions are taken on stdin.
```
#define z(b)for(i=*++v;*++i>95;);printf(b>1?*i?"(%s)_%d":"%s_%d":"%s",*v,b);
main(c,v,d,e,g,h,i)char**v,*i;{scanf("%d%d",&c,&d);for(e=c,h=-d;g=h;e=g)h=e%g;z(-d/e)z(c/e)}
```
[Answer]
## [Retina](https://github.com/m-ender/retina), ~~86~~ 80 bytes
*Thanks to Neil for saving 6 bytes.*
```
^
(
\^.+
)_$&$*
(1+)(\1|¬∂.+)+_(\1)+$
$#3$2_$#2
_1$
m)T`()``.+\)$|\(.[a-z]?\)
¬∂
```
[Try it online!](https://tio.run/##TYuxCsIwFEX39xt9Sp4vDSR1FxfJZIW6GZN0KCKogzhJv6sf0B@LjaI43XO49967x/nWppkQTZTJgwDnFQMFnOMChGYSTvfjoJg4TEiMgEWFJmBhIGgEuNI@CopRsSPsnVCHtnweV45gHCClTee5krUvDWQ0H7SetWzqsMyyvuTJ17Z2yr/WnoLJv93bq19fW1/qFw "Retina ‚Äì Try It Online")
Input is linefeed-separated (test suite uses comma-separation for convenience).
### Explanation
```
^
(
```
We start by prepending a `(` to each molecule. The `^` matches on *line* beginnings because the `m)` towards the end of the program sets multiline mode for all preceding stages.
```
\^.+
)_$&$*
```
We replace the `^[-+]n` part with `)_`, followed by `n` copies of `1` (i.e. we convert the charges to unary, dropping the signs).
```
(1+)(\1|¬∂.+)+_(\1)+$
$#3$2_$#2
```
This stage does three things: it divides both charges by their GCD, converts them back to decimal and swaps them. The GCD can be found quite easily in regex, by matching the longest `1+` that lets us match both charges using only the backreference `\1`. To divide by this, we make use of Retina's "capture count" feature, which tells us how often a group has been used. So `$#2` is the first charge divided by the GCD and `$#3` is the second charge divided by the GCD (both in decimal).
```
_1$
```
We remove `_1`s from the ends of both parts.
```
m)T`()``.+\)$|\(.[a-z]?\)
```
And we drop the parentheses from lines which end in a `)` (i.e. those that had a `_1` there), as well as lines that only contain a single atom.
```
¬∂
```
Finally, we concatenate the two molecules by dropping the linefeed.
[Answer]
# [APL (Dyalog)](https://www.dyalog.com/), ~~60~~ ~~59~~ 61 bytes
+2 since charges must be given signed.
Anonymous infix function. Takes list of ions (anion, cation) as left argument and list of corresponding charges as right argument.
```
{‚àä(‚ç∫{‚çµ‚àß1<‚â¢‚ç∫‚à©‚éïD,‚éïA:1‚åΩ')(',‚ç∫‚ãÑ‚ç∫}¬®m),¬®(m‚Üês‚â†1)/¬®'_',‚àò‚çï¬®s‚Üê‚çµ√∑‚ç®‚àß/‚çµ}‚àò|
```
[Try it online!](https://tio.run/##SyzI0U2pTMzJT///P@1R24TqRx1dGo96d1U/6t36qGO5oc2jzkVA7qOOlY/6prroAAlHK8NHPXvVNTXUdUAS3S1AsvbQilxNnUMrNHKBRhQ/6lxgqKl/aIV6PFBJx4xHvVMPrSgGSgCNPLz9Ue8KoLn6QHYtUK7m/391t1R1BXV/dYU0BWOFQ@uNuJAEjCACHkB@sH@8CUjIECLkmIMkBtXn5wHioqv0SI83AooGQEVBRhrD1fp7wFQaAu3Nh4uAVBkCAA "APL (Dyalog Unicode) ‚Äì Try It Online")
`{`‚Ä¶`}‚àò|`‚ÄÉfunction where `‚ç∫` is left argument and `‚çµ` is right argument's magnitude:
‚ÄÉ`‚àß/‚çµ`‚ÄÉLCM of the charges
‚ÄÉ`‚çµ√∑‚ç®`‚ÄÉdivide the charges by that
‚ÄÉ`s‚Üê`‚ÄÉstore in `s` (for **s**ubscripts)
‚ÄÉ`'_',‚àò‚çï¬®`‚ÄÉformat (stringify) and prepend underbar to each
‚ÄÉ`(`‚Ä¶`)/`‚ÄÉreplicate each letter of each with the corresponding value from:
‚ÄÉ‚ÄÉ`s‚â†1`‚ÄÉIs `s` different from `1`? (gives 1 or 0)
‚ÄÉ‚ÄÉ`m‚Üê`‚ÄÉstore in `m` (for **m**ultiple)
‚ÄÉ`(`‚Ä¶`),¬®`‚ÄÉprepend the following respectively to those:
‚ÄÉ‚ÄÉ`‚ç∫{`‚Ä¶`}¬®m`‚ÄÉfor each, call this function with ions and `m` as arguments:
‚ÄÉ‚ÄÉ‚ÄÉ`‚éïD,‚éïA`‚ÄÉ**D**igits followed by uppercase **A**lphabet
‚ÄÉ‚ÄÉ‚ÄÉ`‚ç∫‚à©`‚ÄÉintersection of ion and that
‚ÄÉ‚ÄÉ‚ÄÉ`‚â¢`‚ÄÉtally the number of characters in that
‚ÄÉ‚ÄÉ‚ÄÉ`1<`‚ÄÉIs one less than that? (i.e. do we have a multi-element ion?)
‚ÄÉ‚ÄÉ‚ÄÉ`‚çµ‚àß`‚ÄÉand do we need multiple of that ion?
‚ÄÉ‚ÄÉ‚ÄÉ`:`‚ÄÉif so, then:
‚ÄÉ‚ÄÉ‚ÄÉ‚ÄÉ`')(',‚ç∫`‚ÄÉprepend the string to the ion
‚ÄÉ‚ÄÉ‚ÄÉ‚ÄÉ`1‚åΩ`‚ÄÉcyclically rotate one step to the left (puts `)` on the right)
‚ÄÉ‚ÄÉ‚ÄÉ`‚ãÑ`‚ÄÉelse
‚ÄÉ‚ÄÉ‚ÄÉ‚ÄÉ`‚ç∫`‚ÄÉreturn the ion unmodified
‚ÄÉ`‚àä`‚ÄÉ**œµ**nlist (flatten)
[Answer]
# [Haskell](https://www.haskell.org/), ~~101~~ 97 bytes
```
(s#t)n m|let x#i|j<-div(lcm n m)i=l$x:[l(x:['(':x++")"|l x<':'])++'_':show j|j>1]=s#n++t#m
l=last
```
[Try it online!](https://tio.run/##ZZBRa4MwFIXf8ysuOjAh88G4J6mDMigZbE2hj6WKbLLaRVdmuglzv93dxGHXLg@G8517z71mV7SvpdbDQFvfsAbqXpcGOr/q97Pwufqg@qkGxKxK9VWXbDTFT0CDpOPcY16voZsFSbBlnAd5kLS7t0/Y9/vbaJu2fsO58WuiU120ZqiLqoEU6uLwCIejWZv3hwY21FuUnu8pj8UgruEkxSglqrXKbzwWjWCuJ/LbsZQoLqrkSy6QrRwTEP@pU9JWRZbcUWSC5THipcLLGltCvkJy3@COcH7U0SAkizLjGKiyUEzWoswFBjhP/PMUkRnHiXbFk4OjLSBz7fLOzLnOBbUElyN28ct@uzmaY4L9Wzd35QpiV2AhdtOVSxGnFCWzMBrHWKYkmd7B@fYlACZmJQm/hx8 "Haskell ‚Äì Try It Online") Example usage: `Fe^+3, O^-2` is taken as `("Fe"#"O")3 2`.
[Answer]
# RPL (HP48 S/SX), 294.5 bytes
Yes, ridiculously large submission, not sure how competitive it will be...
```
DIR
  M
    ¬´ S ROT S SWAP ABS 4 PICK ABS
      DUP2 WHILE DUP2 REPEAT MOD SWAP END DROP2
      SWAP OVER / 4 ROLL J 3 ROLLD / ROT J
      ROT 0 > IF THEN SWAP END + ¬ª
  S
    ¬´ DUP "^" POS DUP2 1 + OVER SIZE SUB OBJü°¢
      3 ROLLD 1 - 1 SWAP SUB ¬ª
  J
    IF OVER 1 ==
    THEN SWAP DROP
    ELSE DUP SIZE DUP2 DUP SUB "Z" > - 1 >
      IF
      THEN "(" SWAP + ")" +
      END
      "_" + SWAP +
    END
END
```
3 routines packaged neatly in a directory. `M` is the main one. It expects 2 strings on the stack formatted as ions and pushes a molecule string onto the stack.
`S` splits the ion into charge as a number and the element formula as a string. For example, `"PO_4^-3"` would be taken from the stack and `-3` and `"PO_4"` pushed onto the stack.
`J` joins the number of ions with the formula and decides whether to wrap the formula into brackets. The bit before `ELSE` deals with 1 ion, leaving the string as it is. For example, if `1` and `"PO_4"` are on the stack, they are replaced by `"PO_4"`. `1` and `"H"` gives `"H"`.
The rest deals with multiple ions; if it's a single atom it's not in brackets, otherwise it is. To decide whether it is, I check the length of the string and check if the last character is `>"Z"`. Boolean expressions return 1 for true and 0 for false. By subtracting the result of this comparison from the length of the string, I get 1 or less when it's single atom, otherwise more: length 1 is a single atom; length 2 will have a letter as the last character; for a single atom it's lower case, so `>"Z"`, making result 1, otherwise 2; length 3 or more means more than 1 atom and with 0 or 1 subtracted from the length the result will be at least 2. For example, `3` and `"PO_4"` gives `"(PO_4)_3"`. `3` and `"Al"` gives `"Al_3"`.
`M` first splits each ion using `S`. After the first line the level 5 of the stack (so the deepest buries object) contains charge of the second ion, level 4 second ion's formula, level 3 first ion's formula, level 2 absolute value of the first ion's charge and level 1 absolute value of the second ion's charge again. For example, if given ions on the stack are `"Al^+3"`, `"SO_4^-2"`, we get `-2`, `"SO_4"`, `"Al"`, `3`, `2`.
Second line calculates gcd of the 2 charges (leaving the charges intact).
Third line divides each charge by the gcd (to calculate the multiples) and joins it with the ion's formula using `J`. So we have two strings each with one given ion with charge removed (or a multiple of it) and a charge of the second one buried behind them. For example, `-2`, `"Al_2"`, `"(SO_4)_3"` (-2 is the charge of SO\_4).
Fourth line unburies the charge and if it's positive it swaps the two strings (so that the cation comes first) before joining them. So in example above, because it's negative, they are joined in the order as they are: `"Al_2(SO_4)_3"`.
[Answer]
# [Python 3](https://docs.python.org/3/), 131 bytes
```
lambda c,a,C,A:g(c,A/gcd(C,A))+g(a,C/gcd(C,A))
from math import*
g=lambda s,n:[s,[s,'(%s)'%s][sum(c<'`'for c in s)>1]+'_%d'%n][n>1]
```
[Try it online!](https://tio.run/##jY3BCoJAFEX3fcVs5M3Ug1BbSQUShKsMWpqYjTkKziiOLfp6GyOK0EXwNvdczn3Noytq5fb55txXqbxmKeGY4g59T1CO/lLwjJrE2EJQw795lre1JDLtClLKpm67@Uxs3hMalRdpNAfU0gwsHUf6LilfwwXyuiWclIpotrXjBSRWBpaKI2VS37Sl6mhOYX8DhBDQRcf8GmHnF/uVwacwWY2E4FPY/yqHYIDTViASx3THV@egO@GFwWDZjPVP "Python 3 ‚Äì Try It Online")
# [Python 2](https://docs.python.org/2/), ~~196~~ ~~174~~ ~~170~~ ~~155~~ ~~149~~ ~~140~~ 136 bytes
```
lambda c,a,C,A:g(c,A/gcd(C,A))+g(a,C/gcd(C,A))
from fractions import*
g=lambda s,n:[s,[s,'(%s)'%s][sum(c<'`'for c in s)>1]+'_'+`n`][n>1]
```
[Try it online!](https://tio.run/##jYzBioMwFEX3fkU25SXjg2I6K6kFKRRXdaBLR9SmTSpoIold9OudtJQOM85i4G3uufe84TZejOaTTD6nrumPp4YIbHCLaayowHSpxIn6xFioqOffOZDW9ETaRoyt0Y60/WDs@Bao5PnHoY4Lh/6ALhyDhSsLd@2pWEMN0lgiSKuJY5uoDKGCsNZ1WWifpsG2eiSSwu4MCDngCjkLZpT/oGnn6SGv3n/PsxeP/ifsszv708lUxX318ag4ruZWnt2diE1f "Python 2 ‚Äì Try It Online")
[Answer]
# [Python 3](https://docs.python.org/3/), 129 bytes
```
lambda E,e,I,i,m=lambda e,i:(len(e)>2<=i)*"("+e+(len(e)>2<=i)*")"+"_%d"%i*(i>1):m(E,i/gcd(i,I))+m(e,I/gcd(i,I))
from math import*
```
[Try it online!](https://tio.run/##jZDPa4MwFMfv@SskUHzRwDDuJLPQQ0t6WQrbUQhOYw0YldQd9te7pJUN1x52yY/Ph@97jzd@Te3Qp3OTF3NXmo@6DPZU0SPV1OQLUFRn0KkeFNmyl1yTCAOOVfyHERxjuanxRkegtwnJDOypfjpXNWh6JCQ24Ar//lFjBxOYcmoDbcbBTtE8Wt1PgN9bFVh1@eymS1BalRV90WOC0E03EB5USEMR0pQyV@cOszXedQ6/Cfl8F@A/Ivlv5JV7@DjFz5I5d7o6RtMHOcF9KnmQEv68Tb44jDFCfhfVYK2qptVOEEIHJZmQqbsF2nWSgR@KOMAl8881BD8BWQz4po7C6WoZ8lJw5LEbxHUm8zc "Python 3 ‚Äì Try It Online")
---
If we need to handle negative charges of the anions, then **153 bytes:**
```
lambda E,e,I,i,a=abs,m=lambda e,i:(len(e)>2<=i)*"("+e+(len(e)>2<=i)*")"+"_%d"%i*(i>1):m(E,a(i)/gcd(a(i),a(I)))+m(e,a(I)/gcd(a(i),a(I)))
from math import*
```
[Try it online!](https://tio.run/##jZBBa4MwGIbv@RUSKH5fzRiNO5VZ6KHFXpbCdhyEtMYaMCqpO@zXu8RJYa6DXZLP5@Elr1/32Vdtkw5l9j7Uyp4KFe2YZgdmmMrU6cpsNmHNzBpq3YDGDX/ODC4p0EQnM4Y0oXJR0IVZgtmscG1hxxQYfLycCwiD/zogYmJBj@NckNK1NrKqryJju9b1y6FzpumBUvpW6cjp60fdXyPl9JoQD5F8@xLivY5ZLGKWsgeOdzif8W3t@auQT78j@c2s/h96yQP9I5dfJPfyOErfJL2TFPmYW93JiXBOP0BuGyEk7OTcOqfP/Ww3ZK8lFzL1tyDbWnIIxdCDXPIw/oQQOuBkILzqKRxHy0mQIicB@yZh7cMX "Python 3 ‚Äì Try It Online")
[Answer]
# JavaScript, 145 bytes
```
(c,a,p,n,g=(a,b)=>b?g(b,a%b):a)=>`(${c})_${n/-g(p,-n)}(${a})_${p/g(p,-n)}`.replace(/\(((\w+)\)(?=_1)|([A-Z][a-z]*)\))/g,"$2$3").replace(/_1/g,"")
```
Takes arguments `c` is cation, `a` is anion, `p` is positive charge, `n` is negative charge.
]