/*
 * $Id$
 *
 * Copyright 2013 Valentyn Kolesnikov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Web.Script.Serialization;

/**
 * Converts numbers to symbols.
 *
 * @author Valentyn V Kolesnikov
 * @version $Revision$ $Date$
 */
public class MoneyToStr {

readonly string json = @"{
  ""CurrencyList"" : {
    ""language"" : { ""-value"" : ""UKR"" },
    ""UKR"" : {
      ""item"" : [
        {
          ""-value"" : ""0"",
          ""-text"" : ""нуль""
        },
        {
          ""-value"" : ""1000_10"",
          ""-text"" : ""тисяч,мільйонів,мільярдів,трильйонів""
        },
        {
          ""-value"" : ""1000_1"",
          ""-text"" : ""тисяча,мільйон,мільярд,трильйон""
        },
        {
          ""-value"" : ""1000_234"",
          ""-text"" : ""тисячі,мільйона,мільярда,трильйона""
        },
        {
          ""-value"" : ""1000_5"",
          ""-text"" : ""тисяч,мільйонів,мільярдів,трильйонів""
        },
        {
          ""-value"" : ""10_19"",
          ""-text"" : ""десять,одинадцять,дванадцять,тринадцять,чотирнадцять,п’ятнадцять,шiстнадцять,сiмнадцять,вiсiмнадцять,дев'ятнадцять""
        },
        {
          ""-value"" : ""1"",
          ""-text"" : ""одна,один,один,одна""
        },
        {
          ""-value"" : ""2"",
          ""-text"" : ""дві,два,два,дві""
        },
        {
          ""-value"" : ""3_9"",
          ""-text"" : ""три,чотири,п’ять,шість,сім,вісім,дев’ять""
        },
        {
          ""-value"" : ""100_900"",
          ""-text"" : ""сто ,двісті ,триста ,чотириста ,п’ятсот ,шістсот ,сімсот ,вісімсот ,дев’ятсот ""
        },
        {
          ""-value"" : ""20_90"",
          ""-text"" : ""двадцять ,тридцять ,сорок ,п’ятдесят ,шістдесят ,сімдесят ,вісімдесят ,дев’яносто ""
        }
      ]
    },
    ""RUS"" : {
      ""item"" : [
        {
          ""-value"" : ""0"",
          ""-text"" : ""ноль""
        },
        {
          ""-value"" : ""1000_10"",
          ""-text"" : ""тысяч,миллионов,миллиардов,триллионов""
        },
        {
          ""-value"" : ""1000_1"",
          ""-text"" : ""тысяча,миллион,миллиард,триллион""
        },
        {
          ""-value"" : ""1000_234"",
          ""-text"" : ""тысячи,миллиона,миллиарда,триллиона""
        },
        {
          ""-value"" : ""1000_5"",
          ""-text"" : ""тысяч,миллионов,миллиардов,триллионов""
        },
        {
          ""-value"" : ""10_19"",
          ""-text"" : ""десять,одиннадцать,двенадцать,тринадцать,четырнадцать,пятнадцать,шестнадцать,семнадцать,восемнадцать,девятнадцать""
        },
        {
          ""-value"" : ""1"",
          ""-text"" : ""одна,один,один,одна""
        },
        {
          ""-value"" : ""2"",
          ""-text"" : ""две,два,два,две""
        },
        {
          ""-value"" : ""3_9"",
          ""-text"" : ""три,четыре,пять,шесть,семь,восемь,девять""
        },
        {
          ""-value"" : ""100_900"",
          ""-text"" : ""сто ,двести ,триста ,четыреста ,пятьсот ,шестьсот ,семьсот ,восемьсот ,девятьсот ""
        },
        {
          ""-value"" : ""20_90"",
          ""-text"" : ""двадцать ,тридцать ,сорок ,пятьдесят ,шестьдесят ,семьдесят ,восемьдесят ,девяносто ""
        }
      ]
    },
    ""ENG"" : {
      ""item"" : [
        {
          ""-value"" : ""0"",
          ""-text"" : ""zero""
        },
        {
          ""-value"" : ""1000_10"",
          ""-text"" : ""thousand,million,billion,trillion""
        },
        {
          ""-value"" : ""1000_1"",
          ""-text"" : ""thousand,million,billion,trillion""
        },
        {
          ""-value"" : ""1000_234"",
          ""-text"" : ""thousand,million,billion,trillion""
        },
        {
          ""-value"" : ""1000_5"",
          ""-text"" : ""thousand,million,billion,trillion""
        },
        {
          ""-value"" : ""10_19"",
          ""-text"" : ""ten,eleven,twelve,thirteen,fourteen,fifteen,sixteen,seventeen,eighteen,nineteen""
        },
        {
          ""-value"" : ""1"",
          ""-text"" : ""one,one,one,one""
        },
        {
          ""-value"" : ""2"",
          ""-text"" : ""two,two,two,two""
        },
        {
          ""-value"" : ""3_9"",
          ""-text"" : ""three,four,five,six,seven,eight,nine""
        },
        {
          ""-value"" : ""100_900"",
          ""-text"" : ""one hundred ,two hundred ,three hundred ,four hundred ,five hundred ,six hundred ,seven hundred ,eight hundred ,nine hundred ""
        },
        {
          ""-value"" : ""20_90"",
          ""-text"" : ""twenty-,thirty-,forty-,fifty-,sixty-,seventy-,eighty-,ninety-""
        }
      ]
    },
    ""RUR"" : [
      {
        ""-CurrID"" : ""810"",
        ""-CurrName"" : ""Российские рубли"",
        ""-language"" : ""RUS"",
        ""-RubOneUnit"" : ""рубль"",
        ""-RubTwoUnit"" : ""рубля"",
        ""-RubFiveUnit"" : ""рублей"",
        ""-RubSex"" : ""M"",
        ""-KopOneUnit"" : ""копейка"",
        ""-KopTwoUnit"" : ""копейки"",
        ""-KopFiveUnit"" : ""копеек"",
        ""-KopSex"" : ""F""
      },
      {
        ""-CurrID"" : ""810"",
        ""-CurrName"" : ""Российские рубли"",
        ""-language"" : ""UKR"",
        ""-RubOneUnit"" : ""рубль"",
        ""-RubTwoUnit"" : ""рубля"",
        ""-RubFiveUnit"" : ""рублів"",
        ""-RubSex"" : ""M"",
        ""-KopOneUnit"" : ""копійка"",
        ""-KopTwoUnit"" : ""копійки"",
        ""-KopFiveUnit"" : ""копійок"",
        ""-KopSex"" : ""F""
      }
    ],
    ""UAH"" : [
      {
        ""-CurrID"" : ""980"",
        ""-CurrName"" : ""Украинскі гривні"",
        ""-language"" : ""RUS"",
        ""-RubOneUnit"" : ""гривня"",
        ""-RubTwoUnit"" : ""гривни"",
        ""-RubFiveUnit"" : ""гривень"",
        ""-RubSex"" : ""F"",
        ""-KopOneUnit"" : ""копейка"",
        ""-KopTwoUnit"" : ""копейки"",
        ""-KopFiveUnit"" : ""копеек"",
        ""-KopSex"" : ""F""
      },
      {
        ""-CurrID"" : ""980"",
        ""-CurrName"" : ""Украинскі гривні"",
        ""-language"" : ""UKR"",
        ""-RubOneUnit"" : ""гривня"",
        ""-RubTwoUnit"" : ""гривні"",
        ""-RubFiveUnit"" : ""гривень"",
        ""-RubSex"" : ""F"",
        ""-KopOneUnit"" : ""копійка"",
        ""-KopTwoUnit"" : ""копійки"",
        ""-KopFiveUnit"" : ""копійок"",
        ""-KopSex"" : ""F""
      }
    ],
    ""USD"" : [
      {
        ""-CurrID"" : ""840"",
        ""-CurrName"" : ""Долари США"",
        ""-language"" : ""RUS"",
        ""-RubOneUnit"" : ""долар"",
        ""-RubTwoUnit"" : ""долара"",
        ""-RubFiveUnit"" : ""доларів"",
        ""-RubSex"" : ""M"",
        ""-KopOneUnit"" : ""цент"",
        ""-KopTwoUnit"" : ""цена"",
        ""-KopFiveUnit"" : ""центов"",
        ""-KopSex"" : ""M""
      },
      {
        ""-CurrID"" : ""840"",
        ""-CurrName"" : ""Долари США"",
        ""-language"" : ""UKR"",
        ""-RubOneUnit"" : ""долар"",
        ""-RubTwoUnit"" : ""долара"",
        ""-RubFiveUnit"" : ""доларів"",
        ""-RubSex"" : ""M"",
        ""-KopOneUnit"" : ""цент"",
        ""-KopTwoUnit"" : ""цена"",
        ""-KopFiveUnit"" : ""центів"",
        ""-KopSex"" : ""M""
      },
      {
        ""-CurrID"" : ""840"",
        ""-CurrName"" : ""Долари США"",
        ""-language"" : ""ENG"",
        ""-RubOneUnit"" : ""dollar"",
        ""-RubTwoUnit"" : ""dollars"",
        ""-RubFiveUnit"" : ""dollars"",
        ""-RubSex"" : ""M"",
        ""-KopOneUnit"" : ""cent"",
        ""-KopTwoUnit"" : ""cents"",
        ""-KopFiveUnit"" : ""cents"",
        ""-KopSex"" : ""M""
      }
     ],
    ""PER10"" : [
      {
        ""-CurrID"" : ""556"",
        ""-CurrName"" : ""Вiдсотки з десятими частинами"",
        ""-language"" : ""RUS"",
        ""-RubOneUnit"" : ""целая,"",
        ""-RubTwoUnit"" : ""целых,"",
        ""-RubFiveUnit"" : ""целых,"",
        ""-RubSex"" : ""F"",
        ""-KopOneUnit"" : ""десятая процента"",
        ""-KopTwoUnit"" : ""десятых процента"",
        ""-KopFiveUnit"" : ""десятых процента"",
        ""-KopSex"" : ""F""
      },
      {
        ""-CurrID"" : ""556"",
        ""-CurrName"" : ""Вiдсотки з десятими частинами"",
        ""-language"" : ""UKR"",
        ""-RubOneUnit"" : ""ціла,"",
        ""-RubTwoUnit"" : ""цілих,"",
        ""-RubFiveUnit"" : ""цілих,"",
        ""-RubSex"" : ""F"",
        ""-KopOneUnit"" : ""десята відсотка"",
        ""-KopTwoUnit"" : ""десятих відсотка"",
        ""-KopFiveUnit"" : ""десятих відсотка"",
        ""-KopSex"" : ""F""
      }
    ],
    ""PER100"" : [
      {
        ""-CurrID"" : ""557"",
        ""-CurrName"" : ""Вiдсотки з сотими частинами"",
        ""-language"" : ""RUS"",
        ""-RubOneUnit"" : ""целая,"",
        ""-RubTwoUnit"" : ""целых,"",
        ""-RubFiveUnit"" : ""целых,"",
        ""-RubSex"" : ""F"",
        ""-KopOneUnit"" : ""сотая процента"",
        ""-KopTwoUnit"" : ""сотых процента"",
        ""-KopFiveUnit"" : ""сотых процента"",
        ""-KopSex"" : ""F""
      },
      {
        ""-CurrID"" : ""557"",
        ""-CurrName"" : ""Вiдсотки з сотими частинами"",
        ""-language"" : ""UKR"",
        ""-RubOneUnit"" : ""ціла,"",
        ""-RubTwoUnit"" : ""цілих,"",
        ""-RubFiveUnit"" : ""цілих,"",
        ""-RubSex"" : ""F"",
        ""-KopOneUnit"" : ""сота відсотка"",
        ""-KopTwoUnit"" : ""сотих відсотка"",
        ""-KopFiveUnit"" : ""сотих відсотка"",
        ""-KopSex"" : ""F""
      }
    ],
    ""PER1000"" : [
      {
        ""-CurrID"" : ""558"",
        ""-CurrName"" : ""Вiдсотки з тисячними частинами"",
        ""-language"" : ""RUS"",
        ""-RubOneUnit"" : ""целая,"",
        ""-RubTwoUnit"" : ""целых,"",
        ""-RubFiveUnit"" : ""целых,"",
        ""-RubSex"" : ""F"",
        ""-KopOneUnit"" : ""тысячная процента"",
        ""-KopTwoUnit"" : ""тысячных процента"",
        ""-KopFiveUnit"" : ""тысячных процента"",
        ""-KopSex"" : ""F""
      },
      {
        ""-CurrID"" : ""558"",
        ""-CurrName"" : ""Вiдсотки з тисячними частинами"",
        ""-language"" : ""UKR"",
        ""-RubOneUnit"" : ""ціла,"",
        ""-RubTwoUnit"" : ""цілих,"",
        ""-RubFiveUnit"" : ""цілих,"",
        ""-RubSex"" : ""F"",
        ""-KopOneUnit"" : ""тисячна відсотка"",
        ""-KopTwoUnit"" : ""тисячних відсотка"",
        ""-KopFiveUnit"" : ""тисячних відсотка"",
        ""-KopSex"" : ""F""
      }
    ],
    ""PER10000"" : [
      {
        ""-CurrID"" : ""559"",
        ""-CurrName"" : ""Вiдсотки з десяти тисячними частинами"",
        ""-language"" : ""RUS"",
        ""-RubOneUnit"" : ""целая,"",
        ""-RubTwoUnit"" : ""целых,"",
        ""-RubFiveUnit"" : ""целых,"",
        ""-RubSex"" : ""F"",
        ""-KopOneUnit"" : ""десятитысячная процента"",
        ""-KopTwoUnit"" : ""десятитысячные процента"",
        ""-KopFiveUnit"" : ""десятитысячных процента"",
        ""-KopSex"" : ""F""
      },
      {
        ""-CurrID"" : ""559"",
        ""-CurrName"" : ""Вiдсотки з десяти тисячними частинами"",
        ""-language"" : ""UKR"",
        ""-RubOneUnit"" : ""ціла,"",
        ""-RubTwoUnit"" : ""цілих,"",
        ""-RubFiveUnit"" : ""цілих,"",
        ""-RubSex"" : ""F"",
        ""-KopOneUnit"" : ""десятитисячна відсотка"",
        ""-KopTwoUnit"" : ""десятитисячних відсотка"",
        ""-KopFiveUnit"" : ""десятитисячних відсотка"",
        ""-KopSex"" : ""M""
      }
    ]
  }
}";

    const int NUM0 = 0;
    const int NUM1 = 1;
    const int NUM2 = 2;
    const int NUM3 = 3;
    const int NUM4 = 4;
    const int NUM5 = 5;
    const int NUM6 = 6;
    const int NUM7 = 7;
    const int NUM8 = 8;
    const int NUM9 = 9;
    const int NUM10 = 10;
    const int NUM11 = 11;
    const int NUM12 = 12;
    const int NUM100 = 100;
    const int NUM1000 = 1000;
    const int NUM10000 = 10000;
    const int INDEX_0 = 0;
    const int INDEX_1 = 1;
    const int INDEX_2 = 2;
    const int INDEX_3 = 3;

    private string currency;
    private string language;
    private string pennies;
    private Dictionary<string, string[]> messages = new Dictionary<string, string[]>();
    private string rubOneUnit;
    private string rubTwoUnit;
    private string rubFiveUnit;
    private string kopOneUnit;
    private string kopTwoUnit;
    private string kopFiveUnit;
    private string rubSex;
    private string kopSex;

    public MoneyToStr(string currency, string language, string pennies) {
        if (currency == null) {
            throw new ArgumentNullException("Currency code is null");
        }
        if (language == null) {
            throw new ArgumentNullException("Language is null");
        }
        if (pennies == null) {
            throw new ArgumentNullException("Pennies is null");
        }
        this.currency = currency;
        this.language = language;
        this.pennies = pennies;
        string languageElement = language;
        JavaScriptSerializer serializer = new JavaScriptSerializer(); 
        dynamic currencyList = serializer.Deserialize<object>(json);

        var items = currencyList["CurrencyList"][languageElement]["item"];
        foreach (var languageItem in items) {
            if (languageItem["-text"] != null) {
                messages[languageItem["-value"]] = languageItem["-text"].Split(',');
            }
        }
        var currencyItem = currencyList["CurrencyList"][this.currency];
        var theISOElement = (dynamic) null;
        foreach (var item in currencyItem) {
            if (item["-language"] == this.language) {
                theISOElement = item;
                break;
            }
        }
        if (theISOElement == null) {
            throw new ArgumentNullException("Currency not found " + this.currency);
        }
        this.rubOneUnit = theISOElement["-RubOneUnit"];
        this.rubTwoUnit = theISOElement["-RubTwoUnit"];
        this.rubFiveUnit = theISOElement["-RubFiveUnit"];
        this.kopOneUnit = theISOElement["-KopOneUnit"];
        this.kopTwoUnit = theISOElement["-KopTwoUnit"];
        this.kopFiveUnit = theISOElement["-KopFiveUnit"];
        this.rubSex = theISOElement["-RubSex"];
        this.kopSex = theISOElement["-KopSex"];
    }

    /**
     * Converts percent to string.
     * @param amount the amount of percent
     * @param lang the language (RUS, UKR)
     * @return the string of percent
     */
    public static string percentToStr(double amount, string lang) {
        if (lang == null) {
            throw new ArgumentNullException("Language is null");
        }
        long intPart = (long) amount;
        long fractPart = 0;
        string result = "";
        if (amount == (long) amount) {
            result = new MoneyToStr("PER10", lang, "TEXT").convert(intPart, fractPart);
        } else if (Math.Round(amount * NUM10, 4) == (long) (amount * NUM10)) {
            fractPart = (long) Math.Round((amount - intPart) * NUM10);
            result = new MoneyToStr("PER10", lang, "TEXT").convert(intPart, fractPart);
        } else if (Math.Round(amount * NUM100, 4) == (long) (amount * NUM100)) {
            fractPart = (long) Math.Round((amount - intPart) * NUM100);
            result = new MoneyToStr("PER100", lang, "TEXT").convert(intPart, fractPart);
        } else if (Math.Round(amount * NUM1000, 4) == (long) (amount * NUM1000)) {
            fractPart = (long) Math.Round((amount - intPart) * NUM1000);
            result = new MoneyToStr("PER1000", lang, "TEXT").convert(intPart, fractPart);
        } else {
            fractPart = (long) Math.Round((amount - intPart) * NUM10000);
            result = new MoneyToStr("PER10000", lang, "TEXT").convert(intPart, fractPart);
        }
        return result;
    }

    /**
     * Converts double value to the text description.
     *
     * @param theMoney
     *            the amount of money in format major.minor
     * @return the string description of money value
     */
    public string convertValue(double theMoney) {
        long intPart = (long) theMoney;
        long fractPart = (long) Math.Round((theMoney - intPart) * NUM100);
        if (currency == "PER1000") {
            fractPart = (long) Math.Round((theMoney - intPart) * NUM1000);
        }
        return convert(intPart, fractPart);
    }

    /**
     * Converts number to currency. Usage: MoneyToStr moneyToStr = new MoneyToStr("UAH"); String result =
     * moneyToStr.convert(123D); Expected: result = сто двадцять три гривні 00 копійок
     *
     * @param theMoney
     *            the amount of money major currency
     * @param theKopeiki
     *            the amount of money minor currency
     * @return the string description of money value
     */
    public string convert(long theMoney, long theKopeiki) {
        StringBuilder money2str = new StringBuilder();
        long triadNum = 0;
        long theTriad;

        long intPart = theMoney;
        if (intPart == 0) {
            money2str.Append(messages["0"][0] + " ");
        }
        do {
            theTriad = intPart % NUM1000;
            money2str.Insert(0, triad2Word(theTriad, triadNum, rubSex));
            if (triadNum == 0) {
                long range10 = (theTriad % NUM100) / NUM10;
                long range = theTriad % NUM10;
                if (range10 == NUM1) {
                    money2str.Append(rubFiveUnit);
                } else {
                    switch (range) {
                    case NUM1:
                        money2str.Append(rubOneUnit);
                        break;
                    case NUM2: case NUM3: case NUM4:
                        money2str.Append(rubTwoUnit);
                        break;
                    default:
                        money2str.Append(rubFiveUnit);
                        break;
                    }
                }
            }
            intPart = intPart / NUM1000;
            triadNum += 1;
        } while (intPart > 0);

        if (pennies == "TEXT") {
            money2str.Append(language == "ENG" ? " and " : " ").Append(theKopeiki == 0 ? (messages["0"][0] + " ") : triad2Word(theKopeiki, 0, kopSex));
        } else {
            money2str.Append(" " + (theKopeiki < 10 ? "0" + Convert.ToString(theKopeiki) : Convert.ToString(theKopeiki)) + " ");
        }
        if (theKopeiki == NUM11 || theKopeiki == NUM12) {
            money2str.Append(kopFiveUnit);
        } else {
            switch (theKopeiki % NUM10) {
            case NUM1:
                money2str.Append(kopOneUnit);
                break;
            case NUM2: case NUM3: case NUM4:
                money2str.Append(kopTwoUnit);
                break;
            default:
                money2str.Append(kopFiveUnit);
                break;
            }
        }
        return money2str.ToString().Trim();
    }

    private string triad2Word(long triad, long triadNum, string sex) {
        StringBuilder triadWord = new StringBuilder();

        if (triad == 0) {
            return "";
        }

        long range = check1(triad, triadWord);
        if (language == "ENG" && triadWord.Length > 0 && triad % NUM10 == 0) {
            triadWord = new StringBuilder(triadWord.ToString(0, triadWord.Length - 1));
            triadWord.Append(" ");
        }

        long range10 = range;
        range = triad % NUM10;
        check2(triadNum, sex, triadWord, triad, range10);
        switch (triadNum) {
        case NUM0:
            break;
        case NUM1: case NUM2: case NUM3: case NUM4:
            if (range10 == NUM1) {
                triadWord.Append(messages["1000_10"][triadNum - 1] + " ");
            } else {
                switch (range) {
                case NUM1:
                    triadWord.Append(messages["1000_1"][triadNum - 1] + " ");
                    break;
                case NUM2: case NUM3: case NUM4:
                    triadWord.Append(messages["1000_234"][triadNum - 1] + " ");
                    break;
                default:
                    triadWord.Append(messages["1000_5"][triadNum - 1] + " ");
                    break;
                }
            }
            break;
        default:
            triadWord.Append("??? ");
            break;
        }
        return triadWord.ToString();
    }

    /**
     * @param triadNum the triad num
     * @param sex the sex
     * @param triadWord the triad word
     * @param triad the triad
     * @param range10 the range 10
     */
    private void check2(long triadNum, string sex, StringBuilder triadWord, long triad, long range10) {
        long range = triad % NUM10;
        if (range10 == 1) {
            triadWord.Append(messages["10_19"][range] + " ");
        } else {
            switch (range) {
            case NUM1:
                if (triadNum == NUM1) {
                    triadWord.Append(messages["1"][INDEX_0] + " ");
                } else if (triadNum == NUM2 || triadNum == NUM3 || triadNum == NUM4) {
                    triadWord.Append(messages["1"][INDEX_1] + " ");
                } else if ("M" == sex) {
                    triadWord.Append(messages["1"][INDEX_2] + " ");
                } else if ("F" == sex) {
                    triadWord.Append(messages["1"][INDEX_3] + " ");
                }
                break;
            case NUM2:
                if (triadNum == NUM1) {
                    triadWord.Append(messages["2"][INDEX_0] + " ");
                } else if (triadNum == NUM2 || triadNum == NUM3 || triadNum == NUM4) {
                    triadWord.Append(messages["2"][INDEX_1] + " ");
                } else if ("M" == sex) {
                    triadWord.Append(messages["2"][INDEX_2] + " ");
                } else if ("F" == sex) {
                    triadWord.Append(messages["2"][INDEX_3] + " ");
                }
                break;
            case NUM3: case NUM4: case NUM5: case NUM6: case NUM7: case NUM8: case NUM9:
                triadWord.Append(concat(new string[] {"", "", ""}, messages["3_9"])[range] + " ");
                break;
            }
        }
    }

    /**
     * @param triad the triad
     * @param triadWord the triad word
     * @return the range
     */
    private long check1(long triad, StringBuilder triadWord) {
        long range = triad / NUM100;
        triadWord.Append(concat(new string[] {""}, messages["100_900"])[range]);

        range = (triad % NUM100) / NUM10;
        triadWord.Append(concat(new string[] {"", ""}, messages["20_90"])[range]);
        return range;
    }

    private string[] concat(string[] first, string[] second) {
        string[] result = new string[first.Length + second.Length];
        first.CopyTo(result, 0);
        second.CopyTo(result, first.Length);
        return result;
    }
}
/*
 * $Id$
 *
 * Copyright 2013 Valentyn Kolesnikov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

library moneytostr;

/**
 * Converts numbers to symbols.
 *
 * @author Valentyn V Kolesnikov
 * @version $Revision$ $Date$
 */
class StringBuilder {
    List _buffer;

    StringBuilder() {
        this._buffer = new List();
    }

    StringBuilder append(text) {
        this._buffer.add(text);
        return this;
    }
        
    StringBuilder insert(index, text) { 
        this._buffer.insert(index, text);
        return this;
    }

    int length() {
        return this.toString().length;
    }

    StringBuilder deleteCharAt(index) {
        var str = this.toString();
        this._buffer = new List();
        append(str.substring(0, index)); 
        return this;
    }

    String toString() {
        return this._buffer.join("");
    }
}

class Currency {
   final _value;
   const Currency._internal(this._value);
   toString() => '$_value';

   static const RUR = const Currency._internal('RUR');
   static const UAH = const Currency._internal('UAH');
   static const USD = const Currency._internal('USD');
   static const PER10 = const Currency._internal('PER10');
   static const PER100 = const Currency._internal('PER100');
   static const PER1000 = const Currency._internal('PER1000');
   static const PER10000 = const Currency._internal('PER10000');
}

class Language {
   final _value;
   const Language._internal(this._value);
   toString() => '$_value';

   static const RUS = const Language._internal('RUS');
   static const UKR = const Language._internal('UKR');
   static const ENG = const Language._internal('ENG');
}

class Pennies {
   final _value;
   const Pennies._internal(this._value);
   toString() => '$_value';

   static const NUMBER = const Pennies._internal('NUMBER');
   static const TEXT = const Pennies._internal('TEXT');
}

