Codeium Golfide

28

6

In this challenge, your task is to take an anion and a cation, and output the chemical formula of the compound. The input follows these 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.

The output should follow these 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.

Here are some additional sample inputs and outputs:

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

Since this is , the shortest answer in bytes wins.

Nissa

Posted 2017-11-13T03:02:08.193

Reputation: 3 334

3Recommended test case: Fe^+2, OH^-1: Fe(OH)_2 for a polyatomic ion with 1 of each element (OH^-1). – pizzapants184 – 2017-11-13T06:38:02.703

Recommended test case: C(NH_2)_3^+1, NO_3: C(NH_2)_3NO_3 – Adám – 2017-11-13T11:09:16.307

1@Adám second ion is charged: NO_3^-1. Also another test case should be the first one paired up with a ^-2, so it would make (C(NH_2)_3)_2.... Or a case where the ion that is needed more than once begins with a bracket. – Heimdall – 2017-11-13T11:43:34.427

@Heimdall The missing ^-1 is a typo. Yes, I wanted such a compound, but didn't find any real-life examples. – Adám – 2017-11-13T11:50:49.400

1@Adám Fe_4(Fe(CN)_6)_3 for Prussian blue. – Colera Su – 2017-11-13T12:32:17.383

Recommended test case: Fe^+3, Fe(CN)_6^-4: Fe_4(Fe(CN)_6)_3 – Adám – 2017-11-14T11:50:47.880

3This might be the funniest challenge title I've seen on this website so far, and that's saying a lot for a ppcg question. Thanks for the good laugh – osuka_ – 2017-11-15T13:56:35.383

1@osuka_ Have you seen "what's the fastest way to kill my family" over on Arqade? Or my other PPCG challenge, "confirm the Illuminati"? – Nissa – 2017-11-15T14:06:22.333

@StephenLeppik I meant on ppcg itself, maybe I should’ve used “community” instead of “website” - but ppcg is probably the funniest community on SE, so the meaning is mostly the same. I have seen the Illuminati question though, I got a hearty laugh out of that too – osuka_ – 2017-11-15T14:16:13.873

Can I assume the absolute value of a charge is no more than ... maybe 4 or 6? – Heimdall – 2017-11-16T11:31:16.903

@Heimdall ions do exist with charges of up to +9… but no more than that. Anions go up to -4 (VERY rarely) and cations go up to +9 (one element only). – Nissa – 2017-11-16T13:46:04.790

Thanks. When you say one element only, I assume you mean multi-atom ions have smaller upper limit. So, can I safely assume down to -4 and up to +9? – Heimdall – 2017-11-16T14:25:27.613

@Heimdall yes. Also, what I really meant was "there's only one cation in total with +9". (And the only polyatomic cation I know is ammonium (NH4) which is +1.) – Nissa – 2017-11-16T14:47:08.123

Answers

4

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,⎕ADigits 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)

Adám

Posted 2017-11-13T03:02:08.193

Reputation: 37 779

6

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)}

Colera Su

Posted 2017-11-13T03:02:08.193

Reputation: 2 291

OP specified the ions can be given in any order. – Heimdall – 2017-11-15T14:33:21.523

6

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 _1s 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.

Martin Ender

Posted 2017-11-13T03:02:08.193

Reputation: 184 808

3

Haskell, 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! Example usage: Fe^+3, O^-2 is taken as ("Fe"#"O")3 2.

Laikoni

Posted 2017-11-13T03:02:08.193

Reputation: 23 676

2

Python 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!

Python 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!

TFeld

Posted 2017-11-13T03:02:08.193

Reputation: 19 246

Python 3 for 131 bytes

– pizzapants184 – 2017-11-13T13:28:10.113

2

Python 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!


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!

Mr. Xcoder

Posted 2017-11-13T03:02:08.193

Reputation: 39 774

2

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".

Heimdall

Posted 2017-11-13T03:02:08.193

Reputation: 191

1

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.

ericw31415

Posted 2017-11-13T03:02:08.193

Reputation: 2 229