Clone
7
BaseN
Peter Harpending edited this page 2025-03-23 18:11:34 -07:00

Base64 vs Base58 Explained

BaseN diagram

Quick Reference

  1. Original article: https://zxq9.com/archives/2688
  2. Base64 Erlang: https://github.com/aeternity/Vanillae/blob/829dd2930ff20ea0473cf2ad562e0a1c2aba0411/utils/vw/src/vb64.erl
  3. Base58 Erlang: https://github.com/aeternity/Vanillae/blob/829dd2930ff20ea0473cf2ad562e0a1c2aba0411/utils/vw/src/vb58.erl
  4. Base64 TypeScript: https://github.com/aeternity/Vanillae/blob/829dd2930ff20ea0473cf2ad562e0a1c2aba0411/bindings/typescript/src/b64.ts
  5. Base58 TypeScript: https://github.com/aeternity/Vanillae/blob/829dd2930ff20ea0473cf2ad562e0a1c2aba0411/bindings/typescript/src/b58.ts

tl;dr

Base64 and Base58 are two schema for taking binary data and encoding it to and from plain text.

  1. Conceptually:

    1. These are NOT two instances of a general "base N" concept.

    2. Base64 thinks of the binary data as a long stream of bytes.

      Base64 converts bytes to/from text 3 bytes at a time in a "fire-and-forget" manner.

    3. Base58 thinks of the binary data as a really really long integer.

      Base58 requires processing the entire bytestring all at once as a singular unit.

    4. Base58 is the general BaseN algorithm. Base64 is simpler because 64 and 256 are both powers of 2 and computers use binary.

  2. Terminologically:

    1. The terms "encode" and "decode" are meant from the perspective of the computer program. From the program's perspective, binary data is what makes sense and plain text is gobbletygook. So
      1. we decode plain text (gahbage) into binary data (what makes sense)
      2. we encode binary data (what makes sense) to plain text (gahbage)
  3. Practically:

    1. Almost every language (including Erlang) has base64 in its standard library, and you should probably just use that.
    2. You probably have to code Base58 yourself.
    3. If you are implementing Base58 in a language that does not have bignum arithmetic, you have to implement it yourself. Thankfully nobody will ever need any language other than Erlang so we can ignore this problem in the context of this wiki.
    4. Base58 is super inefficient both space-wise and time-wise, because it requires processing the entirety of the binary string as a single monolithic piece of data.
    5. Base58 exists to preempt Il10O-type problems (visual ambiguity) and doesn't involve =+/ characters (the idea being email clients are likely to break long lines at these characters, increasing the likelihood of input errors).
    6. Consequently, Base58 is only suitable for binary data that is both
      1. short in length
      2. likely to be entered manually (e.g. wallet/contract addresses)

Base64

The term "binary" is misleading, because it leads people to think that the basic unit in computing is a bit (a 1 or a 0).

