Skip to content

Instantly share code, notes, and snippets.

@paveltyk
Last active October 31, 2017 10:20
Show Gist options
  • Save paveltyk/c73a34fff3b8b51ea8289a3a14930c5c to your computer and use it in GitHub Desktop.
Save paveltyk/c73a34fff3b8b51ea8289a3a14930c5c to your computer and use it in GitHub Desktop.
This is a port of ScatterSwap integer hashing function written in Ruby to Elixir. https://github.com/namick/scatter_swap
defmodule ScatterSwap do
@moduledoc """
Usage:
Pass a number (as an integer) and random spin to the 'hash' function and it will return an obfuscated version of it.
ScatterSwap.hash(1, 0) #=> 4517239960
ScatterSwap.hash(2, 0) #=> 7023641925
ScatterSwap.hash(42, 0) #=> 2912536240
Pass that obfuscated value in 'reverse_hash' function and it will return the original integer.
ScatterSwap.reverse_hash(4517239960, 0) #=> 1
ScatterSwap.reverse_hash(7023641925, 0) #=> 2
ScatterSwap.reverse_hash(2912536240, 0) #=> 42
"""
use Bitwise
alias ClientsbayEvents.ScatterSwap.ListRotate
def hash(digit, spin) when is_integer(digit) do
digit
|> Integer.to_string
|> String.pad_leading(10, "0")
|> String.graphemes
|> Enum.map(&(String.to_integer&1))
|> swap(spin)
|> scatter(spin)
|> Enum.join
|> String.to_integer
end
def reverse_hash(string, spin) when is_binary(string) do
case Integer.parse(string) do
{digit, _} -> reverse_hash(digit, spin)
:error -> :error
end
end
def reverse_hash(digit, spin) when is_integer(digit) do
digit
|> Integer.to_string
|> String.pad_leading(10, "0")
|> String.graphemes
|> Enum.map(&(String.to_integer&1))
|> unscatter(spin)
|> unswap(spin)
|> Enum.join
|> String.to_integer
end
defp swapper_map(idx, spin) when is_integer(idx), do: swapper_map(Enum.to_list(0..9), idx, spin)
defp swapper_map([], _, _), do: []
defp swapper_map(list, idx, spin) do
i = 10 - Enum.count(list)
randomizer = idx + Bitwise.bxor(i, spin)
{el, list} = ListRotate.rotate(list, randomizer) |> List.pop_at(-1)
[el] ++ swapper_map(list, idx, spin)
end
defp swap(list, spin) do
list
|> Enum.with_index
|> Enum.map(fn {digit, index} -> Enum.at(swapper_map(index, spin), digit) end)
end
defp scatter(list, spin) do
randomizer = Bitwise.bxor(spin, Enum.sum(list))
do_scatter(list, randomizer)
end
defp do_scatter([], _), do: []
defp do_scatter(list, randomizer) do
{el, list} = ListRotate.rotate(list, randomizer) |> List.pop_at(-1)
[el] ++ do_scatter(list, randomizer)
end
defp unscatter(list, spin) do
randomizer = Bitwise.bxor(spin, Enum.sum(list))
do_unscatter(list, [], randomizer)
end
defp do_unscatter([], unscattered_list, _), do: unscattered_list
defp do_unscatter(scattered_list, unscattered_list, randomizer) do
{el, rest} = List.pop_at(scattered_list, -1)
unrotated_list = ListRotate.rotate(unscattered_list ++ [el], randomizer * -1)
do_unscatter(rest, unrotated_list, randomizer)
end
defp unswap(list, spin) do
list
|> Enum.with_index
|> Enum.map(fn {digit, index} -> 9 - Enum.find_index(Enum.reverse(swapper_map(index, spin)), fn(el) -> el == digit end) end)
end
end
defmodule ScatterSwap.ListRotate do
def rotate(l, n \\ 1)
def rotate([], _), do: []
def rotate(l, 0), do: l
def rotate([h | t], 1), do: t ++ [h]
def rotate(l, n) when n > 0, do: rotate(rotate(l, 1), n-1)
def rotate(l, n), do: right_rotate(l, -n)
def right_rotate(l, n \\ 1)
def right_rotate(l, n) when n > 0, do: Enum.reverse(l) |> rotate(n) |> Enum.reverse
def right_rotate(l, n), do: rotate(l, -n)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment