Codeium Golfide
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)}
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!
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.
APL (Dyalog), 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!
{
…}∘|
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 subscripts)
'_',∘⍕¨
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 multiple)
(
…),¨
prepend the following respectively to those:
⍺{
…}¨m
for each, call this function with ions and m
as arguments:
⎕D,⎕A
Digits followed by uppercase Alphabet
⍺∩
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)