class MoneyToStr {
static Map json =
{
  "CurrencyList": {
    "language": { "-value": "UKR" },
    "UKR": {
      "item": [
        {
          "-value": "0",
          "-text": "нуль"
        },
        {
          "-value": "1000_10",
          "-text": "тисяч,мільйонів,мільярдів,трильйонів"
        },
        {
          "-value": "1000_1",
          "-text": "тисяча,мільйон,мільярд,трильйон"
        },
        {
          "-value": "1000_234",
          "-text": "тисячі,мільйона,мільярда,трильйона"
        },
        {
          "-value": "1000_5",
          "-text": "тисяч,мільйонів,мільярдів,трильйонів"
        },
        {
          "-value": "10_19",
          "-text": "десять,одинадцять,дванадцять,тринадцять,чотирнадцять,п’ятнадцять,шiстнадцять,сiмнадцять,вiсiмнадцять,дев'ятнадцять"
        },
        {
          "-value": "1",
          "-text": "одна,один,один,одна"
        },
        {
          "-value": "2",
          "-text": "дві,два,два,дві"
        },
        {
          "-value": "3_9",
          "-text": "три,чотири,п’ять,шість,сім,вісім,дев’ять"
        },
        {
          "-value": "100_900",
          "-text": "сто ,двісті ,триста ,чотириста ,п’ятсот ,шістсот ,сімсот ,вісімсот ,дев’ятсот "
        },
        {
          "-value": "20_90",
          "-text": "двадцять ,тридцять ,сорок ,п’ятдесят ,шістдесят ,сімдесят ,вісімдесят ,дев’яносто "
        }
      ]
    },
    "RUS": {
      "item": [
        {
          "-value": "0",
          "-text": "ноль"
        },
        {
          "-value": "1000_10",
          "-text": "тысяч,миллионов,миллиардов,триллионов"
        },
        {
          "-value": "1000_1",
          "-text": "тысяча,миллион,миллиард,триллион"
        },
        {
          "-value": "1000_234",
          "-text": "тысячи,миллиона,миллиарда,триллиона"
        },
        {
          "-value": "1000_5",
          "-text": "тысяч,миллионов,миллиардов,триллионов"
        },
        {
          "-value": "10_19",
          "-text": "десять,одиннадцать,двенадцать,тринадцать,четырнадцать,пятнадцать,шестнадцать,семнадцать,восемнадцать,девятнадцать"
        },
        {
          "-value": "1",
          "-text": "одна,один,один,одна"
        },
        {
          "-value": "2",
          "-text": "две,два,два,две"
        },
        {
          "-value": "3_9",
          "-text": "три,четыре,пять,шесть,семь,восемь,девять"
        },
        {
          "-value": "100_900",
          "-text": "сто ,двести ,триста ,четыреста ,пятьсот ,шестьсот ,семьсот ,восемьсот ,девятьсот "
        },
        {
          "-value": "20_90",
          "-text": "двадцать ,тридцать ,сорок ,пятьдесят ,шестьдесят ,семьдесят ,восемьдесят ,девяносто "
        }
      ]
    },
    "ENG": {
      "item": [
        {
          "-value": "0",
          "-text": "zero"
        },
        {
          "-value": "1000_10",
          "-text": "thousand,million,billion,trillion"
        },
        {
          "-value": "1000_1",
          "-text": "thousand,million,billion,trillion"
        },
        {
          "-value": "1000_234",
          "-text": "thousand,million,billion,trillion"
        },
        {
          "-value": "1000_5",
          "-text": "thousand,million,billion,trillion"
        },
        {
          "-value": "10_19",
          "-text": "ten,eleven,twelve,thirteen,fourteen,fifteen,sixteen,seventeen,eighteen,nineteen"
        },
        {
          "-value": "1",
          "-text": "one,one,one,one"
        },
        {
          "-value": "2",
          "-text": "two,two,two,two"
        },
        {
          "-value": "3_9",
          "-text": "three,four,five,six,seven,eight,nine"
        },
        {
          "-value": "100_900",
          "-text": "one hundred ,two hundred ,three hundred ,four hundred ,five hundred ,six hundred ,seven hundred ,eight hundred ,nine hundred "
        },
        {
          "-value": "20_90",
          "-text": "twenty-,thirty-,forty-,fifty-,sixty-,seventy-,eighty-,ninety-"
        }
      ]
    },
    "RUR": [
      {
        "-CurrID": "810",
        "-CurrName": "Российские рубли",
        "-language": "RUS",
        "-RubOneUnit": "рубль",
        "-RubTwoUnit": "рубля",
        "-RubFiveUnit": "рублей",
        "-RubSex": "M",
        "-RubShortUnit": "руб.",
        "-KopOneUnit": "копейка",
        "-KopTwoUnit": "копейки",
        "-KopFiveUnit": "копеек",
        "-KopSex": "F"
      },
      {
        "-CurrID": "810",
        "-CurrName": "Российские рубли",
        "-language": "UKR",
        "-RubOneUnit": "рубль",
        "-RubTwoUnit": "рублі",
        "-RubFiveUnit": "рублів",
        "-RubSex": "M",
        "-RubShortUnit": "руб.",
        "-KopOneUnit": "копійка",
        "-KopTwoUnit": "копійки",
        "-KopFiveUnit": "копійок",
        "-KopSex": "F"
      },
      {
        "-CurrID": "810",
        "-CurrName": "Российские рубли",
        "-language": "ENG",
        "-RubOneUnit": "ruble",
        "-RubTwoUnit": "rubles",
        "-RubFiveUnit": "rubles",
        "-RubSex": "M",
        "-RubShortUnit": "RUR.",
        "-KopOneUnit": "kopeck",
        "-KopTwoUnit": "kopecks",
        "-KopFiveUnit": "kopecks",
        "-KopSex": "M"
      }
    ],
    "UAH": [
      {
        "-CurrID": "980",
        "-CurrName": "Украинскі гривні",
        "-language": "RUS",
        "-RubOneUnit": "гривня",
        "-RubTwoUnit": "гривни",
        "-RubFiveUnit": "гривень",
        "-RubSex": "F",
        "-RubShortUnit": "грн.",
        "-KopOneUnit": "копейка",
        "-KopTwoUnit": "копейки",
        "-KopFiveUnit": "копеек",
        "-KopSex": "F"
      },
      {
        "-CurrID": "980",
        "-CurrName": "Украинскі гривні",
        "-language": "UKR",
        "-RubOneUnit": "гривня",
        "-RubTwoUnit": "гривні",
        "-RubFiveUnit": "гривень",
        "-RubSex": "F",
        "-RubShortUnit": "грн.",
        "-KopOneUnit": "копійка",
        "-KopTwoUnit": "копійки",
        "-KopFiveUnit": "копійок",
        "-KopSex": "F"
      },
      {
        "-CurrID": "980",
        "-CurrName": "Украинскі гривні",
        "-language": "ENG",
        "-RubOneUnit": "hryvnia",
        "-RubTwoUnit": "hryvnias",
        "-RubFiveUnit": "hryvnias",
        "-RubSex": "M",
        "-RubShortUnit": "UAH.",
        "-KopOneUnit": "kopeck",
        "-KopTwoUnit": "kopecks",
        "-KopFiveUnit": "kopecks",
        "-KopSex": "M"
      }
    ],
    "USD": [
      {
        "-CurrID": "840",
        "-CurrName": "Долари США",
        "-language": "RUS",
        "-RubOneUnit": "доллар",
        "-RubTwoUnit": "доллара",
        "-RubFiveUnit": "долларов",
        "-RubSex": "M",
        "-RubShortUnit": "дол.",
        "-KopOneUnit": "цент",
        "-KopTwoUnit": "цена",
        "-KopFiveUnit": "центов",
        "-KopSex": "M"
      },
      {
        "-CurrID": "840",
        "-CurrName": "Долари США",
        "-language": "UKR",
        "-RubOneUnit": "долар",
        "-RubTwoUnit": "долара",
        "-RubFiveUnit": "доларів",
        "-RubSex": "M",
        "-RubShortUnit": "дол.",
        "-KopOneUnit": "цент",
        "-KopTwoUnit": "цена",
        "-KopFiveUnit": "центів",
        "-KopSex": "M"
      },
      {
        "-CurrID": "840",
        "-CurrName": "Долари США",
        "-language": "ENG",
        "-RubOneUnit": "dollar",
        "-RubTwoUnit": "dollars",
        "-RubFiveUnit": "dollars",
        "-RubSex": "M",
        "-RubShortUnit": "USD.",
        "-KopOneUnit": "cent",
        "-KopTwoUnit": "cents",
        "-KopFiveUnit": "cents",
        "-KopSex": "M"
      }
    ],
    "PER10": [
      {
        "-CurrID": "556",
        "-CurrName": "Вiдсотки з десятими частинами",
        "-language": "RUS",
        "-RubOneUnit": "целая,",
        "-RubTwoUnit": "целых,",
        "-RubFiveUnit": "целых,",
        "-RubSex": "F",
        "-KopOneUnit": "десятая процента",
        "-KopTwoUnit": "десятых процента",
        "-KopFiveUnit": "десятых процента",
        "-KopSex": "F"
      },
      {
        "-CurrID": "556",
        "-CurrName": "Вiдсотки з десятими частинами",
        "-language": "UKR",
        "-RubOneUnit": "ціла,",
        "-RubTwoUnit": "цілих,",
        "-RubFiveUnit": "цілих,",
        "-RubSex": "F",
        "-KopOneUnit": "десята відсотка",
        "-KopTwoUnit": "десятих відсотка",
        "-KopFiveUnit": "десятих відсотка",
        "-KopSex": "F"
      },
      {
        "-CurrID": "560",
        "-CurrName": "Вiдсотки з десятими частинами",
        "-language": "ENG",
        "-RubOneUnit": ",",
        "-RubTwoUnit": "integers,",
        "-RubFiveUnit": "integers,",
        "-RubSex": "F",
        "-KopOneUnit": "tenth of one percent",
        "-KopTwoUnit": "tenth of one percent",
        "-KopFiveUnit": "tenth of one percent",
        "-KopSex": "F"
      }
    ],
    "PER100": [
      {
        "-CurrID": "557",
        "-CurrName": "Вiдсотки з сотими частинами",
        "-language": "RUS",
        "-RubOneUnit": "целая,",
        "-RubTwoUnit": "целых,",
        "-RubFiveUnit": "целых,",
        "-RubSex": "F",
        "-KopOneUnit": "сотая процента",
        "-KopTwoUnit": "сотых процента",
        "-KopFiveUnit": "сотых процента",
        "-KopSex": "F"
      },
      {
        "-CurrID": "557",
        "-CurrName": "Вiдсотки з сотими частинами",
        "-language": "UKR",
        "-RubOneUnit": "ціла,",
        "-RubTwoUnit": "цілих,",
        "-RubFiveUnit": "цілих,",
        "-RubSex": "F",
        "-KopOneUnit": "сота відсотка",
        "-KopTwoUnit": "сотих відсотка",
        "-KopFiveUnit": "сотих відсотка",
        "-KopSex": "F"
      },
      {
        "-CurrID": "561",
        "-CurrName": "Вiдсотки з сотими частинами",
        "-language": "ENG",
        "-RubOneUnit": ",",
        "-RubTwoUnit": "integers,",
        "-RubFiveUnit": "integers,",
        "-RubSex": "F",
        "-KopOneUnit": "hundred percent",
        "-KopTwoUnit": "hundredth of percent",
        "-KopFiveUnit": "hundredth of percent",
        "-KopSex": "F"
      }
    ],
    "PER1000": [
      {
        "-CurrID": "558",
        "-CurrName": "Вiдсотки з тисячними частинами",
        "-language": "RUS",
        "-RubOneUnit": "целая,",
        "-RubTwoUnit": "целых,",
        "-RubFiveUnit": "целых,",
        "-RubSex": "F",
        "-KopOneUnit": "тысячная процента",
        "-KopTwoUnit": "тысячных процента",
        "-KopFiveUnit": "тысячных процента",
        "-KopSex": "F"
      },
      {
        "-CurrID": "558",
        "-CurrName": "Вiдсотки з тисячними частинами",
        "-language": "UKR",
        "-RubOneUnit": "ціла,",
        "-RubTwoUnit": "цілих,",
        "-RubFiveUnit": "цілих,",
        "-RubSex": "F",
        "-KopOneUnit": "тисячна відсотка",
        "-KopTwoUnit": "тисячних відсотка",
        "-KopFiveUnit": "тисячних відсотка",
        "-KopSex": "F"
      },
      {
        "-CurrID": "562",
        "-CurrName": "Вiдсотки з тисячними частинами",
        "-language": "ENG",
        "-RubOneUnit": ",",
        "-RubTwoUnit": "integers,",
        "-RubFiveUnit": "integers,",
        "-RubSex": "F",
        "-KopOneUnit": "thousandth of percent",
        "-KopTwoUnit": "thousandths of percent",
        "-KopFiveUnit": "thousandths of percent",
        "-KopSex": "F"
      }
    ],
    "PER10000": [
      {
        "-CurrID": "559",
        "-CurrName": "Вiдсотки з десяти тисячними частинами",
        "-language": "RUS",
        "-RubOneUnit": "целая,",
        "-RubTwoUnit": "целых,",
        "-RubFiveUnit": "целых,",
        "-RubSex": "F",
        "-KopOneUnit": "десятитысячная процента",
        "-KopTwoUnit": "десятитысячные процента",
        "-KopFiveUnit": "десятитысячных процента",
        "-KopSex": "F"
      },
      {
        "-CurrID": "559",
        "-CurrName": "Вiдсотки з десяти тисячними частинами",
        "-language": "UKR",
        "-RubOneUnit": "ціла,",
        "-RubTwoUnit": "цілих,",
        "-RubFiveUnit": "цілих,",
        "-RubSex": "F",
        "-KopOneUnit": "десятитисячна відсотка",
        "-KopTwoUnit": "десятитисячних відсотка",
        "-KopFiveUnit": "десятитисячних відсотка",
        "-KopSex": "M"
      },
      {
        "-CurrID": "563",
        "-CurrName": "Вiдсотки з десяти тисячними частинами",
        "-language": "ENG",
        "-RubOneUnit": ",",
        "-RubTwoUnit": "integers,",
        "-RubFiveUnit": "integers,",
        "-RubSex": "F",
        "-KopOneUnit": "ten percent",
        "-KopTwoUnit": "ten-percent",
        "-KopFiveUnit": "ten-percent",
        "-KopSex": "F"
      }
    ]
  }
};

    static const int NUM0 = 0;
    static const int NUM1 = 1;
    static const int NUM2 = 2;
    static const int NUM3 = 3;
    static const int NUM4 = 4;
    static const int NUM5 = 5;
    static const int NUM6 = 6;
    static const int NUM7 = 7;
    static const int NUM8 = 8;
    static const int NUM9 = 9;
    static const int NUM10 = 10;
    static const int NUM11 = 11;
    static const int NUM12 = 12;
    static const int NUM100 = 100;
    static const int NUM1000 = 1000;
    static const int NUM10000 = 10000;
    static const int INDEX_0 = 0;
    static const int INDEX_1 = 1;
    static const int INDEX_2 = 2;
    static const int INDEX_3 = 3;

    Currency currency;
    Language language;
    Pennies pennies;
    Map messages = new Map();
    String rubOneUnit;
    String rubTwoUnit;
    String rubFiveUnit;
    String kopOneUnit;
    String kopTwoUnit;
    String kopFiveUnit;
    String rubSex;
    String kopSex;

    MoneyToStr(Currency currency, Language language, Pennies pennies) {
        if (currency == null) {
            throw new Exception("Currency code is null");
        }
        if (language == null) {
            throw new Exception("Language is null");
        }
        if (pennies == null) {
            throw new Exception("Pennies is null");
        }
        this.currency = currency;
        this.language = language;
        this.pennies = pennies;
        Language languageElement = language;
        var currencyList = json;
        var items = currencyList["CurrencyList"][languageElement.toString()]["item"];
        for (var languageItem in items) {
            if (languageItem["-text"] != null) {
                messages[languageItem["-value"]] = languageItem["-text"].split(',');
            }
        }
        var currencyItem = currencyList["CurrencyList"][this.currency.toString()];
        var theISOElement = null;
        for (var item in currencyItem) {
            if (item["-language"] == this.language.toString()) {
                theISOElement = item;
                break;
            }
        }
        if (theISOElement == null) {
            throw new Exception("Currency not found " + this.currency.toString());
        }
        this.rubOneUnit = theISOElement["-RubOneUnit"];
        this.rubTwoUnit = theISOElement["-RubTwoUnit"];
        this.rubFiveUnit = theISOElement["-RubFiveUnit"];
        this.kopOneUnit = theISOElement["-KopOneUnit"];
        this.kopTwoUnit = theISOElement["-KopTwoUnit"];
        this.kopFiveUnit = theISOElement["-KopFiveUnit"];
        this.rubSex = theISOElement["-RubSex"];
        this.kopSex = theISOElement["-KopSex"];
    }

    /**
     * Converts percent to string.
     * @param amount the amount of percent
     * @param lang the language (RUS, UKR)
     * @return the string of percent
     */
    static String percentToStr(double amount, Language lang) {
        if (lang == null) {
            throw new Exception("Language is null");
        }
        int intPart = amount.toInt();
        int fractPart = 0;
        String result = "";
        if (amount == amount.toInt()) {
            result = new MoneyToStr(Currency.PER10, lang, Pennies.TEXT).convert(intPart, fractPart);
        } else if ((amount * NUM10).toStringAsFixed(4) == (amount * NUM10).toInt().toStringAsFixed(4)) {
            fractPart = ((amount - intPart) * NUM10).round().toInt();
            result = new MoneyToStr(Currency.PER10, lang, Pennies.TEXT).convert(intPart, fractPart);
        } else if ((amount * NUM100).toStringAsFixed(4) == (amount * NUM100).toInt().toStringAsFixed(4)) {
            fractPart = ((amount - intPart) * NUM100).round().toInt();
            result = new MoneyToStr(Currency.PER100, lang, Pennies.TEXT).convert(intPart, fractPart);
        } else if ((amount * NUM1000).toStringAsFixed(4) == (amount * NUM1000).toInt().toStringAsFixed(4)) {
            fractPart = ((amount - intPart) * NUM1000).round().toInt();
            result = new MoneyToStr(Currency.PER1000, lang, Pennies.TEXT).convert(intPart, fractPart);
        } else {
            fractPart = ((amount - intPart) * NUM10000).round().toInt();
            result = new MoneyToStr(Currency.PER10000, lang, Pennies.TEXT).convert(intPart, fractPart);
        }
        return result;
    }

    /**
     * Converts double value to the text description.
     *
     * @param theMoney
     *            the amount of money in format major.minor
     * @return the string description of money value
     */
    String convertValue(double theMoney) {
        int intPart = theMoney.toInt();
        int fractPart = ((theMoney - intPart) * NUM100).round().toInt();
        if (currency == Currency.PER1000) {
            fractPart = ((theMoney - intPart) * NUM1000).round().toInt();
        }
        return convert(intPart, fractPart);
    }

    /**
     * Converts number to currency. Usage: MoneyToStr moneyToStr = new MoneyToStr("UAH", "UKR", "NUMBER"); String result =
     * moneyToStr.convertValue(123D); Expected: result = сто двадцять три гривні 00 копійок
     *
     * @param theMoney
     *            the amount of money major currency
     * @param theKopeiki
     *            the amount of money minor currency
     * @return the string description of money value
     */
    String convert(int theMoney, int theKopeiki) {
        StringBuilder money2str = new StringBuilder();
        int triadNum = 0;
        int theTriad;

        int intPart = theMoney.toInt();
        if (intPart == 0) {
            money2str.append(messages["0"][0] + " ");
        }
        do {
            theTriad = intPart % NUM1000;
            money2str.insert(0, triad2Word(theTriad, triadNum, rubSex));
            if (triadNum == 0) {
                int range10 = ((theTriad % NUM100) / NUM10).toInt();
                int range = (theTriad % NUM10).toInt();
                if (range10 == NUM1) {
                    money2str.append(rubFiveUnit);
                } else {
                    switch (range) {
                    case NUM1:
                        money2str.append(rubOneUnit);
                        break;
                    case NUM2: case NUM3: case NUM4:
                        money2str.append(rubTwoUnit);
                        break;
                    default:
                        money2str.append(rubFiveUnit);
                        break;
                    }
                }
            }
            intPart = (intPart / NUM1000).toInt();
            triadNum += 1;
        } while (intPart > 0);

        if (pennies == Pennies.TEXT) {
            money2str.append(language == Language.ENG ? " and " : " ").append(theKopeiki == 0 ? (messages["0"][0] + " ") : triad2Word(theKopeiki, 0, kopSex));
        } else {
            money2str.append(" " + (theKopeiki < 10 ? "0" + theKopeiki.toString() : theKopeiki.toString()) + " ");
        }
        if (theKopeiki == NUM11 || theKopeiki == NUM12) {
            money2str.append(kopFiveUnit);
        } else {
            switch (theKopeiki % NUM10) {
            case NUM1:
                money2str.append(kopOneUnit);
                break;
            case NUM2: case NUM3: case NUM4:
                money2str.append(kopTwoUnit);
                break;
            default:
                money2str.append(kopFiveUnit);
                break;
            }
        }
        return money2str.toString().trim();
    }

    String triad2Word(int triad, int triadNum, String sex) {
        StringBuilder triadWord = new StringBuilder();

        if (triad == 0) {
            return "";
        }

        int range = check1(triad, triadWord);
        if (language == Language.ENG && triadWord.length() > 0 && triad % NUM10 == 0) {
            triadWord.deleteCharAt(triadWord.length() - 1);
            triadWord.append(" ");
        }

        int range10 = range;
        range = triad % NUM10;
        check2(triadNum, sex, triadWord, triad, range10);
        switch (triadNum) {
        case NUM0:
            break;
        case NUM1: case NUM2: case NUM3: case NUM4:
            if (range10 == NUM1) {
                triadWord.append(messages["1000_10"][triadNum - 1] + " ");
            } else {
                switch (range) {
                case NUM1:
                    triadWord.append(messages["1000_1"][triadNum - 1] + " ");
                    break;
                case NUM2: case NUM3: case NUM4:
                    triadWord.append(messages["1000_234"][triadNum - 1] + " ");
                    break;
                default:
                    triadWord.append(messages["1000_5"][triadNum - 1] + " ");
                    break;
                }
            }
            break;
        default:
            triadWord.append("??? ");
            break;
        }
        return triadWord.toString();
    }

    /**
     * @param triadNum the triad num
     * @param sex the sex
     * @param triadWord the triad word
     * @param triad the triad
     * @param range10 the range 10
     */
    void check2(int triadNum, String sex, StringBuilder triadWord, int triad, int range10) {
        int range = triad % NUM10;
        if (range10 == 1) {
            triadWord.append(messages["10_19"][range] + " ");
        } else {
            switch (range) {
            case NUM1:
                if (triadNum == NUM1) {
                    triadWord.append(messages["1"][INDEX_0] + " ");
                } else if (triadNum == NUM2 || triadNum == NUM3 || triadNum == NUM4) {
                    triadWord.append(messages["1"][INDEX_1] + " ");
                } else if ("M" == sex) {
                    triadWord.append(messages["1"][INDEX_2] + " ");
                } else if ("F" == sex) {
                    triadWord.append(messages["1"][INDEX_3] + " ");
                }
                break;
            case NUM2:
                if (triadNum == NUM1) {
                    triadWord.append(messages["2"][INDEX_0] + " ");
                } else if (triadNum == NUM2 || triadNum == NUM3 || triadNum == NUM4) {
                    triadWord.append(messages["2"][INDEX_1] + " ");
                } else if ("M" == sex) {
                    triadWord.append(messages["2"][INDEX_2] + " ");
                } else if ("F" == sex) {
                    triadWord.append(messages["2"][INDEX_3] + " ");
                }
                break;
            case NUM3: case NUM4: case NUM5: case NUM6: case NUM7: case NUM8: case NUM9:
                triadWord.append(concat(["", "", ""], messages["3_9"])[range] + " ");
                break;
            }
        }
    }

    /**
     * @param triad the triad
     * @param triadWord the triad word
     * @return the range
     */
    int check1(int triad, StringBuilder triadWord) {
        int range = (triad / NUM100).toInt();
        triadWord.append(concat([""], messages["100_900"])[range]);

        range = ((triad % NUM100) / NUM10).toInt();
        triadWord.append(concat(["", ""], messages["20_90"])[range]);
        return range;
    }

    List<String> concat(List<String> first, List<String> second) {
        List<String> result = [];
        result.addAll(first);
        result.addAll(second);
        return result;
    }

}
/*
 * $Id$
 *
 * Copyright 2014 Valentyn Kolesnikov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.moneytostr;

import java.io.IOException;
import java.io.InputStream;

/**
 * Converts numbers to symbols.
 *
 * @author Valentyn Kolesnikov
 * @version $Revision$ $Date$
 */
public class MoneyToStr {
    private static final int INDEX_3 = 3;
    private static final int INDEX_2 = 2;
    private static final int INDEX_1 = 1;
    private static final int INDEX_0 = 0;
    private static org.w3c.dom.Document xmlDoc;
    private static final int NUM0 = 0;
    private static final int NUM1 = 1;
    private static final int NUM2 = 2;
    private static final int NUM3 = 3;
    private static final int NUM4 = 4;
    private static final int NUM5 = 5;
    private static final int NUM6 = 6;
    private static final int NUM7 = 7;
    private static final int NUM8 = 8;
    private static final int NUM9 = 9;
    private static final int NUM10 = 10;
    private static final int NUM11 = 11;
    private static final int NUM14 = 14;
    private static final int NUM100 = 100;
    private static final int NUM1000 = 1000;
    private static final int NUM10000 = 10000;
    private static final String CURRENCY_LIST =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"+
"<CurrencyList>\n"+
" \n"+
" <language value=\"UKR\"/>\n"+
" <UKR>\n"+
" <item value=\"0\" text=\"\u043d\u0443\u043b\u044c\"/>\n"+
" <item value=\"1000_10\" text=\"\u0442\u0438\u0441\u044f\u0447,\u043c\u0456\u043b\u044c\u0439\u043e\u043d\u0456\u0432,\u043c\u0456\u043b\u044c\u044f\u0440\u0434\u0456\u0432,\u0442\u0440\u0438\u043b\u044c\u0439\u043e\u043d\u0456\u0432\"/>\n"+
" <item value=\"1000_1\" text=\"\u0442\u0438\u0441\u044f\u0447\u0430,\u043c\u0456\u043b\u044c\u0439\u043e\u043d,\u043c\u0456\u043b\u044c\u044f\u0440\u0434,\u0442\u0440\u0438\u043b\u044c\u0439\u043e\u043d\"/>\n"+
" <item value=\"1000_234\" text=\"\u0442\u0438\u0441\u044f\u0447\u0456,\u043c\u0456\u043b\u044c\u0439\u043e\u043d\u0430,\u043c\u0456\u043b\u044c\u044f\u0440\u0434\u0430,\u0442\u0440\u0438\u043b\u044c\u0439\u043e\u043d\u0430\"/>\n"+
" <item value=\"1000_5\" text=\"\u0442\u0438\u0441\u044f\u0447,\u043c\u0456\u043b\u044c\u0439\u043e\u043d\u0456\u0432,\u043c\u0456\u043b\u044c\u044f\u0440\u0434\u0456\u0432,\u0442\u0440\u0438\u043b\u044c\u0439\u043e\u043d\u0456\u0432\"/>\n"+
" <item value=\"10_19\" text=\"\u0434\u0435\u0441\u044f\u0442\u044c,\u043e\u0434\u0438\u043d\u0430\u0434\u0446\u044f\u0442\u044c,\u0434\u0432\u0430\u043d\u0430\u0434\u0446\u044f\u0442\u044c,\u0442\u0440\u0438\u043d\u0430\u0434\u0446\u044f\u0442\u044c,\u0447\u043e\u0442\u0438\u0440\u043d\u0430\u0434\u0446\u044f\u0442\u044c,\u043f\u2019\u044f\u0442\u043d\u0430\u0434\u0446\u044f\u0442\u044c,\u0448i\u0441\u0442\u043d\u0430\u0434\u0446\u044f\u0442\u044c,\u0441i\u043c\u043d\u0430\u0434\u0446\u044f\u0442\u044c,\u0432i\u0441i\u043c\u043d\u0430\u0434\u0446\u044f\u0442\u044c,\u0434\u0435\u0432\'\u044f\u0442\u043d\u0430\u0434\u0446\u044f\u0442\u044c\"/>\n"+
" <item value=\"1\" text=\"\u043e\u0434\u043d\u0430,\u043e\u0434\u0438\u043d,\u043e\u0434\u0438\u043d,\u043e\u0434\u043d\u0430\"/>\n"+
" <item value=\"2\" text=\"\u0434\u0432\u0456,\u0434\u0432\u0430,\u0434\u0432\u0430,\u0434\u0432\u0456\"/>\n"+
" <item value=\"3_9\" text=\"\u0442\u0440\u0438,\u0447\u043e\u0442\u0438\u0440\u0438,\u043f\u2019\u044f\u0442\u044c,\u0448\u0456\u0441\u0442\u044c,\u0441\u0456\u043c,\u0432\u0456\u0441\u0456\u043c,\u0434\u0435\u0432\u2019\u044f\u0442\u044c\"/>\n"+
" <item value=\"100_900\" text=\"\u0441\u0442\u043e ,\u0434\u0432\u0456\u0441\u0442\u0456 ,\u0442\u0440\u0438\u0441\u0442\u0430 ,\u0447\u043e\u0442\u0438\u0440\u0438\u0441\u0442\u0430 ,\u043f\u2019\u044f\u0442\u0441\u043e\u0442 ,\u0448\u0456\u0441\u0442\u0441\u043e\u0442 ,\u0441\u0456\u043c\u0441\u043e\u0442 ,\u0432\u0456\u0441\u0456\u043c\u0441\u043e\u0442 ,\u0434\u0435\u0432\u2019\u044f\u0442\u0441\u043e\u0442 \"/>\n"+
" <item value=\"20_90\" text=\"\u0434\u0432\u0430\u0434\u0446\u044f\u0442\u044c ,\u0442\u0440\u0438\u0434\u0446\u044f\u0442\u044c ,\u0441\u043e\u0440\u043e\u043a ,\u043f\u2019\u044f\u0442\u0434\u0435\u0441\u044f\u0442 ,\u0448\u0456\u0441\u0442\u0434\u0435\u0441\u044f\u0442 ,\u0441\u0456\u043c\u0434\u0435\u0441\u044f\u0442 ,\u0432\u0456\u0441\u0456\u043c\u0434\u0435\u0441\u044f\u0442 ,\u0434\u0435\u0432\u2019\u044f\u043d\u043e\u0441\u0442\u043e \"/>\n"+
" <item value=\"pdv\" text=\"\u0432 \u0442.\u0447. \u041f\u0414\u0412 \"/>\n"+
" <item value=\"pdv_value\" text=\"20\"/>\n"+
" </UKR>\n"+
" <RUS>\n"+
" <item value=\"0\" text=\"\u043d\u043e\u043b\u044c\"/>\n"+
" <item value=\"1000_10\" text=\"\u0442\u044b\u0441\u044f\u0447,\u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432,\u043c\u0438\u043b\u043b\u0438\u0430\u0440\u0434\u043e\u0432,\u0442\u0440\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432\"/>\n"+
" <item value=\"1000_1\" text=\"\u0442\u044b\u0441\u044f\u0447\u0430,\u043c\u0438\u043b\u043b\u0438\u043e\u043d,\u043c\u0438\u043b\u043b\u0438\u0430\u0440\u0434,\u0442\u0440\u0438\u043b\u043b\u0438\u043e\u043d\"/>\n"+
" <item value=\"1000_234\" text=\"\u0442\u044b\u0441\u044f\u0447\u0438,\u043c\u0438\u043b\u043b\u0438\u043e\u043d\u0430,\u043c\u0438\u043b\u043b\u0438\u0430\u0440\u0434\u0430,\u0442\u0440\u0438\u043b\u043b\u0438\u043e\u043d\u0430\"/>\n"+
" <item value=\"1000_5\" text=\"\u0442\u044b\u0441\u044f\u0447,\u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432,\u043c\u0438\u043b\u043b\u0438\u0430\u0440\u0434\u043e\u0432,\u0442\u0440\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432\"/>\n"+
" <item value=\"10_19\" text=\"\u0434\u0435\u0441\u044f\u0442\u044c,\u043e\u0434\u0438\u043d\u043d\u0430\u0434\u0446\u0430\u0442\u044c,\u0434\u0432\u0435\u043d\u0430\u0434\u0446\u0430\u0442\u044c,\u0442\u0440\u0438\u043d\u0430\u0434\u0446\u0430\u0442\u044c,\u0447\u0435\u0442\u044b\u0440\u043d\u0430\u0434\u0446\u0430\u0442\u044c,\u043f\u044f\u0442\u043d\u0430\u0434\u0446\u0430\u0442\u044c,\u0448\u0435\u0441\u0442\u043d\u0430\u0434\u0446\u0430\u0442\u044c,\u0441\u0435\u043c\u043d\u0430\u0434\u0446\u0430\u0442\u044c,\u0432\u043e\u0441\u0435\u043c\u043d\u0430\u0434\u0446\u0430\u0442\u044c,\u0434\u0435\u0432\u044f\u0442\u043d\u0430\u0434\u0446\u0430\u0442\u044c\"/>\n"+
" <item value=\"1\" text=\"\u043e\u0434\u043d\u0430,\u043e\u0434\u0438\u043d,\u043e\u0434\u0438\u043d,\u043e\u0434\u043d\u0430\"/>\n"+
" <item value=\"2\" text=\"\u0434\u0432\u0435,\u0434\u0432\u0430,\u0434\u0432\u0430,\u0434\u0432\u0435\"/>\n"+
" <item value=\"3_9\" text=\"\u0442\u0440\u0438,\u0447\u0435\u0442\u044b\u0440\u0435,\u043f\u044f\u0442\u044c,\u0448\u0435\u0441\u0442\u044c,\u0441\u0435\u043c\u044c,\u0432\u043e\u0441\u0435\u043c\u044c,\u0434\u0435\u0432\u044f\u0442\u044c\"/>\n"+
" <item value=\"100_900\" text=\"\u0441\u0442\u043e ,\u0434\u0432\u0435\u0441\u0442\u0438 ,\u0442\u0440\u0438\u0441\u0442\u0430 ,\u0447\u0435\u0442\u044b\u0440\u0435\u0441\u0442\u0430 ,\u043f\u044f\u0442\u044c\u0441\u043e\u0442 ,\u0448\u0435\u0441\u0442\u044c\u0441\u043e\u0442 ,\u0441\u0435\u043c\u044c\u0441\u043e\u0442 ,\u0432\u043e\u0441\u0435\u043c\u044c\u0441\u043e\u0442 ,\u0434\u0435\u0432\u044f\u0442\u044c\u0441\u043e\u0442 \"/>\n"+
" <item value=\"20_90\" text=\"\u0434\u0432\u0430\u0434\u0446\u0430\u0442\u044c ,\u0442\u0440\u0438\u0434\u0446\u0430\u0442\u044c ,\u0441\u043e\u0440\u043e\u043a ,\u043f\u044f\u0442\u044c\u0434\u0435\u0441\u044f\u0442 ,\u0448\u0435\u0441\u0442\u044c\u0434\u0435\u0441\u044f\u0442 ,\u0441\u0435\u043c\u044c\u0434\u0435\u0441\u044f\u0442 ,\u0432\u043e\u0441\u0435\u043c\u044c\u0434\u0435\u0441\u044f\u0442 ,\u0434\u0435\u0432\u044f\u043d\u043e\u0441\u0442\u043e \"/>\n"+
" <item value=\"pdv\" text=\"\u0432 \u0442.\u0447. \u041d\u0414\u0421 \"/>\n"+
" <item value=\"pdv_value\" text=\"18\"/>\n"+
" </RUS>\n"+
" <ENG>\n"+
" <item value=\"0\" text=\"zero\"/>\n"+
" <item value=\"1000_10\" text=\"thousand,million,billion,trillion\"/>\n"+
" <item value=\"1000_1\" text=\"thousand,million,billion,trillion\"/>\n"+
" <item value=\"1000_234\" text=\"thousand,million,billion,trillion\"/>\n"+
" <item value=\"1000_5\" text=\"thousand,million,billion,trillion\"/>\n"+
" <item value=\"10_19\" text=\"ten,eleven,twelve,thirteen,fourteen,fifteen,sixteen,seventeen,eighteen,nineteen\"/>\n"+
" <item value=\"1\" text=\"one,one,one,one\"/>\n"+
" <item value=\"2\" text=\"two,two,two,two\"/>\n"+
" <item value=\"3_9\" text=\"three,four,five,six,seven,eight,nine\"/>\n"+
" <item value=\"100_900\" text=\"one hundred ,two hundred ,three hundred ,four hundred ,five hundred ,six hundred ,seven hundred ,eight hundred ,nine hundred \"/>\n"+
" <item value=\"20_90\" text=\"twenty-,thirty-,forty-,fifty-,sixty-,seventy-,eighty-,ninety-\"/>\n"+
" <item value=\"pdv\" text=\"including VAT \"/>\n"+
" <item value=\"pdv_value\" text=\"10\"/>\n"+
" </ENG>\n"+
"\n"+
" <RUR CurrID=\"810\" CurrName=\"\u0420\u043e\u0441\u0441\u0438\u0439\u0441\u043a\u0438\u0435 \u0440\u0443\u0431\u043b\u0438\" language=\"RUS\"\n"+
" RubOneUnit=\"\u0440\u0443\u0431\u043b\u044c\" RubTwoUnit=\"\u0440\u0443\u0431\u043b\u044f\" RubFiveUnit=\"\u0440\u0443\u0431\u043b\u0435\u0439\" RubSex=\"M\" RubShortUnit=\"\u0440\u0443\u0431.\"\n"+
" KopOneUnit=\"\u043a\u043e\u043f\u0435\u0439\u043a\u0430\" KopTwoUnit=\"\u043a\u043e\u043f\u0435\u0439\u043a\u0438\" KopFiveUnit=\"\u043a\u043e\u043f\u0435\u0435\u043a\" KopSex=\"F\"\n"+
" />\n"+
" <UAH CurrID=\"980\" CurrName=\"\u0423\u043a\u0440\u0430\u0438\u043d\u0441\u043a\u0456 \u0433\u0440\u0438\u0432\u043d\u0456\" language=\"RUS\"\n"+
" RubOneUnit=\"\u0433\u0440\u0438\u0432\u043d\u044f\" RubTwoUnit=\"\u0433\u0440\u0438\u0432\u043d\u0438\" RubFiveUnit=\"\u0433\u0440\u0438\u0432\u0435\u043d\u044c\" RubSex=\"F\" RubShortUnit=\"\u0433\u0440\u043d.\"\n"+
" KopOneUnit=\"\u043a\u043e\u043f\u0435\u0439\u043a\u0430\" KopTwoUnit=\"\u043a\u043e\u043f\u0435\u0439\u043a\u0438\" KopFiveUnit=\"\u043a\u043e\u043f\u0435\u0435\u043a\" KopSex=\"F\"\n"+
" />\n"+
" <USD CurrID=\"840\" CurrName=\"\u0414\u043e\u043b\u0430\u0440\u0438 \u0421\u0428\u0410\" language=\"RUS\"\n"+
" RubOneUnit=\"\u0434\u043e\u043b\u043b\u0430\u0440\" RubTwoUnit=\"\u0434\u043e\u043b\u043b\u0430\u0440\u0430\" RubFiveUnit=\"\u0434\u043e\u043b\u043b\u0430\u0440\u043e\u0432\" RubSex=\"M\" RubShortUnit=\"\u0434\u043e\u043b.\"\n"+
" KopOneUnit=\"\u0446\u0435\u043d\u0442\" KopTwoUnit=\"\u0446\u0435\u043d\u0430\" KopFiveUnit=\"\u0446\u0435\u043d\u0442\u043e\u0432\" KopSex=\"M\"\n"+
" />\n"+
"\n"+
" <RUR CurrID=\"810\" CurrName=\"\u0420\u043e\u0441\u0441\u0438\u0439\u0441\u043a\u0438\u0435 \u0440\u0443\u0431\u043b\u0438\" language=\"UKR\"\n"+
" RubOneUnit=\"\u0440\u0443\u0431\u043b\u044c\" RubTwoUnit=\"\u0440\u0443\u0431\u043b\u0456\" RubFiveUnit=\"\u0440\u0443\u0431\u043b\u0456\u0432\" RubSex=\"M\" RubShortUnit=\"\u0440\u0443\u0431.\"\n"+
" KopOneUnit=\"\u043a\u043e\u043f\u0456\u0439\u043a\u0430\" KopTwoUnit=\"\u043a\u043e\u043f\u0456\u0439\u043a\u0438\" KopFiveUnit=\"\u043a\u043e\u043f\u0456\u0439\u043e\u043a\" KopSex=\"F\"\n"+
" /> \n"+
" <UAH CurrID=\"980\" CurrName=\"\u0423\u043a\u0440\u0430\u0438\u043d\u0441\u043a\u0456 \u0433\u0440\u0438\u0432\u043d\u0456\" language=\"UKR\"\n"+
" RubOneUnit=\"\u0433\u0440\u0438\u0432\u043d\u044f\" RubTwoUnit=\"\u0433\u0440\u0438\u0432\u043d\u0456\" RubFiveUnit=\"\u0433\u0440\u0438\u0432\u0435\u043d\u044c\" RubSex=\"F\" RubShortUnit=\"\u0433\u0440\u043d.\"\n"+
" KopOneUnit=\"\u043a\u043e\u043f\u0456\u0439\u043a\u0430\" KopTwoUnit=\"\u043a\u043e\u043f\u0456\u0439\u043a\u0438\" KopFiveUnit=\"\u043a\u043e\u043f\u0456\u0439\u043e\u043a\" KopSex=\"F\"\n"+
" />\n"+
" <USD CurrID=\"840\" CurrName=\"\u0414\u043e\u043b\u0430\u0440\u0438 \u0421\u0428\u0410\" language=\"UKR\"\n"+
" RubOneUnit=\"\u0434\u043e\u043b\u0430\u0440\" RubTwoUnit=\"\u0434\u043e\u043b\u0430\u0440\u0430\" RubFiveUnit=\"\u0434\u043e\u043b\u0430\u0440\u0456\u0432\" RubSex=\"M\" RubShortUnit=\"\u0434\u043e\u043b.\"\n"+
" KopOneUnit=\"\u0446\u0435\u043d\u0442\" KopTwoUnit=\"\u0446\u0435\u043d\u0430\" KopFiveUnit=\"\u0446\u0435\u043d\u0442\u0456\u0432\" KopSex=\"M\"\n"+
" />\n"+
"\n"+
" <RUR CurrID=\"810\" CurrName=\"\u0420\u043e\u0441\u0441\u0438\u0439\u0441\u043a\u0438\u0435 \u0440\u0443\u0431\u043b\u0438\" language=\"ENG\"\n"+
" RubOneUnit=\"ruble\" RubTwoUnit=\"rubles\" RubFiveUnit=\"rubles\" RubSex=\"M\" RubShortUnit=\"RUR.\"\n"+
" KopOneUnit=\"kopeck\" KopTwoUnit=\"kopecks\" KopFiveUnit=\"kopecks\" KopSex=\"M\"\n"+
" /> \n"+
" <UAH CurrID=\"980\" CurrName=\"\u0423\u043a\u0440\u0430\u0438\u043d\u0441\u043a\u0456 \u0433\u0440\u0438\u0432\u043d\u0456\" language=\"ENG\"\n"+
" RubOneUnit=\"hryvnia\" RubTwoUnit=\"hryvnias\" RubFiveUnit=\"hryvnias\" RubSex=\"M\" RubShortUnit=\"UAH.\"\n"+
" KopOneUnit=\"kopeck\" KopTwoUnit=\"kopecks\" KopFiveUnit=\"kopecks\" KopSex=\"M\"\n"+
" />\n"+
" <USD CurrID=\"840\" CurrName=\"\u0414\u043e\u043b\u0430\u0440\u0438 \u0421\u0428\u0410\" language=\"ENG\"\n"+
" RubOneUnit=\"dollar\" RubTwoUnit=\"dollars\" RubFiveUnit=\"dollars\" RubSex=\"M\" RubShortUnit=\"USD.\"\n"+
" KopOneUnit=\"cent\" KopTwoUnit=\"cents\" KopFiveUnit=\"cents\" KopSex=\"M\"\n"+
" />\n"+
"\n"+
" <PER10 CurrID=\"556\" CurrName=\"\u0412i\u0434\u0441\u043e\u0442\u043a\u0438 \u0437 \u0434\u0435\u0441\u044f\u0442\u0438\u043c\u0438 \u0447\u0430\u0441\u0442\u0438\u043d\u0430\u043c\u0438\" language=\"RUS\"\n"+
" RubOneUnit=\"\u0446\u0435\u043b\u0430\u044f,\" RubTwoUnit=\"\u0446\u0435\u043b\u044b\u0445,\" RubFiveUnit=\"\u0446\u0435\u043b\u044b\u0445,\" RubSex=\"F\"\n"+
" KopOneUnit=\"\u0434\u0435\u0441\u044f\u0442\u0430\u044f \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u0430\" KopTwoUnit=\"\u0434\u0435\u0441\u044f\u0442\u044b\u0445 \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u0430\" KopFiveUnit=\"\u0434\u0435\u0441\u044f\u0442\u044b\u0445 \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u0430\" KopSex=\"F\"\n"+
" />\n"+
"\n"+
" <PER100 CurrID=\"557\" CurrName=\"\u0412i\u0434\u0441\u043e\u0442\u043a\u0438 \u0437 \u0441\u043e\u0442\u0438\u043c\u0438 \u0447\u0430\u0441\u0442\u0438\u043d\u0430\u043c\u0438\" language=\"RUS\"\n"+
" RubOneUnit=\"\u0446\u0435\u043b\u0430\u044f,\" RubTwoUnit=\"\u0446\u0435\u043b\u044b\u0445,\" RubFiveUnit=\"\u0446\u0435\u043b\u044b\u0445,\" RubSex=\"F\"\n"+
" KopOneUnit=\"\u0441\u043e\u0442\u0430\u044f \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u0430\" KopTwoUnit=\"\u0441\u043e\u0442\u044b\u0445 \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u0430\" KopFiveUnit=\"\u0441\u043e\u0442\u044b\u0445 \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u0430\" KopSex=\"F\"\n"+
" />\n"+
"\n"+
" <PER1000 CurrID=\"558\" CurrName=\"\u0412i\u0434\u0441\u043e\u0442\u043a\u0438 \u0437 \u0442\u0438\u0441\u044f\u0447\u043d\u0438\u043c\u0438 \u0447\u0430\u0441\u0442\u0438\u043d\u0430\u043c\u0438\" language=\"RUS\"\n"+
" RubOneUnit=\"\u0446\u0435\u043b\u0430\u044f,\" RubTwoUnit=\"\u0446\u0435\u043b\u044b\u0445,\" RubFiveUnit=\"\u0446\u0435\u043b\u044b\u0445,\" RubSex=\"F\"\n"+
" KopOneUnit=\"\u0442\u044b\u0441\u044f\u0447\u043d\u0430\u044f \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u0430\" KopTwoUnit=\"\u0442\u044b\u0441\u044f\u0447\u043d\u044b\u0445 \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u0430\" KopFiveUnit=\"\u0442\u044b\u0441\u044f\u0447\u043d\u044b\u0445 \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u0430\" KopSex=\"F\"\n"+
" />\n"+
"\n"+
" <PER10000 CurrID=\"559\" CurrName=\"\u0412i\u0434\u0441\u043e\u0442\u043a\u0438 \u0437 \u0434\u0435\u0441\u044f\u0442\u0438 \u0442\u0438\u0441\u044f\u0447\u043d\u0438\u043c\u0438 \u0447\u0430\u0441\u0442\u0438\u043d\u0430\u043c\u0438\" language=\"RUS\"\n"+
" RubOneUnit=\"\u0446\u0435\u043b\u0430\u044f,\" RubTwoUnit=\"\u0446\u0435\u043b\u044b\u0445,\" RubFiveUnit=\"\u0446\u0435\u043b\u044b\u0445,\" RubSex=\"F\"\n"+
" KopOneUnit=\"\u0434\u0435\u0441\u044f\u0442\u0438\u0442\u044b\u0441\u044f\u0447\u043d\u0430\u044f \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u0430\" KopTwoUnit=\"\u0434\u0435\u0441\u044f\u0442\u0438\u0442\u044b\u0441\u044f\u0447\u043d\u044b\u0435 \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u0430\" KopFiveUnit=\"\u0434\u0435\u0441\u044f\u0442\u0438\u0442\u044b\u0441\u044f\u0447\u043d\u044b\u0445 \u043f\u0440\u043e\u0446\u0435\u043d\u0442\u0430\" KopSex=\"F\"\n"+
" />\n"+
"\n"+
" <PER10 CurrID=\"556\" CurrName=\"\u0412i\u0434\u0441\u043e\u0442\u043a\u0438 \u0437 \u0434\u0435\u0441\u044f\u0442\u0438\u043c\u0438 \u0447\u0430\u0441\u0442\u0438\u043d\u0430\u043c\u0438\" language=\"UKR\"\n"+
" RubOneUnit=\"\u0446\u0456\u043b\u0430,\" RubTwoUnit=\"\u0446\u0456\u043b\u0438\u0445,\" RubFiveUnit=\"\u0446\u0456\u043b\u0438\u0445,\" RubSex=\"F\"\n"+
" KopOneUnit=\"\u0434\u0435\u0441\u044f\u0442\u0430 \u0432\u0456\u0434\u0441\u043e\u0442\u043a\u0430\" KopTwoUnit=\"\u0434\u0435\u0441\u044f\u0442\u0438\u0445 \u0432\u0456\u0434\u0441\u043e\u0442\u043a\u0430\" KopFiveUnit=\"\u0434\u0435\u0441\u044f\u0442\u0438\u0445 \u0432\u0456\u0434\u0441\u043e\u0442\u043a\u0430\" KopSex=\"F\"\n"+
" />\n"+
"\n"+
" <PER100 CurrID=\"557\" CurrName=\"\u0412i\u0434\u0441\u043e\u0442\u043a\u0438 \u0437 \u0441\u043e\u0442\u0438\u043c\u0438 \u0447\u0430\u0441\u0442\u0438\u043d\u0430\u043c\u0438\" language=\"UKR\"\n"+
" RubOneUnit=\"\u0446\u0456\u043b\u0430,\" RubTwoUnit=\"\u0446\u0456\u043b\u0438\u0445,\" RubFiveUnit=\"\u0446\u0456\u043b\u0438\u0445,\" RubSex=\"F\"\n"+
" KopOneUnit=\"\u0441\u043e\u0442\u0430 \u0432\u0456\u0434\u0441\u043e\u0442\u043a\u0430\" KopTwoUnit=\"\u0441\u043e\u0442\u0438\u0445 \u0432\u0456\u0434\u0441\u043e\u0442\u043a\u0430\" KopFiveUnit=\"\u0441\u043e\u0442\u0438\u0445 \u0432\u0456\u0434\u0441\u043e\u0442\u043a\u0430\" KopSex=\"F\"\n"+
" />\n"+
"\n"+
" <PER1000 CurrID=\"558\" CurrName=\"\u0412i\u0434\u0441\u043e\u0442\u043a\u0438 \u0437 \u0442\u0438\u0441\u044f\u0447\u043d\u0438\u043c\u0438 \u0447\u0430\u0441\u0442\u0438\u043d\u0430\u043c\u0438\" language=\"UKR\"\n"+
" RubOneUnit=\"\u0446\u0456\u043b\u0430,\" RubTwoUnit=\"\u0446\u0456\u043b\u0438\u0445,\" RubFiveUnit=\"\u0446\u0456\u043b\u0438\u0445,\" RubSex=\"F\"\n"+
" KopOneUnit=\"\u0442\u0438\u0441\u044f\u0447\u043d\u0430 \u0432\u0456\u0434\u0441\u043e\u0442\u043a\u0430\" KopTwoUnit=\"\u0442\u0438\u0441\u044f\u0447\u043d\u0438\u0445 \u0432\u0456\u0434\u0441\u043e\u0442\u043a\u0430\" KopFiveUnit=\"\u0442\u0438\u0441\u044f\u0447\u043d\u0438\u0445 \u0432\u0456\u0434\u0441\u043e\u0442\u043a\u0430\" KopSex=\"F\"\n"+
" />\n"+
"\n"+
" <PER10000 CurrID=\"559\" CurrName=\"\u0412i\u0434\u0441\u043e\u0442\u043a\u0438 \u0437 \u0434\u0435\u0441\u044f\u0442\u0438 \u0442\u0438\u0441\u044f\u0447\u043d\u0438\u043c\u0438 \u0447\u0430\u0441\u0442\u0438\u043d\u0430\u043c\u0438\" language=\"UKR\"\n"+
" RubOneUnit=\"\u0446\u0456\u043b\u0430,\" RubTwoUnit=\"\u0446\u0456\u043b\u0438\u0445,\" RubFiveUnit=\"\u0446\u0456\u043b\u0438\u0445,\" RubSex=\"F\"\n"+
" KopOneUnit=\"\u0434\u0435\u0441\u044f\u0442\u0438\u0442\u0438\u0441\u044f\u0447\u043d\u0430 \u0432\u0456\u0434\u0441\u043e\u0442\u043a\u0430\" KopTwoUnit=\"\u0434\u0435\u0441\u044f\u0442\u0438\u0442\u0438\u0441\u044f\u0447\u043d\u0438\u0445 \u0432\u0456\u0434\u0441\u043e\u0442\u043a\u0430\" KopFiveUnit=\"\u0434\u0435\u0441\u044f\u0442\u0438\u0442\u0438\u0441\u044f\u0447\u043d\u0438\u0445 \u0432\u0456\u0434\u0441\u043e\u0442\u043a\u0430\" KopSex=\"M\"\n"+
" />\n"+
"\n"+
" <PER10 CurrID=\"560\" CurrName=\"\u0412i\u0434\u0441\u043e\u0442\u043a\u0438 \u0437 \u0434\u0435\u0441\u044f\u0442\u0438\u043c\u0438 \u0447\u0430\u0441\u0442\u0438\u043d\u0430\u043c\u0438\" language=\"ENG\"\n"+
" RubOneUnit=\",\" RubTwoUnit=\"integers,\" RubFiveUnit=\"integers,\" RubSex=\"F\"\n"+
" KopOneUnit=\"tenth of one percent\" KopTwoUnit=\"tenth of one percent\" KopFiveUnit=\"tenth of one percent\" KopSex=\"F\"\n"+
" />\n"+
"\n"+
" <PER100 CurrID=\"561\" CurrName=\"\u0412i\u0434\u0441\u043e\u0442\u043a\u0438 \u0437 \u0441\u043e\u0442\u0438\u043c\u0438 \u0447\u0430\u0441\u0442\u0438\u043d\u0430\u043c\u0438\" language=\"ENG\"\n"+
" RubOneUnit=\",\" RubTwoUnit=\"integers,\" RubFiveUnit=\"integers,\" RubSex=\"F\"\n"+
" KopOneUnit=\"hundred percent\" KopTwoUnit=\"hundredth of percent\" KopFiveUnit=\"hundredth of percent\" KopSex=\"F\"\n"+
" />\n"+
"\n"+
" <PER1000 CurrID=\"562\" CurrName=\"\u0412i\u0434\u0441\u043e\u0442\u043a\u0438 \u0437 \u0442\u0438\u0441\u044f\u0447\u043d\u0438\u043c\u0438 \u0447\u0430\u0441\u0442\u0438\u043d\u0430\u043c\u0438\" language=\"ENG\"\n"+
" RubOneUnit=\",\" RubTwoUnit=\"integers,\" RubFiveUnit=\"integers,\" RubSex=\"F\"\n"+
" KopOneUnit=\"thousandth of percent\" KopTwoUnit=\"thousandths of percent\" KopFiveUnit=\"thousandths of percent\" KopSex=\"F\"\n"+
" />\n"+
"\n"+
" <PER10000 CurrID=\"563\" CurrName=\"\u0412i\u0434\u0441\u043e\u0442\u043a\u0438 \u0437 \u0434\u0435\u0441\u044f\u0442\u0438 \u0442\u0438\u0441\u044f\u0447\u043d\u0438\u043c\u0438 \u0447\u0430\u0441\u0442\u0438\u043d\u0430\u043c\u0438\" language=\"ENG\"\n"+
" RubOneUnit=\",\" RubTwoUnit=\"integers,\" RubFiveUnit=\"integers,\" RubSex=\"F\"\n"+
" KopOneUnit=\"ten percent\" KopTwoUnit=\"ten-percent\" KopFiveUnit=\"ten-percent\" KopSex=\"F\"\n"+
" />\n"+
"\n"+
"</CurrencyList>\n"+
"";
    private java.util.Map<String, String[]> messages = new java.util.LinkedHashMap<String, String[]>();
    private String rubOneUnit;
    private String rubTwoUnit;
    private String rubFiveUnit;
    private String rubSex;
    private String kopOneUnit;
    private String kopTwoUnit;
    private String kopFiveUnit;
    private String kopSex;
    private String rubShortUnit;
    private final Currency currency;
    private final Language language;
    private final Pennies pennies;

    static {
        javax.xml.parsers.DocumentBuilderFactory docFactory = javax.xml.parsers.DocumentBuilderFactory.newInstance();
        try {
            javax.xml.parsers.DocumentBuilder xmlDocBuilder = docFactory.newDocumentBuilder();
            xmlDoc = xmlDocBuilder.parse(new java.io.ByteArrayInputStream(CURRENCY_LIST.getBytes("UTF8")));
        } catch (Exception ex) {
            throw new UnsupportedOperationException(ex);
        }
    }

    /** Currency. */
    public enum Currency {
        /**.*/
        RUR,
        /**.*/
        UAH,
        /**.*/
        USD,
        /**.*/
        PER10,
        /**.*/
        PER100,
        /**.*/
        PER1000,
        /**.*/
        PER10000
    }

    /** Language. */
    public enum Language {
        /**.*/
        RUS,
        /**.*/
        UKR,
        /**.*/
        ENG
    }

    /** Pennies. */
    public enum Pennies {
        /**.*/
        NUMBER,
        /**.*/
        TEXT
    }

    /**
     * Inits class with currency. Usage: MoneyToStr moneyToStr = new MoneyToStr(
     *     MoneyToStr.Currency.UAH, MoneyToStr.Language.UKR, MoneyToStr.Pennies.NUMBER);
     * Definition for currency is placed into currlist.xml
     *
     * @param currency the currency (UAH, RUR, USD)
     * @param language the language (UKR, RUS, ENG)
     * @param pennies the pennies (NUMBER, TEXT)
     */
    public MoneyToStr(Currency currency, Language language, Pennies pennies) {
        if (currency == null) {
            throw new IllegalArgumentException("currency is null");
        }
        if (language == null) {
            throw new IllegalArgumentException("language is null");
        }
        if (pennies == null) {
            throw new IllegalArgumentException("pennies is null");
        }
        this.currency = currency;
        this.language = language;
        this.pennies = pennies;
        String theISOstr = currency.name();
        org.w3c.dom.Element languageElement = (org.w3c.dom.Element)
            (xmlDoc.getElementsByTagName(language.name())).item(0);
        org.w3c.dom.NodeList items = languageElement.getElementsByTagName("item");
        for (int index = 0; index < items.getLength(); index += 1) {
            org.w3c.dom.Element languageItem = (org.w3c.dom.Element) items.item(index);
            messages.put(languageItem.getAttribute("value"), languageItem.getAttribute("text").split(","));
        }
        org.w3c.dom.NodeList theISOElements = (org.w3c.dom.NodeList) (xmlDoc.getElementsByTagName(theISOstr));
        org.w3c.dom.Element theISOElement = null;
        for (int index = 0; index < theISOElements.getLength(); index += 1) {
            if (((org.w3c.dom.Element) theISOElements.item(index)).getAttribute("language").equals(language.name())) {
                theISOElement = (org.w3c.dom.Element) theISOElements.item(index);
                break;
            }
        }
        rubOneUnit = theISOElement.getAttribute("RubOneUnit");
        rubTwoUnit = theISOElement.getAttribute("RubTwoUnit");
        rubFiveUnit = theISOElement.getAttribute("RubFiveUnit");
        kopOneUnit = theISOElement.getAttribute("KopOneUnit");
        kopTwoUnit = theISOElement.getAttribute("KopTwoUnit");
        kopFiveUnit = theISOElement.getAttribute("KopFiveUnit");
        rubSex = theISOElement.getAttribute("RubSex");
        kopSex = theISOElement.getAttribute("KopSex");
        rubShortUnit = theISOElement.hasAttribute("RubShortUnit") ? theISOElement.getAttribute("RubShortUnit") : "";
    }

    /**
     * Converts percent to string.
     * @param amount the amount of percent
     * @param lang the language (RUS, UKR)
     * @return the string of percent
     */
    public static String percentToStr(Double amount, Language lang) {
        return percentToStr(amount, lang, Pennies.TEXT);
    }

    /**
     * Converts percent to string.
     * @param amount the amount of percent
     * @param lang the language (RUS, UKR, ENG)
     * @param pennies the pennies (NUMBER, TEXT)
     * @return the string of percent
     */
    public static String percentToStr(Double amount, Language lang, Pennies pennies) {
        if (amount == null) {
            throw new IllegalArgumentException("amount is null");
        }
        if (lang == null) {
            throw new IllegalArgumentException("language is null");
        }
        if (pennies == null) {
            throw new IllegalArgumentException("pennies is null");
        }
        Long intPart = amount.longValue();
        Long fractPart = 0L;
        String result;
        if (amount.floatValue() == amount.intValue()) {
            result = new MoneyToStr(Currency.PER10, lang, pennies).convert(amount.longValue(), 0L);
        } else if (Double.valueOf(amount * NUM10).floatValue() == Double.valueOf(amount * NUM10).intValue()) {
            fractPart = Math.round((amount - intPart) * NUM10);
            result = new MoneyToStr(Currency.PER10, lang, pennies).convert(intPart, fractPart);
        } else if (Double.valueOf(amount * NUM100).floatValue() == Double.valueOf(amount * NUM100).intValue()) {
            fractPart = Math.round((amount - intPart) * NUM100);
            result = new MoneyToStr(Currency.PER100, lang, pennies).convert(intPart, fractPart);
        } else if (Double.valueOf(amount * NUM1000).floatValue() == Double.valueOf(amount * NUM1000).intValue()) {
            fractPart = Math.round((amount - intPart) * NUM1000);
            result = new MoneyToStr(Currency.PER1000, lang, pennies).convert(intPart, fractPart);
        } else {
            fractPart = Math.round((amount - intPart) * NUM10000);
            result = new MoneyToStr(Currency.PER10000, lang, pennies).convert(intPart, fractPart);
        }
        return result;
    }

    /**
     * Converts double value to the text description.
     *
     * @param theMoney
     *            the amount of money in format major.minor
     * @return the string description of money value
     */
    public String convert(Double theMoney) {
        if (theMoney == null) {
            throw new IllegalArgumentException("theMoney is null");
        }
        Long intPart = theMoney.longValue();
        Long fractPart = Math.round((theMoney - intPart) * NUM100);
        if (currency == Currency.PER1000) {
            fractPart = Math.round((theMoney - intPart) * NUM1000);
        }
        return convert(intPart, fractPart);
    }

    /**
     * Converts number to currency. Usage: MoneyToStr moneyToStr = new MoneyToStr("UAH"); String result =
     * moneyToStr.convert(123D); Expected: result = сто двадцять три гривні 00 копійок
     *
     * @param theMoney
     *            the amount of money major currency
     * @param theKopeiki
     *            the amount of money minor currency
     * @return the string description of money value
     */
    public String convert(Long theMoney, Long theKopeiki) {
        if (theMoney == null) {
            throw new IllegalArgumentException("theMoney is null");
        }
        if (theKopeiki == null) {
            throw new IllegalArgumentException("theKopeiki is null");
        }
        StringBuilder money2str = new StringBuilder();
        Long triadNum = 0L;
        Long theTriad;

        Long intPart = theMoney;
        if (intPart == 0) {
            money2str.append(messages.get("0")[0] + " ");
        }
        do {
            theTriad = intPart % NUM1000;
            money2str.insert(0, triad2Word(theTriad, triadNum, rubSex));
            if (triadNum == 0) {
                Long range10 = (theTriad % NUM100) / NUM10;
                Long range = theTriad % NUM10;
                if (range10 == NUM1) {
                    money2str.append(rubFiveUnit);
                } else {
                    switch (range.byteValue()) {
                    case NUM1:
                        money2str.append(rubOneUnit);
                        break;
                    case NUM2:
                    case NUM3:
                    case NUM4:
                        money2str.append(rubTwoUnit);
                        break;
                    default:
                        money2str.append(rubFiveUnit);
                        break;
                    }
                }
            }
            intPart = intPart / NUM1000;
            triadNum++;
        } while (intPart > 0);

        if (pennies == Pennies.TEXT) {
            money2str.append(language == Language.ENG ? " and " : " ").append(theKopeiki == 0 ? messages.get("0")[0] + " " : triad2Word(theKopeiki, 0L, kopSex));
        } else {
            money2str.append(" " + (theKopeiki < 10 ? "0" + theKopeiki : theKopeiki) + " ");
        }
        if (theKopeiki >= NUM11 && theKopeiki <= NUM14) {
            money2str.append(kopFiveUnit);
        } else {
            switch ((byte) (theKopeiki % NUM10)) {
            case NUM1:
                money2str.append(kopOneUnit);
                break;
            case NUM2:
            case NUM3:
            case NUM4:
                money2str.append(kopTwoUnit);
                break;
            default:
                money2str.append(kopFiveUnit);
                break;
            }
        }
        return money2str.toString().trim();
    }

    private String triad2Word(Long triad, Long triadNum, String sex) {
        StringBuilder triadWord = new StringBuilder(NUM100);

        if (triad == 0) {
            return "";
        }

        Long range = check1(triad, triadWord);
        if (language == Language.ENG && triadWord.length() > 0 && triad % NUM10 == 0) {
            triadWord.deleteCharAt(triadWord.length() - 1);
            triadWord.append(" ");
        }

        Long range10 = range;
        range = triad % NUM10;
        check2(triadNum, sex, triadWord, triad, range10);
        switch (triadNum.byteValue()) {
        case NUM0:
            break;
        case NUM1:
        case NUM2:
        case NUM3:
        case NUM4:
            if (range10 == NUM1) {
                triadWord.append(messages.get("1000_10")[triadNum.byteValue() - 1] + " ");
            } else {
                switch (range.byteValue()) {
                case NUM1:
                    triadWord.append(messages.get("1000_1")[triadNum.byteValue() - 1] + " ");
                    break;
                case NUM2:
                case NUM3:
                case NUM4:
                    triadWord.append(messages.get("1000_234")[triadNum.byteValue() - 1] + " ");
                    break;
                default:
                    triadWord.append(messages.get("1000_5")[triadNum.byteValue() - 1] + " ");
                    break;
                }
            }
            break;
        default:
            triadWord.append("??? ");
            break;
        }
        return triadWord.toString();
    }

    /**
     * @param triadNum the triad num
     * @param sex the sex
     * @param triadWord the triad word
     * @param triad the triad
     * @param range10 the range 10
     */
    private void check2(Long triadNum, String sex, StringBuilder triadWord, Long triad, Long range10) {
        Long range = triad % NUM10;
        if (range10 == 1) {
            triadWord.append(messages.get("10_19")[range.byteValue()] + " ");
        } else {
            switch (range.byteValue()) {
            case NUM1:
                if (triadNum == NUM1) {
                    triadWord.append(messages.get("1")[INDEX_0] + " ");
                } else if (triadNum == NUM2 || triadNum == NUM3 || triadNum == NUM4) {
                    triadWord.append(messages.get("1")[INDEX_1] + " ");
                } else if ("M".equals(sex)) {
                    triadWord.append(messages.get("1")[INDEX_2] + " ");
                } else if ("F".equals(sex)) {
                    triadWord.append(messages.get("1")[INDEX_3] + " ");
                }
                break;
            case NUM2:
                if (triadNum == NUM1) {
                    triadWord.append(messages.get("2")[INDEX_0] + " ");
                } else if (triadNum == NUM2 || triadNum == NUM3 || triadNum == NUM4) {
                    triadWord.append(messages.get("2")[INDEX_1] + " ");
                } else if ("M".equals(sex)) {
                    triadWord.append(messages.get("2")[INDEX_2] + " ");
                } else if ("F".equals(sex)) {
                    triadWord.append(messages.get("2")[INDEX_3] + " ");
                }
                break;
            case NUM3:
            case NUM4:
            case NUM5:
            case NUM6:
            case NUM7:
            case NUM8:
            case NUM9:
                triadWord.append(concat(new String[] {"", "", ""}, messages.get("3_9"))[range.byteValue()] + " ");
                break;
            default:
                break;
            }
        }
    }

    /**
     * @param triad the triad
     * @param triadWord the triad word
     * @return the range
     */
    private Long check1(Long triad, StringBuilder triadWord) {
        Long range = triad / NUM100;
        triadWord.append(concat(new String[] {""}, messages.get("100_900"))[range.byteValue()]);

        range = (triad % NUM100) / NUM10;
        triadWord.append(concat(new String[] {"", ""}, messages.get("20_90"))[range.byteValue()]);
        return range;
    }

    private <T> T[] concat(T[] first, T[] second) {
        T[] result = java.util.Arrays.copyOf(first, first.length + second.length);
        System.arraycopy(second, 0, result, first.length, second.length);
        return result;
    }

    public static void main(String[] args) {
        String amount = "123.25";
        String language = "ENG";
        String currency = "USD";
        String pennies = "TEXT";
        if (args.length == 0) {
            System.out.println("Usage: java -jar moneytostr.jar --amount=123.25 --language=rus|ukr|eng --currency=rur|uah|usd --pennies=text|number");
        } else {
            for (String arg : args) {
                if (arg.startsWith("--amount=")) {
                    amount = arg.substring(9).trim().replace(",", ".");
                } else if (arg.startsWith("--language=")) {
                    language = arg.substring(11).trim().toUpperCase();
                } else if (arg.startsWith("--currency=")) {
                    currency = arg.substring(11).trim().toUpperCase();
                } else if (arg.startsWith("--pennies=")) {
                    pennies = arg.substring(10).trim().toUpperCase();
                }
            }
            String result = new MoneyToStr(Currency.valueOf(currency), Language.valueOf(language), Pennies.valueOf(pennies)).convert(Double.valueOf(amount));
            System.out.println(result);
        }
    }
    
    public java.util.Map<String, String[]> getMessages() {
        return messages;
    }
    
    public String getRubShortUnit() {
        return rubShortUnit;
    }

    public Language getLanguage() {
        return language;
    }
}
/*
 * $Id$
 *
 * Copyright 2013 Valentyn Kolesnikov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var currencyList =
{
  "CurrencyList": {
    "language": { "-value": "UKR" },
    "UKR": {
      "item": [
        {
          "-value": "0",
          "-text": "нуль"
        },
        {
          "-value": "1000_10",
          "-text": "тисяч,мільйонів,мільярдів,трильйонів"
        },
        {
          "-value": "1000_1",
          "-text": "тисяча,мільйон,мільярд,трильйон"
        },
        {
          "-value": "1000_234",
          "-text": "тисячі,мільйона,мільярда,трильйона"
        },
        {
          "-value": "1000_5",
          "-text": "тисяч,мільйонів,мільярдів,трильйонів"
        },
        {
          "-value": "10_19",
          "-text": "десять,одинадцять,дванадцять,тринадцять,чотирнадцять,п’ятнадцять,шiстнадцять,сiмнадцять,вiсiмнадцять,дев'ятнадцять"
        },
        {
          "-value": "1",
          "-text": "одна,один,один,одна"
        },
        {
          "-value": "2",
          "-text": "дві,два,два,дві"
        },
        {
          "-value": "3_9",
          "-text": "три,чотири,п’ять,шість,сім,вісім,дев’ять"
        },
        {
          "-value": "100_900",
          "-text": "сто ,двісті ,триста ,чотириста ,п’ятсот ,шістсот ,сімсот ,вісімсот ,дев’ятсот "
        },
        {
          "-value": "20_90",
          "-text": "двадцять ,тридцять ,сорок ,п’ятдесят ,шістдесят ,сімдесят ,вісімдесят ,дев’яносто "
        },
        {
          "-value": "pdv",
          "-text": "в т.ч. ПДВ "
        },
        {
          "-value": "pdv_value",
          "-text": "20"
        }
      ]
    },
    "RUS": {
      "item": [
        {
          "-value": "0",
          "-text": "ноль"
        },
        {
          "-value": "1000_10",
          "-text": "тысяч,миллионов,миллиардов,триллионов"
        },
        {
          "-value": "1000_1",
          "-text": "тысяча,миллион,миллиард,триллион"
        },
        {
          "-value": "1000_234",
          "-text": "тысячи,миллиона,миллиарда,триллиона"
        },
        {
          "-value": "1000_5",
          "-text": "тысяч,миллионов,миллиардов,триллионов"
        },
        {
          "-value": "10_19",
          "-text": "десять,одиннадцать,двенадцать,тринадцать,четырнадцать,пятнадцать,шестнадцать,семнадцать,восемнадцать,девятнадцать"
        },
        {
          "-value": "1",
          "-text": "одна,один,один,одна"
        },
        {
          "-value": "2",
          "-text": "две,два,два,две"
        },
        {
          "-value": "3_9",
          "-text": "три,четыре,пять,шесть,семь,восемь,девять"
        },
        {
          "-value": "100_900",
          "-text": "сто ,двести ,триста ,четыреста ,пятьсот ,шестьсот ,семьсот ,восемьсот ,девятьсот "
        },
        {
          "-value": "20_90",
          "-text": "двадцать ,тридцать ,сорок ,пятьдесят ,шестьдесят ,семьдесят ,восемьдесят ,девяносто "
        },
        {
          "-value": "pdv",
          "-text": "в т.ч. НДС "
        },
        {
          "-value": "pdv_value",
          "-text": "18"
        }
      ]
    },
    "ENG": {
      "item": [
        {
          "-value": "0",
          "-text": "zero"
        },
        {
          "-value": "1000_10",
          "-text": "thousand,million,billion,trillion"
        },
        {
          "-value": "1000_1",
          "-text": "thousand,million,billion,trillion"
        },
        {
          "-value": "1000_234",
          "-text": "thousand,million,billion,trillion"
        },
        {
          "-value": "1000_5",
          "-text": "thousand,million,billion,trillion"
        },
        {
          "-value": "10_19",
          "-text": "ten,eleven,twelve,thirteen,fourteen,fifteen,sixteen,seventeen,eighteen,nineteen"
        },
        {
          "-value": "1",
          "-text": "one,one,one,one"
        },
        {
          "-value": "2",
          "-text": "two,two,two,two"
        },
        {
          "-value": "3_9",
          "-text": "three,four,five,six,seven,eight,nine"
        },
        {
          "-value": "100_900",
          "-text": "one hundred ,two hundred ,three hundred ,four hundred ,five hundred ,six hundred ,seven hundred ,eight hundred ,nine hundred "
        },
        {
          "-value": "20_90",
          "-text": "twenty-,thirty-,forty-,fifty-,sixty-,seventy-,eighty-,ninety-"
        },
        {
          "-value": "pdv",
          "-text": "including VAT "
        },
        {
          "-value": "pdv_value",
          "-text": "10"
        }
      ]
    },
    "RUR": [
      {
        "-CurrID": "810",
        "-CurrName": "Российские рубли",
        "-language": "RUS",
        "-RubOneUnit": "рубль",
        "-RubTwoUnit": "рубля",
        "-RubFiveUnit": "рублей",
        "-RubSex": "M",
        "-RubShortUnit": "руб.",
        "-KopOneUnit": "копейка",
        "-KopTwoUnit": "копейки",
        "-KopFiveUnit": "копеек",
        "-KopSex": "F"
      },
      {
        "-CurrID": "810",
        "-CurrName": "Российские рубли",
        "-language": "UKR",
        "-RubOneUnit": "рубль",
        "-RubTwoUnit": "рублі",
        "-RubFiveUnit": "рублів",
        "-RubSex": "M",
        "-RubShortUnit": "руб.",
        "-KopOneUnit": "копійка",
        "-KopTwoUnit": "копійки",
        "-KopFiveUnit": "копійок",
        "-KopSex": "F"
      },
      {
        "-CurrID": "810",
        "-CurrName": "Российские рубли",
        "-language": "ENG",
        "-RubOneUnit": "ruble",
        "-RubTwoUnit": "rubles",
        "-RubFiveUnit": "rubles",
        "-RubSex": "M",
        "-RubShortUnit": "RUR.",
        "-KopOneUnit": "kopeck",
        "-KopTwoUnit": "kopecks",
        "-KopFiveUnit": "kopecks",
        "-KopSex": "M"
      }
    ],
    "UAH": [
      {
        "-CurrID": "980",
        "-CurrName": "Украинскі гривні",
        "-language": "RUS",
        "-RubOneUnit": "гривня",
        "-RubTwoUnit": "гривни",
        "-RubFiveUnit": "гривень",
        "-RubSex": "F",
        "-RubShortUnit": "грн.",
        "-KopOneUnit": "копейка",
        "-KopTwoUnit": "копейки",
        "-KopFiveUnit": "копеек",
        "-KopSex": "F"
      },
      {
        "-CurrID": "980",
        "-CurrName": "Украинскі гривні",
        "-language": "UKR",
        "-RubOneUnit": "гривня",
        "-RubTwoUnit": "гривні",
        "-RubFiveUnit": "гривень",
        "-RubSex": "F",
        "-RubShortUnit": "грн.",
        "-KopOneUnit": "копійка",
        "-KopTwoUnit": "копійки",
        "-KopFiveUnit": "копійок",
        "-KopSex": "F"
      },
      {
        "-CurrID": "980",
        "-CurrName": "Украинскі гривні",
        "-language": "ENG",
        "-RubOneUnit": "hryvnia",
        "-RubTwoUnit": "hryvnias",
        "-RubFiveUnit": "hryvnias",
        "-RubSex": "M",
        "-RubShortUnit": "UAH.",
        "-KopOneUnit": "kopeck",
        "-KopTwoUnit": "kopecks",
        "-KopFiveUnit": "kopecks",
        "-KopSex": "M"
      }
    ],
    "USD": [
      {
        "-CurrID": "840",
        "-CurrName": "Долари США",
        "-language": "RUS",
        "-RubOneUnit": "доллар",
        "-RubTwoUnit": "доллара",
        "-RubFiveUnit": "долларов",
        "-RubSex": "M",
        "-RubShortUnit": "дол.",
        "-KopOneUnit": "цент",
        "-KopTwoUnit": "цена",
        "-KopFiveUnit": "центов",
        "-KopSex": "M"
      },
      {
        "-CurrID": "840",
        "-CurrName": "Долари США",
        "-language": "UKR",
        "-RubOneUnit": "долар",
        "-RubTwoUnit": "долара",
        "-RubFiveUnit": "доларів",
        "-RubSex": "M",
        "-RubShortUnit": "дол.",
        "-KopOneUnit": "цент",
        "-KopTwoUnit": "цена",
        "-KopFiveUnit": "центів",
        "-KopSex": "M"
      },
      {
        "-CurrID": "840",
        "-CurrName": "Долари США",
        "-language": "ENG",
        "-RubOneUnit": "dollar",
        "-RubTwoUnit": "dollars",
        "-RubFiveUnit": "dollars",
        "-RubSex": "M",
        "-RubShortUnit": "USD.",
        "-KopOneUnit": "cent",
        "-KopTwoUnit": "cents",
        "-KopFiveUnit": "cents",
        "-KopSex": "M"
      }
    ],
    "PER10": [
      {
        "-CurrID": "556",
        "-CurrName": "Вiдсотки з десятими частинами",
        "-language": "RUS",
        "-RubOneUnit": "целая,",
        "-RubTwoUnit": "целых,",
        "-RubFiveUnit": "целых,",
        "-RubSex": "F",
        "-KopOneUnit": "десятая процента",
        "-KopTwoUnit": "десятых процента",
        "-KopFiveUnit": "десятых процента",
        "-KopSex": "F"
      },
      {
        "-CurrID": "556",
        "-CurrName": "Вiдсотки з десятими частинами",
        "-language": "UKR",
        "-RubOneUnit": "ціла,",
        "-RubTwoUnit": "цілих,",
        "-RubFiveUnit": "цілих,",
        "-RubSex": "F",
        "-KopOneUnit": "десята відсотка",
        "-KopTwoUnit": "десятих відсотка",
        "-KopFiveUnit": "десятих відсотка",
        "-KopSex": "F"
      },
      {
        "-CurrID": "560",
        "-CurrName": "Вiдсотки з десятими частинами",
        "-language": "ENG",
        "-RubOneUnit": ",",
        "-RubTwoUnit": "integers,",
        "-RubFiveUnit": "integers,",
        "-RubSex": "F",
        "-KopOneUnit": "tenth of one percent",
        "-KopTwoUnit": "tenth of one percent",
        "-KopFiveUnit": "tenth of one percent",
        "-KopSex": "F"
      }
    ],
    "PER100": [
      {
        "-CurrID": "557",
        "-CurrName": "Вiдсотки з сотими частинами",
        "-language": "RUS",
        "-RubOneUnit": "целая,",
        "-RubTwoUnit": "целых,",
        "-RubFiveUnit": "целых,",
        "-RubSex": "F",
        "-KopOneUnit": "сотая процента",
        "-KopTwoUnit": "сотых процента",
        "-KopFiveUnit": "сотых процента",
        "-KopSex": "F"
      },
      {
        "-CurrID": "557",
        "-CurrName": "Вiдсотки з сотими частинами",
        "-language": "UKR",
        "-RubOneUnit": "ціла,",
        "-RubTwoUnit": "цілих,",
        "-RubFiveUnit": "цілих,",
        "-RubSex": "F",
        "-KopOneUnit": "сота відсотка",
        "-KopTwoUnit": "сотих відсотка",
        "-KopFiveUnit": "сотих відсотка",
        "-KopSex": "F"
      },
      {
        "-CurrID": "561",
        "-CurrName": "Вiдсотки з сотими частинами",
        "-language": "ENG",
        "-RubOneUnit": ",",
        "-RubTwoUnit": "integers,",
        "-RubFiveUnit": "integers,",
        "-RubSex": "F",
        "-KopOneUnit": "hundred percent",
        "-KopTwoUnit": "hundredth of percent",
        "-KopFiveUnit": "hundredth of percent",
        "-KopSex": "F"
      }
    ],
    "PER1000": [
      {
        "-CurrID": "558",
        "-CurrName": "Вiдсотки з тисячними частинами",
        "-language": "RUS",
        "-RubOneUnit": "целая,",
        "-RubTwoUnit": "целых,",
        "-RubFiveUnit": "целых,",
        "-RubSex": "F",
        "-KopOneUnit": "тысячная процента",
        "-KopTwoUnit": "тысячных процента",
        "-KopFiveUnit": "тысячных процента",
        "-KopSex": "F"
      },
      {
        "-CurrID": "558",
        "-CurrName": "Вiдсотки з тисячними частинами",
        "-language": "UKR",
        "-RubOneUnit": "ціла,",
        "-RubTwoUnit": "цілих,",
        "-RubFiveUnit": "цілих,",
        "-RubSex": "F",
        "-KopOneUnit": "тисячна відсотка",
        "-KopTwoUnit": "тисячних відсотка",
        "-KopFiveUnit": "тисячних відсотка",
        "-KopSex": "F"
      },
      {
        "-CurrID": "562",
        "-CurrName": "Вiдсотки з тисячними частинами",
        "-language": "ENG",
        "-RubOneUnit": ",",
        "-RubTwoUnit": "integers,",
        "-RubFiveUnit": "integers,",
        "-RubSex": "F",
        "-KopOneUnit": "thousandth of percent",
        "-KopTwoUnit": "thousandths of percent",
        "-KopFiveUnit": "thousandths of percent",
        "-KopSex": "F"
      }
    ],
    "PER10000": [
      {
        "-CurrID": "559",
        "-CurrName": "Вiдсотки з десяти тисячними частинами",
        "-language": "RUS",
        "-RubOneUnit": "целая,",
        "-RubTwoUnit": "целых,",
        "-RubFiveUnit": "целых,",
        "-RubSex": "F",
        "-KopOneUnit": "десятитысячная процента",
        "-KopTwoUnit": "десятитысячные процента",
        "-KopFiveUnit": "десятитысячных процента",
        "-KopSex": "F"
      },
      {
        "-CurrID": "559",
        "-CurrName": "Вiдсотки з десяти тисячними частинами",
        "-language": "UKR",
        "-RubOneUnit": "ціла,",
        "-RubTwoUnit": "цілих,",
        "-RubFiveUnit": "цілих,",
        "-RubSex": "F",
        "-KopOneUnit": "десятитисячна відсотка",
        "-KopTwoUnit": "десятитисячних відсотка",
        "-KopFiveUnit": "десятитисячних відсотка",
        "-KopSex": "M"
      },
      {
        "-CurrID": "563",
        "-CurrName": "Вiдсотки з десяти тисячними частинами",
        "-language": "ENG",
        "-RubOneUnit": ",",
        "-RubTwoUnit": "integers,",
        "-RubFiveUnit": "integers,",
        "-RubSex": "F",
        "-KopOneUnit": "ten percent",
        "-KopTwoUnit": "ten-percent",
        "-KopFiveUnit": "ten-percent",
        "-KopSex": "F"
      }
    ]
  }
};
/**
 * Converts numbers to symbols.
 *
 * @author Valentyn V Kolesnikov
 * @version $Revision$ $Date$
*/

    /** Currency. */
var Currency = (function () {
    function Currency() {
    }
    Currency.UAH = 'UAH';

    Currency.RUR = 'RUR';

    Currency.USD = 'USD';

    Currency.PER10 = 'PER10';

    Currency.PER100 = 'PER100';

    Currency.PER1000 = 'PER1000';

    Currency.PER10000 = 'PER10000';
    return Currency;
})();

    /** Language. */
var Language = (function () {
    function Language() {
    }
    Language.RUS = 'RUS';

    Language.UKR = 'UKR';

    Language.ENG = 'ENG';
    return Language;
})();

    /** Pennies. */
var Pennies = (function () {
    function Pennies() {
    }
    Pennies.NUMBER = 'NUMBER';

    Pennies.TEXT = 'TEXT';
    return Pennies;
})();

var StringBuilder = (function () {
    function StringBuilder() {
        this._buffer = [];
    }
    StringBuilder.prototype.append = function (text) {
        this._buffer[this._buffer.length] = text;
        return this;
    };

    StringBuilder.prototype.insert = function (index, text) {
        this._buffer.splice(index, 0, text);
        return this;
    };

    StringBuilder.prototype.length = function () {
        return this.toString().length;
    };

    StringBuilder.prototype.deleteCharAt = function (index) {
        var str = this.toString();
        this._buffer = [];
        this.append(str.substring(0, index));
        return this;
    };

    StringBuilder.prototype.toString = function () {
        return this._buffer.join("");
    };
    return StringBuilder;
})();

var MoneyToStr = (function () {
    MoneyToStr.NUM0 = 0;
    MoneyToStr.NUM1 = 1;
    MoneyToStr.NUM2 = 2;
    MoneyToStr.NUM3 = 3;
    MoneyToStr.NUM4 = 4;
    MoneyToStr.NUM5 = 5;
    MoneyToStr.NUM6 = 6;
    MoneyToStr.NUM7 = 7;
    MoneyToStr.NUM8 = 8;
    MoneyToStr.NUM9 = 9;
    MoneyToStr.NUM10 = 10;
    MoneyToStr.NUM11 = 11;
    MoneyToStr.NUM12 = 12;
    MoneyToStr.NUM100 = 100;
    MoneyToStr.NUM1000 = 1000;
    MoneyToStr.NUM10000 = 10000;
    MoneyToStr.INDEX_0 = 0;
    MoneyToStr.INDEX_1 = 1;
    MoneyToStr.INDEX_2 = 2;
    MoneyToStr.INDEX_3 = 3;

    MoneyToStr.percentToStr = function (amount, lang) {
        if (amount == null) {
            throw new Error("amount is null");
        }
        if (lang == null) {
            throw new Error("Language is null");
        }
        var intPart = parseInt(amount);
        var fractPart = 0;
        var result;
        if (amount == parseInt(amount)) {
            result = new MoneyToStr(Currency.PER10, lang, Pennies.TEXT).convert(amount, 0);
        } else if ((amount * MoneyToStr.NUM10).toFixed(4) == parseInt(amount * MoneyToStr.NUM10)) {
            fractPart = Math.round((amount - intPart) * MoneyToStr.NUM10);
            result = new MoneyToStr(Currency.PER10, lang, Pennies.TEXT).convert(intPart, fractPart);
        } else if ((amount * MoneyToStr.NUM100).toFixed(4) == parseInt(amount * MoneyToStr.NUM100)) {
            fractPart = Math.round((amount - intPart) * MoneyToStr.NUM100);
            result = new MoneyToStr(Currency.PER100, lang, Pennies.TEXT).convert(intPart, fractPart);
        } else if ((amount * MoneyToStr.NUM1000).toFixed(4) == parseInt(amount * MoneyToStr.NUM1000)) {
            fractPart = Math.round((amount - intPart) * MoneyToStr.NUM1000);
            result = new MoneyToStr(Currency.PER1000, lang, Pennies.TEXT).convert(intPart, fractPart);
        } else {
            fractPart = Math.round((amount - intPart) * MoneyToStr.NUM10000);
            result = new MoneyToStr(Currency.PER10000, lang, Pennies.TEXT).convert(intPart, fractPart);
        }
        return result;
    }

    function MoneyToStr(currency, language, pennies) {
        this.currency = currency;
        this.language = language;
        this.pennies = pennies;
        var languageElement = language;
        var items = currencyList['CurrencyList'][languageElement]['item'];
        this.messages = {};
        for (var index in items) {
            var languageItem = items[index];
            if (languageItem["-text"]) {
                this.messages[languageItem["-value"]] = languageItem["-text"].split(",");
            }
        }
        var currencyItem = currencyList['CurrencyList'][currency]
        var theISOElement = null;
        for (var index in currencyItem) {
            if (currencyItem[index]["-language"] == language) {
                theISOElement = currencyItem[index];
                break;
            }
        }
        if (theISOElement == null) {
            throw new Error("Currency not found " + currency);
        }
        this.rubOneUnit = theISOElement["-RubOneUnit"];
        this.rubTwoUnit = theISOElement["-RubTwoUnit"];
        this.rubFiveUnit = theISOElement["-RubFiveUnit"];
        this.kopOneUnit = theISOElement["-KopOneUnit"];
        this.kopTwoUnit = theISOElement["-KopTwoUnit"];
        this.kopFiveUnit = theISOElement["-KopFiveUnit"];
        this.rubSex = theISOElement["-RubSex"];
        this.kopSex = theISOElement["-KopSex"];
    }

    /**
     * Converts double value to the text description.
     *
     * @param theMoney
     *            the amount of money in format major.minor
     * @return the string description of money value
     */
    MoneyToStr.prototype.convertValue = function (theMoney) {
        if (typeof theMoney === undefined || theMoney == null) {
            throw new Error("theMoney is null");
        }
        var intPart = parseInt(theMoney);
        var fractPart = Math.round((theMoney - intPart) * MoneyToStr.NUM100);
        if (this.currency == Currency.PER1000) {
            fractPart = Math.round((theMoney - intPart) * MoneyToStr.NUM1000);
        }
        return this.convert(intPart, fractPart);
    }

    /**
     * Converts number to currency. Usage: MoneyToStr moneyToStr = new MoneyToStr("UAH"); String result =
     * moneyToStr.convert(123D); Expected: result = сто двадцять три гривні 00 копійок
     *
     * @param theMoney
     *            the amount of money major currency
     * @param theKopeiki
     *            the amount of money minor currency
     * @return the string description of money value
     */
    MoneyToStr.prototype.convert = function (theMoney, theKopeiki) {
        if (typeof theMoney === undefined || theMoney == null) {
            throw new Error("theMoney is null");
        }
        if (typeof theKopeiki === undefined || theKopeiki == null) {
            throw new Error("theKopeiki is null");
        }
        var money2str = new StringBuilder();
        var triadNum = 0;
        var theTriad = 0;
        var intPart = theMoney;
        if (intPart == 0) {
            money2str.append(this.messages["0"][0] + " ");
        }
        do {
            theTriad = parseInt(intPart % MoneyToStr.NUM1000);
            money2str.insert(0, this.triad2Word(theTriad, triadNum, this.rubSex));
            if (triadNum == 0) {
                var range10 = parseInt((theTriad % MoneyToStr.NUM100) / MoneyToStr.NUM10);
                var range = parseInt(theTriad % MoneyToStr.NUM10);
                if (range10 == MoneyToStr.NUM1) {
                    money2str.append(this.rubFiveUnit);
                } else {
                    switch (range) {
                    case MoneyToStr.NUM1:
                        money2str.append(this.rubOneUnit);
                        break;
                    case MoneyToStr.NUM2:
                    case MoneyToStr.NUM3:
                    case MoneyToStr.NUM4:
                        money2str.append(this.rubTwoUnit);
                        break;
                    default:
                        money2str.append(this.rubFiveUnit);
                        break;
                    }
                }
            }
            intPart = parseInt(intPart / MoneyToStr.NUM1000);
            triadNum++;
        } while (intPart > 0);

        if (this.pennies == Pennies.TEXT) {
            money2str.append(this.language == Language.ENG ? " and " : " ").append(theKopeiki == 0 ? this.messages["0"][0] + " " : this.triad2Word(theKopeiki, 0, this.kopSex));
        } else {
            money2str.append(" " + (theKopeiki < 10 ? "0" + theKopeiki : theKopeiki) + " ");
        }
        if (theKopeiki == MoneyToStr.NUM11 || theKopeiki == MoneyToStr.NUM12) {
            money2str.append(this.kopFiveUnit);
        } else {
            switch (parseInt(theKopeiki % MoneyToStr.NUM10)) {
            case MoneyToStr.NUM1:
                money2str.append(this.kopOneUnit);
                break;
            case MoneyToStr.NUM2:
            case MoneyToStr.NUM3:
            case MoneyToStr.NUM4:
                money2str.append(this.kopTwoUnit);
                break;
            default:
                money2str.append(this.kopFiveUnit);
                break;
            }
        }
        return money2str.toString().trim();
    }

    MoneyToStr.prototype.triad2Word = function (triad, triadNum, sex) {
        var triadWord = new StringBuilder();

        if (triad == 0) {
            return "";
        }

        var range = this.check1(triad, triadWord);
        if (this.language == Language.ENG && triadWord.length() > 0 && triad % MoneyToStr.NUM10 == 0) {
            triadWord.deleteCharAt(triadWord.length() - 1);
            triadWord.append(" ");
        }

        var range10 = range;
        range = parseInt(triad % MoneyToStr.NUM10);
        this.check2(triadNum, sex, triadWord, triad, range10);
        switch (triadNum) {
        case MoneyToStr.NUM0:
            break;
        case MoneyToStr.NUM1:
        case MoneyToStr.NUM2:
        case MoneyToStr.NUM3:
        case MoneyToStr.NUM4:
            if (range10 == MoneyToStr.NUM1) {
                triadWord.append(this.messages["1000_10"][triadNum - 1] + " ");
            } else {
                switch (range) {
                case MoneyToStr.NUM1:
                    triadWord.append(this.messages["1000_1"][triadNum - 1] + " ");
                    break;
                case MoneyToStr.NUM2:
                case MoneyToStr.NUM3:
                case MoneyToStr.NUM4:
                    triadWord.append(this.messages["1000_234"][triadNum - 1] + " ");
                    break;
                default:
                    triadWord.append(this.messages["1000_5"][triadNum - 1] + " ");
                    break;
                }
            }
            break;
        default:
            triadWord.append("??? ");
            break;
        }
        return triadWord.toString();
    }

    /**
     * @param triadNum the triad num
     * @param sex the sex
     * @param triadWord the triad word
     * @param triad the triad
     * @param range10 the range 10
     */
    MoneyToStr.prototype.check2 = function (triadNum, sex, triadWord, triad, range10) {
        var range = parseInt(triad % MoneyToStr.NUM10);
        if (range10 == 1) {
            triadWord.append(this.messages["10_19"][range] + " ");
        } else {
            switch (range) {
            case MoneyToStr.NUM1:
                if (triadNum == MoneyToStr.NUM1) {
                    triadWord.append(this.messages["1"][MoneyToStr.INDEX_0] + " ");
                } else if (triadNum == MoneyToStr.NUM2 || triadNum == MoneyToStr.NUM3 || triadNum == MoneyToStr.NUM4) {
                    triadWord.append(this.messages["1"][MoneyToStr.INDEX_1] + " ");
                } else if ("M" == sex) {
                    triadWord.append(this.messages["1"][MoneyToStr.INDEX_2] + " ");
                } else if ("F" == sex) {
                    triadWord.append(this.messages["1"][MoneyToStr.INDEX_3] + " ");
                }
                break;
            case MoneyToStr.NUM2:
                if (triadNum == MoneyToStr.NUM1) {
                    triadWord.append(this.messages["2"][MoneyToStr.INDEX_0] + " ");
                } else if (triadNum == MoneyToStr.NUM2 || triadNum == MoneyToStr.NUM3 || triadNum == MoneyToStr.NUM4) {
                    triadWord.append(this.messages["2"][MoneyToStr.INDEX_1] + " ");
                } else if ("M" == sex) {
                    triadWord.append(this.messages["2"][MoneyToStr.INDEX_2] + " ");
                } else if ("F" == sex) {
                    triadWord.append(this.messages["2"][MoneyToStr.INDEX_3] + " ");
                }
                break;
            case MoneyToStr.NUM3:
            case MoneyToStr.NUM4:
            case MoneyToStr.NUM5:
            case MoneyToStr.NUM6:
            case MoneyToStr.NUM7:
            case MoneyToStr.NUM8:
            case MoneyToStr.NUM9:
                triadWord.append(["", "", ""].concat(this.messages["3_9"])[range] + " ");
                break;
            default:
                break;
            }
        }
    }

    /**
     * @param triad the triad
     * @param triadWord the triad word
     * @return the range
     */
    MoneyToStr.prototype.check1 = function (triad, triadWord) {
        var range = parseInt(triad / MoneyToStr.NUM100);
        triadWord.append([""].concat(this.messages["100_900"])[range]);

        range = parseInt((triad % MoneyToStr.NUM100) / MoneyToStr.NUM10);
        triadWord.append(["", ""].concat(this.messages["20_90"])[range]);
        return range;
    }
    return MoneyToStr;
})();
<?php
/*
 * $Id$
 *
 * Copyright 2013 Valentyn Kolesnikov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
class StringBuilder {
    var $_buffer = array();

    public function __construct() {
        $this->_buffer = array();
    }

    public function append($text) {
        array_push($this->_buffer, $text);
        return $this;
    }

    public function insert($text) {
        array_unshift($this->_buffer, $text);
        return $this;
    }

    public function length() {
        return $this->toString().length();
    }

    public function deleteCharAt($index) {
        $str = toString();
        $this->_buffer = array();
        append($str.substring(0, $index));
        return $this;
    }

    public function toString() {
        return join("", $this->_buffer);
    }
}

/**
 * Converts numbers to symbols.
 *
 * @author Valentyn Kolesnikov
 * @version $Revision$ $Date$
 */
