Index finite lists of positive integers: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎base 11: removed errant tag)
(→‎{{header|jq}}: simplify)
 
(24 intermediate revisions by 9 users not shown)
Line 24: Line 24:
Make the &nbsp; ''rank'' &nbsp; function as a &nbsp; [[wp:bijection| <u>bijection</u>]] &nbsp; and show &nbsp; ''unrank(n)'' &nbsp; for &nbsp; <big>'''n'''</big> &nbsp; varying from &nbsp; '''0''' &nbsp; to &nbsp; '''10'''.
Make the &nbsp; ''rank'' &nbsp; function as a &nbsp; [[wp:bijection| <u>bijection</u>]] &nbsp; and show &nbsp; ''unrank(n)'' &nbsp; for &nbsp; <big>'''n'''</big> &nbsp; varying from &nbsp; '''0''' &nbsp; to &nbsp; '''10'''.
<br><br>
<br><br>

=={{header|11l}}==
{{trans|Python}}

<syntaxhighlight lang="11l">F rank(x)
R BigInt(([1] [+] x).map(String).join(‘A’), radix' 11)

F unrank(n)
V s = String(n, radix' 11)
R s.split(‘A’).map(Int)[1..]

V l = [1, 2, 3, 10, 100, 987654321]
print(l)
V n = rank(l)
print(n)
l = unrank(n)
print(l)</syntaxhighlight>

{{out}}
<pre>
[1, 2, 3, 10, 100, 987654321]
1723765384735274025865314
[1, 2, 3, 10, 100, 987654321]
</pre>

=={{header|Arturo}}==

<syntaxhighlight lang="rebol">rank: function [arr][
if empty? arr -> return 0
from.binary "1" ++ join.with:"0" map arr 'a -> repeat "1" a
]

unrank: function [rnk][
if rnk=1 -> return [0]
bn: as.binary rnk
map split.by:"0" slice bn 1 dec size bn => size
]

l: [1, 2, 3, 5, 8]

print ["The initial list:" l]

r: rank l
print ["Ranked:" r]

u: unrank r
print ["Unranked:" u]</syntaxhighlight>

{{out}}

<pre>The initial list: [1 2 3 5 8]
Ranked: 14401279
Unranked: [1 2 3 5 8]</pre>


=={{header|D}}==
=={{header|D}}==
This solution isn't efficient.
This solution isn't efficient.
{{trans|Python}}
{{trans|Python}}
<lang d>import std.stdio, std.algorithm, std.array, std.conv, std.bigint;
<syntaxhighlight lang="d">import std.stdio, std.algorithm, std.array, std.conv, std.bigint;


BigInt rank(T)(in T[] x) pure /*nothrow*/ @safe {
BigInt rank(T)(in T[] x) pure /*nothrow*/ @safe {
Line 48: Line 101:
s.rank.writeln;
s.rank.writeln;
s.rank.unrank.writeln;
s.rank.unrank.writeln;
}</lang>
}</syntaxhighlight>
{{out}}
{{out}}
<pre>[1, 2, 3, 10, 100, 987654321]
<pre>[1, 2, 3, 10, 100, 987654321]
Line 57: Line 110:
=={{header|FreeBASIC}}==
=={{header|FreeBASIC}}==
Restricted to shortish lists with smallish integers, because the rank integers get really big really fast, and bloating the code with arbitrary precision arithmetic isn't illustrative.
Restricted to shortish lists with smallish integers, because the rank integers get really big really fast, and bloating the code with arbitrary precision arithmetic isn't illustrative.
<lang FreeBASIC>type duple
<syntaxhighlight lang="freebasic">type duple
A as ulongint
A as ulongint
B as ulongint
B as ulongint
Line 149: Line 202:
print R,
print R,
unrank R, X()
unrank R, X()
show_list(X())</lang>
show_list(X())</syntaxhighlight>
{{out}}
{{out}}
<pre>0 []
<pre>0 []
Line 160: Line 213:


A list element n is encoded as a 1 followed by n 0's. Element encodings are concatenated to form a single integer rank. An advantage of this encoding is that no special case is required to handle the empty list.
A list element n is encoded as a 1 followed by n 0's. Element encodings are concatenated to form a single integer rank. An advantage of this encoding is that no special case is required to handle the empty list.
<lang go>package main
<syntaxhighlight lang="go">package main


import (
import (
Line 198: Line 251:
r := rank(u)
r := rank(u)
fmt.Printf("\n%v\n%d\n%d\n", &b, u, &r)
fmt.Printf("\n%v\n%d\n%d\n", &b, u, &r)
}</lang>
}</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 220: Line 273:


A bit of a hack to make a base 11 number then interpret it as base 16, just because that's easiest. Not bijective. Practical though for small lists of large numbers.
A bit of a hack to make a base 11 number then interpret it as base 16, just because that's easiest. Not bijective. Practical though for small lists of large numbers.
<lang go>package main
<syntaxhighlight lang="go">package main


import (
import (
Line 312: Line 365:
}
}
return l
return l
}</lang>
}</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 329: Line 382:
Rank: 1 unrank not bijective
Rank: 1 unrank not bijective
</pre>
</pre>

=={{header|Haskell}}==

<syntaxhighlight lang="haskell">import Data.List

toBase :: Int -> Integer -> [Int]
toBase b = unfoldr f
where
f 0 = Nothing
f n = let (q, r) = n `divMod` fromIntegral b in Just (fromIntegral r, q)

fromBase :: Int -> [Int] -> Integer
fromBase n lst = foldr (\x r -> fromIntegral n*r + fromIntegral x) 0 lst

------------------------------------------------------------
listToInt :: Int -> [Int] -> Integer
listToInt b lst = fromBase (b+1) $ concat seq
where
seq = [ let (q, r) = divMod n b
in replicate q 0 ++ [r+1]
| n <- lst ]

intToList :: Int -> Integer -> [Int]
intToList b lst = go 0 $ toBase (b+1) lst
where
go 0 [] = []
go i (0:xs) = go (i+1) xs
go i (x:xs) = (i*b + x - 1) : go 0 xs</syntaxhighlight>

Using different bases we may enumerate lists.

<pre>*Main> intToList 2 <$> [1..20]
[[0],[1],[2],[0,0],[1,0],[3],[0,1],[1,1],[4],[0,2],[1,2],[2,0],[0,0,0],[1,0,0],[3,0],[0,1,0],[1,1,0],[5],[0,3],[1,3]]

*Main> intToList 3 <$> [1..20]
[[0],[1],[2],[3],[0,0],[1,0],[2,0],[4],[0,1],[1,1],[2,1],[5],[0,2],[1,2],[2,2],[6],[0,3],[1,3],[2,3],[3,0]]

*Main> intToList 10 <$> [1..20]
[[0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[0,0],[1,0],[2,0],[3,0],[4,0],[5,0],[6,0],[7,0],[8,0]]

*Main> listToInt 2 <$> permutations [1,2,3,4]
[2360,2370,2382,2406,2292,2288,5274,5190,5136,5922,5916,5160,4680,4646,4628,4950,4944,4638,2736,2702,2450,2844,2760,2454]</pre>

This mapping is a bijection:

<pre>*Main> (listToInt 3 . intToList 3 <$> [0..100]) == [0..100]
True</pre>





=={{header|J}}==
=={{header|J}}==
Line 336: Line 440:
Implementation:
Implementation:


<lang j>scrunch=:3 :0
<syntaxhighlight lang="j">scrunch=:3 :0
n=.1x+>./y
n=.1x+>./y
#.(1#~##:n),0,n,&#:n#.y
#.(1#~##:n),0,n,&#:n#.y
Line 346: Line 450:
n=.#.m{.(m+1)}.b
n=.#.m{.(m+1)}.b
n #.inv#.(1+2*m)}.b
n #.inv#.(1+2*m)}.b
)</lang>
)</syntaxhighlight>


Example use:
Example use:


<lang J> scrunch 4 5 7 9 0 8 8 7 4 8 8 4 1
<syntaxhighlight lang="j"> scrunch 4 5 7 9 0 8 8 7 4 8 8 4 1
4314664669630761
4314664669630761
hcnurcs 4314664669630761
hcnurcs 4314664669630761
4 5 7 9 0 8 8 7 4 8 8 4 1</lang>
4 5 7 9 0 8 8 7 4 8 8 4 1</syntaxhighlight>


Explanation. We treat the sequence as an n digit number in base m where n is the length of the list and m is 1+the largest value in the list. (This is equivalent to treating it as a polynomial in m with coefficients which are the values of the list.) In other words 4 5 7 9 0 8 8 7 4 8 8 4 1 becomes 4579088748841. Now we just need to encode the base (10, in this case). To do that we treat this number as a sequence of bits and prepend it with 1 1 1 1 0 1 0 1 0. This is a sequence of '1's whose length matches the number of bits needed to represent the base of our polynomial, followed by a 0 followed by the base of our polynomial.
Explanation. We treat the sequence as an n digit number in base m where n is the length of the list and m is 1+the largest value in the list. (This is equivalent to treating it as a polynomial in m with coefficients which are the values of the list, with powers of m increasing from right to left.) In other words 4 5 7 9 0 8 8 7 4 8 8 4 1 becomes 4579088748841. Now we just need to encode the base (10, in this case). To do that we treat this number as a sequence of bits and prepend it with 1 1 1 1 0 1 0 1 0. This is a sequence of '1's whose length matches the number of bits needed to represent the base of our polynomial, followed by a 0 followed by the base of our polynomial.


To extract the original list we reverse this process: Find the position of the first zero, that's the size of our base, extract the base and then use that to find the coefficients of our polynomial, which is or original list.
To extract the original list we reverse this process: Find the position of the first zero, that's the size of our base, extract the base and then use that to find the coefficients of our polynomial, which is or original list.


Whether this is an efficient representation or not depends, of course, on the nature of the list being represented.
Whether this is an efficient representation or not depends, of course, on the nature of the list being represented.



=== Tacit versions ===
=== Tacit versions ===
Line 366: Line 469:
Base 11 encoding:
Base 11 encoding:


<lang j> rank =. 11&#.@:}.@:>@:(,&:>/)@:(<@:(10&,)@:(10&#.^:_1)"0)@:x:
<syntaxhighlight lang="j"> rank =. 11&#.@:}.@:>@:(,&:>/)@:(<@:(10&,)@:(10&#.^:_1)"0)@:x:
unrank=. 10&#.;._1@:(10&,)@:(11&#.^:_1)</lang>
unrank=. 10&#.;._1@:(10&,)@:(11&#.^:_1)</syntaxhighlight>


Example use:
Example use:


