# Position from atomic number in the periodic table

Let us consider the following representation of the periodic table.

     __________________________________________________________________________
| | 1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16  17  18 |
|--------------------------------------------------------------------------|
|1|  1                                                                   2 |
| |                                                                        |
|2|  3   4                                           5   6   7   8   9  10 |
| |                                                                        |
|3| 11  12                                          13  14  15  16  17  18 |
| |                                                                        |
|4| 19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36 |
| |                                                                        |
|5| 37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53  54 |
| |                                                                        |
|6| 55  56      72  73  74  75  76  77  78  79  80  81  82  83  84  85  86 |
| |                                                                        |
|7| 87  88     104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
| |                                                                        |
|8|             57  58  59  60  61  62  63  64  65  66  67  68  69  70  71 |
| |                                                                        |
|9|             89  90  91  92  93  94  95  96  97  98  99 100 101 102 103 |
|__________________________________________________________________________|



Produce a program that takes an atomic number as input, and outputs the row and column of the element of given atomic number.

For instance, giving the atomic number 1 should produce 1 1 (or 1, 1, or anything that looks like a vector of two 1s).

# Details

The representation of the periodic table may be represented in various way. The one presented in this challenge does have the following property : Lantanides and Aktinoides are all in a dedicated row, hence there is no element that is placed at 6, 3 nor 7, 3.

You may take a look at the atomic number repartitions here.

The atomic number is at least 1, at most 118.

# test cases

• 1 -> 1, 1
• 2 -> 1, 18
• 29 -> 4, 11
• 42 -> 5, 6
• 58 -> 8, 5
• 59 -> 8, 6
• 57 -> 8, 4
• 71 -> 8, 18
• 72 -> 6, 4
• 89 -> 9, 4
• 90 -> 9, 5
• 103 -> 9, 18

# Scoring

This is code golf, standard rules applies, hence fewer bytes win.

• There are elements placed at 6, 3 and 7, 3 (Lutetium n = 71 and Lawrencium n = 103). In your table are placed at the end of Lantanides and Aktinoides, but they should not be there. Can you clarify? Jun 16 at 20:44
• It would probably be clearer if your table was showing the atomic numbers instead of the symbols, which we don't really care about in this challenge. Jun 16 at 20:54
• Must we index rows and columns by 1, or is 0-indexing allowed? Jun 16 at 21:28
• @MatteoC. IKAIK, there is multiple ways to represent those ; I based the puzzle on the representation i know most. .Arnauld Indeed ; i will edit the answer as soon as possible ! .MatthewJensen By 1 is asked, so by 1 it must be ! Jun 17 at 1:41
• I wonder if matlab has a builtin for this one. Jun 17 at 20:33

# JavaScript (ES6), 79 bytes

n=>[(n+=--n?n<4?33:n<12?43:n<56?53:n<71?90:n<88?39:n<103?76:25:17)/18|0,n%18+1]


Try it online! (raw output)

Try it online! (builds the periodic table)

Or 76 bytes if 0-indexing is acceptable:

n=>[(n+=--n?n<4?15:n<12?25:n<56?35:n<71?72:n<88?21:n<103?58:7:-1)/18|0,n%18]


Try it online!

# JavaScript, 79 bytes

Inspired by Arnauld's answer. I tried to golf it more, and ended up with this.

n=>[(n+=[113,417,1195,5429,6906,8487,9964].find(x=>n<x/96)%96||25)/18|0,1+n%18]


Try it online!

The outside is the same as Arnauld's answer, but the lookup for what to offset n by is different.

If we create a table the same size as the periodic table, where each item is equal to column + 18 * row - 1 - Z (where Z is the atomic number at that position), we can see some groups emerge:

  ||   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18
==||=========================================================================
1 ||  17 |                                                                33
||-----+                                       +---------------------------
2 ||  33  33                                     |    43  43  43  43  43  43
||---------------------------------------------+  +------------------------
3 ||  43  43                                        | 53  53  53  53  53  53
||------------------------------------------------+
4 ||  53  53  53  53  53  53  53  53  53  53  53  53  53  53  53  53  53  53
||
5 ||  53  53  53  53  53  53  53  53  53  53  53  53  53  53  53  53  53  53
||         +---------------------------------------------------------------
6 ||  53  53 |    39  39  39  39  39  39  39  39  39  39  39  39  39  39  39
||---------+  +------------------------------------------------------------
7 ||  39  39    | 25  25  25  25  25  25  25  25  25  25  25  25  25  25  25
||------------+------------------------------------------------------------
8 ||              90  90  90  90  90  90  90  90  90  90  90  90  90  90  90
||-------------------------------------------------------------------------
9 ||              76  76  76  76  76  76  76  76  76  76  76  76  76  76  76
||


Once we have the right group, we can just add its value to Z, then the row will be (new) Z / 18, the column Z % 18 + 1.

