using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class Program
{
public static void Main()
{
AnyBaseConverter converter10 = new("0123456789");
AnyBaseConverter converter62 = new("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
string year = "9999";
string month = "12";
string day = "31";
string hours = "23";
string minutes = "59";
string seconds = "59";
string milliseconds = "999";
string dt0 = $"{year}{month}{day}{hours}{minutes}{seconds}{milliseconds}"; // yyyyMMddHHmmssfff
string sn = "99999999999999999"; // 17 digits of serial number
string sum = "99999"; // 5 digits of transaction sum
string s0 = dt0 + sn + sum;
Console.WriteLine($"initial: {s0.Length} {s0}");
string dayOfYear = "365"; // december 31
string timeInSeconds = "86399"; // 23:59:59
string dt1 = $"{year}{dayOfYear}{timeInSeconds}{milliseconds}";
string s1 = dt1 + sn + sum;
Console.WriteLine($"base10: {s1.Length} {s1}");
byte[] sBytes = converter10.Decode(s1);
string sBytesStr = new StringBuilder()
.AppendJoin(", ", sBytes.Select(b => b.ToString()))
.ToString();
Console.WriteLine($"base256: {sBytes.Length} [{sBytesStr}]");
string s2 = converter62.Encode(sBytes);
Console.WriteLine($"base62: {s2.Length} {s2}");
}
}
public class AnyBaseConverter
{
// Adapted code of Base62 encoding from:
// https://github .com/ghost1face/base62/blob/master/Base62/Base62Converter.cs
public AnyBaseConverter(string characterSet)
{
if (characterSet == "")
throw new ArgumentException("Character set cannot be empty", characterSet);
if (characterSet.Length > 256)
throw new ArgumentException("Character set length must not exceed 256 characters", characterSet);
this.Base = characterSet.Length;
this.CharacterSet = characterSet;
this.CharacterIds = characterSet.Select((c, i) => (c, i)).ToDictionary(ci => ci.c, ci => ci.i);
if (this.CharacterIds.Count != this.CharacterSet.Length)
throw new ArgumentException("Characters in character set must not repeat", "characterSet");
}
public int Base { get; }
public string CharacterSet { get; }
private readonly Dictionary<char, int> CharacterIds;
public string Encode(byte[] bytes)
{
byte[] converted = this.ConvertBase(bytes, 256, this.Base);
StringBuilder builder = new StringBuilder(converted.Length);
foreach (byte c in converted.AsSpan())
builder.Append(this.CharacterSet[c]);
return builder.ToString();
}
public byte[] Decode(string value)
{
byte[] arr = new byte[value.Length];
for (int i = 0; i < arr.Length; i++)
{
int characterId = this.CharacterIds.GetValueOrDefault(value[i], -1);
if (characterId == -1) throw new ArgumentException("Character must be in specified set, found: " + value);
arr[i] = (byte)characterId;
}
byte[] converted = ConvertBase(arr, this.Base, 256);
return converted;
}
private byte[] ConvertBase(byte[] source, int sourceBase, int targetBase)
{
int count;
List<byte> result = new();
List<byte> quotient = new();
while ((count = source.Length) > 0)
{
quotient.Clear();
int remainder = 0;
for (var i = 0; i < count; i++)
{
int accumulator = source[i] + remainder * sourceBase;
byte digit = (byte)((accumulator - (accumulator % targetBase)) / targetBase);
remainder = accumulator % targetBase;
if (quotient.Count > 0 || digit != 0)
quotient.Add(digit);
}
result.Add((byte)remainder);
source = quotient.ToArray();
}
byte[] output = new byte[result.Count];
for (var (i, j) = (0, result.Count - 1); j >= 0; i++, j--)
output[i] = result[j];
return output;
}
}