// Based on RSDN RusCurrency class using System; using System.Collections.Generic; using System.Text; namespace FastReport.Functions { internal abstract class NumToWordsBase { #region Private Methods private string Str(decimal value, WordInfo senior, WordInfo junior) { bool minus = false; if (value < 0) { value = -value; minus = true; } long n = (long)value; int remainder = (int)((value - n + 0.005m) * 100); if (remainder >= 100) { n++; remainder = 0; } StringBuilder r = new StringBuilder(); Str(n, senior, r); if (minus) r.Insert(0, GetMinus() + " "); if (junior != null) { r.Append(GetDecimalSeparator() + remainder.ToString("00 ")); r.Append(Case(remainder, junior)); } r[0] = char.ToUpper(r[0]); return r.ToString(); } #endregion #region Protected Methods protected abstract string GetFixedWords(bool male, long value); protected abstract string GetTen(bool male, long value); protected abstract string GetHund(bool male, long value); protected abstract WordInfo GetThousands(); protected abstract WordInfo GetMillions(); protected abstract WordInfo GetMilliards(); protected abstract WordInfo GetTrillions(); protected abstract CurrencyInfo GetCurrency(string currencyName); protected abstract string GetZero(); protected abstract string GetMinus(); protected virtual void Str(long value, WordInfo senior, StringBuilder result) { if (value == 0) result.Append(GetZero() + " " + senior.many + " "); else { if (value % 1000 != 0) result.Append(Str1000(value, senior, 1)); else result.Append(" " + senior.many + " "); value /= 1000; result.Insert(0, Str1000(value, GetThousands(), 2)); value /= 1000; result.Insert(0, Str1000(value, GetMillions(), 3)); value /= 1000; result.Insert(0, Str1000(value, GetMilliards(), 4)); value /= 1000; result.Insert(0, Str1000(value, GetTrillions(), 5)); result.Replace(" ", " "); } } protected virtual string Str1000(long value, WordInfo info, int counter) { long val = value % 1000; if (val == 0) return ""; StringBuilder r = new StringBuilder(); // add hundred string hund = GetHund(info.male, val); if (hund != "") r.Append(hund); // decide whether to use the 100-10 separator or not string sep100_10 = Get100_10Separator(); if (value < 1000 && hund == "") sep100_10 = ""; val = val % 100; if (val < GetFixedWordsCount()) { // val is less than fixed words count (usually 20), get fixed words string frac20 = GetFixedWords(info.male, val); if (frac20 != "") r.Append(sep100_10 + frac20); } else { // val is greater than fixed words count (usually 20), get tens separately string ten = GetTen(info.male, val / 10); string frac10 = GetFixedWords(info.male, val % 10); // decide whether to use 10-1 separator or not if (ten != "" && frac10 != "") r.Append(sep100_10 + ten + Get10_1Separator() + frac10); else if (ten != "") r.Append(sep100_10 + ten); else if (frac10 != "") r.Append(sep100_10 + frac10); } // add currency/group word r.Append(" "); r.Append(Case(value, info)); // make the result starting with letter and ending with space if (r.Length != 0) r.Append(" "); return r.ToString().TrimStart(' '); } protected virtual int GetFixedWordsCount() { return 20; } protected virtual string GetDecimalSeparator() { return ""; } protected virtual string Get10_1Separator() { return " "; } protected virtual string Get100_10Separator() { return " "; } protected virtual string Case(long value, WordInfo info) { if (value % 100 == 1) return info.one; return info.many; } #endregion #region Public Methods public string ConvertCurrency(decimal value, string currencyName) { try { CurrencyInfo currency = GetCurrency(currencyName); return Str(value, currency.senior, currency.junior); } catch (KeyNotFoundException e) { throw new Exception($"Currency \"{currencyName}\" is not defined in the \"{GetType().Name}\" converter."); } catch (NotImplementedException e) { throw new Exception("Method " + e.TargetSite.ToString() + " wasn't implemented"); } catch (Exception e) { throw new Exception("There is an exception - " + e.ToString()); } } public string ConvertNumber(decimal value, bool male, string one, string two, string many) { return Str(value, new WordInfo(male, one, two, many), null); } public string ConvertNumber(decimal value, bool male, string seniorOne, string seniorTwo, string seniorMany, string juniorOne, string juniorTwo, string juniorMany) { return Str(value, new WordInfo(male, seniorOne, seniorTwo, seniorMany), new WordInfo(male, juniorOne, juniorTwo, juniorMany)); } #endregion } internal class WordInfo { public bool male; public string one; public string two; public string many; public WordInfo(bool male, string one, string two, string many) { this.male = male; this.one = one; this.two = two; this.many = many; } public WordInfo(string one, string many) : this(true, one, many, many) { } public WordInfo(string all) : this(true, all, all, all) { } } internal class CurrencyInfo { public WordInfo senior; public WordInfo junior; public CurrencyInfo(WordInfo senior, WordInfo junior) { this.senior = senior; this.junior = junior; } } }