Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@timiles
Created October 25, 2018 09:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timiles/e987edddded1046a5fd27dbdf114dcbf to your computer and use it in GitHub Desktop.
Save timiles/e987edddded1046a5fd27dbdf114dcbf to your computer and use it in GitHub Desktop.
Encode and decode between base 10 and base 36
public static class Base36
{
private const string Digits = "0123456789abcdefghijklmnopqrstuvwxyz";
private const string Max = "1y2p0ij32e8e7";
/// <summary>
/// Converts from base 10 to base 36.
/// </summary>
/// <param name="value">Value to convert</param>
/// <returns>Value in base 36</returns>
public static string Encode(long value)
{
if (value < 0)
{
throw new ArgumentOutOfRangeException(nameof(value), value, "value cannot be negative");
}
var encoded = string.Empty;
do
{
encoded = Digits[(int)(value % Digits.Length)] + encoded;
}
while ((value /= Digits.Length) != 0);
return encoded;
}
/// <summary>
/// Converts from base 36 to base 10.
/// </summary>
/// <param name="value">Value to convert</param>
/// <returns>Value in base 10</returns>
public static long Decode(string value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
value = value.ToLowerInvariant();
if (value.Any(c => !Digits.Contains(c)))
{
throw new ArgumentOutOfRangeException(nameof(value), value, "value contains invalid characters");
}
if (Compare(value, Max) > 0)
{
throw new ArgumentOutOfRangeException(nameof(value), value, "value cannot be greater than long.MaxValue");
}
var decoded = 0L;
for (var i = 0; i < value.Length; ++i)
{
decoded += Digits.IndexOf(value[i]) * (long)Math.Pow(Digits.Length, value.Length - i - 1);
}
return decoded;
}
private static int Compare(string valueA, string valueB)
{
if (valueA == valueB)
{
return 0;
}
if (valueA.Length == valueB.Length)
{
for (var i = 0; i < valueA.Length; i++)
{
var digitA = Digits.IndexOf(valueA[i]);
var digitB = Digits.IndexOf(valueB[i]);
if (digitA != digitB)
{
return (digitA < digitB ? -1 : 1);
}
}
}
return (valueA.Length < valueB.Length) ? -1 : 1;
}
}
public class Base36Tests
{
[Theory]
[InlineData(0, "0")]
[InlineData(1, "1")]
[InlineData(10, "a")]
[InlineData(100, "2s")]
[InlineData(long.MaxValue, "1y2p0ij32e8e7")]
public void Encode(long value, string expectedValue)
{
Assert.Equal(expectedValue, Base36.Encode(value));
}
[Theory]
[InlineData(-1)]
public void EncodeInvalidValue_ThrowsException(long value)
{
Assert.ThrowsAny<ArgumentException>(() => Base36.Encode(value));
}
[Theory]
[InlineData("0", 0)]
[InlineData("1", 1)]
[InlineData("a", 10)]
[InlineData("A", 10)]
[InlineData("2s", 100)]
[InlineData("1y2p0ij32e8e7", long.MaxValue)]
public void Decode(string value, long expectedValue)
{
Assert.Equal(expectedValue, Base36.Decode(value));
}
[Theory]
[InlineData(null)]
[InlineData(" ")]
[InlineData("1234/")]
[InlineData("1y2p0ij32e8e8")]
public void DecodeInvalidValue_ThrowsException(string value)
{
Assert.ThrowsAny<ArgumentException>(() => Base36.Decode(value));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment