Base source: https://github.com/PetrPodrazil/UFI
using System;
using System.Collections;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Text.RegularExpressions;
public class UFI
{
public const string b31chars = "0123456789ACDEFGHJKMNPQRSTUVWXY";
private static int CalculateChecksum(string ufi)
{
if (ufi.Length > 15) ufi = ufi.Substring(1);
var weightedSum = 0;
for (int i = 0; i < 15; i++) weightedSum += (i + 2) * b31chars.IndexOf(ufi[i]);
return (31 - weightedSum % 31) % 31;
}
public static bool IsUfiValid(string ufi)
{
ufi = Regex.Replace(ufi?.Trim(), "\\-", ""); // remove dashes
if (ufi.Length != 16) return false; // check length
if (!ufi.All(c => b31chars.Contains(c))) return false; // check characters
var checksum = b31chars.IndexOf(ufi[0]); // checksum (value/index of first character)
var calculatedChecksum = CalculateChecksum(ufi); // calculate checksum
return checksum == calculatedChecksum;
}
public static string GenerateUFI(string VAT, int formulation)
{
if (VAT.Length == 0) return "";
//2.1.1 Step 1 – UFI payload numerical value
string countryCodeIso3166 = GetCountryCodeISO3166(VAT);
long vatLong = VatToNumber(VAT, countryCodeIso3166);
BitArray group = GetCountryGroup(countryCodeIso3166);
BitArray country = GetCountryCode(countryCodeIso3166);
BitArray vatn = VatNumberBits(vatLong, countryCodeIso3166);
BitArray formulationN = formulation.ToBinary(28);
BitArray togetherStep1 = 0.ToBinary(1).Append(vatn).Append(country).Append(group).Append(formulationN);
//2.1.2 Step 2 – UFI payload in base-31
string decimalPayloadString = BinToDec(togetherStep1.ToDigitString());
BigInteger payload = BigInteger.Parse(decimalPayloadString);
string base31PayloadStep2 = BigIntegerToStringBaseX(payload, b31chars).PadLeft(15, '0');
//2.1.3 Step 3 – Character reorganisation
string reorganisedStep3 = CharacterReorganisation(base31PayloadStep2);
//2.1.4 Step 4 – Checksum calculation
int checkSum = CalculateChecksum(reorganisedStep3);
// checksum ervoor
string UFIPlainStep4 = $"{b31chars[checkSum]}{reorganisedStep3}";
// streepjes ertussen
return Regex.Replace(UFIPlainStep4, @"^(....)(....)(....)(....)$", "$1-$2-$3-$4");
}
public static void ReverseUFI(string ufi)
{
if (!IsUfiValid(ufi)) throw new Exception("UFI code is invalid");
// remove dashes
ufi = Regex.Replace(ufi?.Trim(), "\\-", "");
// remove first character
ufi = ufi.Substring(1);
// backwards character reorganisation
var reversed = ReverseCharacterReorganisation(ufi);
// unbase 31
var bigInt = StringBaseXToBigInteger(reversed, b31chars);
// convert to base2, pad to 74 characters
var binary = ToNBase(bigInt, 2).PadLeft(74, '0');
// split binary parts
var parts = SplitSizes(binary, 28, 4, 7, 34, 1);
// convert binary to decimal
var formulationN = BinaryToDecimal(parts[0]);
var countryGroup = BinaryToDecimal(parts[1]);
var country = BinaryToDecimal(parts[2]);
var vatnr = BinaryToDecimal(parts[3]);
Console.WriteLine($"{countryGroup}; {country}; {vatnr}; {formulationN}");
}
public static string[] SplitSizes(string str, params int[] sizes)
{
var length = sizes.Length;
var parts = new string[length];
var start = 0;
for (int i = 0; i < length; i++)
{
parts[i] = str.Substring(start, sizes[i]);
start += sizes[i];
}
return parts;
}
public static BigInteger BinaryToDecimal(string binary) => StringBaseXToBigInteger(binary, "01");
public static string ToNBase(BigInteger a, int n)
{
var sb = new StringBuilder();
while (a > 0)
{
sb.Insert(0, a % n);
a /= n;
}
return sb.ToString();
}
/// <summary>
/// Table 2-2: Rules for VAT number conversion to numerical value
/// </summary>
/// <param name="VAT"></param>
/// <returns></returns>
internal static long VatToNumber(string VAT, string Country)
{
switch (Country)
{
case "CY":
return VatToNumberCy(VAT);
case "ES":
return VatToNumberEs(VAT);
case "FR":
return VatToNumberFr(VAT);
case "GB":
return VatToNumberGb(VAT);
case "IE":
return VatToNumberIe(VAT);
case "IS":
return VatToNumberIs(VAT);
default:
string justNumbers = new string(VAT.Where(char.IsDigit).ToArray());
long.TryParse(justNumbers, out long n);
return n;
}
}
private static long VatToNumberCy(string VAT)
{
string decimalPart = VAT.Substring(2, 8);
char letter = VAT.Substring(10, 1)[0];
int letterIndex = LetterNumberCyGb(letter);
long letterPart = letterIndex * (long)Math.Pow(10, 8);
return letterPart + Convert.ToInt64(decimalPart);
}
internal static long VatToNumberEs(string VAT)
{
string decimalPart = VAT.Substring(3, 7);
int c1 = LetterNumberEsFrIs(VAT.Substring(2, 1)[0]);
int c2 = LetterNumberEsFrIs(VAT.Substring(10, 1)[0]);
long letterPart = (36 * c1 + c2) * (long)Math.Pow(10, 7);
return letterPart + Convert.ToInt64(decimalPart);
}
internal static long VatToNumberFr(string VAT)
{
//FRAB123456789
string decimalPart = VAT.Substring(4, 9);
int c1 = LetterNumberEsFrIs(VAT.Substring(2, 1)[0]);
int c2 = LetterNumberEsFrIs(VAT.Substring(3, 1)[0]);
long letterPart = (36 * c1 + c2) * (long)Math.Pow(10, 9);
return letterPart + Convert.ToInt64(decimalPart);
}
internal static long VatToNumberGb(string VAT)
{
//GB123456789012 or GBAB123
string justNumbers = new string(VAT.Where(char.IsDigit).ToArray());
long.TryParse(justNumbers, out long n);
if (VAT.Length == 7)
{
int c1 = LetterNumberCyGb(VAT.Substring(2, 1)[0]);
int c2 = LetterNumberCyGb(VAT.Substring(3, 1)[0]);
long letterPart = (26 * c1 + c2) * (long)Math.Pow(10, 3);
return letterPart + n;
}
else
{
return (long)Math.Pow(2, 40) + n;
}
}
internal static long VatToNumberIe(string VAT)
{
Regex regexA = new Regex("[0-9][A-Z*+][0-9]{5}[A-Z]");
Match matchA = regexA.Match(VAT);
if (matchA.Success)
{
//"IE9*54321Y"
string d = VAT.Substring(2, 1) + VAT.Substring(VAT.Length - 6, 5);
int c1 = LetterNumberIe(VAT.Substring(3, 1)[0]);
int c2 = LetterNumberIe(VAT.Substring(VAT.Length - 1, 1)[0]);
long letterPart = (26 * c1 + c2) * (long)Math.Pow(10, 6);
return letterPart + Convert.ToInt64(d);
}
else
{
//IE9876543Z
string justNumbers = new string(VAT.Where(char.IsDigit).ToArray());
long.TryParse(justNumbers, out long N);
int c1 = LetterNumberCyGb(VAT.Substring(9, 1)[0]);
int c2 = 0;
if (VAT.Length == 11)
c2 = LetterNumberCyGb(VAT.Substring(10, 1)[0]);
return ((long)Math.Pow(2, 33) + ((26 * c2 + c1) * (long)Math.Pow(10, 7) + Convert.ToInt64(N)));
}
}
internal static long VatToNumberIs(string VAT)
{
//ISAB3D5F
long VN = 0;
char l1 = VAT.Substring(7, 1)[0];
char l2 = VAT.Substring(6, 1)[0];
char l3 = VAT.Substring(5, 1)[0];
char l4 = VAT.Substring(4, 1)[0];
char l5 = VAT.Substring(3, 1)[0];
char l6 = VAT.Substring(2, 1)[0];
VN += 36 * 36 * 36 * 36 * 36 * LetterNumberEsFrIs(l6);
VN += 36 * 36 * 36 * 36 * LetterNumberEsFrIs(l5);
VN += 36 * 36 *36 * LetterNumberEsFrIs(l4);
VN += 36 * 36 * LetterNumberEsFrIs(l3);
VN += 36 * LetterNumberEsFrIs(l2);
VN += LetterNumberEsFrIs(l1);
return VN;
}
private static int LetterNumberIe(char c)
{
if (c == '+') return 26;
if (c == '*') return 27;
return char.ToUpper(c) - 65;
}
private static int LetterNumberCyGb(char c)
{
return char.ToUpper(c) - 65;
}
internal static int LetterNumberEsFrIs(char c)
{
if (char.IsDigit(c)) return (int)char.GetNumericValue(c);
return char.ToUpper(c) - 55;
}
static BitArray VatNumberBits(long VAT, string countryCode)
{
return VAT.ToBinary(41 - GetBitsCount(countryCode));
}
private static readonly int[] mapping = new int[] { 5, 4, 3, 7, 2, 8, 9, 10, 1, 0, 11, 6, 12, 13, 14 };
private static readonly int[] reverseMapping = new int[] { 9, 8, 4, 2, 1, 0, 11, 3, 5, 6, 7, 10, 12, 13, 14 };
private static string CharacterReorganisation(string b31)
{
var sb = new StringBuilder();
for (int i = 0; i < 15; i++)
sb.Append(b31[mapping[i]]);
return sb.ToString();
}
private static string ReverseCharacterReorganisation(string b31)
{
var sb = new StringBuilder();
for (int i = 0; i < 15; i++)
sb.Append(b31[reverseMapping[i]]);
return sb.ToString();
}
/// <summary>
/// Binary string to decimal string
/// 0101 => 5
/// </summary>
private static string BinToDec(string value)
{
BigInteger res = 0;
foreach (char c in value)
{
res <<= 1;
res += c == '1' ? 1 : 0;
}
return res.ToString();
}
/// <summary>
/// BigInteger to any base string
/// </summary>
private static string BigIntegerToStringBaseX(BigInteger value, string baseChars)
{
string result = string.Empty;
int targetBase = baseChars.Length;
do
{
result = baseChars[(int)(value % targetBase)] + result;
value /= targetBase;
}
while (value > 0);
return result;
}
private static BigInteger StringBaseXToBigInteger(string ufi, string baseChars)
{
BigInteger value = 0;
var currentBase = baseChars.Length;
var reversed = ufi.Reverse().ToList();
for (int i = 0; i < reversed.Count; i++)
{
var baseValue = baseChars.IndexOf(reversed[i]);
var digitValue = BigInteger.Pow(new BigInteger(currentBase), i);
value += digitValue * baseValue;
}
return value;
}
private static BitArray GetCountryGroup(string countryCode)
{
switch (countryCode)
{
case "":
return 0.ToBinary(4);
case "FR":
return 1.ToBinary(4);
case "GB":
return 2.ToBinary(4);
case "LT":
case "SE":
return 3.ToBinary(4);
case "HR":
case "IT":
case "LV":
case "NL":
return 4.ToBinary(4);
default:
return 5.ToBinary(4);
}
}
private static int GetBitsCount(string countryCode)
{
switch (countryCode)
{
case "":
case "FR":
case "GB":
return 0;
case "LT":
case "SE":
return 1;
case "HR":
case "IT":
case "LV":
case "NL":
return 4;
default:
return 7;
}
}
private static BitArray GetCountryCode(string countryCode)
{
switch (countryCode)
{
default:
case "":
case "FR":
case "GB":
return new BitArray(0);
case "LT":
return 0.ToBinary(1);
case "SE":
return 1.ToBinary(1);
case "HR":
return 0.ToBinary(4);
case "IT":
return 1.ToBinary(4);
case "LV":
return 2.ToBinary(4);
case "NL":
return 3.ToBinary(4);
case "BG":
return 0.ToBinary(7);
case "CZ":
return 1.ToBinary(7);
case "IE":
return 2.ToBinary(7);
case "ES":
return 3.ToBinary(7);
case "PL":
return 4.ToBinary(7);
case "RO":
return 5.ToBinary(7);
case "SK":
return 6.ToBinary(7);
case "CY":
return 7.ToBinary(7);
case "IS":
return 8.ToBinary(7);
case "BE":
return 9.ToBinary(7);
case "DE":
return 10.ToBinary(7);
case "EE":
return 11.ToBinary(7);
case "GR":
return 12.ToBinary(7);
case "NO":
return 13.ToBinary(7);
case "PT":
return 14.ToBinary(7);
case "AT":
return 15.ToBinary(7);
case "DK":
return 16.ToBinary(7);
case "FI":
return 17.ToBinary(7);
case "HU":
return 18.ToBinary(7);
case "LU":
return 19.ToBinary(7);
case "MT":
return 20.ToBinary(7);
case "SI":
return 21.ToBinary(7);
case "LI":
return 22.ToBinary(7);
}
}
/// <summary>
/// Uppercase ISO-3166-1 alpha-2 country codes from VAT
/// </summary>
/// <param name="VAT"></param>
/// <returns></returns>
private static string GetCountryCodeISO3166(string VAT)
{
if (VAT.Length == 0) return "";
if (char.IsDigit(VAT[0])) return "";
string code = VAT.Substring(0, 2).ToUpper();
if (code == "EL") code = "GR";
return code;
}
}
public static class Extensions
{
public static string ToDigitString(this BitArray array)
{
StringBuilder builder = new StringBuilder();
foreach (bool bit in array.Cast<bool>())
builder.Append(bit ? "1" : "0");
return Reverse(builder.ToString());
}
private static string Reverse(string s)
{
char[] charArray = s.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}
public static BitArray Append(this BitArray current, BitArray after)
{
bool[] bools = new bool[current.Count + after.Count];
current.CopyTo(bools, 0);
after.CopyTo(bools, current.Count);
return new BitArray(bools);
}
public static BitArray ToBinary(this int numeral, int bitCount)
{
BitArray binary = new BitArray(new[] { numeral }) { Length = bitCount };
return binary;
}
public static BitArray ToBinary(this long numeral, int bitCount)
{
BitArray binary = new BitArray(BitConverter.GetBytes(numeral)) { Length = bitCount };
return binary;
}
}
408200cookie-checkUFI (Unique Formula Identifier)