class MoneyToStr {
    var $currencyList = array(
  "CurrencyList" => array(
    "language" => array( "-value" => "UKR" ),
    "UKR" => array(
      "item" => array(
        array(
          "-value" => "0",
          "-text" => "нуль"
        ),
        array(
          "-value" => "1000_10",
          "-text" => "тисяч,мільйонів,мільярдів,трильйонів"
        ),
        array(
          "-value" => "1000_1",
          "-text" => "тисяча,мільйон,мільярд,трильйон"
        ),
        array(
          "-value" => "1000_234",
          "-text" => "тисячі,мільйона,мільярда,трильйона"
        ),
        array(
          "-value" => "1000_5",
          "-text" => "тисяч,мільйонів,мільярдів,трильйонів"
        ),
        array(
          "-value" => "10_19",
          "-text" => "десять,одинадцять,дванадцять,тринадцять,чотирнадцять,п’ятнадцять,шiстнадцять,сiмнадцять,вiсiмнадцять,дев'ятнадцять"
        ),
        array(
          "-value" => "1",
          "-text" => "одна,один,один,одна"
        ),
        array(
          "-value" => "2",
          "-text" => "дві,два,два,дві"
        ),
        array(
          "-value" => "3_9",
          "-text" => "три,чотири,п’ять,шість,сім,вісім,дев’ять"
        ),
        array(
          "-value" => "100_900",
          "-text" => "сто ,двісті ,триста ,чотириста ,п’ятсот ,шістсот ,сімсот ,вісімсот ,дев’ятсот "
        ),
        array(
          "-value" => "20_90",
          "-text" => "двадцять ,тридцять ,сорок ,п’ятдесят ,шістдесят ,сімдесят ,вісімдесят ,дев’яносто "
        )
      )
    ),
    "RUS" => array(
      "item" => array(
        array(
          "-value" => "0",
          "-text" => "ноль"
        ),
        array(
          "-value" => "1000_10",
          "-text" => "тысяч,миллионов,миллиардов,триллионов"
        ),
        array(
          "-value" => "1000_1",
          "-text" => "тысяча,миллион,миллиард,триллион"
        ),
        array(
          "-value" => "1000_234",
          "-text" => "тысячи,миллиона,миллиарда,триллиона"
        ),
        array(
          "-value" => "1000_5",
          "-text" => "тысяч,миллионов,миллиардов,триллионов"
        ),
        array(
          "-value" => "10_19",
          "-text" => "десять,одиннадцать,двенадцать,тринадцать,четырнадцать,пятнадцать,шестнадцать,семнадцать,восемнадцать,девятнадцать"
        ),
        array(
          "-value" => "1",
          "-text" => "одна,один,один,одна"
        ),
        array(
          "-value" => "2",
          "-text" => "две,два,два,две"
        ),
        array(
          "-value" => "3_9",
          "-text" => "три,четыре,пять,шесть,семь,восемь,девять"
        ),
        array(
          "-value" => "100_900",
          "-text" => "сто ,двести ,триста ,четыреста ,пятьсот ,шестьсот ,семьсот ,восемьсот ,девятьсот "
        ),
        array(
          "-value" => "20_90",
          "-text" => "двадцать ,тридцать ,сорок ,пятьдесят ,шестьдесят ,семьдесят ,восемьдесят ,девяносто "
        )
      )
    ),
    "ENG" => array(
      "item" => array(
        array(
          "-value" => "0",
          "-text" => "zero"
        ),
        array(
          "-value" => "1000_10",
          "-text" => "thousand,million,billion,trillion"
        ),
        array(
          "-value" => "1000_1",
          "-text" => "thousand,million,billion,trillion"
        ),
        array(
          "-value" => "1000_234",
          "-text" => "thousand,million,billion,trillion"
        ),
        array(
          "-value" => "1000_5",
          "-text" => "thousand,million,billion,trillion"
        ),
        array(
          "-value" => "10_19",
          "-text" => "ten,eleven,twelve,thirteen,fourteen,fifteen,sixteen,seventeen,eighteen,nineteen"
        ),
        array(
          "-value" => "1",
          "-text" => "one,one,one,one"
        ),
        array(
          "-value" => "2",
          "-text" => "two,two,two,two"
        ),
        array(
          "-value" => "3_9",
          "-text" => "three,four,five,six,seven,eight,nine"
        ),
        array(
          "-value" => "100_900",
          "-text" => "one hundred ,two hundred ,three hundred ,four hundred ,five hundred ,six hundred ,seven hundred ,eight hundred ,nine hundred "
        ),
        array(
          "-value" => "20_90",
          "-text" => "twenty-,thirty-,forty-,fifty-,sixty-,seventy-,eighty-,ninety-"
        )
      )
    ),
    "RUR" => array(
      array(
        "-CurrID" => "810",
        "-CurrName" => "Российские рубли",
        "-language" => "RUS",
        "-RubOneUnit" => "рубль",
        "-RubTwoUnit" => "рубля",
        "-RubFiveUnit" => "рублей",
        "-RubSex" => "M",
        "-KopOneUnit" => "копейка",
        "-KopTwoUnit" => "копейки",
        "-KopFiveUnit" => "копеек",
        "-KopSex" => "F"
      ),
      array(
        "-CurrID" => "810",
        "-CurrName" => "Российские рубли",
        "-language" => "UKR",
        "-RubOneUnit" => "рубль",
        "-RubTwoUnit" => "рубля",
        "-RubFiveUnit" => "рублів",
        "-RubSex" => "M",
        "-KopOneUnit" => "копійка",
        "-KopTwoUnit" => "копійки",
        "-KopFiveUnit" => "копійок",
        "-KopSex" => "F"
      )
    ),
    "UAH" => array(
      array(
        "-CurrID" => "980",
        "-CurrName" => "Украинскі гривні",
        "-language" => "RUS",
        "-RubOneUnit" => "гривня",
        "-RubTwoUnit" => "гривни",
        "-RubFiveUnit" => "гривень",
        "-RubSex" => "F",
        "-KopOneUnit" => "копейка",
        "-KopTwoUnit" => "копейки",
        "-KopFiveUnit" => "копеек",
        "-KopSex" => "F"
      ),
      array(
        "-CurrID" => "980",
        "-CurrName" => "Украинскі гривні",
        "-language" => "UKR",
        "-RubOneUnit" => "гривня",
        "-RubTwoUnit" => "гривні",
        "-RubFiveUnit" => "гривень",
        "-RubSex" => "F",
        "-KopOneUnit" => "копійка",
        "-KopTwoUnit" => "копійки",
        "-KopFiveUnit" => "копійок",
        "-KopSex" => "F"
      )
    ),
    "USD" => array(
      array(
        "-CurrID" => "840",
        "-CurrName" => "Долари США",
        "-language" => "RUS",
        "-RubOneUnit" => "долар",
        "-RubTwoUnit" => "долара",
        "-RubFiveUnit" => "доларів",
        "-RubSex" => "M",
        "-KopOneUnit" => "цент",
        "-KopTwoUnit" => "цена",
        "-KopFiveUnit" => "центов",
        "-KopSex" => "M"
      ),
      array(
        "-CurrID" => "840",
        "-CurrName" => "Долари США",
        "-language" => "UKR",
        "-RubOneUnit" => "долар",
        "-RubTwoUnit" => "долара",
        "-RubFiveUnit" => "доларів",
        "-RubSex" => "M",
        "-KopOneUnit" => "цент",
        "-KopTwoUnit" => "цена",
        "-KopFiveUnit" => "центів",
        "-KopSex" => "M"
      ),
      array(
        "-CurrID" => "840",
        "-CurrName" => "Долари США",
        "-language" => "ENG",
        "-RubOneUnit" => "dollar",
        "-RubTwoUnit" => "dollars",
        "-RubFiveUnit" => "dollars",
        "-RubSex" => "M",
        "-KopOneUnit" => "cent",
        "-KopTwoUnit" => "cents",
        "-KopFiveUnit" => "cents",
        "-KopSex" => "M"
      )
     ),
    "PER10" => array(
      array(
        "-CurrID" => "556",
        "-CurrName" => "Вiдсотки з десятими частинами",
        "-language" => "RUS",
        "-RubOneUnit" => "целая,",
        "-RubTwoUnit" => "целых,",
        "-RubFiveUnit" => "целых,",
        "-RubSex" => "F",
        "-KopOneUnit" => "десятая процента",
        "-KopTwoUnit" => "десятых процента",
        "-KopFiveUnit" => "десятых процента",
        "-KopSex" => "F"
      ),
      array(
        "-CurrID" => "556",
        "-CurrName" => "Вiдсотки з десятими частинами",
        "-language" => "UKR",
        "-RubOneUnit" => "ціла,",
        "-RubTwoUnit" => "цілих,",
        "-RubFiveUnit" => "цілих,",
        "-RubSex" => "F",
        "-KopOneUnit" => "десята відсотка",
        "-KopTwoUnit" => "десятих відсотка",
        "-KopFiveUnit" => "десятих відсотка",
        "-KopSex" => "F"
      )
    ),
    "PER100" => array(
      array(
        "-CurrID" => "557",
        "-CurrName" => "Вiдсотки з сотими частинами",
        "-language" => "RUS",
        "-RubOneUnit" => "целая,",
        "-RubTwoUnit" => "целых,",
        "-RubFiveUnit" => "целых,",
        "-RubSex" => "F",
        "-KopOneUnit" => "сотая процента",
        "-KopTwoUnit" => "сотых процента",
        "-KopFiveUnit" => "сотых процента",
        "-KopSex" => "F"
      ),
      array(
        "-CurrID" => "557",
        "-CurrName" => "Вiдсотки з сотими частинами",
        "-language" => "UKR",
        "-RubOneUnit" => "ціла,",
        "-RubTwoUnit" => "цілих,",
        "-RubFiveUnit" => "цілих,",
        "-RubSex" => "F",
        "-KopOneUnit" => "сота відсотка",
        "-KopTwoUnit" => "сотих відсотка",
        "-KopFiveUnit" => "сотих відсотка",
        "-KopSex" => "F"
      )
    ),
    "PER1000" => array(
      array(
        "-CurrID" => "558",
        "-CurrName" => "Вiдсотки з тисячними частинами",
        "-language" => "RUS",
        "-RubOneUnit" => "целая,",
        "-RubTwoUnit" => "целых,",
        "-RubFiveUnit" => "целых,",
        "-RubSex" => "F",
        "-KopOneUnit" => "тысячная процента",
        "-KopTwoUnit" => "тысячных процента",
        "-KopFiveUnit" => "тысячных процента",
        "-KopSex" => "F"
      ),
      array(
        "-CurrID" => "558",
        "-CurrName" => "Вiдсотки з тисячними частинами",
        "-language" => "UKR",
        "-RubOneUnit" => "ціла,",
        "-RubTwoUnit" => "цілих,",
        "-RubFiveUnit" => "цілих,",
        "-RubSex" => "F",
        "-KopOneUnit" => "тисячна відсотка",
        "-KopTwoUnit" => "тисячних відсотка",
        "-KopFiveUnit" => "тисячних відсотка",
        "-KopSex" => "F"
      )
    ),
    "PER10000" => array(
      array(
        "-CurrID" => "559",
        "-CurrName" => "Вiдсотки з десяти тисячними частинами",
        "-language" => "RUS",
        "-RubOneUnit" => "целая,",
        "-RubTwoUnit" => "целых,",
        "-RubFiveUnit" => "целых,",
        "-RubSex" => "F",
        "-KopOneUnit" => "десятитысячная процента",
        "-KopTwoUnit" => "десятитысячные процента",
        "-KopFiveUnit" => "десятитысячных процента",
        "-KopSex" => "F"
      ),
      array(
        "-CurrID" => "559",
        "-CurrName" => "Вiдсотки з десяти тисячними частинами",
        "-language" => "UKR",
        "-RubOneUnit" => "ціла,",
        "-RubTwoUnit" => "цілих,",
        "-RubFiveUnit" => "цілих,",
        "-RubSex" => "F",
        "-KopOneUnit" => "десятитисячна відсотка",
        "-KopTwoUnit" => "десятитисячних відсотка",
        "-KopFiveUnit" => "десятитисячних відсотка",
        "-KopSex" => "M"
      )
    )
  ));

    const NUM0 = 0;
    const NUM1 = 1;
    const NUM2 = 2;
    const NUM3 = 3;
    const NUM4 = 4;
    const NUM5 = 5;
    const NUM6 = 6;
    const NUM7 = 7;
    const NUM8 = 8;
    const NUM9 = 9;
    const NUM10 = 10;
    const NUM11 = 11;
    const NUM12 = 12;
    const NUM100 = 100;
    const NUM1000 = 1000;
    const NUM10000 = 10000;
    const INDEX_0 = 0;
    const INDEX_1 = 1;
    const INDEX_2 = 2;
    const INDEX_3 = 3;
    private $currency, $language, $pennies, $messages;
    private $rubOneUnit, $rubTwoUnit, $rubFiveUnit, $kopOneUnit, $kopTwoUnit, $kopFiveUnit, $rubSex, $kopSex;

    /**
     * Converts percent to string.
     * @param amount the amount of percent
     * @param lang the language (RUS, UKR)
     * @return the string of percent
     */
    public static function percentToStr($amount, $lang) {
        if ($amount == null) {
            throw new Exception("amount is null");
        }
        if ($lang == null) {
            throw new Exception("Language is null");
        }
        $intPart = intval($amount);
        $fractPart = 0;
        $result = "";
        if ($amount == intval($amount)) {
            $result = (new MoneyToStr("PER10", $lang, "TEXT"))->convert($amount, 0);
        } else if (round($amount * MoneyToStr::NUM10, 4) == intval($amount * MoneyToStr::NUM10)) {
            $fractPart = round(($amount - $intPart) * MoneyToStr::NUM10);
            $result = (new MoneyToStr("PER10", $lang, "TEXT"))->convert($intPart, $fractPart);
        } else if (round($amount * MoneyToStr::NUM100, 4) == intval($amount * MoneyToStr::NUM100)) {
            $fractPart = round(($amount - $intPart) * MoneyToStr::NUM100);
            $result = (new MoneyToStr("PER100", $lang, "TEXT"))->convert($intPart, $fractPart);
        } else if (round($amount * MoneyToStr::NUM1000, 4) == intval($amount * MoneyToStr::NUM1000)) {
            $fractPart = round(($amount - $intPart) * MoneyToStr::NUM1000);
            $result = (new MoneyToStr("PER1000", $lang, "TEXT"))->convert($intPart, $fractPart);
        } else {
            $fractPart = round(($amount - $intPart) * MoneyToStr::NUM10000);
            $result = (new MoneyToStr("PER10000", $lang, "TEXT"))->convert($intPart, $fractPart);
        }
        return $result;
    }

    public function __construct($currency, $language, $pennies) {
        $this->currency = $currency;
        $this->language = $language;
        $this->pennies = $pennies;
        $languageElement = $language;
        $items = $this->currencyList['CurrencyList'][$languageElement]['item'];
        $this->messages = array();
        foreach ($items as $languageItem) {
            if ($languageItem["-text"] != null) {
                $this->messages[$languageItem["-value"]] = explode(",", $languageItem["-text"]);
            }
        }
        $currencyItem = $this->currencyList['CurrencyList'][$currency];
        $theISOElement = null;
        foreach ($currencyItem as $item) {
            if ($item["-language"] == $language) {
                $theISOElement = $item;
                next;
            }
        }
        if (theISOElement == null) {
            throw new Exception("Currency not found " . $currency);
        }
        $this->rubOneUnit = $theISOElement["-RubOneUnit"];
        $this->rubTwoUnit = $theISOElement["-RubTwoUnit"];
        $this->rubFiveUnit = $theISOElement["-RubFiveUnit"];
        $this->kopOneUnit = $theISOElement["-KopOneUnit"];
        $this->kopTwoUnit = $theISOElement["-KopTwoUnit"];
        $this->kopFiveUnit = $theISOElement["-KopFiveUnit"];
        $this->rubSex = $theISOElement["-RubSex"];
        $this->kopSex = $theISOElement["-KopSex"];
    }

    /**
     * Converts double value to the text description.
     *
     * @param theMoney
     *            the amount of money in format major.minor
     * @return the string description of money value
     */
    public function convertValue($theMoney) {
        $intPart = intval($theMoney);
        $fractPart = intval(round((($theMoney - $intPart) * MoneyToStr::NUM100)));
        if ($this->currency == "PER1000") {
            $fractPart = intval(round((($theMoney - $intPart) * MoneyToStr::NUM1000)));
        }
        return $this->convert($intPart, $fractPart);
    }

    /**
     * Converts number to currency. Usage: MoneyToStr moneyToStr = new MoneyToStr("UAH", "UKR", "NUMBER"); String result =
     * moneyToStr.convertValue(123D); Expected: result = сто двадцять три гривні 00 копійок
     *
     * @param theMoney
     *            the amount of money major currency
     * @param theKopeiki
     *            the amount of money minor currency
     * @return the string description of money value
     */
    public function convert($theMoney, $theKopeiki) {
        $money2str = new StringBuilder();
        $triadNum = 0;

        $intPart = intval($theMoney);
        if ($intPart == 0) {
            $money2str->append($this->messages["0"][0] . " ");
        }
        do {
            $theTriad = $intPart % MoneyToStr::NUM1000;
            $money2str->insert($this->triad2Word($theTriad, $triadNum, $this->rubSex));
            if ($triadNum == 0) {
                $range10 = intval(($theTriad % MoneyToStr::NUM100) / MoneyToStr::NUM10);
                $range = intval($theTriad % MoneyToStr::NUM10);
                if ($range10 == MoneyToStr::NUM1) {
                    $money2str->append($this->rubFiveUnit);
                } else {
                    switch ($range) {
                    case MoneyToStr::NUM1:
                        $money2str->append($this->rubOneUnit);
                        break;
                    case MoneyToStr::NUM2: case MoneyToStr::NUM3: case MoneyToStr::NUM4:
                        $money2str->append($this->rubTwoUnit);
                        break;
                    default:
                        $money2str->append($this->rubFiveUnit);
                        break;
                    }
                }
            }
            $intPart = intval($intPart / MoneyToStr::NUM1000);
            $triadNum += 1;
        } while ($intPart > 0);

        if ($this->pennies == "TEXT") {
            $money2str->append($this->language == "ENG" ? " and " : " ")->append($theKopeiki == 0 ? ($this->messages["0"][0] . " ") : $this->triad2Word($theKopeiki, 0, $this->kopSex));
        } else {
            $money2str->append(" " . ($theKopeiki < 10 ? "0" . strval($theKopeiki) : strval($theKopeiki)) . " ");
        }
        if ($theKopeiki == MoneyToStr::NUM11 || $theKopeiki == MoneyToStr::NUM12) {
            $money2str->append($this->kopFiveUnit);
        } else {
            switch ($theKopeiki % MoneyToStr::NUM10) {
            case MoneyToStr::NUM1:
                $money2str->append($this->kopOneUnit);
                break;
            case MoneyToStr::NUM2: case MoneyToStr::NUM3: case MoneyToStr::NUM4:
                $money2str->append($this->kopTwoUnit);
                break;
            default:
                $money2str->append($this->kopFiveUnit);
                break;
            }
        }
        return trim($money2str->toString());
    }

    public function triad2Word($triad, $triadNum, $sex) {
        $triadWord = new StringBuilder();

        if ($triad == 0) {
            return "";
        }

        $range = $this->check1($triad, $triadWord);
        if ($language == "ENG" && $triadWord->length() > 0 && $triad % MoneyToStr::NUM10 == 0) {
            $triadWord->deleteCharAt($triadWord.length() - 1);
            $triadWord->append(" ");
        }

        $range10 = $range;
        $range = $triad % MoneyToStr::NUM10;
        $this->check2($triadNum, $sex, $triadWord, $triad, $range10);
        switch ($triadNum) {
        case MoneyToStr::NUM0:
            break;
        case MoneyToStr::NUM1: case MoneyToStr::NUM2: case MoneyToStr::NUM3: case MoneyToStr::NUM4:
            if ($range10 == MoneyToStr::NUM1) {
                $triadWord->append($this->messages["1000_10"][$triadNum - 1] . " ");
            } else {
                switch ($range) {
                case MoneyToStr::NUM1:
                    $triadWord->append($this->messages["1000_1"][$triadNum - 1] . " ");
                    break;
                case MoneyToStr::NUM2: case MoneyToStr::NUM3: case MoneyToStr::NUM4:
                    $triadWord->append($this->messages["1000_234"][$triadNum - 1] . " ");
                    break;
                default:
                    $triadWord->append($this->messages["1000_5"][$triadNum - 1] . " ");
                    break;
                }
            }
            break;
        default:
            $triadWord->append("??? ");
            break;
        }
        return $triadWord->toString();
    }

    /**
     * @param triadNum the triad num
     * @param sex the sex
     * @param triadWord the triad word
     * @param triad the triad
     * @param range10 the range 10
     */
    public function check2($triadNum, $sex, $triadWord, $triad, $range10) {
        $range = $triad % MoneyToStr::NUM10;
        if ($range10 == 1) {
            $triadWord->append($this->messages["10_19"][$range] . " ");
        } else {
            switch ($range) {
            case MoneyToStr::NUM1:
                if ($triadNum == MoneyToStr::NUM1) {
                    $triadWord->append($this->messages["1"][MoneyToStr::INDEX_0] . " ");
                } else if ($triadNum == MoneyToStr::NUM2 || $triadNum == MoneyToStr::NUM3 || $triadNum == MoneyToStr::NUM4) {
                    $triadWord->append($this->messages["1"][MoneyToStr::INDEX_1] . " ");
                } else if ("M" == $sex) {
                    $triadWord->append($this->messages["1"][MoneyToStr::INDEX_2] . " ");
                } else if ("F" == $sex) {
                    $triadWord->append($this->messages["1"][MoneyToStr::INDEX_3] . " ");
                }
                break;
            case MoneyToStr::NUM2:
                if ($triadNum == MoneyToStr::NUM1) {
                    $triadWord->append($this->messages["2"][MoneyToStr::INDEX_0] . " ");
                } else if ($triadNum == MoneyToStr::NUM2 || $triadNum == MoneyToStr::NUM3 || $triadNum == MoneyToStr::NUM4) {
                    $triadWord->append($this->messages["2"][MoneyToStr::INDEX_1] . " ");
                } else if ("M" == $sex) {
                    $triadWord->append($this->messages["2"][MoneyToStr::INDEX_2] . " ");
                } else if ("F" == $sex) {
                    $triadWord->append($this->messages["2"][MoneyToStr::INDEX_3] . " ");
                }
                break;
            case MoneyToStr::NUM3: case MoneyToStr::NUM4: case MoneyToStr::NUM5: case MoneyToStr::NUM6: case MoneyToStr::NUM7: case MoneyToStr::NUM8: case MoneyToStr::NUM9:
                $triadWord->append($this->concat(["", "", ""], $this->messages["3_9"])[$range] . " ");
                break;
            }
        }
    }

    /**
     * @param triad the triad
     * @param triadWord the triad word
     * @return the range
     */
    private function check1($triad, $triadWord) {
        $range = intval($triad / MoneyToStr::NUM100);
        $triadWord->append($this->concat([""], $this->messages["100_900"])[$range]);

        $range = intval(($triad % MoneyToStr::NUM100) / MoneyToStr::NUM10);
        $triadWord->append($this->concat(["", ""], $this->messages["20_90"])[$range]);
        return $range;
    }

    private function concat($first, $second) {
        $result = array_merge($first, $second);
        return $result;
    }
}

?>
'''
 * $Id$
 *
 * Copyright 2013 Valentyn Kolesnikov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
'''

import math
'''
 * Converts numbers to words.
 *
 * @author Valentyn Kolesnikov
 * @version $Revision$ $Date$
'''
class MoneyToStr:
    class StringBuilder:
        _buffer = []
    
        def __init__(self):
            self._buffer = []
    
        def append(self, text):
            self._buffer.append(text)
            return self
    
        def insert(self, index, text):
            self._buffer.insert(index, text)
            return self
    
        def length(self):
            return self.toString().__len__()
    
        def deleteCharAt(self, index):
            str = self.toString()
            self._buffer = []
            self.append(str[:index])
            return self
    
        def toString(self):
            return "".join(self._buffer)


    currencyList = {
  "CurrencyList" : {
    "language" : { "-value" : "UKR" },
    "UKR" : {
      "item" : [
        {
          "-value" : "0",
          "-text" : "нуль"
        },
        {
          "-value" : "1000_10",
          "-text" : "тисяч,мільйонів,мільярдів,трильйонів"
        },
        {
          "-value" : "1000_1",
          "-text" : "тисяча,мільйон,мільярд,трильйон"
        },
        {
          "-value" : "1000_234",
          "-text" : "тисячі,мільйона,мільярда,трильйона"
        },
        {
          "-value" : "1000_5",
          "-text" : "тисяч,мільйонів,мільярдів,трильйонів"
        },
        {
          "-value" : "10_19",
          "-text" : "десять,одинадцять,дванадцять,тринадцять,чотирнадцять,п’ятнадцять,шiстнадцять,сiмнадцять,вiсiмнадцять,дев'ятнадцять"
        },
        {
          "-value" : "1",
          "-text" : "одна,один,один,одна"
        },
        {
          "-value" : "2",
          "-text" : "дві,два,два,дві"
        },
        {
          "-value" : "3_9",
          "-text" : "три,чотири,п’ять,шість,сім,вісім,дев’ять"
        },
        {
          "-value" : "100_900",
          "-text" : "сто ,двісті ,триста ,чотириста ,п’ятсот ,шістсот ,сімсот ,вісімсот ,дев’ятсот "
        },
        {
          "-value" : "20_90",
          "-text" : "двадцять ,тридцять ,сорок ,п’ятдесят ,шістдесят ,сімдесят ,вісімдесят ,дев’яносто "
        }
      ]
    },
    "RUS" : {
      "item" : [
        {
          "-value" : "0",
          "-text" : "ноль"
        },
        {
          "-value" : "1000_10",
          "-text" : "тысяч,миллионов,миллиардов,триллионов"
        },
        {
          "-value" : "1000_1",
          "-text" : "тысяча,миллион,миллиард,триллион"
        },
        {
          "-value" : "1000_234",
          "-text" : "тысячи,миллиона,миллиарда,триллиона"
        },
        {
          "-value" : "1000_5",
          "-text" : "тысяч,миллионов,миллиардов,триллионов"
        },
        {
          "-value" : "10_19",
          "-text" : "десять,одиннадцать,двенадцать,тринадцать,четырнадцать,пятнадцать,шестнадцать,семнадцать,восемнадцать,девятнадцать"
        },
        {
          "-value" : "1",
          "-text" : "одна,один,один,одна"
        },
        {
          "-value" : "2",
          "-text" : "две,два,два,две"
        },
        {
          "-value" : "3_9",
          "-text" : "три,четыре,пять,шесть,семь,восемь,девять"
        },
        {
          "-value" : "100_900",
          "-text" : "сто ,двести ,триста ,четыреста ,пятьсот ,шестьсот ,семьсот ,восемьсот ,девятьсот "
        },
        {
          "-value" : "20_90",
          "-text" : "двадцать ,тридцать ,сорок ,пятьдесят ,шестьдесят ,семьдесят ,восемьдесят ,девяносто "
        }
      ]
    },
    "ENG" : {
      "item" : [
        {
          "-value" : "0",
          "-text" : "zero"
        },
        {
          "-value" : "1000_10",
          "-text" : "thousand,million,billion,trillion"
        },
        {
          "-value" : "1000_1",
          "-text" : "thousand,million,billion,trillion"
        },
        {
          "-value" : "1000_234",
          "-text" : "thousand,million,billion,trillion"
        },
        {
          "-value" : "1000_5",
          "-text" : "thousand,million,billion,trillion"
        },
        {
          "-value" : "10_19",
          "-text" : "ten,eleven,twelve,thirteen,fourteen,fifteen,sixteen,seventeen,eighteen,nineteen"
        },
        {
          "-value" : "1",
          "-text" : "one,one,one,one"
        },
        {
          "-value" : "2",
          "-text" : "two,two,two,two"
        },
        {
          "-value" : "3_9",
          "-text" : "three,four,five,six,seven,eight,nine"
        },
        {
          "-value" : "100_900",
          "-text" : "one hundred ,two hundred ,three hundred ,four hundred ,five hundred ,six hundred ,seven hundred ,eight hundred ,nine hundred "
        },
        {
          "-value" : "20_90",
          "-text" : "twenty-,thirty-,forty-,fifty-,sixty-,seventy-,eighty-,ninety-"
        }
      ]
    },
    "RUR" : [
      {
        "-CurrID" : "810",
        "-CurrName" : "Российские рубли",
        "-language" : "RUS",
        "-RubOneUnit" : "рубль",
        "-RubTwoUnit" : "рубля",
        "-RubFiveUnit" : "рублей",
        "-RubSex" : "M",
        "-KopOneUnit" : "копейка",
        "-KopTwoUnit" : "копейки",
        "-KopFiveUnit" : "копеек",
        "-KopSex" : "F"
      },
      {
        "-CurrID" : "810",
        "-CurrName" : "Российские рубли",
        "-language" : "UKR",
        "-RubOneUnit" : "рубль",
        "-RubTwoUnit" : "рубля",
        "-RubFiveUnit" : "рублів",
        "-RubSex" : "M",
        "-KopOneUnit" : "копійка",
        "-KopTwoUnit" : "копійки",
        "-KopFiveUnit" : "копійок",
        "-KopSex" : "F"
      }
    ],
    "UAH" : [
      {
        "-CurrID" : "980",
        "-CurrName" : "Украинскі гривні",
        "-language" : "RUS",
        "-RubOneUnit" : "гривня",
        "-RubTwoUnit" : "гривни",
        "-RubFiveUnit" : "гривень",
        "-RubSex" : "F",
        "-KopOneUnit" : "копейка",
        "-KopTwoUnit" : "копейки",
        "-KopFiveUnit" : "копеек",
        "-KopSex" : "F"
      },
      {
        "-CurrID" : "980",
        "-CurrName" : "Украинскі гривні",
        "-language" : "UKR",
        "-RubOneUnit" : "гривня",
        "-RubTwoUnit" : "гривні",
        "-RubFiveUnit" : "гривень",
        "-RubSex" : "F",
        "-KopOneUnit" : "копійка",
        "-KopTwoUnit" : "копійки",
        "-KopFiveUnit" : "копійок",
        "-KopSex" : "F"
      }
    ],
    "USD" : [
      {
        "-CurrID" : "840",
        "-CurrName" : "Долари США",
        "-language" : "RUS",
        "-RubOneUnit" : "долар",
        "-RubTwoUnit" : "долара",
        "-RubFiveUnit" : "доларів",
        "-RubSex" : "M",
        "-KopOneUnit" : "цент",
        "-KopTwoUnit" : "цена",
        "-KopFiveUnit" : "центов",
        "-KopSex" : "M"
      },
      {
        "-CurrID" : "840",
        "-CurrName" : "Долари США",
        "-language" : "UKR",
        "-RubOneUnit" : "долар",
        "-RubTwoUnit" : "долара",
        "-RubFiveUnit" : "доларів",
        "-RubSex" : "M",
        "-KopOneUnit" : "цент",
        "-KopTwoUnit" : "цена",
        "-KopFiveUnit" : "центів",
        "-KopSex" : "M"
      },
      {
        "-CurrID" : "840",
        "-CurrName" : "Долари США",
        "-language" : "ENG",
        "-RubOneUnit" : "dollar",
        "-RubTwoUnit" : "dollars",
        "-RubFiveUnit" : "dollars",
        "-RubSex" : "M",
        "-KopOneUnit" : "cent",
        "-KopTwoUnit" : "cents",
        "-KopFiveUnit" : "cents",
        "-KopSex" : "M"
      }
     ],
    "PER10" : [
      {
        "-CurrID" : "556",
        "-CurrName" : "Вiдсотки з десятими частинами",
        "-language" : "RUS",
        "-RubOneUnit" : "целая,",
        "-RubTwoUnit" : "целых,",
        "-RubFiveUnit" : "целых,",
        "-RubSex" : "F",
        "-KopOneUnit" : "десятая процента",
        "-KopTwoUnit" : "десятых процента",
        "-KopFiveUnit" : "десятых процента",
        "-KopSex" : "F"
      },
      {
        "-CurrID" : "556",
        "-CurrName" : "Вiдсотки з десятими частинами",
        "-language" : "UKR",
        "-RubOneUnit" : "ціла,",
        "-RubTwoUnit" : "цілих,",
        "-RubFiveUnit" : "цілих,",
        "-RubSex" : "F",
        "-KopOneUnit" : "десята відсотка",
        "-KopTwoUnit" : "десятих відсотка",
        "-KopFiveUnit" : "десятих відсотка",
        "-KopSex" : "F"
      }
    ],
    "PER100" : [
      {
        "-CurrID" : "557",
        "-CurrName" : "Вiдсотки з сотими частинами",
        "-language" : "RUS",
        "-RubOneUnit" : "целая,",
        "-RubTwoUnit" : "целых,",
        "-RubFiveUnit" : "целых,",
        "-RubSex" : "F",
        "-KopOneUnit" : "сотая процента",
        "-KopTwoUnit" : "сотых процента",
        "-KopFiveUnit" : "сотых процента",
        "-KopSex" : "F"
      },
      {
        "-CurrID" : "557",
        "-CurrName" : "Вiдсотки з сотими частинами",
        "-language" : "UKR",
        "-RubOneUnit" : "ціла,",
        "-RubTwoUnit" : "цілих,",
        "-RubFiveUnit" : "цілих,",
        "-RubSex" : "F",
        "-KopOneUnit" : "сота відсотка",
        "-KopTwoUnit" : "сотих відсотка",
        "-KopFiveUnit" : "сотих відсотка",
        "-KopSex" : "F"
      }
    ],
    "PER1000" : [
      {
        "-CurrID" : "558",
        "-CurrName" : "Вiдсотки з тисячними частинами",
        "-language" : "RUS",
        "-RubOneUnit" : "целая,",
        "-RubTwoUnit" : "целых,",
        "-RubFiveUnit" : "целых,",
        "-RubSex" : "F",
        "-KopOneUnit" : "тысячная процента",
        "-KopTwoUnit" : "тысячных процента",
        "-KopFiveUnit" : "тысячных процента",
        "-KopSex" : "F"
      },
      {
        "-CurrID" : "558",
        "-CurrName" : "Вiдсотки з тисячними частинами",
        "-language" : "UKR",
        "-RubOneUnit" : "ціла,",
        "-RubTwoUnit" : "цілих,",
        "-RubFiveUnit" : "цілих,",
        "-RubSex" : "F",
        "-KopOneUnit" : "тисячна відсотка",
        "-KopTwoUnit" : "тисячних відсотка",
        "-KopFiveUnit" : "тисячних відсотка",
        "-KopSex" : "F"
      }
    ],
    "PER10000" : [
      {
        "-CurrID" : "559",
        "-CurrName" : "Вiдсотки з десяти тисячними частинами",
        "-language" : "RUS",
        "-RubOneUnit" : "целая,",
        "-RubTwoUnit" : "целых,",
        "-RubFiveUnit" : "целых,",
        "-RubSex" : "F",
        "-KopOneUnit" : "десятитысячная процента",
        "-KopTwoUnit" : "десятитысячные процента",
        "-KopFiveUnit" : "десятитысячных процента",
        "-KopSex" : "F"
      },
      {
        "-CurrID" : "559",
        "-CurrName" : "Вiдсотки з десяти тисячними частинами",
        "-language" : "UKR",
        "-RubOneUnit" : "ціла,",
        "-RubTwoUnit" : "цілих,",
        "-RubFiveUnit" : "цілих,",
        "-RubSex" : "F",
        "-KopOneUnit" : "десятитисячна відсотка",
        "-KopTwoUnit" : "десятитисячних відсотка",
        "-KopFiveUnit" : "десятитисячних відсотка",
        "-KopSex" : "M"
      }
    ]
  }
    }
    NUM0 = 0
    NUM1 = 1
    NUM2 = 2
    NUM3 = 3
    NUM4 = 4
    NUM5 = 5
    NUM6 = 6
    NUM7 = 7
    NUM8 = 8
    NUM9 = 9
    NUM10 = 10
    NUM11 = 11
    NUM12 = 12
    NUM100 = 100
    NUM1000 = 1000
    NUM10000 = 10000
    INDEX_0 = 0
    INDEX_1 = 1
    INDEX_2 = 2
    INDEX_3 = 3

    @staticmethod
    def percentToStr(amount, lang):
        if amount is None:
            raise Exception("amount is null")
        if lang is None:
            raise Exception("Language is null")
        intPart = int(amount);
        fractPart = 0
        result = ""
        if amount == int(amount):
            result = MoneyToStr("PER10", lang, "TEXT").convert(amount, 0);
        elif (round(amount * MoneyToStr.NUM10, 4) == int(amount * MoneyToStr.NUM10)):
            fractPart = round((amount - intPart) * MoneyToStr.NUM10)
            result = MoneyToStr("PER10", lang, "TEXT").convert(intPart, fractPart)
        elif (round(amount * MoneyToStr.NUM100, 4) == int(amount * MoneyToStr.NUM100)):
            fractPart = round((amount - intPart) * MoneyToStr.NUM100)
            result = MoneyToStr("PER100", lang, "TEXT").convert(intPart, fractPart)
        elif (round(amount * MoneyToStr.NUM1000, 4) == int(amount * MoneyToStr.NUM1000)):
            fractPart = ((amount - intPart) * MoneyToStr.NUM1000).round
            result = MoneyToStr("PER1000", lang, "TEXT").convert(intPart, fractPart)
        else:
            fractPart = round((amount - intPart) * MoneyToStr.NUM10000)
            result = MoneyToStr("PER10000", lang, "TEXT").convert(intPart, fractPart)
        return result

    def __init__(self, currency, language, pennies):
        if currency is None:
            raise Exception("Currency is null")
        if language is None:
            raise Exception("Language is null")
        if pennies is None:
            raise Exception("Pennies is null")
        self.currency = currency
        self.language = language
        self.pennies = pennies
        languageElement = language;
        items = self.currencyList["CurrencyList"][languageElement]["item"];
        self.messages = {}
        for languageItem in items:
            if languageItem["-text"] is not None:
                self.messages[languageItem["-value"]] = languageItem["-text"].split(',')
        currencyItem = self.currencyList["CurrencyList"][self.currency]
        theISOElement = None
        for item in currencyItem:
            if item["-language"] == self.language:
                theISOElement = item
                break
        if theISOElement is None:
            raise Exception("Currency not found " + self.currency)
        self.rubOneUnit = theISOElement["-RubOneUnit"]
        self.rubTwoUnit = theISOElement["-RubTwoUnit"]
        self.rubFiveUnit = theISOElement["-RubFiveUnit"]
        self.kopOneUnit = theISOElement["-KopOneUnit"]
        self.kopTwoUnit = theISOElement["-KopTwoUnit"]
        self.kopFiveUnit = theISOElement["-KopFiveUnit"]
        self.rubSex = theISOElement["-RubSex"]
        self.kopSex = theISOElement["-KopSex"]

    '''
     * Converts double value to the text description.
     *
     * @param theMoney
     *            the amount of money in format major.minor
     * @return the string description of money value
    '''
    def convertValue(self, theMoney):
        intPart = int(theMoney)
        fractPart = int(round(((theMoney - intPart) * self.NUM100)))
        if self.currency == "PER1000":
            fractPart = int(round(((theMoney - intPart) * self.NUM1000)))
        return self.convert(intPart, fractPart)

    '''
     * Converts number to currency. Usage: MoneyToStr moneyToStr = new MoneyToStr("UAH", "UKR", "NUMBER"); String result =
     * moneyToStr.convertValue(123D); Expected: result = сто двадцять три гривні 00 копійок
     *
     * @param theMoney
     *            the amount of money major currency
     * @param theKopeiki
     *            the amount of money minor currency
     * @return the string description of money value
    '''
    def convert(self, theMoney, theKopeiki):
        money2str = MoneyToStr.StringBuilder()
        triadNum = 0

        intPart = int(theMoney)
        if intPart == 0:
            money2str.append(messages["0"][0] + " ")
        while True:
            theTriad = intPart % self.NUM1000
            money2str.insert(0, self.triad2Word(theTriad, triadNum, self.rubSex))
            if triadNum == 0:
                range10 = int((theTriad % self.NUM100) / self.NUM10)
                range = int(theTriad % self.NUM10)
                if range10 == self.NUM1:
                    money2str.append(self.rubFiveUnit);
                else:
                    if range == self.NUM1:
                        money2str.append(self.rubOneUnit)
                    elif range == self.NUM2 or range == self.NUM3 or range == self.NUM4:
                        money2str.append(self.rubTwoUnit)
                    else:
                        money2str.append(self.rubFiveUnit)
            intPart = int(intPart / self.NUM1000);
            triadNum += 1;
            if intPart <= 0:
                break

        if self.pennies == "TEXT":
            param1 = " "
            if self.language == "ENG":
                param1 = " and "
            param2 = self.triad2Word(theKopeiki, 0, self.kopSex)
            if int(theKopeiki) == 0:
                param2 = self.messages["0"][0] + " "
            money2str.append(param1).append(param2)
        else:
            param = str(theKopeiki)
            if theKopeiki < 10:
                param = "0" + str(theKopeiki)
            money2str.append(" " + param + " ");
        if theKopeiki == self.NUM11 or theKopeiki == self.NUM12:
            money2str.append(self.kopFiveUnit);
        else:
            if theKopeiki % self.NUM10 == self.NUM1:
                money2str.append(self.kopOneUnit);
            elif theKopeiki % self.NUM10 == self.NUM2 or theKopeiki % self.NUM10 == self.NUM3 or theKopeiki % self.NUM10 == self.NUM4:
                money2str.append(self.kopTwoUnit)
            else:
                money2str.append(self.kopFiveUnit)
        return money2str.toString().strip();

    def triad2Word(self, triad, triadNum, sex):
        triadWord = MoneyToStr.StringBuilder();

        if triad == 0:
            return ""

        range = self.check1(triad, triadWord);
        if self.language == "ENG" and triadWord.length() > 0 and triad % self.NUM10 == 0:
            triadWord.deleteCharAt(triadWord.length() - 1)
            triadWord.append(" ")

        range10 = range
        range = triad % self.NUM10
        self.check2(triadNum, sex, triadWord, triad, range10);
        if int(triadNum) == self.NUM0:
            triadWord.append("")
        elif triadNum == self.NUM1 or triadNum == self.NUM2 or triadNum == self.NUM3 or triadNum == self.NUM4:
            if range10 == self.NUM1:
                triadWord.append(self.messages["1000_10"][triadNum - 1] + " ");
            else:
                if range ==self. NUM1:
                    triadWord.append(self.messages["1000_1"][triadNum - 1] + " ")
                elif range == self.NUM2 or range == self.NUM3 or range == self.NUM4:
                    triadWord.append(self.messages["1000_234"][triadNum - 1] + " ")
                else:
                    triadWord.append(self.messages["1000_5"][triadNum - 1] + " ")
        else:
            triadWord.append("??? ")
        return triadWord.toString()

    '''
     * @param triadNum the triad num
     * @param sex the sex
     * @param triadWord the triad word
     * @param triad the triad
     * @param range10 the range 10
    '''
    def check2(self, triadNum, sex, triadWord, triad, range10):
        range = int(triad % self.NUM10)
        if range10 == 1:
            triadWord.append(self.messages["10_19"][range] + " ");
        else:
            if range == self.NUM1:
                if triadNum == self.NUM1:
                    triadWord.append(self.messages["1"][self.INDEX_0] + " ")
                elif triadNum == self.NUM2 or triadNum == self.NUM3 or triadNum == self.NUM4:
                    triadWord.append(self.messages["1"][self.INDEX_1] + " ")
                elif "M" == sex:
                    triadWord.append(self.messages["1"][self.INDEX_2] + " ")
                elif "F" == sex:
                    triadWord.append(self.messages["1"][self.INDEX_3] + " ")
            elif range == self.NUM2:
                if triadNum == self.NUM1:
                    triadWord.append(self.messages["2"][self.INDEX_0] + " ")
                elif triadNum == self.NUM2 or triadNum == self.NUM3 or triadNum == self.NUM4:
                    triadWord.append(self.messages["2"][self.INDEX_1] + " ")
                elif "M" == sex:
                    triadWord.append(self.messages["2"][self.INDEX_2] + " ")
                elif "F" == sex:
                    triadWord.append(self.messages["2"][self.INDEX_3] + " ")
            elif range == self.NUM3 or range == self.NUM4 or range == self.NUM5 or range == self.NUM6 or range == self.NUM7 or range == self.NUM8 or range == self.NUM9:
                triadWord.append(self.concat(["", "", ""], self.messages["3_9"])[range] + " ");

    '''
     * @param triad the triad
     * @param triadWord the triad word
     * @return the range
    '''
    def check1(self, triad, triadWord):
        range = int(triad / self.NUM100)
        triadWord.append(self.concat([""], self.messages["100_900"])[range])

        range = int((triad % self.NUM100) / self.NUM10);
        triadWord.append(self.concat(["", ""], self.messages["20_90"])[range]);
        return range;

    def concat(self, first, second):
        result = []
        result.extend(first)
        result.extend(second)
        return result
=begin
/*
 * $Id$
 *
 * Copyright 2013 Valentyn Kolesnikov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
=end

=begin
/**
 * Converts numbers to symbols.
 *
 * @author Valentyn V Kolesnikov
 * @version $Revision$ $Date$
 */
=end
class MoneyToStr
@@currencyList =
{
  "CurrencyList" => {
    "language" => { "-value" => "UKR" },
    "UKR" => {
      "item" => [
        {
          "-value" => "0",
          "-text" => "нуль"
        },
        {
          "-value" => "1000_10",
          "-text" => "тисяч,мільйонів,мільярдів,трильйонів"
        },
        {
          "-value" => "1000_1",
          "-text" => "тисяча,мільйон,мільярд,трильйон"
        },
        {
          "-value" => "1000_234",
          "-text" => "тисячі,мільйона,мільярда,трильйона"
        },
        {
          "-value" => "1000_5",
          "-text" => "тисяч,мільйонів,мільярдів,трильйонів"
        },
        {
          "-value" => "10_19",
          "-text" => "десять,одинадцять,дванадцять,тринадцять,чотирнадцять,п’ятнадцять,шiстнадцять,сiмнадцять,вiсiмнадцять,дев'ятнадцять"
        },
        {
          "-value" => "1",
          "-text" => "одна,один,один,одна"
        },
        {
          "-value" => "2",
          "-text" => "дві,два,два,дві"
        },
        {
          "-value" => "3_9",
          "-text" => "три,чотири,п’ять,шість,сім,вісім,дев’ять"
        },
        {
          "-value" => "100_900",
          "-text" => "сто ,двісті ,триста ,чотириста ,п’ятсот ,шістсот ,сімсот ,вісімсот ,дев’ятсот "
        },
        {
          "-value" => "20_90",
          "-text" => "двадцять ,тридцять ,сорок ,п’ятдесят ,шістдесят ,сімдесят ,вісімдесят ,дев’яносто "
        }
      ]
    },
    "RUS" => {
      "item" => [
        {
          "-value" => "0",
          "-text" => "ноль"
        },
        {
          "-value" => "1000_10",
          "-text" => "тысяч,миллионов,миллиардов,триллионов"
        },
        {
          "-value" => "1000_1",
          "-text" => "тысяча,миллион,миллиард,триллион"
        },
        {
          "-value" => "1000_234",
          "-text" => "тысячи,миллиона,миллиарда,триллиона"
        },
        {
          "-value" => "1000_5",
          "-text" => "тысяч,миллионов,миллиардов,триллионов"
        },
        {
          "-value" => "10_19",
          "-text" => "десять,одиннадцать,двенадцать,тринадцать,четырнадцать,пятнадцать,шестнадцать,семнадцать,восемнадцать,девятнадцать"
        },
        {
          "-value" => "1",
          "-text" => "одна,один,один,одна"
        },
        {
          "-value" => "2",
          "-text" => "две,два,два,две"
        },
        {
          "-value" => "3_9",
          "-text" => "три,четыре,пять,шесть,семь,восемь,девять"
        },
        {
          "-value" => "100_900",
          "-text" => "сто ,двести ,триста ,четыреста ,пятьсот ,шестьсот ,семьсот ,восемьсот ,девятьсот "
        },
        {
          "-value" => "20_90",
          "-text" => "двадцать ,тридцать ,сорок ,пятьдесят ,шестьдесят ,семьдесят ,восемьдесят ,девяносто "
        }
      ]
    },
    "ENG" => {
      "item" => [
        {
          "-value" => "0",
          "-text" => "zero"
        },
        {
          "-value" => "1000_10",
          "-text" => "thousand,million,billion,trillion"
        },
        {
          "-value" => "1000_1",
          "-text" => "thousand,million,billion,trillion"
        },
        {
          "-value" => "1000_234",
          "-text" => "thousand,million,billion,trillion"
        },
        {
          "-value" => "1000_5",
          "-text" => "thousand,million,billion,trillion"
        },
        {
          "-value" => "10_19",
          "-text" => "ten,eleven,twelve,thirteen,fourteen,fifteen,sixteen,seventeen,eighteen,nineteen"
        },
        {
          "-value" => "1",
          "-text" => "one,one,one,one"
        },
        {
          "-value" => "2",
          "-text" => "two,two,two,two"
        },
        {
          "-value" => "3_9",
          "-text" => "three,four,five,six,seven,eight,nine"
        },
        {
          "-value" => "100_900",
          "-text" => "one hundred ,two hundred ,three hundred ,four hundred ,five hundred ,six hundred ,seven hundred ,eight hundred ,nine hundred "
        },
        {
          "-value" => "20_90",
          "-text" => "twenty-,thirty-,forty-,fifty-,sixty-,seventy-,eighty-,ninety-"
        }
      ]
    },
    "RUR" => [
      {
        "-CurrID" => "810",
        "-CurrName" => "Российские рубли",
        "-language" => "RUS",
        "-RubOneUnit" => "рубль",
        "-RubTwoUnit" => "рубля",
        "-RubFiveUnit" => "рублей",
        "-RubSex" => "M",
        "-KopOneUnit" => "копейка",
        "-KopTwoUnit" => "копейки",
        "-KopFiveUnit" => "копеек",
        "-KopSex" => "F"
      },
      {
        "-CurrID" => "810",
        "-CurrName" => "Российские рубли",
        "-language" => "UKR",
        "-RubOneUnit" => "рубль",
        "-RubTwoUnit" => "рубля",
        "-RubFiveUnit" => "рублей",
        "-RubSex" => "M",
        "-KopOneUnit" => "копейка",
        "-KopTwoUnit" => "копейки",
        "-KopFiveUnit" => "копеек",
        "-KopSex" => "F"
      }
    ],
    "UAH" => [
      {
        "-CurrID" => "980",
        "-CurrName" => "Украинскі гривні",
        "-language" => "RUS",
        "-RubOneUnit" => "гривня",
        "-RubTwoUnit" => "гривни",
        "-RubFiveUnit" => "гривень",
        "-RubSex" => "F",
        "-KopOneUnit" => "копейка",
        "-KopTwoUnit" => "копейки",
        "-KopFiveUnit" => "копеек",
        "-KopSex" => "F"
      },
      {
        "-CurrID" => "980",
        "-CurrName" => "Украинскі гривні",
        "-language" => "UKR",
        "-RubOneUnit" => "гривня",
        "-RubTwoUnit" => "гривні",
        "-RubFiveUnit" => "гривень",
        "-RubSex" => "F",
        "-KopOneUnit" => "копійка",
        "-KopTwoUnit" => "копійки",
        "-KopFiveUnit" => "копійок",
        "-KopSex" => "F"
      }
    ],
    "USD" => [
      {
        "-CurrID" => "840",
        "-CurrName" => "Долари США",
        "-language" => "RUS",
        "-RubOneUnit" => "долар",
        "-RubTwoUnit" => "долара",
        "-RubFiveUnit" => "доларів",
        "-RubSex" => "M",
        "-KopOneUnit" => "цент",
        "-KopTwoUnit" => "цена",
        "-KopFiveUnit" => "центов",
        "-KopSex" => "M"
      },
      {
        "-CurrID" => "840",
        "-CurrName" => "Долари США",
        "-language" => "UKR",
        "-RubOneUnit" => "долар",
        "-RubTwoUnit" => "долара",
        "-RubFiveUnit" => "доларів",
        "-RubSex" => "M",
        "-KopOneUnit" => "цент",
        "-KopTwoUnit" => "цена",
        "-KopFiveUnit" => "центів",
        "-KopSex" => "M"
      },
      {
        "-CurrID" => "840",
        "-CurrName" => "Долари США",
        "-language" => "ENG",
        "-RubOneUnit" => "dollar",
        "-RubTwoUnit" => "dollars",
        "-RubFiveUnit" => "dollars",
        "-RubSex" => "M",
        "-KopOneUnit" => "cent",
        "-KopTwoUnit" => "cents",
        "-KopFiveUnit" => "cents",
        "-KopSex" => "M"
      }
     ],
    "PER10" => [
      {
        "-CurrID" => "556",
        "-CurrName" => "Вiдсотки з десятими частинами",
        "-language" => "RUS",
        "-RubOneUnit" => "целая,",
        "-RubTwoUnit" => "целых,",
        "-RubFiveUnit" => "целых,",
        "-RubSex" => "F",
        "-KopOneUnit" => "десятая процента",
        "-KopTwoUnit" => "десятых процента",
        "-KopFiveUnit" => "десятых процента",
        "-KopSex" => "F"
      },
      {
        "-CurrID" => "556",
        "-CurrName" => "Вiдсотки з десятими частинами",
        "-language" => "UKR",
        "-RubOneUnit" => "ціла,",
        "-RubTwoUnit" => "цілих,",
        "-RubFiveUnit" => "цілих,",
        "-RubSex" => "F",
        "-KopOneUnit" => "десята відсотка",
        "-KopTwoUnit" => "десятих відсотка",
        "-KopFiveUnit" => "десятих відсотка",
        "-KopSex" => "F"
      }
    ],
    "PER100" => [
      {
        "-CurrID" => "557",
        "-CurrName" => "Вiдсотки з сотими частинами",
        "-language" => "RUS",
        "-RubOneUnit" => "целая,",
        "-RubTwoUnit" => "целых,",
        "-RubFiveUnit" => "целых,",
        "-RubSex" => "F",
        "-KopOneUnit" => "сотая процента",
        "-KopTwoUnit" => "сотых процента",
        "-KopFiveUnit" => "сотых процента",
        "-KopSex" => "F"
      },
      {
        "-CurrID" => "557",
        "-CurrName" => "Вiдсотки з сотими частинами",
        "-language" => "UKR",
        "-RubOneUnit" => "ціла,",
        "-RubTwoUnit" => "цілих,",
        "-RubFiveUnit" => "цілих,",
        "-RubSex" => "F",
        "-KopOneUnit" => "сота відсотка",
        "-KopTwoUnit" => "сотих відсотка",
        "-KopFiveUnit" => "сотих відсотка",
        "-KopSex" => "F"
      }
    ],
    "PER1000" => [
      {
        "-CurrID" => "558",
        "-CurrName" => "Вiдсотки з тисячними частинами",
        "-language" => "RUS",
        "-RubOneUnit" => "целая,",
        "-RubTwoUnit" => "целых,",
        "-RubFiveUnit" => "целых,",
        "-RubSex" => "F",
        "-KopOneUnit" => "тысячная процента",
        "-KopTwoUnit" => "тысячных процента",
        "-KopFiveUnit" => "тысячных процента",
        "-KopSex" => "F"
      },
      {
        "-CurrID" => "558",
        "-CurrName" => "Вiдсотки з тисячними частинами",
        "-language" => "UKR",
        "-RubOneUnit" => "ціла,",
        "-RubTwoUnit" => "цілих,",
        "-RubFiveUnit" => "цілих,",
        "-RubSex" => "F",
        "-KopOneUnit" => "тисячна відсотка",
        "-KopTwoUnit" => "тисячних відсотка",
        "-KopFiveUnit" => "тисячних відсотка",
        "-KopSex" => "F"
      }
    ],
    "PER10000" => [
      {
        "-CurrID" => "559",
        "-CurrName" => "Вiдсотки з десяти тисячними частинами",
        "-language" => "RUS",
        "-RubOneUnit" => "целая,",
        "-RubTwoUnit" => "целых,",
        "-RubFiveUnit" => "целых,",
        "-RubSex" => "F",
        "-KopOneUnit" => "десятитысячная процента",
        "-KopTwoUnit" => "десятитысячные процента",
        "-KopFiveUnit" => "десятитысячных процента",
        "-KopSex" => "F"
      },
      {
        "-CurrID" => "559",
        "-CurrName" => "Вiдсотки з десяти тисячними частинами",
        "-language" => "UKR",
        "-RubOneUnit" => "ціла,",
        "-RubTwoUnit" => "цілих,",
        "-RubFiveUnit" => "цілих,",
        "-RubSex" => "F",
        "-KopOneUnit" => "десятитисячна відсотка",
        "-KopTwoUnit" => "десятитисячних відсотка",
        "-KopFiveUnit" => "десятитисячних відсотка",
        "-KopSex" => "M"
      }
    ]
  }
}
    #/** Currency. */
class Currency
        #/**.*/
        UAH = :UAH
        #/**.*/
        RUR = :RUR
        #/**.*/
        USD = :USD
        #/**.*/
        PER10 = :PER10
        #/**.*/
        PER100 = :PER100
        #/**.*/
        PER1000 = :PER1000
        #/**.*/
        PER10000 = :PER10000
end

    #/** Language. */
class Language
        #/**.*/
        RUS = :RUS
        #/**.*/
        UKR = :UKR
        #/**.*/
        ENG = :ENG
end

    #/** Pennies. */
class Pennies
        #/**.*/
        NUMBER = :NUMBER
        #/**.*/
        TEXT = :TEXT
end

class StringBuilder
    def initialize()
        @buffer = [];
    end

    def append(text)
            @buffer[@buffer.length] = text;
            return self;
    end
        
    def insert(index, text)
            @buffer.insert(index, text);
            return self;
    end

    def length()
            return toString().length;
    end

    def deleteCharAt(index)
            str = toString();
            initialize();
            append(str[0, index]); 
            return self;
    end

    def toString()
            return @buffer.join("");
    end
end

    NUM0 = 0
    NUM1 = 1
    NUM2 = 2
    NUM3 = 3
    NUM4 = 4
    NUM5 = 5
    NUM6 = 6
    NUM7 = 7
    NUM8 = 8
    NUM9 = 9
    NUM10 = 10
    NUM11 = 11
    NUM12 = 12
    NUM100 = 100
    NUM1000 = 1000
    NUM10000 = 10000
    INDEX_0 = 0
    INDEX_1 = 1
    INDEX_2 = 2
    INDEX_3 = 3

    def self.percentToStr(amount, lang)
        if (amount == nil)
            raise ArgumentError, "amount is nil"
        end
        if (lang == nil)
            raise ArgumentError, "Language is nil"
        end
        intPart = amount.to_i;
        fractPart = 0;
        @result = "";
        if (amount == amount.to_i)
            result = MoneyToStr.new(Currency::PER10, lang, Pennies::TEXT).convert(amount, 0);
        elsif ((amount * MoneyToStr::NUM10).round(4) == (amount * MoneyToStr::NUM10).to_i)
            fractPart = ((amount - intPart) * MoneyToStr::NUM10).round;
            result = MoneyToStr.new(Currency::PER10, lang, Pennies::TEXT).convert(intPart, fractPart);
        elsif ((amount * MoneyToStr::NUM100).round(4) == (amount * MoneyToStr::NUM100).to_i)
            fractPart = ((amount - intPart) * MoneyToStr::NUM100).round;
            result = MoneyToStr.new(Currency::PER100, lang, Pennies::TEXT).convert(intPart, fractPart);
        elsif ((amount * MoneyToStr::NUM1000).round(4) == (amount * MoneyToStr::NUM1000).to_i)
            fractPart = ((amount - intPart) * MoneyToStr::NUM1000).round;
            result = MoneyToStr.new(Currency::PER1000, lang, Pennies::TEXT).convert(intPart, fractPart);
        else
            fractPart = ((amount - intPart) * MoneyToStr::NUM10000).round;
            result = MoneyToStr.new(Currency::PER10000, lang, Pennies::TEXT).convert(intPart, fractPart);
        end
        return result;
    end

    def initialize(currency, language, pennies)
        @currency = currency.to_s;
        @language = language.to_s;
        @pennies = pennies.to_s;
        @languageElement = language.to_s;
        @items = @@currencyList['CurrencyList'][@languageElement]['item'];
        @messages = {};
        for languageItem in @items
            if languageItem["-text"] != nil
                @messages[languageItem["-value"]] = languageItem["-text"].split(",");
            end
        end
        currencyItem = @@currencyList['CurrencyList'][@currency]
        theISOElement = nil;
        for item in currencyItem
            if item["-language"] == @language
                theISOElement = item;
                next;
            end
        end
        if theISOElement == nil
            raise ArgumentError, "Currency not found " + @currency
        end
        @rubOneUnit = theISOElement["-RubOneUnit"];
        @rubTwoUnit = theISOElement["-RubTwoUnit"];
        @rubFiveUnit = theISOElement["-RubFiveUnit"];
        @kopOneUnit = theISOElement["-KopOneUnit"];
        @kopTwoUnit = theISOElement["-KopTwoUnit"];
        @kopFiveUnit = theISOElement["-KopFiveUnit"];
        @rubSex = theISOElement["-RubSex"];
        @kopSex = theISOElement["-KopSex"];
    end

=begin
    /**
     * Converts double value to the text description.
     *
     * @param theMoney
     *            the amount of money in format major.minor
     * @return the string description of money value
     */
=end
    def convertValue(theMoney)
        if theMoney == nil
            raise ArgumentError, "theMoney is nil"
        end
        intPart = theMoney.to_i
        fractPart = ((theMoney - intPart) * NUM100).round
        if @currency == Currency::PER1000.to_s
            fractPart = ((theMoney - intPart) * NUM1000).round
        end
        return convert(intPart, fractPart);
    end

=begin
    /**
     * Converts number to currency. Usage: MoneyToStr moneyToStr = new MoneyToStr("UAH"); String result =
     * moneyToStr.convert(123D); Expected: result = сто двадцять три гривні 00 копійок
     *
     * @param theMoney
     *            the amount of money major currency
     * @param theKopeiki
     *            the amount of money minor currency
     * @return the string description of money value
     */
=end
    def convert(theMoney, theKopeiki)
        if theMoney == nil
            raise ArgumentError, "theMoney is nil"
        end
        if theKopeiki == nil
            raise ArgumentError, "theKopeiki is nil"
        end
        money2str = StringBuilder.new();
        triadNum = 0;
        theTriad = nil;

        intPart = theMoney;
        if intPart == 0
            money2str.append(messages["0"][0] + " ");
        end
        begin
            theTriad = intPart % NUM1000;
            money2str.insert(0, triad2Word(theTriad, triadNum, @rubSex));
            if triadNum == 0
                range10 = (theTriad % NUM100) / NUM10;
                range = theTriad % NUM10;
                if range10 == NUM1
                    money2str.append(@rubFiveUnit);
                else
                    case range
                    when NUM1
                        money2str.append(@rubOneUnit)
                    when NUM2, NUM3,  NUM4
                        money2str.append(@rubTwoUnit)
                    else
                        money2str.append(@rubFiveUnit)
                    end
                end
            end
            intPart = intPart / NUM1000;
            triadNum += 1;
        end while intPart > 0

        if @pennies == Pennies::TEXT.to_s
            money2str.append(@language == Language::ENG.to_s ? " and " : " ").append(theKopeiki == 0 ? @messages["0"][0] + " " : triad2Word(theKopeiki, 0, @kopSex));
        else
            money2str.append(" " + (theKopeiki < 10 ? "0" + theKopeiki.to_s : theKopeiki.to_s) + " ");
        end
        if theKopeiki == NUM11 || theKopeiki == NUM12
            money2str.append(@kopFiveUnit);
        else
            case theKopeiki % NUM10
            when NUM1
                money2str.append(@kopOneUnit);
            when NUM2,  NUM3,  NUM4
                money2str.append(@kopTwoUnit);
            else
                money2str.append(@kopFiveUnit);
            end
        end
        return money2str.toString().strip;
    end

    def triad2Word(triad, triadNum, sex)
        triadWord = StringBuilder.new();

        if triad == 0
            return "";
        end

        range = check1(triad, triadWord);
        if @language == Language::ENG.to_s && triadWord.length() > 0 && triad % NUM10 == 0
            triadWord.deleteCharAt(triadWord.length() - 1);
            triadWord.append(" ");
        end

        range10 = range;
        range = triad % NUM10;
        check2(triadNum, sex, triadWord, triad, range10);
        case (triadNum)
        when NUM0
        when NUM1, NUM2, NUM3, NUM4
            if range10 == NUM1
                triadWord.append(@messages["1000_10"][triadNum - 1] + " ");
            else
                case (range)
                when NUM1
                    triadWord.append(@messages["1000_1"][triadNum - 1] + " ");
                when NUM2, NUM3, NUM4
                    triadWord.append(@messages["1000_234"][triadNum - 1] + " ");
                else
                    triadWord.append(@messages["1000_5"][triadNum - 1] + " ");
                end
            end
        else
            triadWord.append("??? ");
        end
        return triadWord.toString();
    end

=begin
    /**
     * @param triadNum the triad num
     * @param sex the sex
     * @param triadWord the triad word
     * @param triad the triad
     * @param range10 the range 10
     */
=end
    def check2(triadNum, sex, triadWord, triad, range10)
        range = triad % NUM10;
        if range10 == 1
            triadWord.append(@messages["10_19"][range] + " ");
        else
            case range
            when NUM1
                if triadNum == NUM1
                    triadWord.append(@messages["1"][INDEX_0] + " ");
                elsif triadNum == NUM2 || triadNum == NUM3 || triadNum == NUM4
                    triadWord.append(@messages["1"][INDEX_1] + " ");
                elsif "M" == sex
                    triadWord.append(@messages["1"][INDEX_2] + " ");
                elsif "F" == sex
                    triadWord.append(@messages["1"][INDEX_3] + " ");
                end
            when NUM2
                if triadNum == NUM1
                    triadWord.append(@messages["2"][INDEX_0] + " ");
                elsif triadNum == NUM2 || triadNum == NUM3 || triadNum == NUM4
                    triadWord.append(@messages["2"][INDEX_1] + " ");
                elsif "M" == sex
                    triadWord.append(@messages["2"][INDEX_2] + " ");
                elsif "F" == sex
                    triadWord.append(@messages["2"][INDEX_3] + " ");
                end
            when NUM3, NUM4, NUM5, NUM6, NUM7, NUM8, NUM9
                triadWord.append((["", "", ""] + @messages["3_9"])[range] + " ");
            end
        end
    end

=begin
    /**
     * @param triad the triad
     * @param triadWord the triad word
     * @return the range
     */
=end
    def check1(triad, triadWord)
        range = triad / NUM100;
        triadWord.append(([""] + @messages["100_900"])[range]);

        range = (triad % NUM100) / NUM10;
        triadWord.append((["", ""] + @messages["20_90"])[range]);
        return range;
    end
end
/*
 * $Id$
 *
 * Copyright 2013 Valentyn Kolesnikov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Converts numbers to symbols.
 *
 * @author Valentyn Kolesnikov
 * @version $Revision$ $Date$
 */
class MoneyToStr {
    val currencyList =
<CurrencyList>
 
    <language value="UKR"/>
    <UKR>
        <item value="0" text="нуль"/>
        <item value="1000_10" text="тисяч,мільйонів,мільярдів,трильйонів"/>
        <item value="1000_1" text="тисяча,мільйон,мільярд,трильйон"/>
        <item value="1000_234" text="тисячі,мільйона,мільярда,трильйона"/>
        <item value="1000_5" text="тисяч,мільйонів,мільярдів,трильйонів"/>
        <item value="10_19" text="десять,одинадцять,дванадцять,тринадцять,чотирнадцять,п’ятнадцять,шiстнадцять,сiмнадцять,вiсiмнадцять,дев'ятнадцять"/>
        <item value="1" text="одна,один,один,одна"/>
        <item value="2" text="дві,два,два,дві"/>
        <item value="3_9" text="три,чотири,п’ять,шість,сім,вісім,дев’ять"/>
        <item value="100_900" text="сто ,двісті ,триста ,чотириста ,п’ятсот ,шістсот ,сімсот ,вісімсот ,дев’ятсот "/>
        <item value="20_90" text="двадцять ,тридцять ,сорок ,п’ятдесят ,шістдесят ,сімдесят ,вісімдесят ,дев’яносто "/>
        <item value="pdv" text="в т.ч. ПДВ "/>
        <item value="pdv_value" text="20"/>
    </UKR>
    <RUS>
        <item value="0" text="ноль"/>
        <item value="1000_10" text="тысяч,миллионов,миллиардов,триллионов"/>
        <item value="1000_1" text="тысяча,миллион,миллиард,триллион"/>
        <item value="1000_234" text="тысячи,миллиона,миллиарда,триллиона"/>
        <item value="1000_5" text="тысяч,миллионов,миллиардов,триллионов"/>
        <item value="10_19" text="десять,одиннадцать,двенадцать,тринадцать,четырнадцать,пятнадцать,шестнадцать,семнадцать,восемнадцать,девятнадцать"/>
        <item value="1" text="одна,один,один,одна"/>
        <item value="2" text="две,два,два,две"/>
        <item value="3_9" text="три,четыре,пять,шесть,семь,восемь,девять"/>
        <item value="100_900" text="сто ,двести ,триста ,четыреста ,пятьсот ,шестьсот ,семьсот ,восемьсот ,девятьсот "/>
        <item value="20_90" text="двадцать ,тридцать ,сорок ,пятьдесят ,шестьдесят ,семьдесят ,восемьдесят ,девяносто "/>
        <item value="pdv" text="в т.ч. НДС "/>
        <item value="pdv_value" text="18"/>
    </RUS>
    <ENG>
        <item value="0" text="zero"/>
        <item value="1000_10" text="thousand,million,billion,trillion"/>
        <item value="1000_1" text="thousand,million,billion,trillion"/>
        <item value="1000_234" text="thousand,million,billion,trillion"/>
        <item value="1000_5" text="thousand,million,billion,trillion"/>
        <item value="10_19" text="ten,eleven,twelve,thirteen,fourteen,fifteen,sixteen,seventeen,eighteen,nineteen"/>
        <item value="1" text="one,one,one,one"/>
        <item value="2" text="two,two,two,two"/>
        <item value="3_9" text="three,four,five,six,seven,eight,nine"/>
        <item value="100_900" text="one hundred ,two hundred ,three hundred ,four hundred ,five hundred ,six hundred ,seven hundred ,eight hundred ,nine hundred "/>
        <item value="20_90" text="twenty-,thirty-,forty-,fifty-,sixty-,seventy-,eighty-,ninety-"/>
        <item value="pdv" text="including VAT "/>
        <item value="pdv_value" text="10"/>
    </ENG>

    <RUR CurrID="810" CurrName="Российские рубли" language="RUS"
         RubOneUnit="рубль" RubTwoUnit="рубля" RubFiveUnit="рублей" RubSex="M" RubShortUnit="руб."
         KopOneUnit="копейка" KopTwoUnit="копейки" KopFiveUnit="копеек" KopSex="F"
    />
    <UAH CurrID="980" CurrName="Украинскі гривні" language="RUS"
         RubOneUnit="гривня" RubTwoUnit="гривни" RubFiveUnit="гривень" RubSex="F" RubShortUnit="грн."
         KopOneUnit="копейка" KopTwoUnit="копейки" KopFiveUnit="копеек" KopSex="F"
    />
    <USD CurrID="840" CurrName="Долари США" language="RUS"
         RubOneUnit="доллар" RubTwoUnit="доллара" RubFiveUnit="долларов" RubSex="M" RubShortUnit="дол."
         KopOneUnit="цент" KopTwoUnit="цена" KopFiveUnit="центов" KopSex="M"
    />

    <RUR CurrID="810" CurrName="Российские рубли" language="UKR"
         RubOneUnit="рубль" RubTwoUnit="рублі" RubFiveUnit="рублів" RubSex="M" RubShortUnit="руб."
         KopOneUnit="копійка" KopTwoUnit="копійки" KopFiveUnit="копійок" KopSex="F"
    /> 
    <UAH CurrID="980" CurrName="Украинскі гривні" language="UKR"
         RubOneUnit="гривня" RubTwoUnit="гривні" RubFiveUnit="гривень" RubSex="F" RubShortUnit="грн."
         KopOneUnit="копійка" KopTwoUnit="копійки" KopFiveUnit="копійок" KopSex="F"
    />
    <USD CurrID="840" CurrName="Долари США" language="UKR"
         RubOneUnit="долар" RubTwoUnit="долара" RubFiveUnit="доларів" RubSex="M" RubShortUnit="дол."
         KopOneUnit="цент" KopTwoUnit="цена" KopFiveUnit="центів" KopSex="M"
    />

    <RUR CurrID="810" CurrName="Российские рубли" language="ENG"
         RubOneUnit="ruble" RubTwoUnit="rubles" RubFiveUnit="rubles" RubSex="M" RubShortUnit="RUR."
         KopOneUnit="kopeck" KopTwoUnit="kopecks" KopFiveUnit="kopecks" KopSex="M"
    /> 
    <UAH CurrID="980" CurrName="Украинскі гривні" language="ENG"
         RubOneUnit="hryvnia" RubTwoUnit="hryvnias" RubFiveUnit="hryvnias" RubSex="M" RubShortUnit="UAH."
         KopOneUnit="kopeck" KopTwoUnit="kopecks" KopFiveUnit="kopecks" KopSex="M"
    />
    <USD CurrID="840" CurrName="Долари США" language="ENG"
         RubOneUnit="dollar" RubTwoUnit="dollars" RubFiveUnit="dollars" RubSex="M" RubShortUnit="USD."
         KopOneUnit="cent" KopTwoUnit="cents" KopFiveUnit="cents" KopSex="M"
    />

    <PER10 CurrID="556" CurrName="Вiдсотки з десятими частинами" language="RUS"
         RubOneUnit="целая," RubTwoUnit="целых," RubFiveUnit="целых," RubSex="F"
         KopOneUnit="десятая процента" KopTwoUnit="десятых процента" KopFiveUnit="десятых процента" KopSex="F"
    />

    <PER100 CurrID="557" CurrName="Вiдсотки з сотими частинами" language="RUS"
         RubOneUnit="целая," RubTwoUnit="целых," RubFiveUnit="целых," RubSex="F"
         KopOneUnit="сотая процента" KopTwoUnit="сотых процента" KopFiveUnit="сотых процента" KopSex="F"
    />

    <PER1000 CurrID="558" CurrName="Вiдсотки з тисячними частинами" language="RUS"
         RubOneUnit="целая," RubTwoUnit="целых," RubFiveUnit="целых," RubSex="F"
         KopOneUnit="тысячная процента" KopTwoUnit="тысячных процента" KopFiveUnit="тысячных процента" KopSex="F"
    />

    <PER10000 CurrID="559" CurrName="Вiдсотки з десяти тисячними частинами" language="RUS"
         RubOneUnit="целая," RubTwoUnit="целых," RubFiveUnit="целых," RubSex="F"
         KopOneUnit="десятитысячная процента" KopTwoUnit="десятитысячные процента" KopFiveUnit="десятитысячных процента" KopSex="F"
    />

    <PER10 CurrID="556" CurrName="Вiдсотки з десятими частинами" language="UKR"
         RubOneUnit="ціла," RubTwoUnit="цілих," RubFiveUnit="цілих," RubSex="F"
         KopOneUnit="десята відсотка" KopTwoUnit="десятих відсотка" KopFiveUnit="десятих відсотка" KopSex="F"
    />

    <PER100 CurrID="557" CurrName="Вiдсотки з сотими частинами" language="UKR"
         RubOneUnit="ціла," RubTwoUnit="цілих," RubFiveUnit="цілих," RubSex="F"
         KopOneUnit="сота відсотка" KopTwoUnit="сотих відсотка" KopFiveUnit="сотих відсотка" KopSex="F"
    />

    <PER1000 CurrID="558" CurrName="Вiдсотки з тисячними частинами" language="UKR"
         RubOneUnit="ціла," RubTwoUnit="цілих," RubFiveUnit="цілих," RubSex="F"
         KopOneUnit="тисячна відсотка" KopTwoUnit="тисячних відсотка" KopFiveUnit="тисячних відсотка" KopSex="F"
    />

    <PER10000 CurrID="559" CurrName="Вiдсотки з десяти тисячними частинами" language="UKR"
         RubOneUnit="ціла," RubTwoUnit="цілих," RubFiveUnit="цілих," RubSex="F"
         KopOneUnit="десятитисячна відсотка" KopTwoUnit="десятитисячних відсотка" KopFiveUnit="десятитисячних відсотка" KopSex="M"
    />

    <PER10 CurrID="560" CurrName="Вiдсотки з десятими частинами" language="ENG"
         RubOneUnit="," RubTwoUnit="integers," RubFiveUnit="integers," RubSex="F"
         KopOneUnit="tenth of one percent" KopTwoUnit="tenth of one percent" KopFiveUnit="tenth of one percent" KopSex="F"
    />

    <PER100 CurrID="561" CurrName="Вiдсотки з сотими частинами" language="ENG"
         RubOneUnit="," RubTwoUnit="integers," RubFiveUnit="integers," RubSex="F"
         KopOneUnit="hundred percent" KopTwoUnit="hundredth of percent" KopFiveUnit="hundredth of percent" KopSex="F"
    />

    <PER1000 CurrID="562" CurrName="Вiдсотки з тисячними частинами" language="ENG"
         RubOneUnit="," RubTwoUnit="integers," RubFiveUnit="integers," RubSex="F"
         KopOneUnit="thousandth of percent" KopTwoUnit="thousandths of percent" KopFiveUnit="thousandths of percent" KopSex="F"
    />

    <PER10000 CurrID="563" CurrName="Вiдсотки з десяти тисячними частинами" language="ENG"
         RubOneUnit="," RubTwoUnit="integers," RubFiveUnit="integers," RubSex="F"
         KopOneUnit="ten percent" KopTwoUnit="ten-percent" KopFiveUnit="ten-percent" KopSex="F"
    />

</CurrencyList>
    private var rubOneUnit : String = ""
    private var rubTwoUnit : String = ""
    private var rubFiveUnit : String = ""
    private var rubSex : String = ""
    private var kopOneUnit : String = ""
    private var kopTwoUnit : String = ""
    private var kopFiveUnit : String = ""
    private var kopSex : String = ""
    private var rubShortUnit : String = ""
    private var currency : String = null
    private var language : String = null
    private var pennies : String = null
    private var messages : Map[String, Array[String]] = Map[String, Array[String]]()

    def this(currency : String, language : String, pennies : String) = {
        this()
        if (currency == null) {
            throw new IllegalArgumentException("currency is null");
        }
        if (language == null) {
            throw new IllegalArgumentException("language is null");
        }
        if (pennies == null) {
            throw new IllegalArgumentException("pennies is null");
        }
        this.currency = currency;
        this.language = language;
        this.pennies = pennies;
        var languageElement = (currencyList \\ "CurrencyList")(0) \ language
        var items = languageElement \ "item"
        for (index <- 0 to items.length - 1) {
            messages += (items(index) \ "@value").toString -> (items(index) \ "@text").toString.split(",")
        }
        var theISOElements = (currencyList \\ "CurrencyList")(0) \ currency
        var theISOElement : scala.xml.Node = null
        for (index <- 0 to theISOElements.length - 1) {
            if ((theISOElements(index) \ "@language").toString == language) {
                theISOElement = theISOElements(index)
            }
        }
        if (theISOElement == null) {
            throw new IllegalArgumentException("Currency not found " + currency);
        }
        rubOneUnit = (theISOElement \ "@RubOneUnit").toString
        rubTwoUnit = (theISOElement \ "@RubTwoUnit").toString
        rubFiveUnit = (theISOElement \ "@RubFiveUnit").toString
        kopOneUnit = (theISOElement \ "@KopOneUnit").toString
        kopTwoUnit = (theISOElement \ "@KopTwoUnit").toString
        kopFiveUnit = (theISOElement \ "@KopFiveUnit").toString
        rubSex = (theISOElement \ "@RubSex").toString
        kopSex = (theISOElement \ "@KopSex").toString
    }

    /**
     * Converts double value to the text description.
     *
     * @param theMoney
     *            the amount of money in format major.minor
     * @return the string description of money value
     */
    def convert(theMoney : Double) : String = {
        var intPart : Long = theMoney.longValue();
        var fractPart : Long = Math.round((theMoney - intPart) * MoneyToStr.NUM100);
        if (currency == "PER1000") {
            fractPart = Math.round((theMoney - intPart) * MoneyToStr.NUM1000);
        }
        return convert(intPart, fractPart);
    }

    /**
     * Converts number to currency. Usage: MoneyToStr moneyToStr = new MoneyToStr("UAH"); String result =
     * moneyToStr.convert(123D); Expected: result = сто двадцять три гривні 00 копійок
     *
     * @param theMoney
     *            the amount of money major currency
     * @param theKopeiki
     *            the amount of money minor currency
     * @return the string description of money value
     */
    def convert(theMoney : Long, theKopeiki : Long) : String = {
        var money2str : StringBuilder = new StringBuilder()
        var triadNum : Long = 0L
        var theTriad: Long = 0L

        var intPart : Long = theMoney
        if (intPart == 0) {
            money2str.append(messages("0")(0) + " ");
        }
        do {
            theTriad = intPart % MoneyToStr.NUM1000;
            money2str.insert(0, triad2Word(theTriad, triadNum, rubSex));
            if (triadNum == 0) {
                var range10 : Long = (theTriad % MoneyToStr.NUM100) / MoneyToStr.NUM10;
                var range : Long = theTriad % MoneyToStr.NUM10;
                if (range10 == MoneyToStr.NUM1) {
                    money2str.append(rubFiveUnit);
                } else {
                    range.byteValue() match {
                    case MoneyToStr.NUM1 =>
                        money2str.append(rubOneUnit);
                    case MoneyToStr.NUM2 |
                        MoneyToStr.NUM3 |
                        MoneyToStr.NUM4 =>
                        money2str.append(rubTwoUnit);
                    case _ =>
                        money2str.append(rubFiveUnit);
                    }
                }
            }
            intPart = intPart / MoneyToStr.NUM1000
            triadNum += 1
        } while (intPart > 0)

        if (pennies == "TEXT") {
            if (language == "ENG") {
                money2str.append(" and ")
            } else {
                money2str.append(" ")
            }
            if (theKopeiki == 0) {
                money2str.append(messages("0")(0) + " ")
            } else {
                money2str.append(triad2Word(theKopeiki, 0L, kopSex))
            }
        } else {
            var value : String = theKopeiki.toString
            if (theKopeiki < 10) {
                value = "0" + theKopeiki
            }
            money2str.append(" " + value + " ");
        }
        if (theKopeiki >= MoneyToStr.NUM11 && theKopeiki <= MoneyToStr.NUM14) {
            money2str.append(kopFiveUnit);
        } else {
            theKopeiki % MoneyToStr.NUM10 match {
            case MoneyToStr.NUM1 =>
                money2str.append(kopOneUnit);
            case MoneyToStr.NUM2 |
                MoneyToStr.NUM3 |
                MoneyToStr.NUM4 =>
                money2str.append(kopTwoUnit);
            case _ =>
                money2str.append(kopFiveUnit);
            }
        }
        return money2str.toString().trim();
    }

    def triad2Word(triad : Long, triadNum : Long, sex : String) : String = {
        var triadWord : StringBuilder = new StringBuilder()

        if (triad == 0) {
            return "";
        }

        var range : Long = check1(triad, triadWord)
        if (language == "ENG" && triadWord.length > 0 && triad % MoneyToStr.NUM10 == 0) {
            triadWord.deleteCharAt(triadWord.length - 1)
            triadWord.append(" ")
        }

        var range10 : Long = range
        range = triad % MoneyToStr.NUM10
        check2(triadNum, sex, triadWord, triad, range10)
        triadNum.byteValue() match {
        case MoneyToStr.NUM0 => ;
        case MoneyToStr.NUM1 |
            MoneyToStr.NUM2 |
            MoneyToStr.NUM3 |
            MoneyToStr.NUM4 =>
            if (range10 == MoneyToStr.NUM1) {
                triadWord.append(messages("1000_10")(triadNum.byteValue() - 1) + " ")
            } else {
                range.byteValue() match {
                case MoneyToStr.NUM1 =>
                    triadWord.append(messages("1000_1")(triadNum.byteValue() - 1) + " ")
                case MoneyToStr.NUM2 |
                    MoneyToStr.NUM3 |
                    MoneyToStr.NUM4 =>
                    triadWord.append(messages("1000_234")(triadNum.byteValue() - 1) + " ")
                case _ =>
                    triadWord.append(messages("1000_5")(triadNum.byteValue() - 1) + " ")
                }
            }
        case _ =>
            triadWord.append("??? ")
        }
        return triadWord.toString();
    }

    /**
     * @param triadNum the triad num
     * @param sex the sex
     * @param triadWord the triad word
     * @param triad the triad
     * @param range10 the range 10
     */
    def check2(triadNum : Long, sex : String, triadWord : StringBuilder, triad : Long, range10 : Long) = {
        var range : Long = triad % MoneyToStr.NUM10
        if (range10 == 1) {
            triadWord.append(messages("10_19")(range.byteValue()) + " ");
        } else {
            range.byteValue() match {
            case MoneyToStr.NUM1 =>
                if (triadNum == MoneyToStr.NUM1) {
                    triadWord.append(messages("1")(MoneyToStr.INDEX_0) + " ")
                } else if (triadNum == MoneyToStr.NUM2 || triadNum == MoneyToStr.NUM3 || triadNum == MoneyToStr.NUM4) {
                    triadWord.append(messages("1")(MoneyToStr.INDEX_1) + " ")
                } else if ("M".equals(sex)) {
                    triadWord.append(messages("1")(MoneyToStr.INDEX_2) + " ")
                } else if ("F".equals(sex)) {
                    triadWord.append(messages("1")(MoneyToStr.INDEX_3) + " ")
                }
            case MoneyToStr.NUM2 =>
                if (triadNum == MoneyToStr.NUM1) {
                    triadWord.append(messages("2")(MoneyToStr.INDEX_0) + " ");
                } else if (triadNum == MoneyToStr.NUM2 || triadNum == MoneyToStr.NUM3 || triadNum == MoneyToStr.NUM4) {
                    triadWord.append(messages("2")(MoneyToStr.INDEX_1) + " ")
                } else if ("M".equals(sex)) {
                    triadWord.append(messages("2")(MoneyToStr.INDEX_2) + " ")
                } else if ("F".equals(sex)) {
                    triadWord.append(messages("2")(MoneyToStr.INDEX_3) + " ")
                }
            case MoneyToStr.NUM3 |
                MoneyToStr.NUM4 |
                MoneyToStr.NUM5 |
                MoneyToStr.NUM6 |
                MoneyToStr.NUM7 |
                MoneyToStr.NUM8 |
                MoneyToStr.NUM9 =>
                var array = Array[String]("", "", "") ++ messages("3_9")
                triadWord.append(array(range.toInt) + " ");
            case _ => ;
            }
        }
    }

    /**
     * @param triad the triad
     * @param triadWord the triad word
     * @return the range
     */
    def check1(triad : Long, triadWord : StringBuilder) : Long = {
        var range : Long = triad / MoneyToStr.NUM100
        var array = Array[String]("") ++ messages("100_900")
        triadWord.append(array(range.byteValue()))

        range = (triad % MoneyToStr.NUM100) / MoneyToStr.NUM10
        array = Array[String]("", "") ++ messages("20_90")
        triadWord.append(array(range.byteValue()))
        return range
    }
}

object MoneyToStr {
    val INDEX_3 = 3
    val INDEX_2 = 2
    val INDEX_1 = 1
    val INDEX_0 = 0
    val NUM0 = 0;
    val NUM1 = 1;
    val NUM2 = 2;
    val NUM3 = 3;
    val NUM4 = 4;
    val NUM5 = 5;
    val NUM6 = 6;
    val NUM7 = 7;
    val NUM8 = 8;
    val NUM9 = 9;
    val NUM10 = 10;
    val NUM11 = 11;
    val NUM14 = 14;
    val NUM100 = 100;
    val NUM1000 = 1000;
    val NUM10000 = 10000;

    /**
     * Converts percent to string.
     * @param amount the amount of percent
     * @param lang the language (RUS, UKR)
     * @return the string of percent
     */
    def percentToStr(amount : Double, lang : String) : String = {
        if (lang == null) {
            throw new IllegalArgumentException("language is null");
        }
        var intPart : Long = amount.longValue();
        var fractPart : Long = 0L;
        var result : String = "";
        if (amount.floatValue() == amount.intValue()) {
            result = new MoneyToStr("PER10", lang, "TEXT").convert(amount.longValue(), 0L);
        } else if ((amount * MoneyToStr.NUM10).toFloat == (amount * MoneyToStr.NUM10).toInt) {
            fractPart = Math.round((amount - intPart) * MoneyToStr.NUM10);
            result = new MoneyToStr("PER10", lang, "TEXT").convert(intPart, fractPart);
        } else if ((amount * MoneyToStr.NUM100).toFloat == (amount * MoneyToStr.NUM100).toInt) {
            fractPart = Math.round((amount - intPart) * MoneyToStr.NUM100);
            result = new MoneyToStr("PER100", lang, "TEXT").convert(intPart, fractPart);
        } else if ((amount * MoneyToStr.NUM1000).toFloat == (amount * MoneyToStr.NUM1000).toInt) {
            fractPart = Math.round((amount - intPart) * MoneyToStr.NUM1000);
            result = new MoneyToStr("PER1000", lang, "TEXT").convert(intPart, fractPart);
        } else {
            fractPart = Math.round((amount - intPart) * MoneyToStr.NUM10000);
            result = new MoneyToStr("PER10000", lang, "TEXT").convert(intPart, fractPart);
        }
        return result;
    }

}
/*
 * $Id$
 *
 * Copyright 2013 Valentyn Kolesnikov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var currencyList =
{
  "CurrencyList": {
    "language": { "-value": "UKR" },
    "UKR": {
      "item": [
        {
          "-value": "0",
          "-text": "нуль"
        },
        {
          "-value": "1000_10",
          "-text": "тисяч,мільйонів,мільярдів,трильйонів"
        },
        {
          "-value": "1000_1",
          "-text": "тисяча,мільйон,мільярд,трильйон"
        },
        {
          "-value": "1000_234",
          "-text": "тисячі,мільйона,мільярда,трильйона"
        },
        {
          "-value": "1000_5",
          "-text": "тисяч,мільйонів,мільярдів,трильйонів"
        },
        {
          "-value": "10_19",
          "-text": "десять,одинадцять,дванадцять,тринадцять,чотирнадцять,п’ятнадцять,шiстнадцять,сiмнадцять,вiсiмнадцять,дев'ятнадцять"
        },
        {
          "-value": "1",
          "-text": "одна,один,один,одна"
        },
        {
          "-value": "2",
          "-text": "дві,два,два,дві"
        },
        {
          "-value": "3_9",
          "-text": "три,чотири,п’ять,шість,сім,вісім,дев’ять"
        },
        {
          "-value": "100_900",
          "-text": "сто ,двісті ,триста ,чотириста ,п’ятсот ,шістсот ,сімсот ,вісімсот ,дев’ятсот "
        },
        {
          "-value": "20_90",
          "-text": "двадцять ,тридцять ,сорок ,п’ятдесят ,шістдесят ,сімдесят ,вісімдесят ,дев’яносто "
        }
      ]
    },
    "RUS": {
      "item": [
        {
          "-value": "0",
          "-text": "ноль"
        },
        {
          "-value": "1000_10",
          "-text": "тысяч,миллионов,миллиардов,триллионов"
        },
        {
          "-value": "1000_1",
          "-text": "тысяча,миллион,миллиард,триллион"
        },
        {
          "-value": "1000_234",
          "-text": "тысячи,миллиона,миллиарда,триллиона"
        },
        {
          "-value": "1000_5",
          "-text": "тысяч,миллионов,миллиардов,триллионов"
        },
        {
          "-value": "10_19",
          "-text": "десять,одиннадцать,двенадцать,тринадцать,четырнадцать,пятнадцать,шестнадцать,семнадцать,восемнадцать,девятнадцать"
        },
        {
          "-value": "1",
          "-text": "одна,один,один,одна"
        },
        {
          "-value": "2",
          "-text": "две,два,два,две"
        },
        {
          "-value": "3_9",
          "-text": "три,четыре,пять,шесть,семь,восемь,девять"
        },
        {
          "-value": "100_900",
          "-text": "сто ,двести ,триста ,четыреста ,пятьсот ,шестьсот ,семьсот ,восемьсот ,девятьсот "
        },
        {
          "-value": "20_90",
          "-text": "двадцать ,тридцать ,сорок ,пятьдесят ,шестьдесят ,семьдесят ,восемьдесят ,девяносто "
        }
      ]
    },
    "ENG": {
      "item": [
        {
          "-value": "0",
          "-text": "zero"
        },
        {
          "-value": "1000_10",
          "-text": "thousand,million,billion,trillion"
        },
        {
          "-value": "1000_1",
          "-text": "thousand,million,billion,trillion"
        },
        {
          "-value": "1000_234",
          "-text": "thousand,million,billion,trillion"
        },
        {
          "-value": "1000_5",
          "-text": "thousand,million,billion,trillion"
        },
        {
          "-value": "10_19",
          "-text": "ten,eleven,twelve,thirteen,fourteen,fifteen,sixteen,seventeen,eighteen,nineteen"
        },
        {
          "-value": "1",
          "-text": "one,one,one,one"
        },
        {
          "-value": "2",
          "-text": "two,two,two,two"
        },
        {
          "-value": "3_9",
          "-text": "three,four,five,six,seven,eight,nine"
        },
        {
          "-value": "100_900",
          "-text": "one hundred ,two hundred ,three hundred ,four hundred ,five hundred ,six hundred ,seven hundred ,eight hundred ,nine hundred "
        },
        {
          "-value": "20_90",
          "-text": "twenty-,thirty-,forty-,fifty-,sixty-,seventy-,eighty-,ninety-"
        }
      ]
    },
    "RUR": [
      {
        "-CurrID": "810",
        "-CurrName": "Российские рубли",
        "-language": "RUS",
        "-RubOneUnit": "рубль",
        "-RubTwoUnit": "рубля",
        "-RubFiveUnit": "рублей",
        "-RubSex": "M",
        "-KopOneUnit": "копейка",
        "-KopTwoUnit": "копейки",
        "-KopFiveUnit": "копеек",
        "-KopSex": "F"
      },
      {
        "-CurrID": "810",
        "-CurrName": "Российские рубли",
        "-language": "UKR",
        "-RubOneUnit": "рубль",
        "-RubTwoUnit": "рубля",
        "-RubFiveUnit": "рублей",
        "-RubSex": "M",
        "-KopOneUnit": "копейка",
        "-KopTwoUnit": "копейки",
        "-KopFiveUnit": "копеек",
        "-KopSex": "F"
      }
    ],
    "UAH": [
      {
        "-CurrID": "980",
        "-CurrName": "Украинскі гривні",
        "-language": "RUS",
        "-RubOneUnit": "гривня",
        "-RubTwoUnit": "гривни",
        "-RubFiveUnit": "гривень",
        "-RubSex": "F",
        "-KopOneUnit": "копейка",
        "-KopTwoUnit": "копейки",
        "-KopFiveUnit": "копеек",
        "-KopSex": "F"
      },
      {
        "-CurrID": "980",
        "-CurrName": "Украинскі гривні",
        "-language": "UKR",
        "-RubOneUnit": "гривня",
        "-RubTwoUnit": "гривні",
        "-RubFiveUnit": "гривень",
        "-RubSex": "F",
        "-KopOneUnit": "копійка",
        "-KopTwoUnit": "копійки",
        "-KopFiveUnit": "копійок",
        "-KopSex": "F"
      }
    ],
    "USD": [
      {
        "-CurrID": "840",
        "-CurrName": "Долари США",
        "-language": "RUS",
        "-RubOneUnit": "долар",
        "-RubTwoUnit": "долара",
        "-RubFiveUnit": "доларів",
        "-RubSex": "M",
        "-KopOneUnit": "цент",
        "-KopTwoUnit": "цена",
        "-KopFiveUnit": "центов",
        "-KopSex": "M"
      },
      {
        "-CurrID": "840",
        "-CurrName": "Долари США",
        "-language": "UKR",
        "-RubOneUnit": "долар",
        "-RubTwoUnit": "долара",
        "-RubFiveUnit": "доларів",
        "-RubSex": "M",
        "-KopOneUnit": "цент",
        "-KopTwoUnit": "цена",
        "-KopFiveUnit": "центів",
        "-KopSex": "M"
      },
      {
        "-CurrID": "840",
        "-CurrName": "Долари США",
        "-language": "ENG",
        "-RubOneUnit": "dollar",
        "-RubTwoUnit": "dollars",
        "-RubFiveUnit": "dollars",
        "-RubSex": "M",
        "-KopOneUnit": "cent",
        "-KopTwoUnit": "cents",
        "-KopFiveUnit": "cents",
        "-KopSex": "M"
      }
     ],
    "PER10": [
      {
        "-CurrID": "556",
        "-CurrName": "Вiдсотки з десятими частинами",
        "-language": "RUS",
        "-RubOneUnit": "целая,",
        "-RubTwoUnit": "целых,",
        "-RubFiveUnit": "целых,",
        "-RubSex": "F",
        "-KopOneUnit": "десятая процента",
        "-KopTwoUnit": "десятых процента",
        "-KopFiveUnit": "десятых процента",
        "-KopSex": "F"
      },
      {
        "-CurrID": "556",
        "-CurrName": "Вiдсотки з десятими частинами",
        "-language": "UKR",
        "-RubOneUnit": "ціла,",
        "-RubTwoUnit": "цілих,",
        "-RubFiveUnit": "цілих,",
        "-RubSex": "F",
        "-KopOneUnit": "десята відсотка",
        "-KopTwoUnit": "десятих відсотка",
        "-KopFiveUnit": "десятих відсотка",
        "-KopSex": "F"
      }
    ],
    "PER100": [
      {
        "-CurrID": "557",
        "-CurrName": "Вiдсотки з сотими частинами",
        "-language": "RUS",
        "-RubOneUnit": "целая,",
        "-RubTwoUnit": "целых,",
        "-RubFiveUnit": "целых,",
        "-RubSex": "F",
        "-KopOneUnit": "сотая процента",
        "-KopTwoUnit": "сотых процента",
        "-KopFiveUnit": "сотых процента",
        "-KopSex": "F"
      },
      {
        "-CurrID": "557",
        "-CurrName": "Вiдсотки з сотими частинами",
        "-language": "UKR",
        "-RubOneUnit": "ціла,",
        "-RubTwoUnit": "цілих,",
        "-RubFiveUnit": "цілих,",
        "-RubSex": "F",
        "-KopOneUnit": "сота відсотка",
        "-KopTwoUnit": "сотих відсотка",
        "-KopFiveUnit": "сотих відсотка",
        "-KopSex": "F"
      }
    ],
    "PER1000": [
      {
        "-CurrID": "558",
        "-CurrName": "Вiдсотки з тисячними частинами",
        "-language": "RUS",
        "-RubOneUnit": "целая,",
        "-RubTwoUnit": "целых,",
        "-RubFiveUnit": "целых,",
        "-RubSex": "F",
        "-KopOneUnit": "тысячная процента",
        "-KopTwoUnit": "тысячных процента",
        "-KopFiveUnit": "тысячных процента",
        "-KopSex": "F"
      },
      {
        "-CurrID": "558",
        "-CurrName": "Вiдсотки з тисячними частинами",
        "-language": "UKR",
        "-RubOneUnit": "ціла,",
        "-RubTwoUnit": "цілих,",
        "-RubFiveUnit": "цілих,",
        "-RubSex": "F",
        "-KopOneUnit": "тисячна відсотка",
        "-KopTwoUnit": "тисячних відсотка",
        "-KopFiveUnit": "тисячних відсотка",
        "-KopSex": "F"
      }
    ],
    "PER10000": [
      {
        "-CurrID": "559",
        "-CurrName": "Вiдсотки з десяти тисячними частинами",
        "-language": "RUS",
        "-RubOneUnit": "целая,",
        "-RubTwoUnit": "целых,",
        "-RubFiveUnit": "целых,",
        "-RubSex": "F",
        "-KopOneUnit": "десятитысячная процента",
        "-KopTwoUnit": "десятитысячные процента",
        "-KopFiveUnit": "десятитысячных процента",
        "-KopSex": "F"
      },
      {
        "-CurrID": "559",
        "-CurrName": "Вiдсотки з десяти тисячними частинами",
        "-language": "UKR",
        "-RubOneUnit": "ціла,",
        "-RubTwoUnit": "цілих,",
        "-RubFiveUnit": "цілих,",
        "-RubSex": "F",
        "-KopOneUnit": "десятитисячна відсотка",
        "-KopTwoUnit": "десятитисячних відсотка",
        "-KopFiveUnit": "десятитисячних відсотка",
        "-KopSex": "M"
      }
    ]
  }
};
/**
 * Converts numbers to symbols.
 *
 * @author Valentyn V Kolesnikov
 * @version $Revision$ $Date$
*/

    /** Currency. */
class Currency {
    constructor(public value:string){
    }
    toString(){
        return this.value;
    }
    /**.*/
    static UAH = new Currency('UAH');
    /**.*/
    static RUR = new Currency('RUR');
    /**.*/
    static USD = new Currency('USD');
    /**.*/
    static PER10 = new Currency('PER10');
    /**.*/
    static PER100 = new Currency('PER100');
    /**.*/
    static PER1000 = new Currency('PER1000');
    /**.*/
    static PER10000 = new Currency('PER10000');
};

    /** Language. */
class Language {
    constructor(public value:string){
    }
    toString(){
        return this.value;
    }
    /**.*/
    static RUS = new Language('RUS');
    /**.*/
    static UKR = new Language('UKR');
    /**.*/
    static ENG = new Language('ENG');
};

    /** Pennies. */
class Pennies {
    constructor(public value:string){
    }
    toString(){
        return this.value;
    }
    /**.*/
    static NUMBER = new Pennies('NUMBER');
    /**.*/
    static TEXT = new Pennies('TEXT');
};

class StringBuilder {
    private _buffer;
    constructor() {
        this._buffer = [];
    }

    append(text) {
            this._buffer[this._buffer.length] = text;
            return this;
    }
        
    insert(index, text) { 
            this._buffer.splice(index, 0, text);
            return this;
    }

    length() {
            return this.toString().length;
    }

    deleteCharAt(index) {
            var str = this.toString();
            this._buffer = [];
            this.append(str.substring(0, index)); 
            return this;
    }

    toString() {
            return this._buffer.join("");
    }
};

class MoneyToStr {
    static NUM0 = 0;
    static NUM1 = 1;
    static NUM2 = 2;
    static NUM3 = 3;
    static NUM4 = 4;
    static NUM5 = 5;
    static NUM6 = 6;
    static NUM7 = 7;
    static NUM8 = 8;
    static NUM9 = 9;
    static NUM10 = 10;
    static NUM11 = 11;
    static NUM12 = 12;
    static NUM100 = 100;
    static NUM1000 = 1000;
    static NUM10000 = 10000;
    static INDEX_0 = 0;
    static INDEX_1 = 1;
    static INDEX_2 = 2;
    static INDEX_3 = 3;
    private rubOneUnit;
    private rubTwoUnit;
    private rubFiveUnit;
    private kopOneUnit;
    private kopTwoUnit;
    private kopFiveUnit;
    private rubSex;
    private kopSex;
    private currency : Currency;
    private language : Language;
    private pennies : Pennies;
    private messages;

    static percentToStr(amount, lang : Language) {
        if (amount == null) {
            throw new Error("amount is null");
        }
        if (lang == null) {
            throw new Error("Language is null");
        }
        var intPart = parseInt(amount);
        var fractPart = 0;
        var result;
        if (amount == amount.toInt()) {
            result = new MoneyToStr(Currency.PER10, lang, Pennies.TEXT).convert(amount, 0);
        } else if ((amount * MoneyToStr.NUM10).toFixed(4) == parseInt("" + amount * MoneyToStr.NUM10).toFixed(4)) {
            fractPart = Math.round((amount - intPart) * MoneyToStr.NUM10);
            result = new MoneyToStr(Currency.PER10, lang, Pennies.TEXT).convert(intPart, fractPart);
        } else if ((amount * MoneyToStr.NUM100).toFixed(4) == parseInt("" + amount * MoneyToStr.NUM100).toFixed(4)) {
            fractPart = Math.round((amount - intPart) * MoneyToStr.NUM100);
            result = new MoneyToStr(Currency.PER100, lang, Pennies.TEXT).convert(intPart, fractPart);
        } else if ((amount * MoneyToStr.NUM1000).toFixed(4) == parseInt("" + amount * MoneyToStr.NUM1000).toFixed(4)) {
            fractPart = Math.round((amount - intPart) * MoneyToStr.NUM1000);
            result = new MoneyToStr(Currency.PER1000, lang, Pennies.TEXT).convert(intPart, fractPart);
        } else {
            fractPart = Math.round((amount - intPart) * MoneyToStr.NUM10000);
            result = new MoneyToStr(Currency.PER10000, lang, Pennies.TEXT).convert(intPart, fractPart);
        }
        return result;
    }
    constructor(currency : Currency, language : Language, pennies : Pennies) {
        this.currency = currency;
        this.language = language;
        this.pennies = pennies;
        var languageElement = language;
        var items = currencyList['CurrencyList'][languageElement.toString()]['item'];
        this.messages = {};
        for (var index in items) {
            var languageItem = items[index];
            if (languageItem["-text"]) {
                this.messages[languageItem["-value"]] = languageItem["-text"].split(",");
            }
        }
        var currencyItem = currencyList['CurrencyList'][currency.toString()]
        var theISOElement = null;
        for (var index in currencyItem) {
            if (currencyItem[index]["-language"] == language.toString()) {
                theISOElement = currencyItem[index];
                break;
            }
        }
        if (theISOElement == null) {
            throw new Error("Currency not found " + currency.toString());
        }
        this.rubOneUnit = theISOElement["-RubOneUnit"];
        this.rubTwoUnit = theISOElement["-RubTwoUnit"];
        this.rubFiveUnit = theISOElement["-RubFiveUnit"];
        this.kopOneUnit = theISOElement["-KopOneUnit"];
        this.kopTwoUnit = theISOElement["-KopTwoUnit"];
        this.kopFiveUnit = theISOElement["-KopFiveUnit"];
        this.rubSex = theISOElement["-RubSex"];
        this.kopSex = theISOElement["-KopSex"];
    }

    /**
     * Converts double value to the text description.
     *
     * @param theMoney
     *            the amount of money in format major.minor
     * @return the string description of money value
     */
    convertValue(theMoney) {
        if (typeof theMoney === undefined || theMoney == null) {
            throw new Error("theMoney is null");
        }
        var intPart = parseInt(theMoney);
        var fractPart = Math.round((theMoney - intPart) * MoneyToStr.NUM100);
        if (this.currency == Currency.PER1000) {
            fractPart = Math.round((theMoney - intPart) * MoneyToStr.NUM1000);
        }
        return this.convert(intPart, fractPart);
    }

    /**
     * Converts number to currency. Usage: MoneyToStr moneyToStr = new MoneyToStr("UAH"); String result =
     * moneyToStr.convert(123D); Expected: result = сто двадцять три гривні 00 копійок
     *
     * @param theMoney
     *            the amount of money major currency
     * @param theKopeiki
     *            the amount of money minor currency
     * @return the string description of money value
     */
    convert(theMoney, theKopeiki) {
        if (typeof theMoney === undefined || theMoney == null) {
            throw new Error("theMoney is null");
        }
        if (typeof theKopeiki === undefined || theKopeiki == null) {
            throw new Error("theKopeiki is null");
        }
        var money2str = new StringBuilder();
        var triadNum = 0;
        var theTriad = 0;
        var intPart = theMoney;
        if (intPart == 0) {
            money2str.append(this.messages["0"][0] + " ");
        }
        do {
            theTriad = parseInt("" + intPart % MoneyToStr.NUM1000);
            money2str.insert(0, this.triad2Word(theTriad, triadNum, this.rubSex));
            if (triadNum == 0) {
                var range10 = parseInt("" + (theTriad % MoneyToStr.NUM100) / MoneyToStr.NUM10);
                var range = parseInt("" + theTriad % MoneyToStr.NUM10);
                if (range10 == MoneyToStr.NUM1) {
                    money2str.append(this.rubFiveUnit);
                } else {
                    switch (range) {
                    case MoneyToStr.NUM1:
                        money2str.append(this.rubOneUnit);
                        break;
                    case MoneyToStr.NUM2:
                    case MoneyToStr.NUM3:
                    case MoneyToStr.NUM4:
                        money2str.append(this.rubTwoUnit);
                        break;
                    default:
                        money2str.append(this.rubFiveUnit);
                        break;
                    }
                }
            }
            intPart = parseInt("" + intPart / MoneyToStr.NUM1000);
            triadNum++;
        } while (intPart > 0);

        if (this.pennies == Pennies.TEXT) {
            money2str.append(this.language == Language.ENG ? " and " : " ").append(theKopeiki == 0 ? this.messages["0"][0] + " " : this.triad2Word(theKopeiki, 0, this.kopSex));
        } else {
            money2str.append(" " + (theKopeiki < 10 ? "0" + theKopeiki : theKopeiki) + " ");
        }
        if (theKopeiki == MoneyToStr.NUM11 || theKopeiki == MoneyToStr.NUM12) {
            money2str.append(this.kopFiveUnit);
        } else {
            switch (parseInt("" + theKopeiki % MoneyToStr.NUM10)) {
            case MoneyToStr.NUM1:
                money2str.append(this.kopOneUnit);
                break;
            case MoneyToStr.NUM2:
            case MoneyToStr.NUM3:
            case MoneyToStr.NUM4:
                money2str.append(this.kopTwoUnit);
                break;
            default:
                money2str.append(this.kopFiveUnit);
                break;
            }
        }
        return money2str.toString().trim();
    }
    triad2Word(triad, triadNum, sex) {
        var triadWord = new StringBuilder();

        if (triad == 0) {
            return "";
        }

        var range = this.check1(triad, triadWord);
        if (this.language == Language.ENG && triadWord.length() > 0 && triad % MoneyToStr.NUM10 == 0) {
            triadWord.deleteCharAt(triadWord.length() - 1);
            triadWord.append(" ");
        }

        var range10 = range;
        range = parseInt("" + triad % MoneyToStr.NUM10);
        this.check2(triadNum, sex, triadWord, triad, range10);
        switch (triadNum) {
        case MoneyToStr.NUM0:
            break;
        case MoneyToStr.NUM1:
        case MoneyToStr.NUM2:
        case MoneyToStr.NUM3:
        case MoneyToStr.NUM4:
            if (range10 == MoneyToStr.NUM1) {
                triadWord.append(this.messages["1000_10"][triadNum - 1] + " ");
            } else {
                switch (range) {
                case MoneyToStr.NUM1:
                    triadWord.append(this.messages["1000_1"][triadNum - 1] + " ");
                    break;
                case MoneyToStr.NUM2:
                case MoneyToStr.NUM3:
                case MoneyToStr.NUM4:
                    triadWord.append(this.messages["1000_234"][triadNum - 1] + " ");
                    break;
                default:
                    triadWord.append(this.messages["1000_5"][triadNum - 1] + " ");
                    break;
                }
            }
            break;
        default:
            triadWord.append("??? ");
            break;
        }
        return triadWord.toString();
    }

    /**
     * @param triadNum the triad num
     * @param sex the sex
     * @param triadWord the triad word
     * @param triad the triad
     * @param range10 the range 10
     */
    check2(triadNum, sex, triadWord, triad, range10) {
        var range = parseInt("" + triad % MoneyToStr.NUM10);
        if (range10 == 1) {
            triadWord.append(this.messages["10_19"][range] + " ");
        } else {
            switch (range) {
            case MoneyToStr.NUM1:
                if (triadNum == MoneyToStr.NUM1) {
                    triadWord.append(this.messages["1"][MoneyToStr.INDEX_0] + " ");
                } else if (triadNum == MoneyToStr.NUM2 || triadNum == MoneyToStr.NUM3 || triadNum == MoneyToStr.NUM4) {
                    triadWord.append(this.messages["1"][MoneyToStr.INDEX_1] + " ");
                } else if ("M" == sex) {
                    triadWord.append(this.messages["1"][MoneyToStr.INDEX_2] + " ");
                } else if ("F" == sex) {
                    triadWord.append(this.messages["1"][MoneyToStr.INDEX_3] + " ");
                }
                break;
            case MoneyToStr.NUM2:
                if (triadNum == MoneyToStr.NUM1) {
                    triadWord.append(this.messages["2"][MoneyToStr.INDEX_0] + " ");
                } else if (triadNum == MoneyToStr.NUM2 || triadNum == MoneyToStr.NUM3 || triadNum == MoneyToStr.NUM4) {
                    triadWord.append(this.messages["2"][MoneyToStr.INDEX_1] + " ");
                } else if ("M" == sex) {
                    triadWord.append(this.messages["2"][MoneyToStr.INDEX_2] + " ");
                } else if ("F" == sex) {
                    triadWord.append(this.messages["2"][MoneyToStr.INDEX_3] + " ");
                }
                break;
            case MoneyToStr.NUM3:
            case MoneyToStr.NUM4:
            case MoneyToStr.NUM5:
            case MoneyToStr.NUM6:
            case MoneyToStr.NUM7:
            case MoneyToStr.NUM8:
            case MoneyToStr.NUM9:
                triadWord.append(["", "", ""].concat(this.messages["3_9"])[range] + " ");
                break;
            default:
                break;
            }
        }
    }

    /**
     * @param triad the triad
     * @param triadWord the triad word
     * @return the range
     */
    check1(triad, triadWord) {
        var range = parseInt("" + triad / MoneyToStr.NUM100);
        triadWord.append([""].concat(this.messages["100_900"])[range]);

        range = parseInt("" + (triad % MoneyToStr.NUM100) / MoneyToStr.NUM10);
        triadWord.append(["", ""].concat(this.messages["20_90"])[range]);
        return range;
    }
}