<lang J> rank 1 2 3 10 100 987654321 135792468107264516704251 7x
<syntaxhighlight lang="j"> rank 1 2 3 10 100 987654321 135792468107264516704251 7x
187573177082615698496949025806128189691804770100426
187573177082615698496949025806128189691804770100426
unrank 187573177082615698496949025806128189691804770100426x
unrank 187573177082615698496949025806128189691804770100426x
1 2 3 10 100 987654321 135792468107264516704251 7</lang>
1 2 3 10 100 987654321 135792468107264516704251 7</syntaxhighlight>


Prime factorization (Gödelian) encoding:
Prime factorization (Gödelian) encoding:


<lang j> rank=. */@:(^~ p:@:i.@:#)@:>:@:x:
<syntaxhighlight lang="j"> rank=. */@:(^~ p:@:i.@:#)@:>:@:x:
unrank=. <:@:(#;.1@:~:@:q:)</lang>
unrank=. <:@:(#;.1@:~:@:q:)</syntaxhighlight>


Example use:
Example use:


<lang J> rank 1 11 16 1 3 9 0 2 15 7 19 10
<syntaxhighlight lang="j"> rank 1 11 16 1 3 9 0 2 15 7 19 10
6857998574998940803374702726455974765530187550029640884386375715876970128518999225074067307280381624132537960815429687500
6857998574998940803374702726455974765530187550029640884386375715876970128518999225074067307280381624132537960815429687500
unrank 6857998574998940803374702726455974765530187550029640884386375715876970128518999225074067307280381624132537960815429687500x
unrank 6857998574998940803374702726455974765530187550029640884386375715876970128518999225074067307280381624132537960815429687500x
1 11 16 1 3 9 0 2 15 7 19 10</lang>
1 11 16 1 3 9 0 2 15 7 19 10</syntaxhighlight>


=== Bijective ===
=== Bijective ===
Line 394: Line 497:
Using the method of the Python version (shifted):
Using the method of the Python version (shifted):


<lang j> rank=. 1 -~ #.@:(1 , >@:(([ , 0 , ])&.>/)@:(<@:($&1)"0))@:x:
<syntaxhighlight lang="j"> rank=. 1 -~ #.@:(1 , >@:(([ , 0 , ])&.>/)@:(<@:($&1)"0))@:x:
unrank=. #;._2@:((0 ,~ }.)@:(#.^:_1)@:(1&+))</lang>
unrank=. #;._2@:((0 ,~ }.)@:(#.^:_1)@:(1&+))</syntaxhighlight>


Example use:
Example use:


<lang J> >@:((] ; unrank ; rank@:unrank)&.>)@:i. 11
<syntaxhighlight lang="j"> >@:((] ; unrank ; rank@:unrank)&.>)@:i. 11
┌──┬───────┬──┐
┌──┬───────┬──┐
│0 │0 │0 │
│0 │0 │0 │
Line 427: Line 530:
┌─────────┬────────┬─────────┐
┌─────────┬────────┬─────────┐
│1 2 3 5 8│14401278│1 2 3 5 8│
│1 2 3 5 8│14401278│1 2 3 5 8│
└─────────┴────────┴─────────┘</lang>
└─────────┴────────┴─────────┘</syntaxhighlight>


=={{header|Java}}==
=={{header|Java}}==
Translation of [[Index_finite_lists_of_positive_integers#Python|Python]] via [[Index_finite_lists_of_positive_integers#D|D]]
Translation of [[Index_finite_lists_of_positive_integers#Python|Python]] via [[Index_finite_lists_of_positive_integers#D|D]]
{{works with|Java|8}}
{{works with|Java|8}}
<lang java>import java.math.BigInteger;
<syntaxhighlight lang="java">import java.math.BigInteger;
import static java.util.Arrays.stream;
import static java.util.Arrays.stream;
import java.util.*;
import java.util.*;
Line 459: Line 562:
System.out.println(unrank(rank(s)));
System.out.println(unrank(rank(s)));
}
}
}</lang>
}</syntaxhighlight>
<pre>[1, 2, 3, 10, 100, 987654321]
<pre>[1, 2, 3, 10, 100, 987654321]
37699814998383067155219233
37699814998383067155219233
[1, 2, 3, 10, 100, 987654321]</pre>
[1, 2, 3, 10, 100, 987654321]</pre>

=={{header|jq}}==
'''Works with gojq'''

'''Works with jq''' within the limits of jq's support for large integer arithmetic

'''Works with jaq within the limits of jaq's support for large integers'''

The main point of interest of this entry is probably the use of the
Fibonacci encoding of positive integers (see
e.g. https://en.wikipedia.org/wiki/Fibonacci_coding and
[[:Category:jq/fibonacci.jq]]). This is the focus of the first
subsection. The second subsection focuses on the "n 0s" encoding.

The Go implementation of jq supports indefinitely large integers and
so, apart from machine limitations, the programs shown here should
work using gojq without further qualification.

The C implementation of jq, as of version 1.6, supports arbitrarily
large literal integers, and the `tonumber` filter retains precision
allowing seamless translation between strings and numbers.

The following is slightly more verbose than it need be but for the
sake of compatibility with jaq. Also note that trivial changes would
be required if using jaq as jaq does not (as of this writing in 2024)
support the `include` or `module` directives.

===Map based on Fibonacci encoding===

Since each Fibonacci-encoded integer ends with "11" and
contains no other instances of "11" before the end,
the original sequence of integers can trivially be recovered after
simple concatenation of the encodings. However, the Fibonacci
encoding of an integer can begin with 0s, so here we simply prefix
the binary string with a "1".

For example: 1 2 3 => 11 011 0011 => 1110110011

In the following, we will simply interpret
this as an integer in base 2 to avoid unnecessary complications
arising from implementation-specific limits.
<syntaxhighlight lang="jq">
include "fibonacci" {search: "./"}; # see https://rosettacode.org/wiki/Category:Jq/fibonacci.jq

# Input: an array of integers
# Output: an integer-valued binary string, being the reverse of the concatenated Fibonacci-encoded values
def rank:
map(fibencode | map(tostring) | join(""))
| "1" + join("");

# Input a bitstring or 0-1 integer interpreted as a bitstring
# Output: an array of integers
def unrank:
tostring
| .[1:]
| split("11")
| .[:-1]
| map(. + "11" | fibdecode) ;

# Output: a PRN in range(0;$n) where $n is .
def prn:
if . == 1 then 0
else . as $n
| (($n-1)|tostring|length) as $w
| [limit($w; inputs) | tostring] | join("") | sub("^0+";"") | tonumber
| if . < $n then . else $n | prn end
end;

### The task
# Encode and decode a random number of distinct positive numbers chosen at random.
# Produce a JSON object showing the set of numbers, their encoding, and
# the result of comparing the original set with the reconstructed set.
def task:
(11 | prn) + 1
| . as $numbers
| [range(0;$numbers) | 100000 | prn + 1]
| . as $numbers
| rank
| . as $encoded
# now decode:
| unrank
| {$numbers, encoded: ($encoded|tonumber), check: ($numbers == .)}
;

task
</syntaxhighlight>
'''Invocation''':
<pre>
< /dev/random tr -cd '0-9' | fold -w 1 | jq -nrf index-finite-lists-of-positive-integers.jq
</pre>
{{output}}
<pre>
{
"numbers": [
92408,
42641,
35563,
17028,
49093
],
"encoded": 101000101000001010101000111000001010001000100101100010101010000000010011001001001001000101011000101010100000010000011,
"check": true
}
</pre>


===Bijective map based on "n 0s" encoding===
<syntaxhighlight lang="jq">
### Infrastructure

# Input: a string in base $b (2 to 35 inclusive)
# Output: the decimal value
def frombase($b):
def decimalValue:
if 48 <= . and . <= 57 then . - 48
elif 65 <= . and . <= 90 then . - 55 # (10+.-65)
elif 97 <= . and . <= 122 then . - 87 # (10+.-97)
else "decimalValue" | error
end;
reduce (explode|reverse[]|decimalValue) as $x ({p:1};
.value += (.p * $x)
| .p *= $b)
| .value ;

def binary_digits:
if . == 0 then 0
else [recurse( if . == 0 then empty else ./2 | floor end ) % 2 | tostring]
| reverse
| .[1:] # remove the leading 0
| join("")
end ;


### rank and unrank
# Each integer n in the list is mapped to '1' plus n '0's.
# The empty list is mapped to '0'
def rank:
if length == 0 then 0
else reduce .[] as $i ("";
. += "1" + ("0" * $i))
| frombase(2)
end ;

def unrank:
if . == 0 then []
else binary_digits
| split("1")
| .[1:]
| map(length)
end ;

### Illustration
range(1;11)
| . as $i
| unrank
| . as $unrank
| [$i, $unrank, rank]
</syntaxhighlight>
{{output}}
<pre>
[1,[0],1]
[2,[1],2]
[3,[0,0],3]
[4,[2],4]
[5,[1,0],5]
[6,[0,1],6]
[7,[0,0,0],7]
[8,[3],8]
[9,[2,0],9]
[10,[1,1],10]
</pre>


=={{header|Julia}}==
=={{header|Julia}}==
{{trans|Python}}
{{trans|Python}}


<lang julia>using LinearAlgebra
<syntaxhighlight lang="julia">using LinearAlgebra
LinearAlgebra.rank(x::Vector{<:Integer}) = parse(BigInt, "1a" * join(x, 'a'), base=11)
LinearAlgebra.rank(x::Vector{<:Integer}) = parse(BigInt, "1a" * join(x, 'a'), base=11)
function unrank(n::Integer)
function unrank(n::Integer)
Line 482: Line 756:
n = rank(v)
n = rank(v)
v = unrank(n)
v = unrank(n)
println("# v = $v\n -> n = $n\n -> v = $v")</lang>
println("# v = $v\n -> n = $n\n -> v = $v")</syntaxhighlight>


{{out}}
{{out}}
Line 490: Line 764:


=={{header|Kotlin}}==
=={{header|Kotlin}}==
<lang scala>// version 1.1.2
<syntaxhighlight lang="scala">// version 1.1.2


import java.math.BigInteger
import java.math.BigInteger
Line 543: Line 817:
println("${"%2d".format(i)} -> ${li.toString().padEnd(9)} -> ${rank2(li)}")
println("${"%2d".format(i)} -> ${li.toString().padEnd(9)} -> ${rank2(li)}")
}
}
}</lang>
}</syntaxhighlight>


{{out}}
{{out}}
Line 571: Line 845:


=={{header|Mathematica}} / {{header|Wolfram Language}}==
=={{header|Mathematica}} / {{header|Wolfram Language}}==
<lang Mathematica>ClearAll[Rank,Unrank]
<syntaxhighlight lang="mathematica">ClearAll[Rank,Unrank]
Rank[x_List]:=FromDigits[Catenate[Riffle[IntegerDigits/@x,{{15}},{1,-1,2}]],16]
Rank[x_List]:=FromDigits[Catenate[Riffle[IntegerDigits/@x,{{15}},{1,-1,2}]],16]
Unrank[n_Integer]:=FromDigits/@SequenceSplit[IntegerDigits[n,16],{15}]
Unrank[n_Integer]:=FromDigits/@SequenceSplit[IntegerDigits[n,16],{15}]
Rank[{0,1,2,3,10,100,987654321,0}]
Rank[{0,1,2,3,10,100,987654321,0}]
Unrank[%]
Unrank[%]
First@*Unrank@*Rank@*List /@ Range[0, 20]</lang>
First@*Unrank@*Rank@*List /@ Range[0, 20]</syntaxhighlight>
{{out}}
{{out}}
<pre>4886947482322057719812858634706703
<pre>4886947482322057719812858634706703
Line 585: Line 859:
{{trans|Go}}
{{trans|Go}}
{{libheader|bignum}}
{{libheader|bignum}}
<lang Nim>import strformat, strutils
<syntaxhighlight lang="nim">import strformat, strutils
import bignum
import bignum


Line 615: Line 889:
let u = b.unrank()
let u = b.unrank()
let r = u.rank()
let r = u.rank()
echo &"\n{b}\n{u}\n{r}"</lang>
echo &"\n{b}\n{u}\n{r}"</syntaxhighlight>


{{out}}
{{out}}
Line 638: Line 912:
{{trans|Raku}}
{{trans|Raku}}
{{libheader|ntheory}}
{{libheader|ntheory}}
<lang perl>use bigint;
<syntaxhighlight lang="perl">use bigint;
use ntheory qw(fromdigits todigitstring);
use ntheory qw(fromdigits todigitstring);
use feature 'say';
use feature 'say';
Line 647: Line 921:
say join ' ', @n = qw<12 11 0 7 9 15 15 5 7 13 5 5>;
say join ' ', @n = qw<12 11 0 7 9 15 15 5 7 13 5 5>;
say $n = rank(@n);
say $n = rank(@n);
say join ' ', unrank $n;</lang>
say join ' ', unrank $n;</syntaxhighlight>
{{out}}
{{out}}
<pre>12 11 0 7 9 15 15 5 7 13 5 5
<pre>12 11 0 7 9 15 15 5 7 13 5 5
Line 658: Line 932:
{{libheader|Phix/mpfr}}
{{libheader|Phix/mpfr}}
Note this is ''not'' supported under pwa/p2js because mpz_set_str() currently only handles bases 2, 8, 10, and 16.
Note this is ''not'' supported under pwa/p2js because mpz_set_str() currently only handles bases 2, 8, 10, and 16.
<!--<lang Phix>-->
<!--<syntaxhighlight lang="phix">-->
<span style="color: #008080;">without</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">without</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">include</span> <span style="color: #004080;">mpfr</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span>
<span style="color: #008080;">include</span> <span style="color: #004080;">mpfr</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span>
Line 683: Line 957:
<span style="color: #004080;">sequence</span> <span style="color: #000000;">u</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">unrank</span><span style="color: #0000FF;">(</span><span style="color: #000000;">n</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">u</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">unrank</span><span style="color: #0000FF;">(</span><span style="color: #000000;">n</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%V\n"</span><span style="color: #0000FF;">,{{</span><span style="color: #000000;">l</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">mpz_get_str</span><span style="color: #0000FF;">(</span><span style="color: #000000;">n</span><span style="color: #0000FF;">),</span><span style="color: #000000;">u</span><span style="color: #0000FF;">}})</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%V\n"</span><span style="color: #0000FF;">,{{</span><span style="color: #000000;">l</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">mpz_get_str</span><span style="color: #0000FF;">(</span><span style="color: #000000;">n</span><span style="color: #0000FF;">),</span><span style="color: #000000;">u</span><span style="color: #0000FF;">}})</span>
<!--</lang>-->
<!--</syntaxhighlight>-->
{{out}}
{{out}}
<pre>
<pre>
Line 691: Line 965:
===bijective===
===bijective===
{{trans|Python}}
{{trans|Python}}
<!--<lang Phix>(phixonline)-->
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">unrank</span><span style="color: #0000FF;">(</span><span style="color: #004080;">atom</span> <span style="color: #000000;">n</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">unrank</span><span style="color: #0000FF;">(</span><span style="color: #004080;">atom</span> <span style="color: #000000;">n</span><span style="color: #0000FF;">)</span>
Line 718: Line 992:
<span style="color: #004080;">sequence</span> <span style="color: #000000;">x</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">2</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">3</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">5</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">8</span><span style="color: #0000FF;">}</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">x</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">2</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">3</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">5</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">8</span><span style="color: #0000FF;">}</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%v =&gt; %d =&gt; %v\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">rank</span><span style="color: #0000FF;">(</span><span style="color: #000000;">x</span><span style="color: #0000FF;">),</span><span style="color: #000000;">unrank</span><span style="color: #0000FF;">(</span><span style="color: #000000;">rank</span><span style="color: #0000FF;">(</span><span style="color: #000000;">x</span><span style="color: #0000FF;">))})</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%v =&gt; %d =&gt; %v\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">rank</span><span style="color: #0000FF;">(</span><span style="color: #000000;">x</span><span style="color: #0000FF;">),</span><span style="color: #000000;">unrank</span><span style="color: #0000FF;">(</span><span style="color: #000000;">rank</span><span style="color: #0000FF;">(</span><span style="color: #000000;">x</span><span style="color: #0000FF;">))})</span>
<!--</lang>-->
<!--</syntaxhighlight>-->
{{out}}
{{out}}
<pre>
<pre>
Line 736: Line 1,010:


=={{header|Python}}==
=={{header|Python}}==
<lang python>def rank(x): return int('a'.join(map(str, [1] + x)), 11)
<syntaxhighlight lang="python">def rank(x): return int('a'.join(map(str, [1] + x)), 11)


def unrank(n):
def unrank(n):
Line 748: Line 1,022:
print n
print n
l = unrank(n)
l = unrank(n)
print l</lang>
print l</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 758: Line 1,032:
=== Bijection ===
=== Bijection ===
Each number in the list is stored as a length of 1s, separated by 0s, and the resulting string is prefixed by '1', then taken as a binary number. Empty list is mapped to 0 as a special case. Don't use it on large numbers.
Each number in the list is stored as a length of 1s, separated by 0s, and the resulting string is prefixed by '1', then taken as a binary number. Empty list is mapped to 0 as a special case. Don't use it on large numbers.
<lang python>def unrank(n):
<syntaxhighlight lang="python">def unrank(n):
return map(len, bin(n)[3:].split("0")) if n else []
return map(len, bin(n)[3:].split("0")) if n else []


Line 770: Line 1,044:
x = [1, 2, 3, 5, 8];
x = [1, 2, 3, 5, 8];
print x, rank(x), unrank(rank(x))
print x, rank(x), unrank(rank(x))
</syntaxhighlight>
</lang>
{{out}}
{{out}}
<pre>
<pre>
Line 787: Line 1,061:
[1, 2, 3, 5, 8] 14401279 [1, 2, 3, 5, 8]
[1, 2, 3, 5, 8] 14401279 [1, 2, 3, 5, 8]
</pre>
</pre>

=={{header|Quackery}}==

<syntaxhighlight lang="quackery"> [ $ "" swap
witheach
[ number$
char A join join ]
11 base put
$->n drop
base release ] is rank ( [ --> n )

[ 11 base put
number$
base release
[] $ "" rot
witheach
[ dup char A = iff
[ drop
$->n drop join
$ "" ]
else join ]
drop ] is unrank ( n --> [ )
</syntaxhighlight>

{{out}}
Testing in the Quackery shell.
<pre>/O> []
... 5 random 5 + times
... [ 10 random 1+ join ]
... say " Random list: "
... dup echo cr
... rank
... say " That list, ranked: "
... dup echo cr
... unrank
... say "That number, unranked: "
... echo cr
...
Random list: [ 9 9 10 6 1 7 5 2 ]
That list, ranked: 459086440222376570
That number, unranked: [ 9 9 10 6 1 7 5 2 ]

Stack empty.</pre>


=={{header|Racket}}==
=={{header|Racket}}==
Line 792: Line 1,109:
{{trans|Tcl}} (which gives credit to [[#D]])
{{trans|Tcl}} (which gives credit to [[#D]])


<lang racket>#lang racket/base
<syntaxhighlight lang="racket">#lang racket/base
(require (only-in racket/string string-join string-split))
(require (only-in racket/string string-join string-split))


Line 813: Line 1,130:
(displayln loi)
(displayln loi)
(displayln rnk)
(displayln rnk)
(displayln urk))</lang>
(displayln urk))</syntaxhighlight>


{{out}}
{{out}}
Line 824: Line 1,141:
(formerly Perl 6)
(formerly Perl 6)
Here is a cheap solution using a base-11 encoding and string operations:
Here is a cheap solution using a base-11 encoding and string operations:
<lang perl6>sub rank(*@n) { :11(@n.join('A')) }
<syntaxhighlight lang="raku" line>sub rank(*@n) { :11(@n.join('A')) }
sub unrank(Int $n) { $n.base(11).split('A') }
sub unrank(Int $n) { $n.base(11).split('A') }


say my @n = (1..20).roll(12);
say my @n = (1..20).roll(12);
say my $n = rank(@n);
say my $n = rank(@n);
say unrank $n;</lang>
say unrank $n;</syntaxhighlight>
{{out}}
{{out}}
<pre>1 11 16 1 3 9 0 2 15 7 19 10
<pre>1 11 16 1 3 9 0 2 15 7 19 10
Line 837: Line 1,154:
Here is a bijective solution that does not use string operations.
Here is a bijective solution that does not use string operations.


<lang perl6>multi infix:<rad> () { 0 }
<syntaxhighlight lang="raku" line>multi infix:<rad> () { 0 }
multi infix:<rad> ($a) { $a }
multi infix:<rad> ($a) { $a }
multi infix:<rad> ($a, $b) { $a * $*RADIX + $b }
multi infix:<rad> ($a, $b) { $a * $*RADIX + $b }
Line 877: Line 1,194:
my @unrank = unrank $_;
my @unrank = unrank $_;
say "$_ -> [$@unrank] -> {rank @unrank}";
say "$_ -> [$@unrank] -> {rank @unrank}";
}</lang>
}</syntaxhighlight>


{{out}}
{{out}}
Line 896: Line 1,213:


No checks are made that the numbers are non-negative integers or malformed integers.
No checks are made that the numbers are non-negative integers or malformed integers.
<lang rexx>/*REXX program assigns an integer for a finite list of arbitrary non-negative integers. */
<syntaxhighlight lang="rexx">/*REXX program assigns an integer for a finite list of arbitrary non-negative integers. */
parse arg $ /*obtain optional argument (int list).*/
parse arg $ /*obtain optional argument (int list).*/
if $='' | $="," then $=3 14 159 265358979323846 /*Not specified? Then use the default.*/
if $='' | $="," then $=3 14 159 265358979323846 /*Not specified? Then use the default.*/
Line 909: Line 1,226:
/*──────────────────────────────────────────────────────────────────────────────────────*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
rank: return x2d( translate( space( arg(1) ), 'c', ",") )
rank: return x2d( translate( space( arg(1) ), 'c', ",") )
unrank: return space( translate( d2x( arg(1) ), ',', "C") )</lang>
unrank: return space( translate( d2x( arg(1) ), ',', "C") )</syntaxhighlight>
{{out|output|text=&nbsp; when using the default input:}}
{{out|output|text=&nbsp; when using the default input:}}
<pre>
<pre>
Line 919: Line 1,236:
=={{header|Ruby}}==
=={{header|Ruby}}==
{{trans|Python}}
{{trans|Python}}
<lang ruby>def rank(arr)
<syntaxhighlight lang="ruby">def rank(arr)
arr.join('a').to_i(11)
arr.join('a').to_i(11)
end
end


def unrank(n)
def unrank(n)
n.to_s(11).split('a').collect{|x| x.to_i}
n.to_s(11).split('a').map(&:to_i)
end
end


Line 932: Line 1,249:
p n
p n
l = unrank(n)
l = unrank(n)
p l</lang>
p l</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 941: Line 1,258:
=== Bijection ===
=== Bijection ===
{{trans|Python}}
{{trans|Python}}
<lang ruby>def unrank(n)
<syntaxhighlight lang="ruby">def unrank(n)
return [0] if n==1
return [0] if n==1
n.to_s(2)[1..-1].split('0',-1).map(&:size)
n.to_s(2)[1..-1].split('0',-1).map(&:size)
Line 957: Line 1,274:
puts
puts
x = [1, 2, 3, 5, 8]
x = [1, 2, 3, 5, 8]
puts "#{x} => #{rank(x)} => #{unrank(rank(x))}"</lang>
puts "#{x} => #{rank(x)} => #{unrank(rank(x))}"</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 977: Line 1,294:
=={{header|Scala}}==
=={{header|Scala}}==
{{Out}}Best seen in running your browser either by [https://scalafiddle.io/sf/7NvnU4t/0 ScalaFiddle (ES aka JavaScript, non JVM)] or [https://scastie.scala-lang.org/l0uAGyyCTDSAV9Q45vRGaA Scastie (remote JVM)].
{{Out}}Best seen in running your browser either by [https://scalafiddle.io/sf/7NvnU4t/0 ScalaFiddle (ES aka JavaScript, non JVM)] or [https://scastie.scala-lang.org/l0uAGyyCTDSAV9Q45vRGaA Scastie (remote JVM)].
<lang Scala>object IndexFiniteList extends App {
<syntaxhighlight lang="scala">object IndexFiniteList extends App {
val (defBase, s) = (10, Seq(1, 2, 3, 10, 100, 987654321))
val (defBase, s) = (10, Seq(1, 2, 3, 10, 100, 987654321))


Line 992: Line 1,309:
println(unrank(ranked).mkString("[", ", ", "]"))
println(unrank(ranked).mkString("[", ", ", "]"))


}</lang>
}</syntaxhighlight>


=={{header|Sidef}}==
=={{header|Sidef}}==
{{trans|Ruby}}
{{trans|Ruby}}
<lang ruby>func rank(Array arr) {
<syntaxhighlight lang="ruby">func rank(Array arr) {
Number(arr.join('a'), 11)
Number(arr.join('a'), 11)
}
}
Line 1,009: Line 1,326:
say n
say n
var l = unrank(n)
var l = unrank(n)
say l</lang>
say l</syntaxhighlight>
{{out}}
{{out}}
<pre>[1, 2, 3, 10, 100, 987654321]
<pre>[1, 2, 3, 10, 100, 987654321]
Line 1,016: Line 1,333:


'''Bijection:'''
'''Bijection:'''
<lang ruby>func unrank(Number n) {
<syntaxhighlight lang="ruby">func unrank(Number n) {
n == 1 ? [0]
n == 1 ? [0]
: n.base(2).substr(1).split('0', -1).map{.len}
: n.base(2).substr(1).split('0', -1).map{.len}
Line 1,032: Line 1,349:
say ''
say ''
var x = [1, 2, 3, 5, 8]
var x = [1, 2, 3, 5, 8]
say "#{x} => #{rank(x)} => #{unrank(rank(x))}"</lang>
say "#{x} => #{rank(x)} => #{unrank(rank(x))}"</syntaxhighlight>
{{out}}
{{out}}
<pre> 0 : [] : 0
<pre> 0 : [] : 0
Line 1,051: Line 1,368:
{{works with|Tcl|8.6}}
{{works with|Tcl|8.6}}
Inspired by the [[#D|D solution]].
Inspired by the [[#D|D solution]].
<lang tcl>package require Tcl 8.6
<syntaxhighlight lang="tcl">package require Tcl 8.6


proc rank {integers} {
proc rank {integers} {
Line 1,059: Line 1,376:
proc unrank {codedValue} {
proc unrank {codedValue} {
lmap i [split $codedValue 8] {scan $i %llo}
lmap i [split $codedValue 8] {scan $i %llo}
}</lang>
}</syntaxhighlight>
Demonstrating:
Demonstrating:
<lang tcl>set s {1 2 3 10 100 987654321 135792468107264516704251 7}
<syntaxhighlight lang="tcl">set s {1 2 3 10 100 987654321 135792468107264516704251 7}
puts "prior: $s"
puts "prior: $s"
set c [rank $s]
set c [rank $s]
puts "encoded: $c"
puts "encoded: $c"
set t [unrank $c]
set t [unrank $c]
puts "after: $t"</lang>
puts "after: $t"</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 1,077: Line 1,394:
{{trans|Kotlin}}
{{trans|Kotlin}}
{{libheader|Wren-big}}
{{libheader|Wren-big}}
<lang ecmascript>import "/big" for BigInt
<syntaxhighlight lang="wren">import "./big" for BigInt


// Separates each integer in the list with an 'a' then encodes in base 11.
// Separates each integer in the list with an 'a' then encodes in base 11.
Line 1,117: Line 1,434:
System.print("Rank = %(r)")
System.print("Rank = %(r)")
li = unrank2.call(r)
li = unrank2.call(r)
System.print("After unranking : %(li)")</lang>
System.print("After unranking : %(li)")</syntaxhighlight>


{{out}}
{{out}}
Line 1,134: Line 1,451:
=={{header|zkl}}==
=={{header|zkl}}==
Using GMP, base 11 and sometimes strings to represent big ints.
Using GMP, base 11 and sometimes strings to represent big ints.
<lang zkl>var BN=Import("zklBigNum");
<syntaxhighlight lang="zkl">var BN=Import("zklBigNum");
fcn rank(ns) { BN(ns.concat("A"),11) }
fcn rank(ns) { BN(ns.concat("A"),11) }
fcn unrank(bn) { bn.toString(11).split("a").apply("toInt") }
fcn unrank(bn) { bn.toString(11).split("a").apply("toInt") }
fcn unrankS(bn){ bn.toString(11).split("a") }</lang>
fcn unrankS(bn){ bn.toString(11).split("a") }</syntaxhighlight>
<lang zkl>fcn rankz(ns,S=False){
<syntaxhighlight lang="zkl">fcn rankz(ns,S=False){
ns.println();
ns.println();
rank(ns).println();
rank(ns).println();
Line 1,145: Line 1,462:
}
}
rankz(T(1,2,3,10,100,987654321));
rankz(T(1,2,3,10,100,987654321));
rankz(T(1,2,3,10,100,987654321,"135792468107264516704251",7),True);</lang>
rankz(T(1,2,3,10,100,987654321,"135792468107264516704251",7),True);</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>

Latest revision as of 08:39, 1 February 2024

Task
Index finite lists of positive integers
You are encouraged to solve this task according to the task description, using any language you may know.

It is known that the set of finite lists of positive integers is   countable.

This means that there exists a subset of natural integers which can be mapped to the set of finite lists of positive integers.


Task

Implement such a mapping:

  •   write a function     rank     which assigns an integer to any finite, arbitrarily long list of arbitrary large positive integers.
  •   write a function   unrank   which is the   rank   inverse function.


Demonstrate your solution by:

  •   picking a random-length list of random positive integers
  •   turn it into an integer,   and
  •   get the list back.


There are many ways to do this.   Feel free to choose any one you like.


Extra credit

Make the   rank   function as a   bijection   and show   unrank(n)   for   n   varying from   0   to   10.

11l

Translation of: Python
F rank(x)
   R BigInt(([1] [+] x).map(String).join(‘A’), radix' 11)

F unrank(n)
   V s = String(n, radix' 11)
   R s.split(‘A’).map(Int)[1..]

V l = [1, 2, 3, 10, 100, 987654321]
print(l)
V n = rank(l)
print(n)
l = unrank(n)
print(l)
Output:
[1, 2, 3, 10, 100, 987654321]
1723765384735274025865314
[1, 2, 3, 10, 100, 987654321]

Arturo

rank: function [arr][
    if empty? arr -> return 0
    from.binary "1" ++ join.with:"0" map arr 'a -> repeat "1" a
]

unrank: function [rnk][
    if rnk=1 -> return [0]
    bn: as.binary rnk 
    map split.by:"0" slice bn 1 dec size bn => size
]

l: [1, 2, 3, 5, 8]

print ["The initial list:" l]

r: rank l
print ["Ranked:" r]

u: unrank r
print ["Unranked:" u]
Output:
The initial list: [1 2 3 5 8] 
Ranked: 14401279 
Unranked: [1 2 3 5 8]

D

This solution isn't efficient.

Translation of: Python
import std.stdio, std.algorithm, std.array, std.conv, std.bigint;

BigInt rank(T)(in T[] x) pure /*nothrow*/ @safe {
    return BigInt("0x" ~ x.map!text.join('F'));
}

BigInt[] unrank(BigInt n) pure /*nothrow @safe*/ {
    string s;
    while (n) {
        s = "0123456789ABCDEF"[n % 16] ~ s;
        n /= 16;
    }
    return s.split('F').map!BigInt.array;
}

void main() {
    immutable s = [1, 2, 3, 10, 100, 987654321];
    s.writeln;
    s.rank.writeln;
    s.rank.unrank.writeln;
}
Output:
[1, 2, 3, 10, 100, 987654321]
37699814998383067155219233
[1, 2, 3, 10, 100, 987654321]

FreeBASIC

Restricted to shortish lists with smallish integers, because the rank integers get really big really fast, and bloating the code with arbitrary precision arithmetic isn't illustrative.

type duple
    A as ulongint
    B as ulongint
end type

function two_to_one( A as ulongint, B as ulongint ) as ulongint   'converts two numbers into one
    dim as uinteger ret = A*A + B*B + 2*A*B - 3*A - B             'according to the table
    return 1 + ret/2                                              '    1  2  3  4  5
end function                                                      '    -------------
                                                                  ' 1| 1  3  6 10 15
function one_to_two( R as ulongint ) as duple                     ' 2| 2  5  9 14 20
    dim as uinteger t = int((-1+sqr(8*R-7))/2)                    ' 3| 4  8 13 19 26
    dim as duple ret                                              ' 4| 7 12 18 25 33 
    ret.A = (t*t+3*t+4)/2-R
    t = int((-1+sqr(8*R-7))/2)                                    'and the inverse of this
    ret.B = R-t*(t+1)/2 
    return ret
end function

function rank( N() as ulongint) as ulongint
    dim as uinteger ret, num = ubound(N)+1                        
    if num = 0 then return 0                                      'define a value of 0 for the empty list
    if num = 1 then return two_to_one( N(0), 1 )                  
    ret = two_to_one( N(0), N(1) )                                
    for i as uinteger = 2 to num-1                                'progressively encode the list by 
        ret = two_to_one( ret, N(i) )                             'applying 2to1 on the result of the
    next i                                                        'previous calculation with the next list element
    return two_to_one(ret, num)                                   'store the length of the list as
end function                                                      'the final component

sub unrank( R as ulongint, N() as ulongint )
    dim as duple temp
    if R = 0 then                                                 'zero yields the empty list
        redim N(-1)
        return
    end if
    dim as ulongint num, Q(0 to 1)
    temp = one_to_two( R )
    num = temp.B                                                  'first get the length of the encoded list
    redim N(0 to num-1) as ulongint
    if num = 1 then                                               '(singleton handled as a special case)
        N(0)=temp.A
        return
    end if
    for i as integer = num-1 to 2 step -1                         'get back the list elements one by one
        temp = one_to_two( temp.A )                               'in the reverse order they were added
        N(i) = temp.B
    next i
    temp = one_to_two( temp.A )                                   'finally get the initial two list elements
    N(0) = temp.A
    N(1) = temp.B
end sub

sub show_list( L() as ulongint )
    dim as integer num = ubound(L)
    if num=-1 then
        print "[]"
        return
    end if
    print "[";
    for i as integer = 0 to num-1
        print str(L(i))+", ";
    next i
    print str(L(num))+"]"
end sub
'A few tests
dim as duple temp
redim as uinteger ex0(-1) 'empty list
dim as ulongint R = rank(ex0())
R = rank(ex0())
print R,
redim as ulongint X(0 to 1)
unrank R, X()
show_list(X())

dim as uinteger ex1(0 to 0) = {13}   'list with 1 element
R = rank(ex1())
print R,
unrank R, X()
show_list(X())

dim as uinteger ex2(0 to 1) = {19, 361}   'list with 2 elements
R = rank(ex2())
print R,
redim as ulongint X(0 to 1)
unrank R, X()
show_list(X())

dim as uinteger ex6(0 to 5) = {1,2,1,2,3,1}   'list with 6 elements
R = rank(ex6())
print R,
unrank R, X()
show_list(X())
Output:
0             []
79            [13]
2591460030    [19, 361]
9576882       [1, 2, 1, 2, 3, 1]

Go

Bijective

A list element n is encoded as a 1 followed by n 0's. Element encodings are concatenated to form a single integer rank. An advantage of this encoding is that no special case is required to handle the empty list.

package main

import (
    "fmt"
    "math/big"
)

func rank(l []uint) (r big.Int) {
    for _, n := range l {
        r.Lsh(&r, n+1)
        r.SetBit(&r, int(n), 1)
    }
    return
}

func unrank(n big.Int) (l []uint) {
    m := new(big.Int).Set(&n)
    for a := m.BitLen(); a > 0; {
        m.SetBit(m, a-1, 0)
        b := m.BitLen()
        l = append(l, uint(a-b-1))
        a = b
    }
    return
}

func main() {
    var b big.Int
    for i := 0; i <= 10; i++ {
        b.SetInt64(int64(i))
        u := unrank(b)
        r := rank(u)
        fmt.Println(i, u, &r)
    }
    b.SetString("12345678901234567890", 10)
    u := unrank(b)
    r := rank(u)
    fmt.Printf("\n%v\n%d\n%d\n", &b, u, &r)
}
Output:
0 [] 0
1 [0] 1
2 [1] 2
3 [0 0] 3
4 [2] 4
5 [1 0] 5
6 [0 1] 6
7 [0 0 0] 7
8 [3] 8
9 [2 0] 9
10 [1 1] 10

12345678901234567890
[1 1 1 0 1 1 1 2 1 1 2 0 3 0 2 0 0 1 1 0 3 0 0 0 0 4 1 1 0 1 2 1]
12345678901234567890

Alternative

A bit of a hack to make a base 11 number then interpret it as base 16, just because that's easiest. Not bijective. Practical though for small lists of large numbers.

package main

import (
    "fmt"
    "math/big"
    "math/rand"
    "strings"
    "time"
)

// Prepend base 10 representation with an "a" and you get a base 11 number.
// Unfortunately base 11 is a little awkward with big.Int, so just treat it
// as base 16.
func rank(l []big.Int) (r big.Int, err error) {
    if len(l) == 0 {
        return
    }
    s := make([]string, len(l))
    for i, n := range l {
        ns := n.String()
        if ns[0] == '-' {
            return r, fmt.Errorf("negative integers not mapped")
        }
        s[i] = "a" + ns
    }
    r.SetString(strings.Join(s, ""), 16)
    return
}

// Split the base 16 representation at "a", recover the base 10 numbers.
func unrank(r big.Int) ([]big.Int, error) {
    s16 := fmt.Sprintf("%x", &r)
    switch {
    case s16 == "0":
        return nil, nil // empty list
    case s16[0] != 'a':
        return nil, fmt.Errorf("unrank not bijective")
    }
    s := strings.Split(s16[1:], "a")
    l := make([]big.Int, len(s))
    for i, s1 := range s {
        if _, ok := l[i].SetString(s1, 10); !ok {
            return nil, fmt.Errorf("unrank not bijective")
        }
    }
    return l, nil
}

func main() {
    // show empty list
    var l []big.Int
    r, _ := rank(l)
    u, _ := unrank(r)
    fmt.Println("Empty list:", l, &r, u)

    // show random list
    l = random()
    r, _ = rank(l)
    u, _ = unrank(r)
    fmt.Println("\nList:")
    for _, n := range l {
        fmt.Println("  ", &n)
    }
    fmt.Println("Rank:")
    fmt.Println("  ", &r)
    fmt.Println("Unranked:")
    for _, n := range u {
        fmt.Println("  ", &n)
    }

    // show error with list containing negative
    var n big.Int
    n.SetInt64(-5)
    _, err := rank([]big.Int{n})
    fmt.Println("\nList element:", &n, err)

    // show technique is not bijective
    n.SetInt64(1)
    _, err = unrank(n)
    fmt.Println("Rank:", &n, err)
}

// returns 0 to 5 numbers in the range 1 to 2^100
func random() []big.Int {
    r := rand.New(rand.NewSource(time.Now().Unix()))
    l := make([]big.Int, r.Intn(6))
    one := big.NewInt(1)
    max := new(big.Int).Lsh(one, 100)
    for i := range l {
        l[i].Add(one, l[i].Rand(r, max))
    }
    return l
}
Output:
Empty list: [] 0 []

List:
   170245492534662309353778826165
   82227712638678862510272817700
Rank:
   17827272030291729487097780664374477811820701746650470453292650775464474368
Unranked:
   170245492534662309353778826165
   82227712638678862510272817700

List element: -5 negative integers not handled
Rank: 1 unrank not bijective

Haskell

import Data.List

toBase :: Int -> Integer -> [Int]
toBase b = unfoldr f
  where
    f 0 = Nothing
    f n = let (q, r) = n `divMod` fromIntegral b in Just (fromIntegral r, q)

fromBase :: Int -> [Int] -> Integer
fromBase n lst = foldr (\x r -> fromIntegral n*r + fromIntegral x) 0 lst

------------------------------------------------------------
               
listToInt :: Int -> [Int] -> Integer
listToInt b lst = fromBase (b+1) $ concat seq
  where
    seq = [ let (q, r) = divMod n b
            in replicate q 0 ++ [r+1]
          | n <- lst ]

intToList :: Int -> Integer -> [Int]
intToList b lst = go 0 $ toBase (b+1) lst
  where
    go 0 [] = []
    go i (0:xs) = go (i+1) xs
    go i (x:xs) = (i*b + x - 1) : go 0 xs

Using different bases we may enumerate lists.

*Main> intToList 2 <$> [1..20]
[[0],[1],[2],[0,0],[1,0],[3],[0,1],[1,1],[4],[0,2],[1,2],[2,0],[0,0,0],[1,0,0],[3,0],[0,1,0],[1,1,0],[5],[0,3],[1,3]]

*Main> intToList 3 <$> [1..20]
[[0],[1],[2],[3],[0,0],[1,0],[2,0],[4],[0,1],[1,1],[2,1],[5],[0,2],[1,2],[2,2],[6],[0,3],[1,3],[2,3],[3,0]]

*Main> intToList 10 <$> [1..20]
[[0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[0,0],[1,0],[2,0],[3,0],[4,0],[5,0],[6,0],[7,0],[8,0]]

*Main> listToInt 2 <$> permutations [1,2,3,4]
[2360,2370,2382,2406,2292,2288,5274,5190,5136,5922,5916,5160,4680,4646,4628,4950,4944,4638,2736,2702,2450,2844,2760,2454]

This mapping is a bijection:

*Main> (listToInt 3 . intToList 3 <$> [0..100]) == [0..100]
True



J

Explicit version

Implementation:

scrunch=:3 :0
  n=.1x+>./y
  #.(1#~##:n),0,n,&#:n#.y
)

hcnurcs=:3 :0
  b=.#:y
  m=.b i.0
  n=.#.m{.(m+1)}.b
  n #.inv#.(1+2*m)}.b
)

Example use:

   scrunch 4 5 7 9 0 8 8 7 4 8 8 4 1
4314664669630761
   hcnurcs 4314664669630761
4 5 7 9 0 8 8 7 4 8 8 4 1

Explanation. We treat the sequence as an n digit number in base m where n is the length of the list and m is 1+the largest value in the list. (This is equivalent to treating it as a polynomial in m with coefficients which are the values of the list, with powers of m increasing from right to left.) In other words 4 5 7 9 0 8 8 7 4 8 8 4 1 becomes 4579088748841. Now we just need to encode the base (10, in this case). To do that we treat this number as a sequence of bits and prepend it with 1 1 1 1 0 1 0 1 0. This is a sequence of '1's whose length matches the number of bits needed to represent the base of our polynomial, followed by a 0 followed by the base of our polynomial.

To extract the original list we reverse this process: Find the position of the first zero, that's the size of our base, extract the base and then use that to find the coefficients of our polynomial, which is or original list.

Whether this is an efficient representation or not depends, of course, on the nature of the list being represented.

Tacit versions

Base 11 encoding:

   rank  =. 11&#.@:}.@:>@:(,&:>/)@:(<@:(10&,)@:(10&#.^:_1)"0)@:x:
   unrank=. 10&#.;._1@:(10&,)@:(11&#.^:_1)

Example use:

   rank 1 2 3 10 100 987654321 135792468107264516704251 7x
187573177082615698496949025806128189691804770100426
   
   unrank 187573177082615698496949025806128189691804770100426x
1 2 3 10 100 987654321 135792468107264516704251 7

Prime factorization (Gödelian) encoding:

   rank=. */@:(^~ p:@:i.@:#)@:>:@:x:
   unrank=. <:@:(#;.1@:~:@:q:)

Example use:

   rank 1 11 16 1 3 9 0 2 15 7 19 10
6857998574998940803374702726455974765530187550029640884386375715876970128518999225074067307280381624132537960815429687500
   
   unrank 6857998574998940803374702726455974765530187550029640884386375715876970128518999225074067307280381624132537960815429687500x
1 11 16 1 3 9 0 2 15 7 19 10

Bijective

Using the method of the Python version (shifted):

   rank=. 1 -~ #.@:(1 , >@:(([ , 0 , ])&.>/)@:(<@:($&1)"0))@:x:
   unrank=. #;._2@:((0 ,~ }.)@:(#.^:_1)@:(1&+))

Example use:

   >@:((] ; unrank ; rank@:unrank)&.>)@:i. 11
┌──┬───────┬──┐
0 0      0 
├──┼───────┼──┤
1 0 0    1 
├──┼───────┼──┤
2 1      2 
├──┼───────┼──┤
3 0 0 0  3 
├──┼───────┼──┤
4 0 1    4 
├──┼───────┼──┤
5 1 0    5 
├──┼───────┼──┤
6 2      6 
├──┼───────┼──┤
7 0 0 0 07 
├──┼───────┼──┤
8 0 0 1  8 
├──┼───────┼──┤
9 0 1 0  9 
├──┼───────┼──┤
100 2    10
└──┴───────┴──┘
   
   (] ; rank ; unrank@:rank) 1 2 3 5 8
┌─────────┬────────┬─────────┐
1 2 3 5 8144012781 2 3 5 8
└─────────┴────────┴─────────┘

Java

Translation of Python via D

Works with: Java version 8
import java.math.BigInteger;
import static java.util.Arrays.stream;
import java.util.*;
import static java.util.stream.Collectors.*;

public class Test3 {
    static BigInteger rank(int[] x) {
        String s = stream(x).mapToObj(String::valueOf).collect(joining("F"));
        return new BigInteger(s, 16);
    }

    static List<BigInteger> unrank(BigInteger n) {
        BigInteger sixteen = BigInteger.valueOf(16);
        String s = "";
        while (!n.equals(BigInteger.ZERO)) {
            s = "0123456789ABCDEF".charAt(n.mod(sixteen).intValue()) + s;
            n = n.divide(sixteen);
        }
        return stream(s.split("F")).map(x -> new BigInteger(x)).collect(toList());
    }

    public static void main(String[] args) {
        int[] s = {1, 2, 3, 10, 100, 987654321};
        System.out.println(Arrays.toString(s));
        System.out.println(rank(s));
        System.out.println(unrank(rank(s)));
    }
}
[1, 2, 3, 10, 100, 987654321]
37699814998383067155219233
[1, 2, 3, 10, 100, 987654321]

jq

Works with gojq

Works with jq within the limits of jq's support for large integer arithmetic

Works with jaq within the limits of jaq's support for large integers

The main point of interest of this entry is probably the use of the Fibonacci encoding of positive integers (see e.g. https://en.wikipedia.org/wiki/Fibonacci_coding and Category:jq/fibonacci.jq). This is the focus of the first subsection. The second subsection focuses on the "n 0s" encoding.

The Go implementation of jq supports indefinitely large integers and so, apart from machine limitations, the programs shown here should work using gojq without further qualification.

The C implementation of jq, as of version 1.6, supports arbitrarily large literal integers, and the `tonumber` filter retains precision allowing seamless translation between strings and numbers.

The following is slightly more verbose than it need be but for the sake of compatibility with jaq. Also note that trivial changes would be required if using jaq as jaq does not (as of this writing in 2024) support the `include` or `module` directives.

Map based on Fibonacci encoding

Since each Fibonacci-encoded integer ends with "11" and contains no other instances of "11" before the end, the original sequence of integers can trivially be recovered after simple concatenation of the encodings. However, the Fibonacci encoding of an integer can begin with 0s, so here we simply prefix the binary string with a "1".

For example: 1 2 3 => 11 011 0011 => 1110110011

In the following, we will simply interpret this as an integer in base 2 to avoid unnecessary complications arising from implementation-specific limits.

include "fibonacci" {search: "./"}; # see https://rosettacode.org/wiki/Category:Jq/fibonacci.jq

# Input: an array of integers
# Output: an integer-valued binary string, being the reverse of the concatenated Fibonacci-encoded values
def rank:
  map(fibencode | map(tostring) | join(""))
  | "1" + join("");

# Input a bitstring or 0-1 integer interpreted as a bitstring
# Output: an array of integers
def unrank:
  tostring
  | .[1:]
  | split("11")
  | .[:-1]
  | map(. + "11" | fibdecode) ;

# Output: a PRN in range(0;$n) where $n is .
def prn:
  if . == 1 then 0
  else . as $n
  | (($n-1)|tostring|length) as $w
  | [limit($w; inputs) | tostring] | join("") | sub("^0+";"") | tonumber
  | if . < $n then . else $n | prn end
  end;

### The task
# Encode and decode a random number of distinct positive numbers chosen at random.
# Produce a JSON object showing the set of numbers, their encoding, and 
# the result of comparing the original set with the reconstructed set.
def task:
  (11 | prn) + 1
  | . as $numbers
  | [range(0;$numbers) | 100000 | prn + 1]
  | . as $numbers
  | rank
  | . as $encoded
  # now decode:
  | unrank
  | {$numbers, encoded: ($encoded|tonumber), check: ($numbers == .)}
;

task

Invocation:

< /dev/random tr -cd '0-9' | fold -w 1 | jq -nrf index-finite-lists-of-positive-integers.jq
Output:
{
  "numbers": [
    92408,
    42641,
    35563,
    17028,
    49093
  ],
  "encoded": 101000101000001010101000111000001010001000100101100010101010000000010011001001001001000101011000101010100000010000011,
  "check": true
}


Bijective map based on "n 0s" encoding

### Infrastructure

# Input: a string in base $b (2 to 35 inclusive)
# Output: the decimal value
def frombase($b):
  def decimalValue:
    if   48 <= . and . <= 57 then . - 48
    elif 65 <= . and . <= 90 then . - 55  # (10+.-65)
    elif 97 <= . and . <= 122 then . - 87 # (10+.-97)
    else "decimalValue" | error
    end;
  reduce (explode|reverse[]|decimalValue) as $x ({p:1};
    .value += (.p * $x)
    | .p *= $b)
  | .value ;

def binary_digits:
  if . == 0 then 0
  else [recurse( if . == 0 then empty else ./2 | floor end ) % 2 | tostring]
    | reverse
    | .[1:] # remove the leading 0
    | join("")
  end ;


### rank and unrank
# Each integer n in the list is mapped to '1' plus n '0's.
# The empty list is mapped to '0'
def rank:
  if length == 0 then 0
  else reduce .[] as $i ("";
    . += "1" + ("0" * $i))
  | frombase(2)
  end ;

def unrank:
  if . == 0 then []
  else binary_digits
  | split("1")
  | .[1:]
  | map(length)
  end ;

### Illustration
range(1;11)
| . as $i
| unrank
| . as $unrank
| [$i, $unrank, rank]
Output:
[1,[0],1]
[2,[1],2]
[3,[0,0],3]
[4,[2],4]
[5,[1,0],5]
[6,[0,1],6]
[7,[0,0,0],7]
[8,[3],8]
[9,[2,0],9]
[10,[1,1],10]

Julia

Translation of: Python
using LinearAlgebra
LinearAlgebra.rank(x::Vector{<:Integer}) = parse(BigInt, "1a" * join(x, 'a'), base=11)
function unrank(n::Integer)
    s = ""
    while !iszero(n)
        ind = n % 11 + 1
        n ÷= 11
        s = "0123456789a"[ind:ind] * s
    end
    return parse.(Int, split(s, 'a'))[2:end]
end

v = [0, 1, 2, 3, 10, 100, 987654321]
n = rank(v)
v = unrank(n)
println("# v = $v\n -> n = $n\n -> v = $v")
Output:
# v = [0, 1, 2, 3, 10, 100, 987654321]
 -> n = 207672721333439869642567444
 -> v = [0, 1, 2, 3, 10, 100, 987654321]

Kotlin

// version 1.1.2

import java.math.BigInteger

/* Separates each integer in the list with an 'a' then encodes in base 11. Empty list mapped to '-1' */
fun rank(li: List<Int>) = when (li.size) {
    0    -> -BigInteger.ONE
    else ->  BigInteger(li.joinToString("a"), 11)
}

fun unrank(r: BigInteger) = when (r) {
    -BigInteger.ONE -> emptyList<Int>()
    else            -> r.toString(11).split('a').map { if (it != "") it.toInt() else 0 }
}


/* Each integer n in the list mapped to '1' plus n '0's. Empty list mapped to '0' */
fun rank2(li:List<Int>): BigInteger {
    if (li.isEmpty()) return BigInteger.ZERO
    val sb = StringBuilder()
    for (i in li) sb.append("1" + "0".repeat(i))
    return BigInteger(sb.toString(), 2)
}

fun unrank2(r: BigInteger) = when (r) {
    BigInteger.ZERO -> emptyList<Int>()
    else            -> r.toString(2).drop(1).split('1').map { it.length }
}

fun main(args: Array<String>) {
    var li: List<Int>
    var r: BigInteger
    li = listOf(0, 1, 2, 3, 10, 100, 987654321)
    println("Before ranking   : $li")
    r = rank(li)
    println("Rank = $r")
    li = unrank(r)
    println("After unranking  : $li")

    println("\nAlternative approach (not suitable for large numbers)...\n")
    li = li.dropLast(1)
    println("Before ranking   : $li")
    r = rank2(li)
    println("Rank = $r")
    li = unrank2(r)
    println("After unranking  : $li")

    println()
    for (i in 0..10) {
        val bi = BigInteger.valueOf(i.toLong())
        li = unrank2(bi)
        println("${"%2d".format(i)} -> ${li.toString().padEnd(9)} -> ${rank2(li)}")
    }
}
Output:
Before ranking   : [0, 1, 2, 3, 10, 100, 987654321]
Rank = 828335141480036653618783
After unranking  : [0, 1, 2, 3, 10, 100, 987654321]

Alternative approach (not suitable for large numbers)...

Before ranking   : [0, 1, 2, 3, 10, 100]
Rank = 4364126777249122850009283661412696064
After unranking  : [0, 1, 2, 3, 10, 100]

 0 -> []        -> 0
 1 -> [0]       -> 1
 2 -> [1]       -> 2
 3 -> [0, 0]    -> 3
 4 -> [2]       -> 4
 5 -> [1, 0]    -> 5
 6 -> [0, 1]    -> 6
 7 -> [0, 0, 0] -> 7
 8 -> [3]       -> 8
 9 -> [2, 0]    -> 9
10 -> [1, 1]    -> 10

Mathematica / Wolfram Language

ClearAll[Rank,Unrank]
Rank[x_List]:=FromDigits[Catenate[Riffle[IntegerDigits/@x,{{15}},{1,-1,2}]],16]
Unrank[n_Integer]:=FromDigits/@SequenceSplit[IntegerDigits[n,16],{15}]
Rank[{0,1,2,3,10,100,987654321,0}]
Unrank[%]
First@*Unrank@*Rank@*List /@ Range[0, 20]
Output:
4886947482322057719812858634706703
{0, 1, 2, 3, 10, 100, 987654321, 0}
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}

Nim

Translation of: Go
Library: bignum
import strformat, strutils
import bignum

func rank(list: openArray[uint]): Int =
  result = newInt(0)
  for n in list:
    result = result shl (n + 1)
    result = result.setBit(n)

func unrank(n: Int): seq[uint] =
  var m = n.clone
  var a = if m.isZero: 0u else: m.bitLen.uint
  while a > 0:
    m = m.clearBit(a - 1)
    let b = if m.isZero: 0u else: m.bitLen.uint
    result.add(a - b - 1)
    a = b

when isMainModule:

  var b: Int
  for i in 0..10:
    b = newInt(i)
    let u = b.unrank()
    let r = u.rank()
    echo &"{i:2d} {u:>9s} {r:>2s}"

  b = newInt("12345678901234567890")
  let u = b.unrank()
  let r = u.rank()
  echo &"\n{b}\n{u}\n{r}"
Output:
 0        @[]  0
 1       @[0]  1
 2       @[1]  2
 3    @[0, 0]  3
 4       @[2]  4
 5    @[1, 0]  5
 6    @[0, 1]  6
 7 @[0, 0, 0]  7
 8       @[3]  8
 9    @[2, 0]  9
10    @[1, 1] 10

12345678901234567890
@[1, 1, 1, 0, 1, 1, 1, 2, 1, 1, 2, 0, 3, 0, 2, 0, 0, 1, 1, 0, 3, 0, 0, 0, 0, 4, 1, 1, 0, 1, 2, 1]
12345678901234567890

Perl

The base-11 approach requires bigint pragma for all but trivial lists. Using ntheory module for base conversions.

Translation of: Raku
Library: ntheory
use bigint;
use ntheory qw(fromdigits todigitstring);
use feature 'say';

sub rank   { join   '', fromdigits(join('a',@_), 11) }
sub unrank { split 'a', todigitstring(@_[0],     11) }

say join ' ', @n = qw<12 11 0 7 9 15 15 5 7 13 5 5>;
say $n = rank(@n);
say join ' ', unrank $n;
Output:
12 11 0 7 9 15 15 5 7 13 5 5
16588666500024842935939135419
12 11 0 7 9 15 15 5 7 13 5 5

Phix

base 11

Translation of: Sidef
Library: Phix/mpfr

Note this is not supported under pwa/p2js because mpz_set_str() currently only handles bases 2, 8, 10, and 16.

without javascript_semantics 
include mpfr.e
 
procedure rank(mpz r, sequence s)
    s = deep_copy(s)
    for i=1 to length(s) do
        s[i] = sprintf("%d",s[i])
    end for
    mpz_set_str(r,join(s,'a'),11)
end procedure
 
function unrank(mpz i)
    sequence res = split(mpz_get_str(i,11),'a')
    for j=1 to length(res) do
        {{res[j]}} = scanf(res[j],"%d")
    end for
    return res
end function
 
sequence l = {1, 2, 3, 10, 100, 987654321}
mpz n = mpz_init()
rank(n,l)
sequence u = unrank(n)
printf(1,"%V\n",{{l,mpz_get_str(n),u}})
Output:
{{1,2,3,10,100,987654321},"14307647611639042485573",{1,2,3,10,100,987654321}}

bijective

Translation of: Python
with javascript_semantics 
function unrank(atom n)
    sequence res = sprintf("%0b",n)
    if res="1" then return {0} end if  
    res = split(res[2..$],'0',false)
    for i=1 to length(res) do res[i] = length(res[i]) end for
    return res
end function
 
function rank(sequence x)
    if x={} then return 0 end if
    sequence y = repeat(0,length(x))
    for i=1 to length(x) do
        y[i] = repeat('1',x[i])
    end for
    atom {{res}} = scanf("0b1"&join(y,'0'),"%d")
    return res
end function

for i=0 to 10 do
    sequence a = unrank(i)
    printf(1,"%3d : %-18v: %d\n",{i, a, rank(a)})
end for
 
sequence x = {1, 2, 3, 5, 8}
printf(1,"%v => %d => %v\n",{x,rank(x),unrank(rank(x))})
Output:
  0 : {}                : 0
  1 : {0}               : 1
  2 : {0,0}             : 2
  3 : {1}               : 3
  4 : {0,0,0}           : 4
  5 : {0,1}             : 5
  6 : {1,0}             : 6
  7 : {2}               : 7
  8 : {0,0,0,0}         : 8
  9 : {0,0,1}           : 9
 10 : {0,1,0}           : 10
{1,2,3,5,8} => 14401279 => {1,2,3,5,8}

Python

def rank(x): return int('a'.join(map(str, [1] + x)), 11)

def unrank(n):
	s = ''
	while n: s,n = "0123456789a"[n%11] + s, n//11
	return map(int, s.split('a'))[1:]

l = [1, 2, 3, 10, 100, 987654321]
print l
n = rank(l)
print n
l = unrank(n)
print l
Output:
[0, 1, 2, 3, 10, 100, 987654321]
207672721333439869642567444
[0, 1, 2, 3, 10, 100, 987654321]

Bijection

Each number in the list is stored as a length of 1s, separated by 0s, and the resulting string is prefixed by '1', then taken as a binary number. Empty list is mapped to 0 as a special case. Don't use it on large numbers.

def unrank(n):
        return map(len, bin(n)[3:].split("0")) if n else []

def rank(x):
        return int('1' + '0'.join('1'*a for a in x), 2) if x else 0

for x in range(11):
        print x, unrank(x), rank(unrank(x))

print
x = [1, 2, 3, 5, 8];
print x, rank(x), unrank(rank(x))
Output:
0 [] 0
1 [0] 1
2 [0, 0] 2
3 [1] 3
4 [0, 0, 0] 4
5 [0, 1] 5
6 [1, 0] 6
7 [2] 7
8 [0, 0, 0, 0] 8
9 [0, 0, 1] 9
10 [0, 1, 0] 10

[1, 2, 3, 5, 8] 14401279 [1, 2, 3, 5, 8]

Quackery

  [ $ "" swap
    witheach
      [ number$
        char A join join ]
    11 base put
    $->n drop
    base release ]         is rank   ( [ --> n )

  [ 11 base put
    number$
    base release
    [] $ "" rot
    witheach
      [ dup char A = iff
          [ drop
            $->n drop join
            $ "" ]
        else join ]
    drop ]                 is unrank ( n --> [ )
Output:

Testing in the Quackery shell.

/O> []
... 5 random 5 + times
...  [ 10 random 1+ join ]
... say "          Random list: "
... dup echo cr
... rank
... say "    That list, ranked: "
... dup echo cr
... unrank
... say "That number, unranked: "
... echo cr
... 
          Random list: [ 9 9 10 6 1 7 5 2 ]
    That list, ranked: 459086440222376570
That number, unranked: [ 9 9 10 6 1 7 5 2 ]

Stack empty.

Racket

Translation of: Tcl

(which gives credit to #D)

#lang racket/base
(require (only-in racket/string string-join string-split))

(define (integer->octal-string i)
  (number->string i 8))

(define (octal-string->integer s)
  (string->number s 8))

(define (rank is)
  (string->number (string-join (map integer->octal-string is) "8")))

(define (unrank ranking)
  (map octal-string->integer (string-split (number->string ranking 10) "8")))

(module+ test
  (define loi '(1 2 3 10 100 987654321 135792468107264516704251 7))
  (define rnk (rank loi))
  (define urk (unrank rnk))
  (displayln loi)
  (displayln rnk)
  (displayln urk))
Output:
(1 2 3 10 100 987654321 135792468107264516704251 7)
1828381281448726746426183460251416730347660304377387
(1 2 3 10 100 987654321 135792468107264516704251 7)

Raku

(formerly Perl 6) Here is a cheap solution using a base-11 encoding and string operations:

sub rank(*@n)      { :11(@n.join('A')) }
sub unrank(Int $n) { $n.base(11).split('A') }

say my @n = (1..20).roll(12);
say my $n = rank(@n);
say unrank $n;
Output:
1 11 16 1 3 9 0 2 15 7 19 10
25155454474293912130094652799
1 11 16 1 3 9 0 2 15 7 19 10

Here is a bijective solution that does not use string operations.

multi infix:<rad> ()       { 0 }
multi infix:<rad> ($a)     { $a }
multi infix:<rad> ($a, $b) { $a * $*RADIX + $b }
 
multi expand(Int $n is copy, 1) { $n }
multi expand(Int $n is copy, Int $*RADIX) {
    my \RAD = $*RADIX;
 
    my @reversed-digits = gather while $n > 0 {
	take $n % RAD;
	$n div= RAD;
    }
 
    eager for ^RAD {
	[rad] reverse @reversed-digits[$_, * + RAD ... *]
    }
}
 
multi compress(@n where @n == 1) { @n[0] }
multi compress(@n is copy) {
    my \RAD = my $*RADIX = @n.elems;
 
    [rad] reverse gather while @n.any > 0 {
	    (state $i = 0) %= RAD;
	    take @n[$i] % RAD;
	    @n[$i] div= RAD;
	    $i++;
	}
}
 
sub rank(@n) { compress (compress(@n), @n - 1)}
sub unrank(Int $n) { my ($a, $b) = expand $n, 2; expand $a, $b + 1 }

my @list = (^10).roll((2..20).pick);
my $rank = rank @list;
say "[$@list] -> $rank -> [{unrank $rank}]";

for ^10 {
    my @unrank = unrank $_;
    say "$_ -> [$@unrank] -> {rank @unrank}";
}
Output:
[7 1 4 7 7 0 2 7 7 0 7 7] -> 20570633300796394530947471 -> [7 1 4 7 7 0 2 7 7 0 7 7]
0 -> [0] -> 0
1 -> [1] -> 1
2 -> [0 0] -> 2
3 -> [1 0] -> 3
4 -> [2] -> 4
5 -> [3] -> 5
6 -> [0 1] -> 6
7 -> [1 1] -> 7
8 -> [0 0 0] -> 8
9 -> [1 0 0] -> 9

REXX

This REXX version can handle zeros as well as any sized (decimal) positive integers.

No checks are made that the numbers are non-negative integers or malformed integers.

/*REXX program assigns an integer for a finite list of arbitrary non-negative integers. */
parse arg $                                      /*obtain optional argument  (int list).*/
if $='' | $=","  then $=3 14 159 265358979323846 /*Not specified?  Then use the default.*/
                                                 /* [↑]  kinda use decimal digits of pi.*/
$= translate( space($),   ',',   " ")            /*use a  commatized  list of integers. */
numeric digits max(9, 2 * length($) )            /*ensure enough dec. digits to handle $*/

                 say 'original list='   $        /*display the original list of integers*/
N=   rank($);    say '  map integer='   N        /*generate and display the map integer.*/
O= unrank(N);    say '       unrank='   O        /*generate original integer and display*/
exit                                             /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
rank:    return    x2d( translate( space( arg(1) ),  'c',  ",") )
unrank:  return  space( translate(   d2x( arg(1) ),  ',',  "C") )
output   when using the default input:
original list= 3,14,159,265358979323846
  map integer= 18594192178172074189223245894
       unrank= 3,14,159,265358979323846

Ruby

Translation of: Python
def rank(arr)
  arr.join('a').to_i(11)
end

def unrank(n)
  n.to_s(11).split('a').map(&:to_i)
end

l = [1, 2, 3, 10, 100, 987654321]
p l
n = rank(l)
p n
l = unrank(n)
p l
Output:
[1, 2, 3, 10, 100, 987654321]
14307647611639042485573
[1, 2, 3, 10, 100, 987654321]

Bijection

Translation of: Python
def unrank(n)
  return [0] if n==1
  n.to_s(2)[1..-1].split('0',-1).map(&:size)
end

def rank(x)
  return 0 if x.empty?
  ('1' + x.map{ |a| '1'*a }.join('0')).to_i(2)
end

for x in 0..10
  puts "%3d : %-18s: %d" % [x, a=unrank(x), rank(a)]
end

puts
x = [1, 2, 3, 5, 8]
puts "#{x} => #{rank(x)} => #{unrank(rank(x))}"
Output:
  0 : []                : 0
  1 : [0]               : 1
  2 : [0, 0]            : 2
  3 : [1]               : 3
  4 : [0, 0, 0]         : 4
  5 : [0, 1]            : 5
  6 : [1, 0]            : 6
  7 : [2]               : 7
  8 : [0, 0, 0, 0]      : 8
  9 : [0, 0, 1]         : 9
 10 : [0, 1, 0]         : 10

[1, 2, 3, 5, 8] => 14401279 => [1, 2, 3, 5, 8]

Scala

Output:

Best seen in running your browser either by ScalaFiddle (ES aka JavaScript, non JVM) or Scastie (remote JVM).

object IndexFiniteList extends App {
  val (defBase, s) = (10, Seq(1, 2, 3, 10, 100, 987654321))

  def rank(x: Seq[Int], base: Int = defBase) =
    BigInt(x.map(Integer.toString(_, base)).mkString(base.toHexString), base + 1)

  def unrank(n: BigInt, base: Int = defBase): List[BigInt] =
    n.toString(base + 1).split((base).toHexString).map(BigInt(_)).toList

  val ranked = rank(s)

  println(s.mkString("[", ", ", "]"))
  println(ranked)
  println(unrank(ranked).mkString("[", ", ", "]"))

}

Sidef

Translation of: Ruby
func rank(Array arr) {
    Number(arr.join('a'), 11)
}

func unrank(Number n) {
    n.base(11).split('a').map { Num(_) }
}

var l = [1, 2, 3, 10, 100, 987654321]
say l
var n = rank(l)
say n
var l = unrank(n)
say l
Output:
[1, 2, 3, 10, 100, 987654321]
14307647611639042485573
[1, 2, 3, 10, 100, 987654321]

Bijection:

func unrank(Number n) {
    n == 1 ? [0]
           : n.base(2).substr(1).split('0', -1).map{.len}
}

func rank(Array x) {
    x.is_empty ? 0
               : Number('1' + x.map { '1' * _ }.join('0'), 2)
}

for x in (0..10) {
    printf("%3d : %-18s: %d\n", x, unrank(x), rank(unrank(x)))
}

say ''
var x = [1, 2, 3, 5, 8]
say "#{x} => #{rank(x)} => #{unrank(rank(x))}"
Output:
  0 : []                : 0
  1 : [0]               : 1
  2 : [0, 0]            : 2
  3 : [1]               : 3
  4 : [0, 0, 0]         : 4
  5 : [0, 1]            : 5
  6 : [1, 0]            : 6
  7 : [2]               : 7
  8 : [0, 0, 0, 0]      : 8
  9 : [0, 0, 1]         : 9
 10 : [0, 1, 0]         : 10

[1, 2, 3, 5, 8] => 14401279 => [1, 2, 3, 5, 8]

Tcl

Works with: Tcl version 8.6

Inspired by the D solution.

package require Tcl 8.6

proc rank {integers} {
    join [lmap i $integers {format %llo $i}] 8
}

proc unrank {codedValue} {
    lmap i [split $codedValue 8] {scan $i %llo}
}

Demonstrating:

set s {1 2 3 10 100 987654321 135792468107264516704251 7}
puts "prior: $s"
set c [rank $s]
puts "encoded: $c"
set t [unrank $c]
puts "after: $t"
Output:
prior: 1 2 3 10 100 987654321 135792468107264516704251 7
encoded: 1828381281448726746426183460251416730347660304377387
after: 1 2 3 10 100 987654321 135792468107264516704251 7

Wren

Translation of: Kotlin
Library: Wren-big
import "./big" for BigInt

// Separates each integer in the list with an 'a' then encodes in base 11.
// Empty list mapped to '-1'.
var rank = Fn.new { |li|
    if (li.count == 0) return BigInt.minusOne
    return BigInt.fromBaseString(li.join("a"), 11)
}

var unrank = Fn.new { |r|
    if (r == BigInt.minusOne) return []
    return r.toBaseString(11).split("a").map { |d| (d != "") ? Num.fromString(d) : 0 }.toList
}

// Each integer n in the list mapped to '1' plus n '0's.
// Empty list mapped to '0'
var rank2 = Fn.new { |li|
    if (li.isEmpty) return BigInt.zero
    var sb = ""
    for (i in li) sb = sb + "1" + ("0" * i)
    return BigInt.fromBaseString(sb, 2)
}

var unrank2 = Fn.new { |r|
    if (r == BigInt.zero) return []
    return r.toBaseString(2)[1..-1].split("1").map { |d| d.count }.toList
}

var li = [0, 1, 2, 3, 10, 100, 987654321]
System.print("Before ranking  : %(li)")
var r = rank.call(li)
System.print("Rank = %(r)")
li = unrank.call(r)
System.print("After unranking : %(li)")
System.print("\nAlternative approach (not suitable for large numbers)...\n")
li = li[0..-2]
System.print("Before ranking  : %(li)")
r = rank2.call(li)
System.print("Rank = %(r)")
li = unrank2.call(r)
System.print("After unranking : %(li)")
Output:
Before ranking  : [0, 1, 2, 3, 10, 100, 987654321]
Rank = 828335141480036653618783
After unranking : [0, 1, 2, 3, 10, 100, 987654321]

Alternative approach (not suitable for large numbers)...

Before ranking  : [0, 1, 2, 3, 10, 100]
Rank = 4364126777249122850009283661412696064
After unranking : [0, 1, 2, 3, 10, 100]

zkl

Using GMP, base 11 and sometimes strings to represent big ints.

var BN=Import("zklBigNum");
fcn rank(ns)   { BN(ns.concat("A"),11) }
fcn unrank(bn) { bn.toString(11).split("a").apply("toInt") }
fcn unrankS(bn){ bn.toString(11).split("a") }
fcn rankz(ns,S=False){
   ns.println();
   rank(ns).println();
   if(S) ns:rank(_):unrankS(_).println();
   else  ns:rank(_):unrank(_) .println();
}
rankz(T(1,2,3,10,100,987654321));
rankz(T(1,2,3,10,100,987654321,"135792468107264516704251",7),True);
Output:
L(1,2,3,10,100,987654321)
14307647611639042485573
L(1,2,3,10,100,987654321)
L(1,2,3,10,100,987654321,"135792468107264516704251",7)
187573177082615698496949025806128189691804770100426
L("1","2","3","10","100","987654321","135792468107264516704251","7")