public static void Main()
Test(2, Currency.NZD, "Two dollars.");
Test(5, Currency.NZD, "Five dollars.");
Test(5.20m, Currency.NZD, "Five dollars and twenty cents.");
Test(10.01m, Currency.NZD, "Ten dollars and one cent.");
Test(8m, Currency.GBP, "Eight pounds.");
Test(100, Currency.NZD, "One hundred dollars.");
Test(10.5m, Currency.GBP, "Ten pounds and fifty pence.");
Test(1, Currency.NZD, "One dollar.");
Test(57m, Currency.JPY, "Fifty-seven yen.");
Test(57.6m, Currency.JPY, "Fifty-seven yen.");
Test(0.10m, Currency.NZD, "Ten cents.");
Test(0, Currency.NZD, "Zero dollars.");
Test(0.9999999999m, Currency.NZD, "One dollar.");
Test(-10.99m, Currency.NZD, "Minus ten dollars and ninety-nine cents.");
Test(-0.95m, Currency.NZD, "Minus ninety-five cents.");
Test(0.10m, Currency.NZD, "Ten cents.");
Test(-0.01m, Currency.NZD, "Minus one cent.");
Test(0.01m, Currency.NZD, "One cent.");
Test(10500500001.55m, Currency.NZD, "One billion five hundred million five hundred thousand and one dollars and fifty-five cents.");
static string CurrencyInWords(decimal amount, Currency currency)
amount = Math.Round(amount, 2);
var wholeAmount = (int)amount;
var fractionAmount = (int)((amount - (int)amount) * 100m);
var words = (wholeAmount, fractionAmount) switch {
(_, 0) => WholeAmountWords(wholeAmount, currency),
(0, _) => FractionalAmountWords(fractionAmount, currency),
_ when !currency.SupportsFractionalAmounts() => WholeAmountWords(wholeAmount, currency),
_ => $"{WholeAmountWords(wholeAmount, currency)} and {FractionalAmountWords(Math.Abs(fractionAmount), currency)}",
return $"{words.Transform(To.SentenceCase)}.";
static string WholeAmountWords(int wholeAmount, Currency currency)
var currencyWord = wholeAmount != 1 ? currency.PluralisedWord() : currency.Word;
return $"{wholeAmount.ToWords()} {currencyWord}";
static string FractionalAmountWords(int fractionAmount, Currency currency)
if (!currency.SupportsFractionalAmounts())
var currencyWord = Math.Abs(fractionAmount) > 1 && currency != Currency.GBP ?
currency.SmallerWord.ToString().Pluralize() :
currency.SmallerWord.ToString();
return $"{fractionAmount.ToWords()} {currencyWord}";
public static readonly Currency NZD = new Currency("NZD", "dollar", "cent");
public static readonly Currency GBP = new Currency("GBP", "pound", "pence");
public static readonly Currency JPY = new Currency("JPY", "yen", "");
public string Symbol { get; }
public string Word { get; }
public string SmallerWord { get; }
public bool SupportsFractionalAmounts() {
return SmallerWord != "";
public string PluralisedWord() {
return Symbol == "JPY" ? Word : Word.Pluralize();
Currency(string symbol, string word, string smallerWord)
SmallerWord = smallerWord;
static void Test(decimal amount, Currency currency, string expected)
Console.WriteLine($"Test: {amount}, {currency.Symbol}, {expected}");
actual = CurrencyInWords(amount, currency);
Console.WriteLine("PASS.\n");
actual = "Exception: " + e.Message;
Console.WriteLine("FAIL:");
Console.WriteLine(" Expected: " + expected);
Console.WriteLine(" Actual : " + actual);
Console.WriteLine($"{pass} / {pass + fail} tests passed.\n");
Console.WriteLine(fail == 0 ? "SUCCESS!" : "FAILURE!");