Given the name of a cation and anion, output "S" (soluble) or "I" (insoluble). The table we will be using is from wikipedia: https://en.wikipedia.org/wiki/Solubility_chart. It is copied at the end of the question for future reference.

Input: The cation, followed by the anion, separated by a space. The cation will be one of the following:

Lithium Sodium Potassium Ammonium Beryllium Magnesium Calcium 
Strontium Barium Zinc Iron(II) Copper(II) Aluminium Iron(III) Lead(II) Silver

and the anion will be one of the following:

Fluoride Chloride Bromide Iodide Carbonate Chlorate Hydroxide Cyanide Cyanate 
Thiocyanate Nitrate Oxide Phosphate Sulfate Dichromate

Each will have its first letter capitalized.

Example Input: Sodium Chloride

Output: A truthy value, or S, if it is soluble, falsey or I otherwise. If the wikipedia page lists anything else (e.g. slightly soluble, or reacts with water) or if the input is not in the form "cation anion," your program may do anything (undefined behavior), so it may output 'S', 'I', or anything else.



The rows are cations in the order listed above and the columns are anions. For example, since Magnesium Iodide is soluble, and Magnesium was the 6th cation and Iodide was the 4th anion, the 6th row and 4th column has the character 'S'. The ? indicates undefined behavior.

  • 1
    \$\begingroup\$ I like this because the undefined behaviour of ?s gives a lot of freedom in what algorithms one can use. \$\endgroup\$
    – Jo King
    Aug 7, 2018 at 3:57
  • 1
    \$\begingroup\$ @FryAmTheEggman Despite the kolmogorov-complexity tag, the challenge does not ask to output the table, but the correct value for a given (cation, anion) pair. \$\endgroup\$
    – Arnauld
    Aug 7, 2018 at 8:48
  • 4
    \$\begingroup\$ I removed the kolmogorov-complexity tag and added the decision-problem tag, since this isn't about creating a fixed (or partially fixed) output, but determining whether a certain input meets a some criteria. \$\endgroup\$ Aug 7, 2018 at 8:57
  • \$\begingroup\$ Would you consider allowing to output any 2 distinct consistent values rather than just truthy/'S' or falsy/'I'? \$\endgroup\$
    – Arnauld
    Aug 7, 2018 at 10:22
  • \$\begingroup\$ I'd suggest dropping the "separated by a space" specification and instead saying something along the lines of "in addition to the site defaults, the two inputs may be accepted as a single input separated by a consistent unused character (e.g. a space)". Two inputs may allow more golfing creativity here (e.g. curried functions). \$\endgroup\$ Aug 7, 2018 at 13:24

6 Answers 6


JavaScript (Node.js), 143 bytes

Returns 1 for soluble, 0 for insoluble.

s=>Buffer(`## 5)6.'04+ n:# (F* E"/$;&-"/"7&#.%`).map(c=>S+='1'.repeat(c-32)+0,S='')|S[parseInt(s.split` `[1].slice(1,7)+s[0]+s[1],35)%1325%508]

Try it online!


Conversion of the input string to a lookup index

We first build a key by extracting the 2nd to 7th characters of the anion and adding the two first characters of the cation:

key = s.split` `[1].slice(1, 7) + s[0] + s[1]


'Lithium Fluoride'  --> 'luoridLi'
'Sodium Fluoride'   --> 'luoridSo'
'Sodium Dichromate' --> 'ichromSo'
'Calcium Oxide'     --> 'xideCa'

We turn this into a lookup index by parsing it in base-35 and applying a modulo 1325 followed by a modulo 508 (brute-forced values):

parseInt(key, 35) % 1325 % 508

Compression of the lookup table

Because there are significantly more soluble pairs than insoluble ones, we fill all unused entries in the lookup with soluble.

By encoding soluble with 1 and insoluble with 0, our lookup table essentially consists of long strings of 1's followed by a 0:


We compress it by storing the lengths of the strings of 1's as ASCII characters in the range [32-126].


Ruby -n, 96 92 75 70 69 65 bytes

