Last active
December 18, 2015 22:49
-
-
Save cobusc/5856819 to your computer and use it in GitHub Desktop.
Linear to non-linear sequence mapper and base62 encoder.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(map). | |
-export([map/1, | |
to_base_62/1, | |
test/1]). | |
%% | |
%% @doc Deterministically map a linear integer sequence to a non-linear sequence. | |
%% | |
-spec map(N::non_neg_integer()) -> string(). | |
map(N) | |
when is_integer(N), N >= 0 -> | |
% Get LSB representation. It makes matching easier. | |
LsbBin = <<N:128/little-unsigned-integer>>, | |
% Extract the first 3 bytes, as well as the rest. | |
<<A:8,B:8,C:8,D/binary>> = LsbBin, | |
% Get a permutation of the first 3 bytes. There are only 5 variants, | |
% since we do not use the trivial case (A, B, C). | |
Val = | |
case N rem 5 of | |
0 -> <<A:8, C:8, B:8, D/binary>>; | |
1 -> <<B:8, A:8, C:8, D/binary>>; | |
2 -> <<B:8, C:8, A:8, D/binary>>; | |
3 -> <<C:8, A:8, B:8, D/binary>>; | |
4 -> <<C:8, B:8, A:8, D/binary>> | |
end, | |
% Convert permutation back to an integer representation | |
binary:decode_unsigned(Val, little). | |
%% | |
%% @doc Integer to base62 encoding | |
%% | |
-spec to_base_62(N::non_neg_integer()) -> string(). | |
to_base_62(N) | |
when is_integer(N) -> | |
to_base_62(N, []). | |
to_base_62(0=N, []) -> | |
c(N); | |
to_base_62(0, Acc) -> | |
lists:map(fun c/1, Acc); | |
to_base_62(N, Acc) -> | |
to_base_62(N div 62, [N rem 62 | Acc]). | |
c(N) when is_integer(N), 0=<N, N=<9 -> | |
$0 + N; | |
c(N) when is_integer(N), 10=<N, N=<35 -> | |
$a + (N-10) ; | |
c(N) when is_integer(N), 36=<N, N=<61 -> | |
$A + (N-36). | |
%% | |
%% @doc A simple test function. | |
%% | |
test(N) -> | |
Sample = lists:seq(1,N), | |
Values = [ map(X) || X <- Sample ], | |
F = fun(X) -> | |
io:format("~s~n", [to_base_62(X)]) | |
end, | |
ok = lists:foreach(F, Values), | |
Uniques = lists:usort(Values), | |
io:format("Number of duplicates in first ~B values = ~B~n", [N, N - erlang:length(Uniques)]). | |
%% Erlang R14B04 (erts-5.8.5) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false] | |
%% | |
%% Eshell V5.8.5 (abort with ^G) | |
%% 1> c(map). | |
%% {ok,map} | |
%% 2> map:test(10). | |
%% 48 | |
%% y64 | |
%% co | |
%% 16c8 | |
%% 5 | |
%% oM | |
%% 1Vle | |
%% x2 | |
%% 2tri | |
%% a | |
%% Number of duplicates in first 10 values = 0 | |
%% ok |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment