Periodic table

From Rosetta Code
Revision as of 17:44, 20 June 2022 by SqrtNegInf (talk | contribs) (Add Perl, move Raku)
Task
Periodic table
You are encouraged to solve this task according to the task description, using any language you may know.
Task

Display the row and column in the periodic table of the given atomic number.

The periodic table

Let us consider the following periodic table representation.

     __________________________________________________________________________ 
    |   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16  17  18 |
    |                                                                          |
    |1  H                                                                   He |
    |                                                                          |
    |2  Li  Be                                          B   C   N   O   F   Ne |
    |                                                                          |
    |3  Na  Mg                                          Al  Si  P   S   Cl  Ar |
    |                                                                          |
    |4  K   Ca  Sc  Ti  V   Cr  Mn  Fe  Co  Ni  Cu  Zn  Ga  Ge  As  Se  Br  Kr |
    |                                                                          |
    |5  Rb  Sr  Y   Zr  Nb  Mo  Tc  Ru  Rh  Pd  Ag  Cd  In  Sn  Sb  Te  I   Xe |
    |                                                                          |
    |6  Cs  Ba  *   Hf  Ta  W   Re  Os  Ir  Pt  Au  Hg  Tl  Pb  Bi  Po  At  Rn |
    |                                                                          |
    |7  Fr  Ra  °   Rf  Db  Sg  Bh  Hs  Mt  Ds  Rg  Cn  Nh  Fl  Mc  Lv  Ts  Og |
    |__________________________________________________________________________|
    |                                                                          |
    |                                                                          |
    |8  Lantanoidi* La  Ce  Pr  Nd  Pm  Sm  Eu  Gd  Tb  Dy  Ho  Er  Tm  Yb  Lu |
    |                                                                          |
    |9   Aktinoidi° Ak  Th  Pa  U   Np  Pu  Am  Cm  Bk  Cf  Es  Fm  Md  No  Lr |
    |__________________________________________________________________________|
Example test cases;
  •   1 -> 1 1
  •   2 -> 1 18
  •   29 -> 4 11
  •   42 -> 5 6
  •   57 -> 8 4
  •   58 -> 8 5
  •   72 -> 6 4
  •   89 -> 9 4
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.


See also




ALGOL 68

<lang algol68>BEGIN # display the period and group number of an element, #

     # given its atomic number                            #
   INT max atomic number = 118; # highest known element   #
   # the positions are stored as:                         #
   #     ( group number * group multiplier ) + period     #
   INT group multiplier  = 100;
   [ 1 : max atomic number ]INT position;
   # construct the positions of the elements in the table #
   BEGIN
       STRING periodic table = "-                ="
                             + "--          -----="
                             + "--          -----="
                             + "-----------------="
                             + "-----------------="
                             + "--8--------------="
                             + "--9--------------="
                             ;
       INT period  := 1;
       INT group   := 1;
       INT element := 1;
       FOR t FROM LWB periodic table TO UPB periodic table DO
           CHAR p = periodic table[ t ];
           IF p = "8" OR p = "9" THEN
               # lantanoids or actinoids                   #
               INT series period = IF p = "8" THEN 8 ELSE 9 FI;
               INT series group := 4;
               FOR e TO 15 DO
                   position[ element ] := ( group multiplier * series group ) + series period;
                   element            +:= 1;
                   series group       +:= 1
               OD
           ELIF p /= " " THEN
               # there is a single element here            #
               position[ element ] := ( group multiplier * group ) + period;
               element            +:= 1;
               IF p = "=" THEN
                   # final element of the period           #
                   period +:= 1;
                   group   := 0
               FI
           FI;
           group +:= 1
       OD
   END;
   # display the period and group numbers of test elements #
   []INT test = ( 1, 2, 29, 42, 57, 58, 59, 71, 72, 89, 90, 103, 113 );
   FOR t FROM LWB test TO UPB test DO
       INT e = test[ t ];
       IF e < LWB position OR e > UPB position THEN
           print( ( "Invalid element: ", whole( e, 0 ), newline ) )
       ELSE
           INT period = position[ e ] MOD  group multiplier;
           INT group  = position[ e ] OVER group multiplier;
           print( ( "Element ", whole( e,      -3 )
                  , " -> ", whole( period,  0 ), ", ", whole( group,  -2 )
                  , newline
                  )
                )
       FI
   OD

END</lang>

Output:
Element   1 -> 1,  1
Element   2 -> 1, 18
Element  29 -> 4, 11
Element  42 -> 5,  6
Element  57 -> 8,  4
Element  58 -> 8,  5
Element  59 -> 8,  6
Element  71 -> 8, 18
Element  72 -> 6,  4
Element  89 -> 9,  4
Element  90 -> 9,  5
Element 103 -> 9, 18
Element 113 -> 7, 13

Go

Translation of: Wren

<lang ecmascript>package main

import (

   "fmt"
   "log"

)

var limits = [][2]int{

   {3, 10}, {11, 18}, {19, 36}, {37, 54}, {55, 86}, {87, 118},

}

func periodicTable(n int) (int, int) {

   if n < 1 || n > 118 {
       log.Fatal("Atomic number is out of range.")
   }
   if n == 1 {
       return 1, 1
   }
   if n == 2 {
       return 1, 18
   }
   if n >= 57 && n <= 71 {
       return 8, n - 53
   }
   if n >= 89 && n <= 103 {
       return 9, n - 85
   }
   var row, start, end int
   for i := 0; i < len(limits); i++ {
       limit := limits[i]
       if n >= limit[0] && n <= limit[1] {
           row, start, end = i+2, limit[0], limit[1]
           break
       }
   }
   if n < start+2 || row == 4 || row == 5 {
       return row, n - start + 1
   }
   return row, n - end + 18

}

func main() {

   for _, n := range []int{1, 2, 29, 42, 57, 58, 59, 71, 72, 89, 90, 103, 113} {
       row, col := periodicTable(n)
       fmt.Printf("Atomic number %3d -> %d, %-2d\n", n, row, col)
   }

}</lang>

Output:
Atomic number   1 -> 1, 1 
Atomic number   2 -> 1, 18
Atomic number  29 -> 4, 11
Atomic number  42 -> 5, 6 
Atomic number  57 -> 8, 4 
Atomic number  58 -> 8, 5 
Atomic number  59 -> 8, 6 
Atomic number  71 -> 8, 18
Atomic number  72 -> 6, 4 
Atomic number  89 -> 9, 4 
Atomic number  90 -> 9, 5 
Atomic number 103 -> 9, 18
Atomic number 113 -> 7, 13

Julia

Translation of: Wren

<lang ruby>const limits = [3:10, 11:18, 19:36, 37:54, 55:86, 87:118]

function periodic_table(n)

   (n < 1 || n > 118) && error("Atomic number is out of range.")
   n == 1 && return [1, 1]
   n == 2 && return [1, 18]
   57 <= n <= 71 && return [8, n - 53]
   89 <= n <= 103 && return [9, n - 85]
   row, limitstart, limitstop = 0, 0, 0
   for i in eachindex(limits)
       if limits[i].start <= n <= limits[i].stop
           row, limitstart, limitstop = i + 1, limits[i].start, limits[i].stop
           break
       end
   end
   return (n < limitstart + 2 || row == 4 || row == 5) ?
       [row, n - limitstart + 1] : [row, n - limitstop + 18]

end

for n in [1, 2, 29, 42, 57, 58, 59, 71, 72, 89, 90, 103, 113]

   rc = periodic_table(n)
   println("Atomic number ", lpad(n, 3), " -> ($(rc[1]), $(rc[2]))")

end

</lang>

Output:
Atomic number   1 -> (1, 1)
Atomic number   2 -> (1, 18)
Atomic number  29 -> (4, 11)
Atomic number  42 -> (5, 6)
Atomic number  57 -> (8, 4)
Atomic number  58 -> (8, 5)
Atomic number  59 -> (8, 6)
Atomic number  71 -> (8, 18)
Atomic number  72 -> (6, 4)
Atomic number  89 -> (9, 4)
Atomic number  90 -> (9, 5)
Atomic number 103 -> (9, 18)
Atomic number 113 -> (7, 13)

Perl

Translation of: Raku

<lang perl>use strict; use warnings; no warnings 'uninitialized'; use feature 'say'; use List::Util <sum head>;

sub divmod { int $_[0]/$_[1], $_[0]%$_[1] }

my $b = 18; my(@offset,@span,$cnt); push @span, ($cnt++) x $_ for <1 3 8 44 15 17 15 15>; @offset = (16, 10, 10, (2*$b)+1, (-2*$b)-15, (2*$b)+1, (-2*$b)-15);

for my $n (<1 2 29 42 57 58 72 89 90 103 118>) {

   printf "%3d: %2d, %2d\n", $n, map { $_+1 } divmod $n-1 + sum(head $span[$n-1], @offset), $b;

}</lang>

Output:
  1:  1,  1
  2:  1, 18
 29:  4, 11
 42:  5,  6
 57:  8,  4
 58:  8,  5
 72:  6,  4
 89:  9,  4
 90:  9,  5
103:  9, 18

Phix

with javascript_semantics
constant match_wp = false

function prc(integer n)
    constant t = {0,2,10,18,36,54,86,118,119}
    integer row = abs(binary_search(n,t,true))-1,
            col = n-t[row]
    if col>1+(row>1) then
        col = 18-(t[row+1]-n)
        if match_wp then
            if col<=2 then return {row+2,col+14} end if
        else -- matches above ascii:
            if col<=2+(row>5) then return {row+2,col+15} end if
        end if
    end if
    return {row,col}
end function

sequence pt = repeat(repeat("   ",19),10)
pt[1][2..$] = apply(true,sprintf,{{"%3d"},tagset(18)})  -- column headings
for i=1 to 9 do pt[i+1][1] = sprintf("%3d",i) end for -- row numbers
for i=1 to 118 do
    integer {r,c} = prc(i)
    pt[r+1][c+1] = sprintf("%3d",i)
end for
if not match_wp then -- (ascii only:)
    pt[7][4] = " L*"
    pt[8][4] = " A*"
    pt[9][2..4] = {"Lanthanide:"}
    pt[10][2..4] = {"  Actinide:"}
end if
printf(1,"%s\n",{join(apply(true,join,{pt,{"|"}}),"\n")})
Output:

With match_wp set to true:

   |  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| 71| 72| 73| 74| 75| 76| 77| 78| 79| 80| 81| 82| 83| 84| 85| 86
  7| 87| 88|103|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|   |
  9|   |   | 89| 90| 91| 92| 93| 94| 95| 96| 97| 98| 99|100|101|102|   |

Or with match_wp false:

   |  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| L*| 72| 73| 74| 75| 76| 77| 78| 79| 80| 81| 82| 83| 84| 85| 86
  7| 87| 88| A*|104|105|106|107|108|109|110|111|112|113|114|115|116|117|118
  8|Lanthanide:| 57| 58| 59| 60| 61| 62| 63| 64| 65| 66| 67| 68| 69| 70| 71
  9|  Actinide:| 89| 90| 91| 92| 93| 94| 95| 96| 97| 98| 99|100|101|102|103

Python

A solution trying hard not to encode too much data about the table.

<lang Python> def perta(atomic) -> (int, int):

   NOBLES = 2, 10, 18, 36, 54, 86, 118
   INTERTWINED = 0, 0, 0, 0, 0, 57, 89
   INTERTWINING_SIZE = 14
   LINE_WIDTH = 18
   prev_noble = 0
   for row, noble in enumerate(NOBLES):
       if atomic <= noble:  # we are at the good row. We now need to determine the column
           nb_elem = noble - prev_noble  # number of elements on that row
           rank =  atomic - prev_noble  # rank of the input element among elements
           if INTERTWINED[row] and INTERTWINED[row] <= atomic <= INTERTWINED[row] + INTERTWINING_SIZE:  # lantanides or actinides
               row += 2
               col = rank + 1
           else:  # not a lantanide nor actinide
               # handle empty spaces between 1-2, 4-5 and 12-13.
               nb_empty = LINE_WIDTH - nb_elem  # spaces count as columns
               inside_left_element_rank = 2 if noble > 2 else 1
               col = rank + (nb_empty if rank > inside_left_element_rank else 0)
           break
       prev_noble = noble
   return row+1, col


  1. small test suite

TESTS = {

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

}

for input, out in TESTS.items():

   found = perta(input)
   print('TEST:{:3d} -> '.format(input) + str(found) + (f' ; ERROR: expected {out}' if found != out else ))

</lang>

Raku

<lang perl6>my $b = 18; my @offset = (16, 10, 10, (2*$b)+1, (-2*$b)-15, (2*$b)+1, (-2*$b)-15); my @span = flat ^8 Zxx <1 3 8 44 15 17 15 15>;

for <1 2 29 42 57 58 72 89 90 103> -> $n {

   printf "%3d: %2d, %2d\n", $n, map {$_+1}, ($n-1 + [+] @offset.head(@span[$n-1])).polymod($b).reverse;

}</lang>

Output:
  1:  1,  1
  2:  1, 18
 29:  4, 11
 42:  5,  6
 57:  8,  4
 58:  8,  5
 72:  6,  4
 89:  9,  4
 90:  9,  5
103:  9, 18

Wren

Library: Wren-fmt

There is a discrepancy between how the periodic table is arranged in the Wikipedia article and how it is arranged in the task description. I've used the latter in the following script. <lang ecmascript>import "./fmt" for Fmt

var limits = [3..10, 11..18, 19..36, 37..54, 55..86, 87..118]

var periodicTable = Fn.new { |n|

   if (n < 1 || n > 118) Fiber.abort("Atomic number is out of range.")
   if (n == 1) return [1, 1]
   if (n == 2) return [1, 18]
   if (n >= 57 && n <= 71)  return [8, n - 53]
   if (n >= 89 && n <= 103) return [9, n - 85]    
   var row
   var start
   var end
   for (i in 0...limits.count) {
       var limit = limits[i]
       if (n >= limit.from && n <= limit.to) {
           row = i + 2
           start = limit.from
           end = limit.to
           break
       }
   }
   if (n < start + 2 || row == 4 || row == 5) return [row, n - start + 1]
   return [row, n - end + 18]

}

for (n in [1, 2, 29, 42, 57, 58, 59, 71, 72, 89, 90, 103, 113]) {

   var rc = periodicTable.call(n)
   Fmt.print("Atomic number $3d -> $d, $-2d", n, rc[0], rc[1])

}</lang>

Output:
Atomic number   1 -> 1, 1 
Atomic number   2 -> 1, 18
Atomic number  29 -> 4, 11
Atomic number  42 -> 5, 6 
Atomic number  57 -> 8, 4 
Atomic number  58 -> 8, 5 
Atomic number  59 -> 8, 6 
Atomic number  71 -> 8, 18
Atomic number  72 -> 6, 4 
Atomic number  89 -> 9, 4 
Atomic number  90 -> 9, 5 
Atomic number 103 -> 9, 18
Atomic number 113 -> 7, 13

XPL0

<lang XPL0>proc ShowPosn(N); \Show row and column for element int N, M, A, B, I, R, C; [A:= [ 1, 2, 5, 13, 57, 72, 89, 104]; \magic numbers

B:= [-1, 15, 25, 35, 72, 21, 58,   7];

I:= 7; while A(I) > N do I:= I-1; M:= N + B(I); R:= M/18 +1; C:= rem(0) +1; IntOut(0, N); Text(0, " -> "); IntOut(0, R); Text(0, ", "); IntOut(0, C); CrLf(0); ];

int Element, I; [Element:= [1, 2, 29, 42, 57, 58, 72, 89, 90, 103]; for I:= 0 to 10-1 do ShowPosn(Element(I)); ]</lang>

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