p /ra|[SPm]o|^[^C]*F|h.*D/?1:/Le|[MAIZ].*y|[OPDFbv]|[tr]i.*S/?0:1

Try it online!

I'm not very good at generating hashes and lookup tables, so instead I opted to take advantage of all those question-mark wildcards to simplify the logical structure of the table, and then apply some pure Regex magic.

Update: Altered assignment of some question marks and further simplified the logic of matching.

Update 2: Just 2 months later, I've come up with another revamp of the table to save a few more bytes.

The table that we are going to produce looks like this:

Lithium    111101111110011
Sodium     111111111111111
Potassium  111111111111111
Ammonium   111111111111111
Beryllium  111101111110010
Magnesium  111101000010010
Calcium    011101111110010
Strontium  111101111110000
Barium     111101111110000
Zinc       111101000010010
Iron(II)   111101000010010
Copper(II) 011101000010010
Aluminium  111101000010010
Iron(III)  111101000010010
Lead(II)   100001000010000
Silver     100001000010000

Now, the following compounds can be considered soluble:

  • ra Nitrate, Chlorate
  • [SPm]o Sodium, Potassium, Ammonium
  • ^[^C]*F Fluoride, but not Calcium or Copper
  • h.*D Lithium Dichromate

Of the remaining compounds, the following are insoluble:

  • Le Lead
  • [MAIZ]i.*y Magnesium, Aluminium, Iron (and other cations with indicated charge), Zinc compounds with block of anions containing y (Hydroxide-Thiocyanate)
  • [OPDFbv] Oxide, Phosphate, Dichromate, Fluoride, Carbonate, Silver
  • [tr]i.*S Strontium and Barium Sulfates

Everything else is soluble.


Python 2, 166 161 131 bytes

lambda n:chr(hash(n)%1482%737%388%310%295%262%254)not in'gwQFCAkOExUWHJ0gJyicLLKviDK3PEDDxslKztFUV1ja4ukdbe7x9Xd5'.decode('base64')

Try it online!

  • \$\begingroup\$ How did you find so many mod numbers? \$\endgroup\$
    – AlexRacer
    Aug 13, 2018 at 21:08
  • 1
    \$\begingroup\$ @AlexRacer I've written a Python script that tries out integers up to a certain limit, in such a way that calculating the modulo doesn't yield the same results for a soluble and an insoluble input. By repeatedly running this script. I got all these numbers. \$\endgroup\$
    – ovs
    Aug 13, 2018 at 21:18
  • \$\begingroup\$ @AlexRacer I've used that script lots of times before this challenge, ex: codegolf.stackexchange.com/a/115706/64121. Normally these modulo chains are a little shorter. \$\endgroup\$
    – ovs
    Aug 13, 2018 at 21:21

Python 2, 180 177 151 149 147 bytes

def f(s):a,b=s.split();return bin(int('7YNQYE3M7HE5OU3HHE71UMXBVRATPSZTSCV0O4WI84X5KTNE92TMLA',36))[(int(a[1:4],35)|int(b[2:6],35))%3419%529%277-6]

Try it online!

  • \$\begingroup\$ Can 17*(b%91%61%17)%272 not be b%91%61%17*17%272? \$\endgroup\$ Aug 7, 2018 at 12:57

Pascal (FPC), 387 358 353 348 341 319 297 bytes

var s,t:string;j:word;c:array[0..14]of word=(5055,8191,8191,8191,93,63,4143,175,4271,63,127,31,95,3,67);begin read(s);t:=copy(s,pos(' ',s)+1,6);j:=pos(t[1..2],'OxCyCaPhThDiHyFlIoSuBrChChNi')div 2;if'a'=t[6]then j:=12;write(c[pos(s[1..2],'LiSoPoAmBeMaCaStBaZiIrCoAlLeSi')div 2]shr(13-j)mod 2>0)end.

Try it online!