The lookup table [113,417,1195,5429,6906,8487,9964] will be more obvious in this solution with a factor of 100, instead of 96:

n=>[(n+=[117,433,1243,5653,7190,8839,10376].find(x=>n<x/100)%100||25)/18|0,1+n%18]


Each item i in the list [117,433,1243,5653,7190,8839,10376] corresponds to one of the groups such that the group value is i % 100 and all atomic numbers in the group are less than i / 100. I reduced the factor to 96 to save bytes.

## Old invalid answer (0-indexed), 76 bytes

n=>[(n+=[82,341,999,4573,5825,7151,8403].find(x=>n<x/81)%81-2||7)/18|0,n%18]


Try it online!

• The OP replied to 3 people in 1 comment without pinging you: it does have to be 1-based indexing. Jun 18 at 22:14
• @PeterCordes Thank you, I did see that, just haven’t had a chance to update. Jun 19 at 2:50

# 05AB1E, 32 bytes

T•3¾§Ue•44вÝ•¨²>Ùö€•Ƶlв+ª˜18‰>Iè


Try it online or verify all test cases. (You can add --debug-stack as argument to the singular TIO to see all individual steps.)

Explanation:

T          # Push 10
•3¾§Ue•   # Push compressed integer 15829433590
44в      # Convert it to base-44 as list: [2,7,43,14,16,14,14]
Ý     # Convert each inner value to a list in the range [0,value]
•¨²>Ùö€•  # Push compressed integer 180810357611003
Ƶl       # Push compressed integer 148
в      # Convert to base-148 as list: [17,30,48,129,93,147,111]
+     # Add these to each value of the inner lists at the same positions
ª         # Append this list of lists, which will first implicitly converts
# the initial 10 to list [1,0]
˜        # Flatten it
18‰     # Divmod-18 each inner integer
>    # Increase each integer in each pair by 1
Iè  # 0-based index the input into this list of pairs
# (after which the result is output implicitly)


See this 05AB1E tip of mine (sections How to compress large integers? and How to compress integer lists?) to understand why •3¾§Ue• is 15829433590; •3¾§Ue•44в is [2,7,43,14,16,14,14]; •¨²>Ùö€• is 180810357611003; Ƶl is 148; and •¨²>Ùö€•Ƶlв is [17,30,48,129,93,147,111].

# x86 32-bit machine code, 39 36 bytes

99 42 A9 FE 42 3C 02 76 1A 8D 4A 02 D1 E9 0F AF C9 D1 E1 28 C8 77 ED 4A 42 2C F1 7F 04 42 7B F8 4A 04 03 C3


Try it online!

This takes a 32-bit integer in EAX, following the regparm(1) convention. It returns two 32-bit integers in EAX and EDX; this document seems to say that this would be the standard way of returning a structure of two 32-bit integers, but it apparently doesn't actually work that way (or am I misreading that document?), which is why the linked TIO program is coded differently.

-3 by calculating line lengths instead of hardcoding them.

In assembly:

f:  cdq
inc edx
.byte 0xA9, 0xFE
a:  inc edx
cmp al, 2
jbe e
lea ecx, [edx + 2]
shr ecx, 1
imul ecx, ecx
shl ecx, 1
s:  sub al, cl
ja a
dec edx
b:  inc edx
sub al, -15
jg t
inc edx
jpo b
dec edx
e:  ret


The main part of the code works as follows:

• If AL ≤ 2 (in the first two columns), jump straight to the end.
• Calculate the length of the current line as $$\ \lfloor\frac{\textrm{line number} + 2}{2}\rfloor^2\cdot 2\$$ and subtract it from AL.
• If the result is positive, jump back to repeat, moving to the next line.
• Otherwise, add a net +18 to AL and return.

Special cases are handled as follows:

• sub al, -15 is used to check for being in the extracted block and reused to adjust the horizontal position for elements in that block.
• That check gives a false positive for elements 21 and 39 (the only ones in column 3). That is corrected for by looking at the parity flag after inc edx; the incremented value is 5 (1012) or 6 (1102), with even parity, for elements 21 and 39, whereas it is 7 (1112) or 8 (10002), with odd parity, for elements that do belong in the extracted block.
• Helium (2) is dealt with by .byte 0xA9, 0xFE, which subsumes the following two instructions into a test eax, 0x023C42FE. The following be condition is (CF or ZF); CF is always 0 after a test, and in this case, ZF is 1 only for element 1.
• Windows 32-bit calling conventions return 64-bit structs in EDX:EAX. But i386 SysV only does that for int64_t, not for structs larger than 4 bytes, unfortunately. Apparently regparm doesn't change return values, just arg passing. godbolt.org/z/811E7srM1 confirms that MSVC normal and -Gv (vectorcall) returns a struct in EDX:EAX, but regparm only returns in registers for a 64-bit integer type. (And BTW, nice trick with test eax, imm32 consuming 2 instructions on loop entry and doing useful FLAGS setting. Your asm source can use meaningful label names and comments, though.) Jun 18 at 22:22
• Yeah, Agner's table 7 seems to be wrong, saying "GNU" compilers targeting 32-bit will return in "I" (integer registers) for a small struct. Maybe he's looking at GCC targeting Windows? But he says "Gnu except MacOS" implying that it covers Linux, and says MacOS also returns small structs in registers. I think MacOS uses i386 SysV for 32-bit code, so yeah, that table has some errors. Jun 18 at 22:37

# Python, 124 bytes

def f(n):
for m,z in zip(b'hYWH97%\x13\r\x0b\5\3\2\1',b'*,\x0f)+\x0e\r\x0cw\x0bv\n\xa2\t'):
if m<=n:return z%9+1,z//9+n-m

Attempt This Online!

# Charcoal, 50 bytes

ＮθＩＥ↨⁺θ⊟⊟ΦＩ⪪⪪”←＆“▷/»h⦄¤Ａh)｜J\-w~~∕↔AＸ”¶¦ ‹⊟ιθ¹⁸⁺ικ


Try it online! Link is to verbose version of code. Explanation: Port of @Arnauld's JavaScript answer.

Ｎθ                              Input as a number
”...”              Compressed lookup table
⪪     ¶             Split on newlines
⪪                    Split each element on space
Ｉ                     Cast to integer
Φ                      Filtered where
ι        Current pair
⊟         Remove last element
‹          Is less than
θ       Input number
⊟                       Get last matching element
⊟                        Get first element
⁺                          Plus
θ                         Input number
↨                           Base conversion (i.e. divmod)
¹⁸     Literal integer 18
Ｅ                       ⁺ικ  Increment the second element
Ｉ                             Cast to string
Implicitly print


# Rust Nightly, 239 bytes + #![feature(exclusive_range_pattern)]

|p:u32|match p{1=>(1,1),2=>(18,1),3|4|11|12=>(p%8-2,p/8+2),5..11|13..19=>((p-3)%8+11,(p-3)/8+2),19..57=>((p-1)%18+1,(p-1)/18+3),72..87|104..119=>((p-25)%32-11,(p-25)/32+5),87|88=>(p-86,7),57..72|89..104=>((p-25)%32+4,(p-25)/32+7),_=>(2,1)}


Basically just matches in some squares then uses some division and mod to format the remaining numbers.

Playground

The actual original data is 236 bytes (118 * 2) so this code is actually barely longer than the data it's trying to encode. I tried I guess :/

# R, 113 bytes

\(x,p=matrix(c(1,0:-15,2:4,0:-9,5:12,0:-9,13:56,0,72:88,0,104:118,0:-2,57:71,0:-2,89:103),9,,T))which(p==x,T)[1,]

Attempt This Online!

Creates the matrix, then looks up values. Trying to redefine + so I can write

\(x,+=\(x)0:-x,p=matrix(c(1,+15,2:4,+9,5:12,+9,13:56,0,72:88,0,104:118,+2,57:71,+2,89:103),9,,T))which(p==x,T)[1,]


Is 116 characters.

# Vyxal, 32 bytes

₀f»#tṪİ□»₆τʀ»₃¡s↳‡∴»⁺/τ+Jf18vḋ›i


Try it Online!

Port of 05AB1E.

## How?

₀f»#tṪİ□»₆τʀ»₃¡s↳‡∴»⁺/τ+Jf18vḋ›i
₀f                               # Push ten converted to a list of digits: [1, 0]
»#tṪİ□»                        # Push compressed integer 145680302990
₆τ                      # Convert to base-64 as list: [2, 7, 43, 14, 16, 14, 14]
ʀ                     # Convert each to a range [0, item]
»₃¡s↳‡∴»             # Push compressed integer 180810357611003
⁺/τ          # Convert to base-148 as list: [17, 30, 48, 129, 93, 147, 111]
+         # Add the values in these two lists at the same positions
J        # Join the [1, 0] and this list together
f       # Flatten this list
18vḋ   # Divmod each by 18
›  # Increment each
i # 0-based index the input into this list


# Ruby, 79 bytes

->n{j=0
"hilt8GXg".bytes{|i|n>i%104&&M=n+"+^9lG=3#"[j-=1].ord}
[M/18-1,M%18+1]}


Try it online!

Uses 1 based indexing per the question, and comes out the same length as a couple of other answers.

The approach is similar to Arnauld's but I use a magic string and loop to iterate through all the discontinuities. The character is taken mod 104 to be able to cover all the values from 0 to 103 without the need to use nonprintable characters.

The raw output is stored in M (use a capital letter for a consonant, to avoid Ruby saying it can't find a variable that was defined inside a loop.) A second magic string is used to look up the correct offset.

The output is given as row M/18-1 (the -1 is used to avoid offsets smaller than ASCII 32 in the magic string) and column M%18+1 (+1 is used because the columns are 1-indexed.)