using System.Collections.Generic;
using System.Text.RegularExpressions;
public static void Main()
Console.WriteLine("Hello World");
var hashids = new Hashids("this is my salt");
var id = hashids.Encode(123456789,1,2,3,4,5,6);
var numbers = hashids.Decode(id);
Console.WriteLine(numbers);
for (i=0; i<numbers.Length;i++) {
Console.WriteLine(numbers[i]);
Console.WriteLine("Hello World");
public const string DEFAULT_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
public const string DEFAULT_SEPS = "cfhistuCFHISTU";
private const int MIN_ALPHABET_LENGTH = 16;
private const double SEP_DIV = 3.5;
private const double GUARD_DIV = 12.0;
private int minHashLength;
private Regex guardsRegex;
private static Lazy<Regex> hexValidator = new Lazy<Regex>(() => new Regex("^[0-9a-fA-F]+$"));
private static Lazy<Regex> hexSplitter = new Lazy<Regex>(() => new Regex(@"[\w\W]{1,12}"));
private static Lazy<Regex> hexValidator = new Lazy<Regex>(() => new Regex("^[0-9a-fA-F]+$", RegexOptions.Compiled));
private static Lazy<Regex> hexSplitter = new Lazy<Regex>(() => new Regex(@"[\w\W]{1,12}", RegexOptions.Compiled));
public Hashids() : this(string.Empty, 0, DEFAULT_ALPHABET, DEFAULT_SEPS)
public Hashids(string salt = "", int minHashLength = 0, string alphabet = DEFAULT_ALPHABET, string seps = DEFAULT_SEPS)
if (string.IsNullOrWhiteSpace(alphabet))
throw new ArgumentNullException("alphabet");
this.alphabet = new string(alphabet.ToCharArray().Distinct().ToArray());
this.minHashLength = minHashLength;
if (this.alphabet.Length < 16)
throw new ArgumentException("alphabet must contain atleast 4 unique characters.", "alphabet");
public virtual string Encode(params int[] numbers)
if (numbers.Any(n => n < 0)) return string.Empty;
return this.GenerateHashFrom(numbers.Select(n => (long)n).ToArray());
public virtual string Encode(IEnumerable<int> numbers)
return this.Encode(numbers.ToArray());
public virtual int[] Decode(string hash)
return this.GetNumbersFrom(hash).Select(n => (int)n).ToArray();
public virtual string EncodeHex(string hex)
if (!hexValidator.Value.IsMatch(hex))
var numbers = new List<long>();
var matches = hexSplitter.Value.Matches(hex);
foreach (Match match in matches)
var number = Convert.ToInt64(string.Concat("1", match.Value), 16);
return this.EncodeLong(numbers.ToArray());
public virtual string DecodeHex(string hash)
var ret = new StringBuilder();
var numbers = this.DecodeLong(hash);
foreach (var number in numbers)
ret.Append(string.Format("{0:X}", number).Substring(1));
public long[] DecodeLong(string hash)
return this.GetNumbersFrom(hash);
public string EncodeLong(params long[] numbers)
if (numbers.Any(n => n < 0)) return string.Empty;
return this.GenerateHashFrom(numbers);
public string EncodeLong(IEnumerable<long> numbers)
return this.EncodeLong(numbers.ToArray());
seps = new string(seps.ToCharArray().Intersect(alphabet.ToCharArray()).ToArray());
alphabet = new string(alphabet.ToCharArray().Except(seps.ToCharArray()).ToArray());
seps = ConsistentShuffle(seps, salt);
if (seps.Length == 0 || (alphabet.Length / seps.Length) > SEP_DIV)
var sepsLength = (int)Math.Ceiling(alphabet.Length / SEP_DIV);
if (sepsLength > seps.Length)
var diff = sepsLength - seps.Length;
seps += alphabet.Substring(0, diff);
alphabet = alphabet.Substring(diff);
else seps = seps.Substring(0, sepsLength);
sepsRegex = new Regex(string.Concat("[", seps, "]"));
sepsRegex = new Regex(string.Concat("[", seps, "]"), RegexOptions.Compiled);
alphabet = ConsistentShuffle(alphabet, salt);
private void SetupGuards()
var guardCount = (int)Math.Ceiling(alphabet.Length / GUARD_DIV);
guards = seps.Substring(0, guardCount);
seps = seps.Substring(guardCount);
guards = alphabet.Substring(0, guardCount);
alphabet = alphabet.Substring(guardCount);
guardsRegex = new Regex(string.Concat("[", guards, "]"));
guardsRegex = new Regex(string.Concat("[", guards, "]"), RegexOptions.Compiled);
private string GenerateHashFrom(long[] numbers)
if (numbers == null || numbers.Length == 0)
var ret = new StringBuilder();
var alphabet = this.alphabet;
for (var i = 0; i < numbers.Length; i++)
numbersHashInt += (int)(numbers[i] % (i + 100));
var lottery = alphabet[(int)(numbersHashInt % alphabet.Length)];
ret.Append(lottery.ToString());
for (var i = 0; i < numbers.Length; i++)
var buffer = lottery + this.salt + alphabet;
alphabet = ConsistentShuffle(alphabet, buffer.Substring(0, alphabet.Length));
var last = this.Hash(number, alphabet);
if (i + 1 < numbers.Length)
number %= ((int)last[0] + i);
var sepsIndex = ((int)number % this.seps.Length);
ret.Append(this.seps[sepsIndex]);
if (ret.Length < this.minHashLength)
var guardIndex = ((int)(numbersHashInt + (int)ret[0]) % this.guards.Length);
var guard = this.guards[guardIndex];
if (ret.Length < this.minHashLength)
guardIndex = ((int)(numbersHashInt + (int)ret[2]) % this.guards.Length);
guard = this.guards[guardIndex];
var halfLength = (int)(alphabet.Length / 2);
while (ret.Length < this.minHashLength)
alphabet = ConsistentShuffle(alphabet, alphabet);
ret.Insert(0, alphabet.Substring(halfLength));
ret.Append(alphabet.Substring(0, halfLength));
var excess = ret.Length - this.minHashLength;
ret.Remove(0, excess / 2);
ret.Remove(this.minHashLength, ret.Length - this.minHashLength);
private string Hash(long input, string alphabet)
var hash = new StringBuilder();
Console.WriteLine("4.1 input: "+ input);
hash.Insert(0, alphabet[(int)(input % alphabet.Length)]);
input = (input / alphabet.Length);
Console.WriteLine("4.2 input: "+ input);
private long Unhash(string input, string alphabet)
for (var i = 0; i < input.Length; i++)
var pos = alphabet.IndexOf(input[i]);
number += (long)(pos * Math.Pow(alphabet.Length, input.Length - i - 1));
private long[] GetNumbersFrom(string hash)
if (string.IsNullOrWhiteSpace(hash))
var alphabet = new string(this.alphabet.ToCharArray());
var ret = new List<long>();
var hashBreakdown = guardsRegex.Replace(hash, " ");
var hashArray = hashBreakdown.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (hashArray.Length == 3 || hashArray.Length == 2)
hashBreakdown = hashArray[i];
if (hashBreakdown[0] != default(char))
var lottery = hashBreakdown[0];
hashBreakdown = hashBreakdown.Substring(1);
hashBreakdown = sepsRegex.Replace(hashBreakdown, " ");
hashArray = hashBreakdown.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
for (var j = 0; j < hashArray.Length; j++)
var subHash = hashArray[j];
var buffer = lottery + this.salt + alphabet;
alphabet = ConsistentShuffle(alphabet, buffer.Substring(0, alphabet.Length));
ret.Add(Unhash(subHash, alphabet));
if (EncodeLong(ret.ToArray()) != hash)
private string ConsistentShuffle(string alphabet, string salt)
if (string.IsNullOrWhiteSpace(salt))
var letters = alphabet.ToCharArray();
for (int i = letters.Length - 1, v = 0, p = 0; i > 0; i--, v++)
return new string(letters);