var a:string='LiSoPoAmBeMaCaStBaZiIrCoAlLeSi'; //string containing first 2 letters of cations (can also be const)
                                               //luckily, Iron(II) and Iron(III) are compatible, they can have the same outputs
    b:string='OxCyCaPhThDiHyFlIoSuBrChChNi'; //string containing first 2 letters of anions (can also be const)
                                             //the order is different from the Wikipedia chart;
                                             //Chloride and Chlorate are next to each other to find the right index easier
                                             //Cyanide and Cyanate are compatible - 1 column less
                                             //overall, they are ordered to minimize the numbers in c
    c:array[0..14]of word=(5055,8191,8191,8191,93,63,4143,175,4271,63,127,31,95,3,67);
      //One number for each cation; one bit for solubility of particular combination; the bit for input combination will be found using i and j
  read(s); //put input into s
  t:=copy(s,pos(' ',s)+1,6); //find the 2nd word in s (characters after space), take first 6 letters and copy them into t (6th letter is needed later)
  i:=pos(s[1..2],a)div 2; //position of first 2 letters of cation in a
                          //divided by 2 to get index for c
                          //*in golfed code, expression for i is inserted directly into write function
  j:=pos(t[1..2],b)div 2; //position of first 2 letters of anion in b
                          //divided by 2 to get the particular bit of c[i]
  if(j=11)and(t[6]='a')then j:=j+1; //if the anion is Chlorate, j is wrong and needs to be increased (specifically to 12);
                                    //only Chlorate has 'a' as 6th character, j doesn't need to be checked, but it's here for easier understanding
  writeln((c[i]shr(13-j))mod 2>0); //take i-th element of c, shift it right to put the correct bit at last position,
                                   //extract that bit, check if greater than 0

Jelly,  67 61 60 50 47  44 bytes


A monadic link returning a list which is empty for I and non-empty for S (in Jelly empty lists are falsey while non-empty ones are truthy).

Try it online! (footer ”S”IÇ? is if LastLink(x) is Truthy then "S" else "I")

Or see all cases formatted as a grid matching the order of the grid in the OP.


After creating sets of inputs that must be S and I and evaluating these inputs as base ten (Python: dec=lambda s:sum(10**i*ord(c) for i, c in enumerate(s[::d]))) and using a few loops of modulo-ing values and set checking the hash used here was found.

The insoluble key integers are created in the code by evaluating a base 250 encoded integer, converting it to base  25 ...16 * ...10 and cumulatively summing the result...

* the base reductions were achieved by adding some redundant keys

OḌ%⁽Ƭ%⁽£ṇ%⁽¡ẹ%249ḟ“...’D‘Ĥ - Main Link: list of characters   e.g. "Calcium Carbonate"
O                            - cast to a list of ordinals      [67,97,108,99,105,117,109,32,67,97,114,98,111,110,97,116,101]
 Ḍ                           - convert from base ten           778907829795030961
   ⁽Ƭ                       - base 250 literal = 4258
  %                          - modulo                          625
       ⁽£ṇ                   - base 250 literal = 1721
      %                      - modulo                          625
           ⁽¡ẹ               - base 250 literal = 1215
          %                  - modulo                          625
               249           - literal 249
              %              - modulo                          127
                           ¤ - nilad followed by link(s) as a nilad:
                   “...’     -   literal in base 250    = 382193517807860310905428231939605402667395154
                        D    -   convert to decimal     = [3,8,2,1,9,3,5,1,7,8,0,7,8,6,0,3,1,0,9,0,5,4,2,8,2,3,1,9,3,9,6,0,5,4,0,2,6,6,7,3,9,5,1,5,4]
                         ‘   -   increment (vectorises) = [4,9,3,2,10,4,6,2,8,9,1,8,9,7,1,4,2,1,10,1,6,5,3,9,3,4,2,10,4,10,7,1,6,5,1,3,7,7,8,4,10,6,2,6,5]
                          Ä  -   cumulative sum         = [4,13,16,18,28,32,38,40,48,57,58,66,75,82,83,87,89,90,100,101,107,112,115,124,127,131,133,143,147,157,164,165,171,176,177,180,187,194,202,206,216,222,224,230,235]
                             -     ...note the redundant keys are --->            48       66 75                                115                                                     187             216
                  ḟ          - filter discard (implicit wrap)  [] (if 127 was not in the list above this would've been [127])

Your Answer

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

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