# Golf a Solubility Chart

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.

Table:

?,S,S,S,?,S,S,S,?,S,S,?,I,S,S
S,S,S,S,S,S,S,S,S,S,S,?,S,S,S
S,S,S,S,S,S,S,S,S,S,S,?,S,S,S
S,S,S,S,S,S,S,S,?,S,S,?,S,S,S
S,S,S,?,?,?,?,?,?,?,S,?,?,S,?
?,S,S,S,I,S,I,?,?,?,S,I,I,S,I
I,S,S,S,I,S,?,S,?,?,S,?,I,?,I
?,S,S,S,I,S,S,?,?,?,S,?,?,I,?
?,S,S,S,I,S,S,S,?,?,S,?,?,I,?
?,S,S,S,I,S,I,I,?,?,S,I,I,S,I
S,S,S,S,I,S,I,?,?,?,S,I,I,S,I
?,S,S,?,I,S,I,?,?,I,S,I,I,S,I
S,S,S,?,?,S,I,?,?,?,S,I,I,S,I
?,S,S,?,?,S,I,?,?,?,S,I,I,?,I
?,?,?,I,I,S,I,?,?,?,S,I,I,I,?
S,I,I,I,I,S,?,I,I,?,S,?,I,?,I


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.

• I like this because the undefined behaviour of ?s gives a lot of freedom in what algorithms one can use. – Jo King Aug 7 '18 at 3:57
• @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. – Arnauld Aug 7 '18 at 8:48
• 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. – Stewie Griffin Aug 7 '18 at 8:57
• Would you consider allowing to output any 2 distinct consistent values rather than just truthy/'S' or falsy/'I'? – Arnauld Aug 7 '18 at 10:22
• 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). – Jonathan Allan Aug 7 '18 at 13:24

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

## How?

### 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]


Examples:

'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:

11101110011111111111111111111101111111110111111111111111111111101111111111111101111111011111
11111111111011111111111111111111011111111111001111111111111111111111111111111111111111111111
11111111111111111111111111111111011111111111111111111111111011100111111110111111111111111111
11111111111111111111011111111110011111111111111111111111111111111111110110111111111111111011
11011111111111111111111111111101111110111111111111101101111111111111110110111111111111111111
11111011111101110111111111111110111110


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

# Ruby-n, 9692757069 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
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, 166161 131 bytes

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


Try it online!

• How did you find so many mod numbers? – AlexRacer Aug 13 '18 at 21:08
• @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. – ovs Aug 13 '18 at 21:18
• @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. – ovs Aug 13 '18 at 21:21

# Python 2, 180177151149 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!

• Can 17*(b%91%61%17)%272 not be b%91%61%17*17%272? – Jonathan Frech Aug 7 '18 at 12:57

# Pascal (FPC), 387358353348341319 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!

### Explanation:

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
s,t:string;
i,j:word;
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
begin
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
end.


# Jelly,  67 61 60 50 47  44 bytes

OḌ%⁽Æ¬%⁽£ṇ%⁽¡ẹ%249ḟ“ıA¬ɲḃẏCċtȯƁƤçȤċŒḲOƲ’D‘Ä¤


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.

### How?

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