Created
October 25, 2018 09:14
-
-
Save timiles/e987edddded1046a5fd27dbdf114dcbf to your computer and use it in GitHub Desktop.
Encode and decode between base 10 and base 36
This file contains hidden or 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
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