Files
CroakFang df178687ba update
2022-12-13 22:47:31 +08:00

185 lines
4.0 KiB
C#

using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace BaseNcoding
{
public abstract class Base
{
public uint CharsCount { get; }
public double BitsPerChars => (double)BlockBitsCount / BlockCharsCount;
public int BlockBitsCount { get; protected set; }
public int BlockCharsCount { get; protected set; }
public string Alphabet { get; }
public char Special { get; }
public abstract bool HasSpecial { get; }
public Encoding Encoding { get; set; }
public bool Parallel { get; set; }
protected readonly int[] InvAlphabet;
public Base(uint charsCount, string alphabet, char special, Encoding encoding = null, bool parallel = false)
{
if (alphabet.Length != charsCount)
throw new ArgumentException($"Base string should contain {charsCount} chars");
for (int i = 0; i < charsCount; i++)
for (int j = i + 1; j < charsCount; j++)
if (alphabet[i] == alphabet[j])
throw new ArgumentException("Base string should contain distinct chars");
if (alphabet.Contains(special))
throw new ArgumentException("Base string should not contain special char");
CharsCount = charsCount;
Alphabet = alphabet;
Special = special;
int bitsPerChar = LogBase2(charsCount);
BlockBitsCount = Lcm(bitsPerChar, 8);
BlockCharsCount = BlockBitsCount / bitsPerChar;
InvAlphabet = new int[Alphabet.Max() + 1];
for (int i = 0; i < InvAlphabet.Length; i++)
InvAlphabet[i] = -1;
for (int i = 0; i < charsCount; i++)
InvAlphabet[Alphabet[i]] = i;
Encoding = encoding ?? Encoding.UTF8;
Parallel = parallel;
}
public virtual string EncodeString(string data)
{
return Encode(Encoding.GetBytes(data));
}
public abstract string Encode(byte[] data);
public string DecodeToString(string data)
{
return Encoding.GetString(Decode(Regex.Replace(data, @"\r\n?|\n", "")));
}
public abstract byte[] Decode(string data);
/// <summary>
/// From: http://stackoverflow.com/a/600306/1046374
/// </summary>
/// <param name="x"></param>
/// <returns></returns>
public static bool IsPowerOf2(uint x)
{
uint xint = x;
if (x - xint != 0)
return false;
return (xint & (xint - 1)) == 0;
}
/// <summary>
/// From: http://stackoverflow.com/a/13569863/1046374
/// </summary>
public static int Lcm(int a, int b)
{
int num1, num2;
if (a > b)
{
num1 = a;
num2 = b;
}
else
{
num1 = b;
num2 = a;
}
for (int i = 1; i <= num2; i++)
{
int mult = num1 * i;
if (mult % num2 == 0)
return mult;
}
return num2;
}
private static int LogBase2(uint x)
{
int r = 0;
while ((x >>= 1) != 0)
r++;
return r;
}
private static int LogBaseN(uint x, uint n)
{
int r = 0;
while ((x /= n) != 0)
r++;
return r;
}
public static int GetOptimalBitsCount2(uint charsCount, out uint charsCountInBits,
uint maxBitsCount = 64, bool base2BitsCount = false)
{
int result = 0;
charsCountInBits = 0;
int n1 = LogBase2(charsCount);
double charsCountLog = Math.Log(2, charsCount);
double maxRatio = 0;
for (int n = n1; n <= maxBitsCount; n++)
{
if (base2BitsCount && n % 8 != 0)
continue;
uint l1 = (uint)Math.Ceiling(n * charsCountLog);
double ratio = (double)n / l1;
if (ratio > maxRatio)
{
maxRatio = ratio;
result = n;
charsCountInBits = l1;
}
}
return result;
}
protected static int GetOptimalBitsCount(uint charsCount, out uint charsCountInBits,
uint maxBitsCount = 64, uint radix = 2)
{
int result = 0;
charsCountInBits = 0;
int n0 = LogBaseN(charsCount, radix);
double charsCountLog = Math.Log(radix, charsCount);
double maxRatio = 0;
for (int n = n0; n <= maxBitsCount; n++)
{
uint k = (uint)Math.Ceiling(n * charsCountLog);
double ratio = (double)n / k;
if (ratio > maxRatio)
{
maxRatio = ratio;
result = n;
charsCountInBits = k;
}
}
return result;
}
}
}