It is more accurate to think of the basic unit in computing as a byte, which is a number between 0 (2#0000_0000) and 255 (2#1111_1111).

(If you're totally bewildered by binary notation, you might want to read § Long Division and come back).

Base64 processes a binary string 3 bytes at a time (which is 24 bits). It regroups those 3 groups of 8 bits into 4 groups of 6 bits.

ABCDEFGH 12345678 ABCDEFGH
ABCDEF GH1234 5678AB CDEFGH

We now have 4 numbers ranging between 0 (2#00_0000) and 63 (2#11_1111), hence the name "Base64".

Each number in the range 0..63 is assigned to a character from the alphabet (see § Base64 Alphabet)

% abbreviated
% 0..25 map to $A..$Z
int2char(N) when 0 =< N, N =< 25  -> $A + N;
% 26..51 map to $a..$z
int2char(N) when 26 =< N, N =< 51 -> $a + (N - 26);
% 52..61 map to $0..$9
int2char(N) when 52 =< N, N =< 61 -> $0 + (N - 52);
% special cases
int2char(62) -> $+;
int2char(63) -> $/.

% $A..$Z map to 0..25
char2int(C) when $A =< C, C =< $Z -> C - $A;
% $a..$z map to 26..51
char2int(C) when $a =< C, C =< $z -> C - $a;
% $0..$9 map to 52..61
char2int(C) when $0 =< C, C =< $9 -> C - $0;
% special cases
char2int($+) -> 62;
char2int($/) -> 63.

The only stupid cases arise when the number of bytes in the binary data is not a multiple of 3. In this case there are two padding rules:

% general case: at least 3 bytes (24 bits = 6+6+6+6) remaining
%
% 12345678 abcdefgh 12345678    ...
% 123456 78abcd efgh12 345678   ...
%   A      B     C      D       Rest
% convert to chars ->
%   CA    CB     CC    CD
enc(<<A:6, B:6, C:6, D:6, Rest/binary>>) ->
    CA = int2char(A),
    CB = int2char(B),
    CC = int2char(C),
    CD = int2char(D),
    [CA, CB, CC, CD | enc(Rest)];
% terminal case: 2 bytes (16 bits = 6+6+4) remaining
%
% 12345678 abcdefgh
% 123456 78abcd   efgh__
%    A     B     C bsl 2
% convert to chars ->
%   CA     CB       CC    =
enc(<<A:6, B:6, C:4>>) ->
    CA = int2char(A),
    CB = int2char(B),
    CC = int2char(C bsl 2),
    [CA, CB, CC, $=];
% terminal case: 1 byte (8 bits = 6+2) remaining
%
% 12345678 ->
% 123456   78____
%    A     B bsl 4
% convert to chars ->
%   CA      CB     =    =
enc(<<A:6, B:2>>) ->
    CA = int2char(A),
    CB = int2char(B bsl 4),
    [CA, CB, $=, $=];
% terminal case: 0 bytes remaining
enc(<<>>) ->
    [].

By the way, that's the entire encode procedure right there.

The decode procedure is similarly simple but slightly trickier:

dec(Base64_String) ->
    dec(Base64_String, <<>>).


% terminal case: two equal signs at the end = 1 byte (8 bits = 6+2) remaining
% input (characters) ->
%         W       X     =    =
% convert to numbers ->
%       abcdef gh____   =    =
%         NW      NX
% regroup ->
%          abcdefgh  ____        abcdef   gh____
%       <<LastByte:8, 0:4>> = <<  NW:6,    NX:6   >>
dec([W, X, $=, $=], Acc) ->
    NW = char2int(W),
    NX = char2int(X),
    <<LastByte:8, 0:4>> = <<NW:6, NX:6>>,
    <<Acc/binary, LastByte:8>>;
% terminal case: one equal sign at the end = 2 bytes remaining
%
% input (characters) ->
%         W       X     Y    =
% convert to numbers ->
%       abcdef gh1234  5678__  =
%         NW      NX    NY
% regroup ->
%          abcdefgh  12345678   __          abcdef    gh1234   5678__
%       <<   B1:8,     B2:8,   0:2  >> = <<  NW:6,     NX:6     NY:6   >>
dec([W, X, Y, $=], Acc) ->
    NW = char2int(W),
    NX = char2int(X),
    NY = char2int(Y),
    <<B1:8, B2:8, 0:2>> = <<NW:6, NX:6, NY:6>>,
    <<Acc/binary, B1:8, B2:8>>;
% terminal case: 0 bytes remaining
% nothing to do
dec([], Acc) ->
    Acc;
% general case: no equal signs = 3 or more bytes remaining
%
% input (characters) ->
%         W       X      Y      Z
% convert to numbers ->
%       abcdef gh1234  5678ab cdefgh
%         NW      NX    NY      NZ
% decompose ->
%          abcdefgh  12345678   abcdefgh          abcdef    gh1234   5678ab   cdefgh
%       <<   B1:8,     B2:8,      B3:2   >> = <<  NW:6,     NX:6     NY:6,    NZ:6   >>
dec([W, X, Y, Z | Rest], Acc) ->
    NW = char2int(W),
    NX = char2int(X),
    NY = char2int(Y),
    NZ = char2int(Z),
    NewAcc = <<Acc/binary, NW:6, NX:6, NY:6, NZ:6>>,
    dec(Rest, NewAcc).

This is marginally trickier because

  1. it needs an accumulator
  2. the order of the cases matters

Long Division

As mentioned above, Base58 is the "general" BaseN algorithm applied to the case N=58. This general algorithm I am calling the quotient-remainder (QR) algorithm. You might know it as the long division algorithm you learned in school.

Consider an integer, say 8763. To understand the QR algorithm you need to make a conceptual distinction between the "conceptual integer" 8763 and its representation as the sequence of symbols 10#8763.

The way our notation for numbers works is as follows:

    10#8763 = 8 * 10^3 (1000)
            + 7 * 10^2 (100)
            + 6 * 10^1 (10)
            + 3 * 10^0 (1)

So in each "slot" there can exist one of 10 symbols: 0123456789. Each slot corresponds to a power of 10. You multiply the number in each slot by the corresponding power of 10 and then take the sum to "obtain" the pure integer.

Binary notation works exactly the same way, except each slot corresponds to a power of 2, and consequently each slot contains one of the two symbols 01.

For instance, to convert 2#1010_0111 to its "conceptual integer", we do exactly the same process:

    2#1010\_0111 = 1 * 2^7 (128)
                 + 0 * 2^6 (64)
                 + 1 * 2^5 (32)
                 + 0 * 2^4 (16)
                 + 0 * 2^3 (8)
                 + 1 * 2^2 (4)
                 + 1 * 2^1 (2)
                 + 1 * 2^0 (1)
                 = 128 + 32 + 4 + 2 + 1
                 = 167

Exercise for the reader: take this binary counting concept and figure out how you could use your fingers to count up to 1023.

All we have done so far is convert things to base 10. This isn't hard because it's already familiar.

If you are a native English speaker and kind of know French, it's much easier to translate French into English than it is to translate English into French. Expressing things in an unfamiliar context is significantly more challenging than comprehending things in the unfamiliar context.

How might we take our integer 8763 and convert it into base 2?

It's pretty simple: divide by 2 over and over using the long division you learned in elementary school (you do remember how to do long division, right?)

        quotients  remainders
          |----|    |-|
    8763 = 4381 *2 + 1
    4381 = 2190 *2 + 1
    2190 = 1095 *2 + 0
    1095 =  547 *2 + 1
     547 =  273 *2 + 1
     273 =  136 *2 + 1
     136 =   68 *2 + 0
      68 =   34 *2 + 0
      34 =   17 *2 + 0
      17 =    8 *2 + 1
       8 =    4 *2 + 0
       4 =    2 *2 + 0
       2 =    1 *2 + 0
       1 =    0 *2 + 1
       0 =    DONE

The sequence of remainders in reverse order is the binary representation of 8763. Equivalently, the sequence of remainders "grows to the left".

16> 2#1000_1000_1110_11.
8763

Notice that if we delete digits off the right end, we get back the sequence of quotients:

16> 2#1000_1000_1110_11.
8763
17> 2#1000_1000_1110_1.
4381
18> 2#1000_1000_1110.
2190
19> 2#1000_1000_111.
1095
20> 2#1000_1000_11.
547

Take a minute and figure out for yourself why this makes sense.

At any rate, the way to code this is

-module(ntb).

-export([
    n_to_base/2
]).


n_to_base(N, Base) when N >= 0, Base >= 2 ->
    n_to_base(N, Base, []).

n_to_base(0, _Base, Digits) ->
    Digits;
n_to_base(N, Base, Digits) ->
    NewN      = N div Base,
    NewDigit  = N rem Base,
    NewDigits = [NewDigit | Digits],
    n_to_base(NewN, Base, NewDigits).
3> ntb:n_to_base(8763, 2).
[1,0,0,0,1,0,0,0,1,1,1,0,1,1]

Let us do the calculation manually in Base58 to get our digits:

8763 = 151*58 +  5
 151 =   2*58 + 35
   2 =   0*58 +  2
   0 = DONE

So in "Base58", the sequence of digits is [2, 35, 5]. Our program agrees:

8> ntb:n_to_base(8763, 58).
[2,35,5]

Our Base58 code is going to contain exactly this idea modulo an unpleasant volume of edge case stupidity.

Base58 algorithm

Conceptually, we are doing the following:

binary data <-> integer <-> sequence of numbers 0-57 <-> text characters

The unpleasantry comes from the fact that positional notation for numbers makes no distinction between the sequence of symbols 8763 and 00008763.

That is, if we have a bytestring with a bunch of leading 0 bytes, those bytes are not going to change the integer that bytestring corresponds to. That is, all of the following byte arrays point to the same integer:

ABCD_EFGH
0000_0000 ABCD_EFGH
0000_0000 0000_0000 ABCD_EFGH
0000_0000 0000_0000 0000_0000 ABCD_EFGH

So when we're going from integers back to binary, we have an ambiguity. Because we could in theory have a billion leading zero bytes that are just totally unaccounted for.

The hack to deal with this is that leading 0 bytes are represented as leading $1s in the text representation ($0 is excluded from the Base58 alphabet to avoid visual collision with $O).

This ends up meaning we have to do an extra preprocessing step

  1. on decode (text -> binary): pre-strip all the leading $1 characters off the text representation and account for them as leading 0 bytes

  2. on encode (binary -> text): and conversely pre-strip all the leading 0 bytes and account for them as leading $1 characters.

The result is as follows:

-module(b58).

-export([
    enc/1,
    dec/1,
    chars58/2,
    unchars58/2,
    int2char/1,
    char2int/1
]).


%% consume all the leading 0 bytes
enc(<<0:8, Rest/binary>>) ->
    [int2char(0) | enc(Rest)];
enc(Bytes) when is_binary(Bytes) ->
    %% Binary -> integer
    N = binary:decode_unsigned(Bytes),
    %% Integer -> chars
    chars58(N, []).

%% combining steps of converting to numbers 0..57 and then converting those
%% numbers to ascii characters just to avoid traversing the list twice
chars58(N, Acc) when N > 0 ->
    Q = N div 58,
    R = N rem 58,
    NewAcc = [int2char(R) | Acc],
    chars58(Q, NewAcc);
chars58(0, Acc) ->
    Acc.


%% consume all the leading 1 characters
dec([$1 | Rest]) ->
    %io:format("dec([$1 | ~p])~n", [Rest]),
    <<0:8, (dec(Rest))/binary>>;
%% this case corresponds to the bytestring being entirely 0 bytes
%% in this case, our usual approach (seen below) would compute N=0 and then
%% binary:encode_unsigned/1 encodes 0 to <<0>>, thus incorrectly adding an
%% extra zero byte. this case preempts that
dec([]) ->
    %io:format("dec([])~n", []),
    <<>>;
dec(Chars) when is_list(Chars) ->
    %io:format("dec(~p)~n", [Chars]),
    %% chars -> integer
    N = unchars58(Chars, 0),
    %% integer -> binary
    binary:encode_unsigned(N).

unchars58([Char | Rest], AccN) ->
    %io:format("unchars58([~p | ~p], ~p)~n", [Char, Rest, AccN]),
    %% Char corresponds to a number in 0..57
    Digit057 = char2int(Char),
    % You can imagine the entire original digit as
    %      AccN ++ Char ++ Rest
    % multiplying AccN by 58 corresponds to "shifting" AccN 1 space to the
    % left. Then we "++ Char" at the right end, then continue on.
    NewAccN  = AccN*58 + Digit057,
    unchars58(Rest, NewAccN);
unchars58([], AccN) ->
    %io:format("unchars58([], ~p)~n", [AccN]),
    AccN.


int2char( 0) -> $1;
int2char( 1) -> $2;
int2char( 2) -> $3;
int2char( 3) -> $4;
int2char( 4) -> $5;
int2char( 5) -> $6;
int2char( 6) -> $7;
int2char( 7) -> $8;
int2char( 8) -> $9;
int2char( 9) -> $A;
int2char(10) -> $B;
int2char(11) -> $C;
int2char(12) -> $D;
int2char(13) -> $E;
int2char(14) -> $F;
int2char(15) -> $G;
int2char(16) -> $H;
int2char(17) -> $J;
int2char(18) -> $K;
int2char(19) -> $L;
int2char(20) -> $M;
int2char(21) -> $N;
int2char(22) -> $P;
int2char(23) -> $Q;
int2char(24) -> $R;
int2char(25) -> $S;
int2char(26) -> $T;
int2char(27) -> $U;
int2char(28) -> $V;
int2char(29) -> $W;
int2char(30) -> $X;
int2char(31) -> $Y;
int2char(32) -> $Z;
int2char(33) -> $a;
int2char(34) -> $b;
int2char(35) -> $c;
int2char(36) -> $d;
int2char(37) -> $e;
int2char(38) -> $f;
int2char(39) -> $g;
int2char(40) -> $h;
int2char(41) -> $i;
int2char(42) -> $j;
int2char(43) -> $k;
int2char(44) -> $m;
int2char(45) -> $n;
int2char(46) -> $o;
int2char(47) -> $p;
int2char(48) -> $q;
int2char(49) -> $r;
int2char(50) -> $s;
int2char(51) -> $t;
int2char(52) -> $u;
int2char(53) -> $v;
int2char(54) -> $w;
int2char(55) -> $x;
int2char(56) -> $y;
int2char(57) -> $z.

char2int($1) ->  0;
char2int($2) ->  1;
char2int($3) ->  2;
char2int($4) ->  3;
char2int($5) ->  4;
char2int($6) ->  5;
char2int($7) ->  6;
char2int($8) ->  7;
char2int($9) ->  8;
char2int($A) ->  9;
char2int($B) -> 10;
char2int($C) -> 11;
char2int($D) -> 12;
char2int($E) -> 13;
char2int($F) -> 14;
char2int($G) -> 15;
char2int($H) -> 16;
char2int($J) -> 17;
char2int($K) -> 18;
char2int($L) -> 19;
char2int($M) -> 20;
char2int($N) -> 21;
char2int($P) -> 22;
char2int($Q) -> 23;
char2int($R) -> 24;
char2int($S) -> 25;
char2int($T) -> 26;
char2int($U) -> 27;
char2int($V) -> 28;
char2int($W) -> 29;
char2int($X) -> 30;
char2int($Y) -> 31;
char2int($Z) -> 32;
char2int($a) -> 33;
char2int($b) -> 34;
char2int($c) -> 35;
char2int($d) -> 36;
char2int($e) -> 37;
char2int($f) -> 38;
char2int($g) -> 39;
char2int($h) -> 40;
char2int($i) -> 41;
char2int($j) -> 42;
char2int($k) -> 43;
char2int($m) -> 44;
char2int($n) -> 45;
char2int($o) -> 46;
char2int($p) -> 47;
char2int($q) -> 48;
char2int($r) -> 49;
char2int($s) -> 50;
char2int($t) -> 51;
char2int($u) -> 52;
char2int($v) -> 53;
char2int($w) -> 54;
char2int($x) -> 55;
char2int($y) -> 56;
char2int($z) -> 57.
4> b58:chars58(8763, []).
"3c6"
6> b58:char2int($3).
2
7> b58:char2int($c).
35
8> b58:char2int($6).
5
43> binary:encode_unsigned(8763).
<<"\";">>
44> b58:enc(<<"\";">>).
"3c6"
45> b58:enc(<<0, 0, 0, "\";">>).
"1113c6"
46> b58:dec("3c6").
<<"\";">>
47> b58:dec("13c6").
<<0,34,59>>
48> b58:dec("11113c6").
<<0,0,0,0,34,59>>

Tables etc

Base64 Alphabet

int2char( 0) -> $A;
int2char( 1) -> $B;
int2char( 2) -> $C;
int2char( 3) -> $D;
int2char( 4) -> $E;
int2char( 5) -> $F;
int2char( 6) -> $G;
int2char( 7) -> $H;
int2char( 8) -> $I;
int2char( 9) -> $J;
int2char(10) -> $K;
int2char(11) -> $L;
int2char(12) -> $M;
int2char(13) -> $N;
int2char(14) -> $O;
int2char(15) -> $P;
int2char(16) -> $Q;
int2char(17) -> $R;
int2char(18) -> $S;
int2char(19) -> $T;
int2char(20) -> $U;
int2char(21) -> $V;
int2char(22) -> $W;
int2char(23) -> $X;
int2char(24) -> $Y;
int2char(25) -> $Z;
int2char(26) -> $a;
int2char(27) -> $b;
int2char(28) -> $c;
int2char(29) -> $d;
int2char(30) -> $e;
int2char(31) -> $f;
int2char(32) -> $g;
int2char(33) -> $h;
int2char(34) -> $i;
int2char(35) -> $j;
int2char(36) -> $k;
int2char(37) -> $l;
int2char(38) -> $m;
int2char(39) -> $n;
int2char(40) -> $o;
int2char(41) -> $p;
int2char(42) -> $q;
int2char(43) -> $r;
int2char(44) -> $s;
int2char(45) -> $t;
int2char(46) -> $u;
int2char(47) -> $v;
int2char(48) -> $w;
int2char(49) -> $x;
int2char(50) -> $y;
int2char(51) -> $z;
int2char(52) -> $0;
int2char(53) -> $1;
int2char(54) -> $2;
int2char(55) -> $3;
int2char(56) -> $4;
int2char(57) -> $5;
int2char(58) -> $6;
int2char(59) -> $7;
int2char(60) -> $8;
int2char(61) -> $9;
int2char(62) -> $+;
int2char(63) -> $/.

char2int($A) ->  0;
char2int($B) ->  1;
char2int($C) ->  2;
char2int($D) ->  3;
char2int($E) ->  4;
char2int($F) ->  5;
char2int($G) ->  6;
char2int($H) ->  7;
char2int($I) ->  8;
char2int($J) ->  9;
char2int($K) -> 10;
char2int($L) -> 11;
char2int($M) -> 12;
char2int($N) -> 13;
char2int($O) -> 14;
char2int($P) -> 15;
char2int($Q) -> 16;
char2int($R) -> 17;
char2int($S) -> 18;
char2int($T) -> 19;
char2int($U) -> 20;
char2int($V) -> 21;
char2int($W) -> 22;
char2int($X) -> 23;
char2int($Y) -> 24;
char2int($Z) -> 25;
char2int($a) -> 26;
char2int($b) -> 27;
char2int($c) -> 28;
char2int($d) -> 29;
char2int($e) -> 30;
char2int($f) -> 31;
char2int($g) -> 32;
char2int($h) -> 33;
char2int($i) -> 34;
char2int($j) -> 35;
char2int($k) -> 36;
char2int($l) -> 37;
char2int($m) -> 38;
char2int($n) -> 39;
char2int($o) -> 40;
char2int($p) -> 41;
char2int($q) -> 42;
char2int($r) -> 43;
char2int($s) -> 44;
char2int($t) -> 45;
char2int($u) -> 46;
char2int($v) -> 47;
char2int($w) -> 48;
char2int($x) -> 49;
char2int($y) -> 50;
char2int($z) -> 51;
char2int($0) -> 52;
char2int($1) -> 53;
char2int($2) -> 54;
char2int($3) -> 55;
char2int($4) -> 56;
char2int($5) -> 57;
char2int($6) -> 58;
char2int($7) -> 59;
char2int($8) -> 60;
char2int($9) -> 61;
char2int($+) -> 62;
char2int($/) -> 63.

Base58 Alphabet

int2char( 0) -> $1;
int2char( 1) -> $2;
int2char( 2) -> $3;
int2char( 3) -> $4;
int2char( 4) -> $5;
int2char( 5) -> $6;
int2char( 6) -> $7;
int2char( 7) -> $8;
int2char( 8) -> $9;
int2char( 9) -> $A;
int2char(10) -> $B;
int2char(11) -> $C;
int2char(12) -> $D;
int2char(13) -> $E;
int2char(14) -> $F;
int2char(15) -> $G;
int2char(16) -> $H;
int2char(17) -> $J;
int2char(18) -> $K;
int2char(19) -> $L;
int2char(20) -> $M;
int2char(21) -> $N;
int2char(22) -> $P;
int2char(23) -> $Q;
int2char(24) -> $R;
int2char(25) -> $S;
int2char(26) -> $T;
int2char(27) -> $U;
int2char(28) -> $V;
int2char(29) -> $W;
int2char(30) -> $X;
int2char(31) -> $Y;
int2char(32) -> $Z;
int2char(33) -> $a;
int2char(34) -> $b;
int2char(35) -> $c;
int2char(36) -> $d;
int2char(37) -> $e;
int2char(38) -> $f;
int2char(39) -> $g;
int2char(40) -> $h;
int2char(41) -> $i;
int2char(42) -> $j;
int2char(43) -> $k;
int2char(44) -> $m;
int2char(45) -> $n;
int2char(46) -> $o;
int2char(47) -> $p;
int2char(48) -> $q;
int2char(49) -> $r;
int2char(50) -> $s;
int2char(51) -> $t;
int2char(52) -> $u;
int2char(53) -> $v;
int2char(54) -> $w;
int2char(55) -> $x;
int2char(56) -> $y;
int2char(57) -> $z.

char2int($1) ->  0;
char2int($2) ->  1;
char2int($3) ->  2;
char2int($4) ->  3;
char2int($5) ->  4;
char2int($6) ->  5;
char2int($7) ->  6;
char2int($8) ->  7;
char2int($9) ->  8;
char2int($A) ->  9;
char2int($B) -> 10;
char2int($C) -> 11;
char2int($D) -> 12;
char2int($E) -> 13;
char2int($F) -> 14;
char2int($G) -> 15;
char2int($H) -> 16;
char2int($J) -> 17;
char2int($K) -> 18;
char2int($L) -> 19;
char2int($M) -> 20;
char2int($N) -> 21;
char2int($P) -> 22;
char2int($Q) -> 23;
char2int($R) -> 24;
char2int($S) -> 25;
char2int($T) -> 26;
char2int($U) -> 27;
char2int($V) -> 28;
char2int($W) -> 29;
char2int($X) -> 30;
char2int($Y) -> 31;
char2int($Z) -> 32;
char2int($a) -> 33;
char2int($b) -> 34;
char2int($c) -> 35;
char2int($d) -> 36;
char2int($e) -> 37;
char2int($f) -> 38;
char2int($g) -> 39;
char2int($h) -> 40;
char2int($i) -> 41;
char2int($j) -> 42;
char2int($k) -> 43;
char2int($m) -> 44;
char2int($n) -> 45;
char2int($o) -> 46;
char2int($p) -> 47;
char2int($q) -> 48;
char2int($r) -> 49;
char2int($s) -> 50;
char2int($t) -> 51;
char2int($u) -> 52;
char2int($v) -> 53;
char2int($w) -> 54;
char2int($x) -> 55;
char2int($y) -> 56;
char2int($z) -> 57.