packages feed

safe-money 0.7.1 → 0.8

raw patch · 5 files changed

+1663/−1297 lines, 5 files

Files

changelog.md view
@@ -1,9 +1,39 @@+# Version 0.8++* _COMPILER ASSISTED BREAKING CHANGE_. Introduced `DecimalConf`, which all+  of the `xxxToDecimal` and `xxxFromDecimal` are now using. This concentrates+  all decimal rendering and parsing settings in a single place, and more+  importantly, it makes all of the `xxxToDecimal` return a `Text` value+  directly, rather than a `Maybe Text`. In turn, `Separators` has been+  introduced for configuring decimal and thousands separators. See issue #30.++* _COMPILER ASSISTED BREAKING CHANGE_. The `Scale` type family was renamed to+  `UnitScale`.++* _COMPILER ASSISTED BREAKING CHANGE_. Previous instances of the `Scale` type+  family where the `currency` and the `unit` were the same (e.g., `Scale "USD"+  "USD"`), intended to convey a canonical scale for the currency, are now+  written as `CurrencyScale currency` (e.g., `CurrencyScale "USD"`).++* _COMPILER ASSISTED BREAKING CHANGE_. A new datatype called `Scale` has been+  introduced. This datatype replaces term-level uses of `Rational` to convey the+  scale of a discrete monetary amount. The rationale for this, beyond improved+  type errors, is that checking for the scale's validity can be done at a single+  place (namely, the new `scaleFromRational`), rather than everywhere a+  term-level scale was expected as input. See issue #43.++* Added new approximation method `HalfEven`, also known as “Bankers rounding”.+  See issue #42.++* New currencies: VES (see issue #33), UYW.++ # Version 0.7.1  * Fixed compilation with GHC 8.6.  * Fixed an issue when rendering decimal values smaller than the smallest-  possible fractional part. (Issue #39)+  possible fractional part. See issue #39.   # Version 0.7
safe-money.cabal view
@@ -1,5 +1,5 @@ name: safe-money-version: 0.7.1+version: 0.8 license: BSD3 license-file: LICENSE copyright: Copyright (c) Renzo Carbonara 2016-2018
src/Money.hs view
@@ -11,845 +11,869 @@ -- -- Note: This module exports support for many well-known currencies -- out-of-the-box, but you are not limited to the currencies mentioned here. You--- can simply create a new 'I.Scale' instance, and /voilà/. If you want to add a--- new currency to the out-of-the-box offer, please request so in--- https://github.com/k0001/safe-money/issues and the authors will see to it.------ This module offers plenty of documentation, but for a deep explanation of--- how all of the pieces fit together, please read--- <https://ren.zone/articles/safe-money>. Notice, however, that this library--- has changed a bit since that article was written. You can always see the--- [change log](https://github.com/k0001/safe-money/blob/master/changelog.md) to--- understand what has changed.------ Also, keep in mind that useful instances for the many types defined by--- @safe-money@ can be found in these other libraries:------ * [safe-money-aeson](https://hackage.haskell.org/package/safe-money-aeson):---   `FromJSON` and `ToJSON` instances (from the---   [aeson](https://hackage.haskell.org/package/aeson) library).------ * [safe-money-cereal](https://hackage.haskell.org/package/safe-money-cereal):---   `Serialize` instances (from the---   [cereal](https://hackage.haskell.org/package/cereal) library).------ * [safe-money-serialise](https://hackage.haskell.org/package/safe-money-serialise):---   `Serialise` instances (from the---   [serialise](https://hackage.haskell.org/package/serialise) library).------ * [safe-money-store](https://hackage.haskell.org/package/safe-money-store):---   `Store` instances (from the---   [store](https://hackage.haskell.org/package/store) library).------ * [safe-money-xmlbf](https://hackage.haskell.org/package/safe-money-xmlbf):---   `FromXml` and `ToXml` instances (from the---   [xmlbf](https://hackage.haskell.org/package/xmlbf) library).-module Money- ( -- * Dense monetary values-   I.Dense- , I.denseCurrency- , I.dense- , I.dense'- , I.denseFromDiscrete- , I.denseFromDecimal- , I.denseToDecimal-   -- * Discrete monetary values- , I.Discrete- , I.Discrete'- , I.discrete- , I.discreteCurrency- , I.discreteFromDense- , I.discreteFromDecimal- , I.discreteToDecimal-   -- * Currency scales- , I.Scale- , I.GoodScale- , I.ErrScaleNonCanonical- , I.scale-   -- * Currency exchange- , I.ExchangeRate- , I.exchangeRate- , I.exchange- , I.exchangeRateRecip- , I.exchangeRateFromDecimal- , I.exchangeRateToDecimal- , I.exchangeRateToRational-   -- * Serializable representations- , I.SomeDense- , I.toSomeDense- , I.mkSomeDense- , I.fromSomeDense- , I.withSomeDense- , I.someDenseCurrency- , I.someDenseAmount- , I.SomeDiscrete- , I.toSomeDiscrete- , I.mkSomeDiscrete- , I.fromSomeDiscrete- , I.withSomeDiscrete- , I.someDiscreteCurrency- , I.someDiscreteScale- , I.someDiscreteAmount- , I.SomeExchangeRate- , I.toSomeExchangeRate- , I.mkSomeExchangeRate- , I.fromSomeExchangeRate- , I.withSomeExchangeRate- , I.someExchangeRateSrcCurrency- , I.someExchangeRateDstCurrency- , I.someExchangeRateRate- -- * Misc- , I.Approximation(Round, Floor, Ceiling, Truncate)- ) where--import qualified Money.Internal as I------------------------------------------------------------------------------------- Currency scales---- | United Arab Emirates dirham-type instance I.Scale "AED" "AED" = '(100, 1)-type instance I.Scale "AED" "dirham" = '(1, 1)-type instance I.Scale "AED" "fils" = '(100, 1)--- | Afghan afghani-type instance I.Scale "AFN" "AFN" = '(100, 1)-type instance I.Scale "AFN" "afghani" = '(1, 1)-type instance I.Scale "AFN" "pul" = '(100, 1)--- | Albanian lek-type instance I.Scale "ALL" "ALL" = '(100, 1)-type instance I.Scale "ALL" "lek" = '(1, 1)-type instance I.Scale "ALL" "qindarke" = '(100, 1)--- | Armenian dram-type instance I.Scale "AMD" "AMD" = '(100, 1)-type instance I.Scale "AMD" "dram" = '(1, 1)-type instance I.Scale "AMD" "luma" = '(100, 1)--- | Netherlands Antillean guilder-type instance I.Scale "ANG" "ANG" = '(100, 1)-type instance I.Scale "ANG" "guilder" = '(1, 1)-type instance I.Scale "ANG" "cent" = '(100, 1)--- | Angolan kwanza-type instance I.Scale "AOA" "AOA" = '(100, 1)-type instance I.Scale "AOA" "kwanza" = '(1, 1)-type instance I.Scale "AOA" "centimo" = '(100, 1)--- | Argentine peso-type instance I.Scale "ARS" "ARS" = '(100, 1)-type instance I.Scale "ARS" "peso" = '(1, 1)-type instance I.Scale "ARS" "centavo" = '(100, 1)--- | Australian dollar-type instance I.Scale "AUD" "AUD" = '(100, 1)-type instance I.Scale "AUD" "dollar" = '(1, 1)-type instance I.Scale "AUD" "cent" = '(100, 1)--- | Aruban florin-type instance I.Scale "AWG" "AWG" = '(100, 1)-type instance I.Scale "AWG" "florin" = '(1, 1)-type instance I.Scale "AWG" "cent" = '(100, 1)--- | Azerbaijani manat-type instance I.Scale "AZN" "AZN" = '(100, 1)-type instance I.Scale "AZN" "manat" = '(1, 1)-type instance I.Scale "AZN" "qapik" = '(100, 1)--- | Bosnia and Herzegovina convertible mark-type instance I.Scale "BAM" "BAM" = '(100, 1)-type instance I.Scale "BAM" "mark" = '(1, 1)-type instance I.Scale "BAM" "fening" = '(100, 1)--- | Barbadian dollar-type instance I.Scale "BBD" "BBD" = '(100, 1)-type instance I.Scale "BBD" "dollar" = '(1, 1)-type instance I.Scale "BBD" "cent" = '(100, 1)--- | Bangladeshi taka-type instance I.Scale "BDT" "BDT" = '(100, 1)-type instance I.Scale "BDT" "taka" = '(1, 1)-type instance I.Scale "BDT" "paisa" = '(100, 1)--- | Bulgarian lev-type instance I.Scale "BGN" "BGN" = '(100, 1)-type instance I.Scale "BGN" "lev" = '(1, 1)-type instance I.Scale "BGN" "stotinka" = '(100, 1)--- | Bahraini dinar-type instance I.Scale "BHD" "BHD" = '(1000, 1)-type instance I.Scale "BHD" "dinar" = '(1, 1)-type instance I.Scale "BHD" "fils" = '(1000, 1)--- | Burundi franc-type instance I.Scale "BIF" "BIF" = '(100, 1)-type instance I.Scale "BIF" "franc" = '(1, 1)-type instance I.Scale "BIF" "centime" = '(100, 1)--- | Bermudian dollar-type instance I.Scale "BMD" "BMD" = '(100, 1)-type instance I.Scale "BMD" "dollar" = '(1, 1)-type instance I.Scale "BMD" "cent" = '(100, 1)--- | Brunei dollar-type instance I.Scale "BND" "BND" = '(100, 1)-type instance I.Scale "BND" "dollar" = '(1, 1)-type instance I.Scale "BND" "sen" = '(100, 1)--- | Bolivian boliviano-type instance I.Scale "BOB" "BOB" = '(100, 1)-type instance I.Scale "BOB" "boliviano" = '(1, 1)-type instance I.Scale "BOB" "centavo" = '(100, 1)--- | Bolivian Mvdol-type instance I.Scale "BOV" "BOV" = '(100, 1)--- | Brazilian real-type instance I.Scale "BRL" "BRL" = '(100, 1)-type instance I.Scale "BRL" "real" = '(1, 1)-type instance I.Scale "BRL" "centavo" = '(100, 1)--- | Bahamian dollar-type instance I.Scale "BSD" "BSD" = '(100, 1)-type instance I.Scale "BSD" "dollar" = '(1, 1)-type instance I.Scale "BSD" "cent" = '(1, 1)--- | Bhutanese ngultrum-type instance I.Scale "BTN" "BTN" = '(100, 1)-type instance I.Scale "BTN" "ngultrum" = '(1, 1)-type instance I.Scale "BTN" "chetrum" = '(100, 1)--- | Botswana pula-type instance I.Scale "BWP" "BWP" = '(100, 1)-type instance I.Scale "BWP" "pula" = '(1, 1)-type instance I.Scale "BWP" "thebe" = '(100, 1)--- | Belarusian ruble-type instance I.Scale "BYN" "BYN" = '(100, 1)--- | Belarusian ruble-type instance I.Scale "BYR" "BYR" = '(100, 1)-type instance I.Scale "BYR" "ruble" = '(1, 1)-type instance I.Scale "BYR" "kapyeyka" = '(100, 1)--- | Belize dollar-type instance I.Scale "BZD" "BZD" = '(100, 1)-type instance I.Scale "BZD" "dollar" = '(1, 1)-type instance I.Scale "BZD" "cent" = '(100, 1)--- | Canadian dollar-type instance I.Scale "CAD" "CAD" = '(100, 1)-type instance I.Scale "CAD" "dollar" = '(1, 1)-type instance I.Scale "CAD" "cent" = '(100, 1)--- | Congolese franc-type instance I.Scale "CDF" "CDF" = '(100, 1)-type instance I.Scale "CDF" "franc" = '(1, 1)-type instance I.Scale "CDF" "centime" = '(100, 1)--- | WIR euro-type instance I.Scale "CHE" "CHE" = '(100, 1)--- | Swiss franc-type instance I.Scale "CHF" "CHF" = '(100, 1)-type instance I.Scale "CHF" "franc" = '(1, 1)-type instance I.Scale "CHF" "rappen" = '(100, 1)--- | WIR franc-type instance I.Scale "CHW" "CHW" = '(100, 1)--- | Chilean unidad de fomento-type instance I.Scale "CLF" "CLF" = '(100, 1)--- | Chilean peso-type instance I.Scale "CLP" "CLP" = '(100, 1)-type instance I.Scale "CLP" "peso" = '(1, 1)-type instance I.Scale "CLP" "centavo" = '(100, 1)--- | Chinese Renminbi-type instance I.Scale "CNY" "CNY" = '(100, 1)-type instance I.Scale "CNY" "yuan" = '(1, 1)-type instance I.Scale "CNY" "fen" = '(100, 1)--- | Colombian peso-type instance I.Scale "COP" "COP" = '(100, 1)-type instance I.Scale "COP" "peso" = '(1, 1)-type instance I.Scale "COP" "centavo" = '(100, 1)--- | Colombian unidad de valor real-type instance I.Scale "COU" "COU" = '(100, 1)--- | Costa Rican colon-type instance I.Scale "CRC" "CRC" = '(100, 1)-type instance I.Scale "CRC" "colon" = '(1, 1)-type instance I.Scale "CRC" "centimo" = '(100, 1)--- | Cuban peso convertible-type instance I.Scale "CUC" "CUC" = '(100, 1)-type instance I.Scale "CUC" "peso" = '(1, 1)-type instance I.Scale "CUC" "centavo" = '(100, 1)--- | Cuban peso-type instance I.Scale "CUP" "CUP" = '(100, 1)-type instance I.Scale "CUP" "peso" = '(1, 1)-type instance I.Scale "CUP" "centavo" = '(100, 1)--- | Cape Verdean escudo-type instance I.Scale "CVE" "CVE" = '(100, 1)-type instance I.Scale "CVE" "escudo" = '(1, 1)-type instance I.Scale "CVE" "centavo" = '(100, 1)--- | Czech koruna-type instance I.Scale "CZK" "CZK" = '(100, 1)-type instance I.Scale "CZK" "koruna" = '(1, 1)-type instance I.Scale "CZK" "haler" = '(100, 1)--- | Djiboutian franc-type instance I.Scale "DJF" "DJF" = '(100, 1)-type instance I.Scale "DJF" "franc" = '(1, 1)-type instance I.Scale "DJF" "centime" = '(100, 1)--- | Danish krone-type instance I.Scale "DKK" "DKK" = '(100, 1)-type instance I.Scale "DKK" "krone" = '(1, 1)-type instance I.Scale "DKK" "ore" = '(100, 1)--- | Dominican peso-type instance I.Scale "DOP" "DOP" = '(100, 1)-type instance I.Scale "DOP" "peso" = '(1, 1)-type instance I.Scale "DOP" "centavo" = '(100, 1)--- | Algerian dinar-type instance I.Scale "DZD" "DZD" = '(100, 1)-type instance I.Scale "DZD" "dinar" = '(1, 1)-type instance I.Scale "DZD" "santeem" = '(100, 1)--- | Egyptian pound-type instance I.Scale "EGP" "EGP" = '(100, 1)-type instance I.Scale "EGP" "pound" = '(1, 1)-type instance I.Scale "EGP" "piastre" = '(100, 1)--- | Eritrean nakfa-type instance I.Scale "ERN" "ERN" = '(100, 1)-type instance I.Scale "ERN" "nafka" = '(1, 1)-type instance I.Scale "ERN" "cent" = '(100, 1)--- | Ethiopian birr-type instance I.Scale "ETB" "ETB" = '(100, 1)-type instance I.Scale "ETB" "birr" = '(1, 1)-type instance I.Scale "ETB" "santim" = '(100, 1)--- | European euro-type instance I.Scale "EUR" "EUR" = '(100, 1)-type instance I.Scale "EUR" "euro" = '(1, 1)-type instance I.Scale "EUR" "cent" = '(100, 1)--- | Fijian dollar-type instance I.Scale "FJD" "FJD" = '(100, 1)--- | Falkland Islands pound-type instance I.Scale "FKP" "FKP" = '(100, 1)-type instance I.Scale "FKP" "pound" = '(1, 1)-type instance I.Scale "FKP" "penny" = '(100, 1)--- | Pound sterling-type instance I.Scale "GBP" "GBP" = '(100, 1)-type instance I.Scale "GBP" "pound" = '(1, 1)-type instance I.Scale "GBP" "penny" = '(100, 1)--- | Georgian lari-type instance I.Scale "GEL" "GEL" = '(100, 1)-type instance I.Scale "GEL" "lari" = '(1, 1)-type instance I.Scale "GEL" "tetri" = '(100, 1)--- | Ghanaian cedi-type instance I.Scale "GHS" "GHS" = '(100, 1)-type instance I.Scale "GHS" "cedi" = '(1, 1)-type instance I.Scale "GHS" "pesewa" = '(100, 1)--- | Gibraltar pound-type instance I.Scale "GIP" "GIP" = '(100, 1)-type instance I.Scale "GIP" "pound" = '(1, 1)-type instance I.Scale "GIP" "penny" = '(100, 1)--- | Gambian dalasi-type instance I.Scale "GMD" "GMD" = '(100, 1)-type instance I.Scale "GMD" "dalasi" = '(1, 1)-type instance I.Scale "GMD" "butut" = '(100, 1)--- | Guinean franc-type instance I.Scale "GNF" "GNF" = '(100, 1)-type instance I.Scale "GNF" "franc" = '(1, 1)-type instance I.Scale "GNF" "centime" = '(100, 1)--- | Guatemalan quetzal-type instance I.Scale "GTQ" "GTQ" = '(100, 1)-type instance I.Scale "GTQ" "quetzal" = '(1, 1)-type instance I.Scale "GTQ" "centavo" = '(100, 1)--- | Guyanese dollar-type instance I.Scale "GYD" "GYD" = '(100, 1)-type instance I.Scale "GYD" "dollar" = '(1, 1)-type instance I.Scale "GYD" "cent" = '(100, 1)--- | Hong Kong dollar-type instance I.Scale "HKD" "HKD" = '(100, 1)-type instance I.Scale "HKD" "dollar" = '(1, 1)-type instance I.Scale "HKD" "cent" = '(100, 1)--- | Honduran lempira-type instance I.Scale "HNL" "HNL" = '(100, 1)-type instance I.Scale "HNL" "lempira" = '(1, 1)-type instance I.Scale "HNL" "centavo" = '(100, 1)--- | Croatian kuna-type instance I.Scale "HRK" "HRK" = '(100, 1)-type instance I.Scale "HRK" "kuna" = '(1, 1)-type instance I.Scale "HRK" "lipa" = '(100, 1)--- | Haitian gourde-type instance I.Scale "HTG" "HTG" = '(100, 1)-type instance I.Scale "HTG" "gourde" = '(1, 1)-type instance I.Scale "HTG" "centime" = '(1, 1)--- | Hungarian forint-type instance I.Scale "HUF" "HUF" = '(100, 1)-type instance I.Scale "HUF" "forint" = '(1, 1)-type instance I.Scale "HUF" "filler" = '(100, 1)--- | Indonesian rupiah-type instance I.Scale "IDR" "IDR" = '(100, 1)-type instance I.Scale "IDR" "rupiah" = '(1, 1)-type instance I.Scale "IDR" "sen" = '(100, 1)--- | Israeli new shekel-type instance I.Scale "ILS" "ILS" = '(100, 1)-type instance I.Scale "ILS" "shekel" = '(1, 1)-type instance I.Scale "ILS" "agora" = '(100, 1)--- | Indian rupee-type instance I.Scale "INR" "INR" = '(100, 1)-type instance I.Scale "INR" "rupee" = '(1, 1)-type instance I.Scale "INR" "paisa" = '(100, 1)--- | Iraqi dinar-type instance I.Scale "IQD" "IQD" = '(1000, 1)-type instance I.Scale "IQD" "dinar" = '(1, 1)-type instance I.Scale "IQD" "fils" = '(1000, 1)--- | Iranian rial-type instance I.Scale "IRR" "IRR" = '(100, 1)-type instance I.Scale "IRR" "rial" = '(1, 1)-type instance I.Scale "IRR" "dinar" = '(100, 1)--- | Icelandic króna-type instance I.Scale "ISK" "ISK" = '(100, 1)-type instance I.Scale "ISK" "krona" = '(1, 1)-type instance I.Scale "ISK" "eyrir" = '(100, 1)--- | Jamaican dollar-type instance I.Scale "JMD" "JMD" = '(100, 1)-type instance I.Scale "JMD" "dollar" = '(1, 1)-type instance I.Scale "JMD" "cent" = '(100, 1)--- | Jordanian dinar-type instance I.Scale "JOD" "JOD" = '(100, 1)-type instance I.Scale "JOD" "dinar" = '(1, 1)-type instance I.Scale "JOD" "piastre" = '(100, 1)--- | Japanese yen-type instance I.Scale "JPY" "JPY" = '(100, 1)-type instance I.Scale "JPY" "yen" = '(1, 1)-type instance I.Scale "JPY" "sen" = '(100, 1)--- | Kenyan shilling-type instance I.Scale "KES" "KES" = '(100, 1)-type instance I.Scale "KES" "shilling" = '(1, 1)-type instance I.Scale "KES" "cent" = '(100, 1)--- | Kyrgyzstani som-type instance I.Scale "KGS" "KGS" = '(100, 1)-type instance I.Scale "KGS" "som" = '(1, 1)-type instance I.Scale "KGS" "tyiyn" = '(100, 1)--- | Cambodian riel-type instance I.Scale "KHR" "KHR" = '(100, 1)-type instance I.Scale "KHR" "riel" = '(1, 1)-type instance I.Scale "KHR" "sen" = '(100, 1)--- | Comorian franc-type instance I.Scale "KMF" "KMF" = '(100, 1)-type instance I.Scale "KMF" "franc" = '(1, 1)-type instance I.Scale "KMF" "centime" = '(100, 1)--- | North Korean won-type instance I.Scale "KPW" "KPW" = '(100, 1)-type instance I.Scale "KPW" "won" = '(1, 1)-type instance I.Scale "KPW" "chon" = '(100, 1)--- | South Korean won-type instance I.Scale "KRW" "KRW" = '(100, 1)-type instance I.Scale "KRW" "won" = '(1, 1)-type instance I.Scale "KRW" "jeon" = '(100, 1)--- | Kuwaiti dinar-type instance I.Scale "KWD" "KWD" = '(1000, 1)-type instance I.Scale "KWD" "dinar" = '(1, 1)-type instance I.Scale "KWD" "fils" = '(1000, 1)--- | Cayman Islands dollar-type instance I.Scale "KYD" "KYD" = '(100, 1)-type instance I.Scale "KYD" "dollar" = '(1, 1)-type instance I.Scale "KYD" "cent" = '(100, 1)--- | Kazakhstani tenge-type instance I.Scale "KZT" "KZT" = '(100, 1)-type instance I.Scale "KZT" "tenge" = '(1, 1)-type instance I.Scale "KZT" "tiyin" = '(100, 1)--- | Lao kip-type instance I.Scale "LAK" "LAK" = '(100, 1)-type instance I.Scale "LAK" "kip" = '(1, 1)-type instance I.Scale "LAK" "att" = '(100, 1)--- | Lebanese pound-type instance I.Scale "LBP" "LBP" = '(100, 1)-type instance I.Scale "LBP" "pound" = '(1, 1)-type instance I.Scale "LBP" "piastre" = '(100, 1)--- | Sri Lankan rupee-type instance I.Scale "LKR" "LKR" = '(100, 1)-type instance I.Scale "LKR" "rupee" = '(1, 1)-type instance I.Scale "LKR" "cent" = '(100, 1)--- | Liberian dollar-type instance I.Scale "LRD" "LRD" = '(100, 1)-type instance I.Scale "LRD" "dollar" = '(1, 1)-type instance I.Scale "LRD" "cent" = '(100, 1)--- | Lesotho loti-type instance I.Scale "LSL" "LSL" = '(100, 1)-type instance I.Scale "LSL" "loti" = '(1, 1)-type instance I.Scale "LSL" "sente" = '(100, 1)--- | Libyan dinar-type instance I.Scale "LYD" "LYD" = '(100, 1)-type instance I.Scale "LYD" "dinar" = '(1, 1)-type instance I.Scale "LYD" "dirham" = '(1000, 1)--- | Moroccan dirham-type instance I.Scale "MAD" "MAD" = '(100, 1)-type instance I.Scale "MAD" "dirham" = '(1, 1)-type instance I.Scale "MAD" "centime" = '(100, 1)--- | Moldovan leu-type instance I.Scale "MDL" "MDL" = '(100, 1)-type instance I.Scale "MDL" "leu" = '(100, 1)-type instance I.Scale "MDL" "ban" = '(100, 1)--- | Malagasy ariary-type instance I.Scale "MGA" "MGA" = '(5, 1)-type instance I.Scale "MGA" "ariary" = '(1, 1)-type instance I.Scale "MGA" "iraimbilanja" = '(5, 1)--- | Macedonian denar-type instance I.Scale "MKD" "MKD" = '(100, 1)-type instance I.Scale "MKD" "denar" = '(1, 1)-type instance I.Scale "MKD" "deni" = '(100, 1)--- | Myanmar kyat-type instance I.Scale "MMK" "MMK" = '(100, 1)-type instance I.Scale "MMK" "kyat" = '(1, 1)-type instance I.Scale "MMK" "pya" = '(100, 1)--- | Mongolian tugrik-type instance I.Scale "MNT" "MNT" = '(100, 1)-type instance I.Scale "MNT" "tugrik" = '(1, 1)-type instance I.Scale "MNT" "mongo" = '(100, 1)--- | Macanese pataca-type instance I.Scale "MOP" "MOP" = '(100, 1)-type instance I.Scale "MOP" "pataca" = '(1, 1)-type instance I.Scale "MOP" "avo" = '(100, 1)--- | Mauritanian ouguiya-type instance I.Scale "MRO" "MRO" = '(5, 1)-type instance I.Scale "MRO" "ouguiya" = '(1, 1)-type instance I.Scale "MRO" "khoums" = '(5, 1)--- | Mauritian rupee-type instance I.Scale "MUR" "MUR" = '(100, 1)-type instance I.Scale "MUR" "rupee" = '(1, 1)-type instance I.Scale "MUR" "cent" = '(100, 1)--- | Maldivian rufiyaa-type instance I.Scale "MVR" "MVR" = '(100, 1)-type instance I.Scale "MVR" "rufiyaa" = '(1, 1)-type instance I.Scale "MVR" "laari" = '(100, 1)--- | Malawian kwacha-type instance I.Scale "MWK" "MWK" = '(100, 1)-type instance I.Scale "MWK" "kwacha" = '(1, 1)-type instance I.Scale "MWK" "tambala" = '(100, 1)--- | Mexican peso-type instance I.Scale "MXN" "MXN" = '(100, 1)-type instance I.Scale "MXN" "peso" = '(1, 1)-type instance I.Scale "MXN" "centavo" = '(100, 1)--- | Mexican unidad de inversion-type instance I.Scale "MXV" "MXV" = '(100, 1)--- | Malaysian ringgit-type instance I.Scale "MYR" "MYR" = '(100, 1)-type instance I.Scale "MYR" "ringgit" = '(1, 1)-type instance I.Scale "MYR" "sen" = '(100, 1)--- | Mozambican metical-type instance I.Scale "MZN" "MZN" = '(100, 1)-type instance I.Scale "MZN" "metical" = '(1, 1)-type instance I.Scale "MZN" "centavo" = '(100, 1)--- | Namibian dollar-type instance I.Scale "NAD" "NAD" = '(100, 1)-type instance I.Scale "NAD" "dollar" = '(1, 1)-type instance I.Scale "NAD" "cent" = '(100, 1)--- | Nigerian naira-type instance I.Scale "NGN" "NGN" = '(100, 1)-type instance I.Scale "NGN" "naira" = '(1, 1)-type instance I.Scale "NGN" "kobo" = '(100, 1)--- | Nicaraguan cordoba-type instance I.Scale "NIO" "NIO" = '(100, 1)-type instance I.Scale "NIO" "cordoba" = '(1, 1)-type instance I.Scale "NIO" "centavo" = '(100, 1)--- | Norwegian krone-type instance I.Scale "NOK" "NOK" = '(100, 1)-type instance I.Scale "NOK" "krone" = '(1, 1)-type instance I.Scale "NOK" "ore" = '(100, 1)--- | Nepalese rupee-type instance I.Scale "NPR" "NPR" = '(100, 1)-type instance I.Scale "NPR" "rupee" = '(1, 1)-type instance I.Scale "NPR" "paisa" = '(100, 1)--- | New Zealand dollar-type instance I.Scale "NZD" "NZD" = '(100, 1)-type instance I.Scale "NZD" "dollar" = '(1, 1)-type instance I.Scale "NZD" "cent" = '(100, 1)--- | Omani rial-type instance I.Scale "OMR" "OMR" = '(1000, 1)-type instance I.Scale "OMR" "rial" = '(1, 1)-type instance I.Scale "OMR" "baisa" = '(1000, 1)--- | Panamenian balboa-type instance I.Scale "PAB" "PAB" = '(100, 1)-type instance I.Scale "PAB" "balboa" = '(1, 1)-type instance I.Scale "PAB" "centesimo" = '(100, 1)--- | Peruvian sol-type instance I.Scale "PEN" "PEN" = '(100, 1)-type instance I.Scale "PEN" "sol" = '(1, 1)-type instance I.Scale "PEN" "centimo" = '(100, 1)--- | Papua New Guinean kina-type instance I.Scale "PGK" "PGK" = '(100, 1)-type instance I.Scale "PGK" "kina" = '(1, 1)-type instance I.Scale "PGK" "toea" = '(100, 1)--- | Philippine peso-type instance I.Scale "PHP" "PHP" = '(100, 1)-type instance I.Scale "PHP" "peso" = '(1, 1)-type instance I.Scale "PHP" "centavo" = '(100, 1)--- | Pakistani rupee-type instance I.Scale "PKR" "PKR" = '(100, 1)-type instance I.Scale "PKR" "rupee" = '(1, 1)-type instance I.Scale "PKR" "paisa" = '(100, 1)--- | Polish zloty-type instance I.Scale "PLN" "PLN" = '(100, 1)-type instance I.Scale "PLN" "zloty" = '(1, 1)-type instance I.Scale "PLN" "grosz" = '(100, 1)--- | Paraguayan guarani-type instance I.Scale "PYG" "PYG" = '(100, 1)-type instance I.Scale "PYG" "guarani" = '(1, 1)-type instance I.Scale "PYG" "centimo" = '(100, 1)--- | Qatari riyal-type instance I.Scale "QAR" "QAR" = '(100, 1)-type instance I.Scale "QAR" "riyal" = '(1, 1)-type instance I.Scale "QAR" "dirham" = '(100, 1)--- | Romanian leu-type instance I.Scale "RON" "RON" = '(100, 1)-type instance I.Scale "RON" "leu" = '(1, 1)-type instance I.Scale "RON" "ban" = '(100, 1)--- | Serbian dinar-type instance I.Scale "RSD" "RSD" = '(100, 1)-type instance I.Scale "RSD" "dinar" = '(1, 1)-type instance I.Scale "RSD" "para" = '(100, 1)--- | Russian ruble-type instance I.Scale "RUB" "RUB" = '(100, 1)-type instance I.Scale "RUB" "ruble" = '(1, 1)-type instance I.Scale "RUB" "kopek" = '(100, 1)--- | Rwandan franc-type instance I.Scale "RWF" "RWF" = '(100, 1)-type instance I.Scale "RWF" "franc" = '(1, 1)-type instance I.Scale "RWF" "centime" = '(100, 1)--- | Saudi Arabian riyal-type instance I.Scale "SAR" "SAR" = '(100, 1)-type instance I.Scale "SAR" "riyal" = '(1, 1)-type instance I.Scale "SAR" "halala" = '(100, 1)--- | Solomon Islands dollar-type instance I.Scale "SBD" "SBD" = '(100, 1)-type instance I.Scale "SBD" "dollar" = '(100, 1)-type instance I.Scale "SBD" "cent" = '(100, 1)--- | Seychellois rupee-type instance I.Scale "SCR" "SCR" = '(100, 1)-type instance I.Scale "SCR" "rupee" = '(1, 1)-type instance I.Scale "SCR" "cent" = '(100, 1)--- | Sudanese pound-type instance I.Scale "SDG" "SDG" = '(100, 1)-type instance I.Scale "SDG" "pound" = '(1, 1)-type instance I.Scale "SDG" "piastre" = '(100, 1)--- | Swedish krona-type instance I.Scale "SEK" "SEK" = '(100, 1)-type instance I.Scale "SEK" "krona" = '(1, 1)-type instance I.Scale "SEK" "ore" = '(100, 1)--- | Singapore dollar-type instance I.Scale "SGD" "SGD" = '(100, 1)-type instance I.Scale "SGD" "dollar" = '(1, 1)-type instance I.Scale "SGD" "cent" = '(100, 1)--- | Saint Helena pound-type instance I.Scale "SHP" "SHP" = '(100, 1)-type instance I.Scale "SHP" "pound" = '(1, 1)-type instance I.Scale "SHP" "penny" = '(100, 1)--- | Sierra Leonean leone-type instance I.Scale "SLL" "SLL" = '(100, 1)-type instance I.Scale "SLL" "leone" = '(1, 1)-type instance I.Scale "SLL" "cent" = '(100, 1)--- | Somali shilling-type instance I.Scale "SOS" "SOS" = '(100, 1)-type instance I.Scale "SOS" "shilling" = '(1, 1)-type instance I.Scale "SOS" "cent" = '(100, 1)--- | Surinamese dollar-type instance I.Scale "SRD" "SRD" = '(100, 1)-type instance I.Scale "SRD" "dollar" = '(1, 1)-type instance I.Scale "SRD" "cent" = '(100, 1)--- | South Sudanese pound-type instance I.Scale "SSP" "SSP" = '(100, 1)-type instance I.Scale "SSP" "pound" = '(1, 1)-type instance I.Scale "SSP" "piastre" = '(100, 1)--- | Sao Tome and Principe dobra-type instance I.Scale "STD" "STD" = '(100, 1)-type instance I.Scale "STD" "dobra" = '(1, 1)-type instance I.Scale "STD" "centimo" = '(100, 1)--- | Salvadoran colon-type instance I.Scale "SVC" "SVC" = '(100, 1)-type instance I.Scale "SVC" "colon" = '(1, 1)-type instance I.Scale "SVC" "centavo" = '(100, 1)--- | Syrian pound-type instance I.Scale "SYP" "SYP" = '(100, 1)-type instance I.Scale "SYP" "pound" = '(1, 1)-type instance I.Scale "SYP" "piastre" = '(100, 1)--- | Swazi lilangeni-type instance I.Scale "SZL" "SZL" = '(100, 1)-type instance I.Scale "SZL" "lilangeni" = '(1, 1)-type instance I.Scale "SZL" "cent" = '(100, 1)--- | Thai baht-type instance I.Scale "THB" "THB" = '(100, 1)-type instance I.Scale "THB" "baht" = '(1, 1)-type instance I.Scale "THB" "satang" = '(100, 1)--- | Tajikistani somoni-type instance I.Scale "TJS" "TJS" = '(100, 1)-type instance I.Scale "TJS" "somoni" = '(1, 1)-type instance I.Scale "TJS" "diram" = '(100, 1)--- | Turkmen manat-type instance I.Scale "TMT" "TMT" = '(100, 1)-type instance I.Scale "TMT" "manat" = '(1, 1)-type instance I.Scale "TMT" "tennesi" = '(100, 1)--- | Tunisian dinar-type instance I.Scale "TND" "TND" = '(1000, 1)-type instance I.Scale "TND" "dinar" = '(1, 1)-type instance I.Scale "TND" "millime" = '(1000, 1)--- | Tongan pa’anga-type instance I.Scale "TOP" "TOP" = '(100, 1)-type instance I.Scale "TOP" "pa'anga" = '(1, 1)-type instance I.Scale "TOP" "seniti" = '(100, 1)--- | Turkish lira-type instance I.Scale "TRY" "TRY" = '(100, 1)-type instance I.Scale "TRY" "lira" = '(1, 1)-type instance I.Scale "TRY" "kurus" = '(100, 1)--- | Tobago Trinidad and Tobago dollar-type instance I.Scale "TTD" "TTD" = '(100, 1)-type instance I.Scale "TTD" "dollar" = '(1, 1)-type instance I.Scale "TTD" "cent" = '(100, 1)--- | New Taiwan dollar-type instance I.Scale "TWD" "TWD" = '(100, 1)-type instance I.Scale "TWD" "dollar" = '(1, 1)-type instance I.Scale "TWD" "cent" = '(100, 1)--- | Tanzanian shilling-type instance I.Scale "TZS" "TZS" = '(100, 1)-type instance I.Scale "TZS" "shilling" = '(1, 1)-type instance I.Scale "TZS" "cent" = '(100, 1)--- | Ukrainian hryvnia-type instance I.Scale "UAH" "UAH" = '(100, 1)-type instance I.Scale "UAH" "hryvnia" = '(1, 1)-type instance I.Scale "UAH" "kopiyka" = '(100, 1)--- | Ugandan shilling-type instance I.Scale "UGX" "UGX" = '(100, 1)-type instance I.Scale "UGX" "shilling" = '(1, 1)-type instance I.Scale "UGX" "cent" = '(100, 1)--- | United States dollar-type instance I.Scale "USD" "USD" = '(100, 1)-type instance I.Scale "USD" "dollar" = '(1, 1)-type instance I.Scale "USD" "cent" = '(100, 1)--- | United States dollar (next day)-type instance I.Scale "USN" "USN" = '(100, 1)--- | Uruguayan peso en unidades-type instance I.Scale "UYI" "UYI" = '(100, 1)--- | Uruguayan peso-type instance I.Scale "UYU" "UYU" = '(100, 1)-type instance I.Scale "UYU" "peso" = '(1, 1)-type instance I.Scale "UYU" "centesimo" = '(100, 1)--- | Uzbekistani som-type instance I.Scale "UZS" "UZS" = '(100, 1)-type instance I.Scale "UZS" "som" = '(1, 1)-type instance I.Scale "UZS" "tiyin" = '(100, 1)--- | Venezuelan bolivar-type instance I.Scale "VEF" "VEF" = '(100, 1)-type instance I.Scale "VEF" "bolivar" = '(1, 1)-type instance I.Scale "VEF" "centimo" = '(100, 1)--- | Vietnamese dong-type instance I.Scale "VND" "VND" = '(10, 1)-type instance I.Scale "VND" "dong" = '(1, 1)-type instance I.Scale "VND" "hao" = '(10, 1)--- | Vanuatu vatu-type instance I.Scale "VUV" "VUV" = '(1, 1)-type instance I.Scale "VUV" "vatu" = '(1, 1)--- | Samoan tālā-type instance I.Scale "WST" "WST" = '(100, 1)-type instance I.Scale "WST" "tala" = '(1, 1)-type instance I.Scale "WST" "sene" = '(100, 1)--- | Central African CFA franc-type instance I.Scale "XAF" "XAF" = '(100, 1)-type instance I.Scale "XAF" "franc" = '(1, 1)-type instance I.Scale "XAF" "centime" = '(100, 1)--- | East Caribbean dollar-type instance I.Scale "XCD" "XCD" = '(100, 1)-type instance I.Scale "XCD" "dollar" = '(1, 1)-type instance I.Scale "XCD" "cent" = '(100, 1)--- | International Monetary Fund Special Drawing Right-type instance I.Scale "XDR" "XDR" = '(100, 1)--- | West African CFA franc-type instance I.Scale "XOF" "XOF" = '(100, 1)-type instance I.Scale "XOF" "franc" = '(1, 1)-type instance I.Scale "XOF" "centime" = '(100, 1)--- | CFP franc-type instance I.Scale "XPF" "XPF" = '(100, 1)-type instance I.Scale "XPF" "franc" = '(1, 1)-type instance I.Scale "XPF" "centime" = '(100, 1)--- | Sucre-type instance I.Scale "XSU" "XSU" = '(100, 1)--- | African Development Bank unit of account-type instance I.Scale "XUA" "XUA" = '(100, 1)--- | Yemeni rial-type instance I.Scale "YER" "YER" = '(100, 1)-type instance I.Scale "YER" "rial" = '(1, 1)-type instance I.Scale "YER" "fils" = '(100, 1)--- | South African rand-type instance I.Scale "ZAR" "ZAR" = '(100, 1)-type instance I.Scale "ZAR" "rand" = '(1, 1)-type instance I.Scale "ZAR" "cent" = '(100, 1)--- | Zambian kwacha-type instance I.Scale "ZMW" "ZMW" = '(100, 1)-type instance I.Scale "ZMW" "kwacha" = '(1, 1)-type instance I.Scale "ZMW" "ngwee" = '(100, 1)--- | Zimbawe dollar-type instance I.Scale "ZWL" "ZWL" = '(100, 1)-type instance I.Scale "ZWL" "dollar" = '(1, 1)-type instance I.Scale "ZWL" "cent" = '(100, 1)---- | Gold. No canonical smallest unit. Unusable instance.-type instance I.Scale "XAU" "XAU" = I.ErrScaleNonCanonical "XAU"-type instance I.Scale "XAU" "troy-ounce" = '(1, 1)-type instance I.Scale "XAU" "grain" = '(480, 1)-type instance I.Scale "XAU" "milligrain" = '(480000, 1)-type instance I.Scale "XAU" "micrograin" = '(480000000, 1)-type instance I.Scale "XAU" "kilogram" = '(31103477, 1000000000)-type instance I.Scale "XAU" "gram" = '(31103477, 1000000)-type instance I.Scale "XAU" "milligram" = '(31103477, 1000)-type instance I.Scale "XAU" "microgram" = '(31103477, 1)---- | Silver. No canonical smallest unit. Unusable instance.-type instance I.Scale "XAG" "XAG" = I.ErrScaleNonCanonical "XAG"-type instance I.Scale "XAG" "troy-ounce" = '(1, 1)-type instance I.Scale "XAG" "grain" = '(480, 1)-type instance I.Scale "XAG" "milligrain" = '(480000, 1)-type instance I.Scale "XAG" "micrograin" = '(480000000, 1)-type instance I.Scale "XAG" "kilogram" = '(31103477, 1000000000)-type instance I.Scale "XAG" "gram" = '(31103477, 1000000)-type instance I.Scale "XAG" "milligram" = '(31103477, 1000)-type instance I.Scale "XAG" "microgram" = '(31103477, 1)---- | Palladium. No canonical smallest unit. Unusable instance.-type instance I.Scale "XPD" "XPD" = I.ErrScaleNonCanonical "XPD"-type instance I.Scale "XPD" "troy-ounce" = '(1, 1)-type instance I.Scale "XPD" "grain" = '(480, 1)-type instance I.Scale "XPD" "milligrain" = '(480000, 1)-type instance I.Scale "XPD" "micrograin" = '(480000000, 1)-type instance I.Scale "XPD" "kilogram" = '(31103477, 1000000000)-type instance I.Scale "XPD" "gram" = '(31103477, 1000000)-type instance I.Scale "XPD" "milligram" = '(31103477, 1000)-type instance I.Scale "XPD" "microgram" = '(31103477, 1)---- | Platinum. No canonical smallest unit. Unusable instance.-type instance I.Scale "XPT" "XPT" = I.ErrScaleNonCanonical "XPT"-type instance I.Scale "XPT" "troy-ounce" = '(1, 1)-type instance I.Scale "XPT" "grain" = '(480, 1)-type instance I.Scale "XPT" "milligrain" = '(480000, 1)-type instance I.Scale "XPT" "micrograin" = '(480000000, 1)-type instance I.Scale "XPT" "kilogram" = '(31103477, 1000000000)-type instance I.Scale "XPT" "gram" = '(31103477, 1000000)-type instance I.Scale "XPT" "milligram" = '(31103477, 1000)-type instance I.Scale "XPT" "microgram" = '(31103477, 1)---- | Bitcoin-type instance I.Scale "BTC" "BTC" = '(100000000, 1)-type instance I.Scale "BTC" "bitcoin" = '(1, 1)-type instance I.Scale "BTC" "millibitcoin" = '(1000, 1)-type instance I.Scale "BTC" "satoshi" = '(100000000, 1)---- | Bitcoin-type instance I.Scale "XBT" "XBT" = '(100000000, 1)-type instance I.Scale "XBT" "bitcoin" = '(1, 1)-type instance I.Scale "XBT" "satoshi" = '(100000000, 1)---- | Ether-type instance I.Scale "ETH" "ETH" = '(1000000000000000000, 1)-type instance I.Scale "ETH" "ether" = '(1, 1)-type instance I.Scale "ETH" "kwei" = '(1000, 1)-type instance I.Scale "ETH" "babbage" = '(1000, 1)-type instance I.Scale "ETH" "mwei" = '(1000000, 1)-type instance I.Scale "ETH" "lovelace" = '(1000000, 1)-type instance I.Scale "ETH" "gwei" = '(1000000000, 1)-type instance I.Scale "ETH" "shannon" = '(1000000000, 1)-type instance I.Scale "ETH" "microether" = '(1000000000000, 1)-type instance I.Scale "ETH" "szabo" = '(1000000000000, 1)-type instance I.Scale "ETH" "finney" = '(1000000000000000, 1)-type instance I.Scale "ETH" "milliether" = '(1000000000000000, 1)-type instance I.Scale "ETH" "wei" = '(1000000000000000000, 1)---- | Ada-type instance I.Scale "ADA" "ADA" = '(1000000, 1)-type instance I.Scale "ADA" "ada" = '(1, 1)-type instance I.Scale "ADA" "lovelace" = '(1000000, 1)---- | Litecoin-type instance I.Scale "LTC" "LTC" = '(100000000, 1)-type instance I.Scale "LTC" "litecoin" = '(1, 1)-type instance I.Scale "LTC" "lite" = '(1000, 1)-type instance I.Scale "LTC" "photon" = '(100000000, 1)---- | Ripple-type instance I.Scale "XRP" "XRP" = '(1000000, 1)-type instance I.Scale "XRP" "ripple" = '(1, 1)-type instance I.Scale "XRP" "drop" = '(1000000, 1)---- | Monero-type instance I.Scale "XMR" "XMR" = '(1000000000000, 1)-type instance I.Scale "XMR" "monero" = '(1, 1)-type instance I.Scale "XMR" "decinero" = '(10, 1)-type instance I.Scale "XMR" "centinero" = '(100, 1)-type instance I.Scale "XMR" "millinero" = '(1000, 1)-type instance I.Scale "XMR" "micronero" = '(1000000, 1)-type instance I.Scale "XMR" "nanonero" = '(1000000000, 1)-type instance I.Scale "XMR" "piconero" = '(1000000000000, 1)+-- can simply create a new 'I.UnitScale' instance, and /voilà/. If you want to add a+-- new currency to the out-of-the-box offer, please request so in+-- https://github.com/k0001/safe-money/issues and the authors will see to it.+--+-- This module offers plenty of documentation, but for a deep explanation of+-- how all of the pieces fit together, please read+-- <https://ren.zone/articles/safe-money>. Notice, however, that this library+-- has changed a bit since that article was written. You can always see the+-- [change log](https://github.com/k0001/safe-money/blob/master/changelog.md) to+-- understand what has changed.+--+-- Also, keep in mind that useful instances for the many types defined by+-- @safe-money@ can be found in these other libraries:+--+-- * [safe-money-aeson](https://hackage.haskell.org/package/safe-money-aeson):+--   `FromJSON` and `ToJSON` instances (from the+--   [aeson](https://hackage.haskell.org/package/aeson) library).+--+-- * [safe-money-cereal](https://hackage.haskell.org/package/safe-money-cereal):+--   `Serialize` instances (from the+--   [cereal](https://hackage.haskell.org/package/cereal) library).+--+-- * [safe-money-serialise](https://hackage.haskell.org/package/safe-money-serialise):+--   `Serialise` instances (from the+--   [serialise](https://hackage.haskell.org/package/serialise) library).+--+-- * [safe-money-store](https://hackage.haskell.org/package/safe-money-store):+--   `Store` instances (from the+--   [store](https://hackage.haskell.org/package/store) library).+--+-- * [safe-money-xmlbf](https://hackage.haskell.org/package/safe-money-xmlbf):+--   `FromXml` and `ToXml` instances (from the+--   [xmlbf](https://hackage.haskell.org/package/xmlbf) library).+module Money+ ( -- * Dense monetary values+   I.Dense+ , I.denseCurrency+ , I.dense+ , I.dense'+ , I.denseFromDiscrete+ , I.denseFromDecimal+ , I.denseToDecimal+   -- * Discrete monetary values+ , I.Discrete+ , I.Discrete'+ , I.discrete+ , I.discreteCurrency+ , I.discreteFromDense+ , I.discreteFromDecimal+ , I.discreteToDecimal+   -- * Currency scales+ , I.Scale+ , I.scaleFromRational+ , I.scaleToRational+ , I.scale+ , I.UnitScale+ , I.CurrencyScale+ , I.GoodScale+ , I.ErrScaleNonCanonical+   -- * Currency exchange+ , I.ExchangeRate+ , I.exchangeRate+ , I.exchange+ , I.exchangeRateRecip+ , I.exchangeRateFromDecimal+ , I.exchangeRateToDecimal+ , I.exchangeRateToRational+   -- * Serializable representations+ , I.SomeDense+ , I.toSomeDense+ , I.mkSomeDense+ , I.fromSomeDense+ , I.withSomeDense+ , I.someDenseCurrency+ , I.someDenseAmount+ , I.SomeDiscrete+ , I.toSomeDiscrete+ , I.mkSomeDiscrete+ , I.fromSomeDiscrete+ , I.withSomeDiscrete+ , I.someDiscreteCurrency+ , I.someDiscreteScale+ , I.someDiscreteAmount+ , I.SomeExchangeRate+ , I.toSomeExchangeRate+ , I.mkSomeExchangeRate+ , I.fromSomeExchangeRate+ , I.withSomeExchangeRate+ , I.someExchangeRateSrcCurrency+ , I.someExchangeRateDstCurrency+ , I.someExchangeRateRate+ -- * Miscellaneous+ , I.Approximation(..)+ -- ** Decimal config+ , I.DecimalConf(..)+ , I.defaultDecimalConf+ -- *** Separators+ , I.Separators+ , I.mkSeparators+ , I.separatorsComma+ , I.separatorsCommaDot+ , I.separatorsCommaNarrownbsp+ , I.separatorsCommaNbsp+ , I.separatorsCommaThinsp+ , I.separatorsCommaSpace+ , I.separatorsDot+ , I.separatorsDotComma+ , I.separatorsDotNarrownbsp+ , I.separatorsDotThinsp+ , I.separatorsDotNbsp+ , I.separatorsDotSpace+ ) where++import qualified Money.Internal as I++--------------------------------------------------------------------------------+-- Currency scales++-- | United Arab Emirates dirham+type instance I.CurrencyScale "AED" = I.UnitScale "AED" "fils"+type instance I.UnitScale "AED" "dirham" = '(1, 1)+type instance I.UnitScale "AED" "fils" = '(100, 1)+-- | Afghan afghani+type instance I.CurrencyScale "AFN" = I.UnitScale "AFN" "pul"+type instance I.UnitScale "AFN" "afghani" = '(1, 1)+type instance I.UnitScale "AFN" "pul" = '(100, 1)+-- | Albanian lek+type instance I.CurrencyScale "ALL" = I.UnitScale "ALL" "lek"+type instance I.UnitScale "ALL" "lek" = '(1, 1)+type instance I.UnitScale "ALL" "qindarke" = '(100, 1)+-- | Armenian dram+type instance I.CurrencyScale "AMD" = I.UnitScale "AMD" "luma"+type instance I.UnitScale "AMD" "dram" = '(1, 1)+type instance I.UnitScale "AMD" "luma" = '(100, 1)+-- | Netherlands Antillean guilder+type instance I.CurrencyScale "ANG" = I.UnitScale "AMD" "cent"+type instance I.UnitScale "ANG" "guilder" = '(1, 1)+type instance I.UnitScale "ANG" "cent" = '(100, 1)+-- | Angolan kwanza+type instance I.CurrencyScale "AOA" = I.UnitScale "AOA" "centimo"+type instance I.UnitScale "AOA" "kwanza" = '(1, 1)+type instance I.UnitScale "AOA" "centimo" = '(100, 1)+-- | Argentine peso+type instance I.CurrencyScale "ARS" = I.UnitScale "ARS" "centavo"+type instance I.UnitScale "ARS" "peso" = '(1, 1)+type instance I.UnitScale "ARS" "centavo" = '(100, 1)+-- | Australian dollar+type instance I.CurrencyScale "AUD" = I.UnitScale "AUD" "cent"+type instance I.UnitScale "AUD" "dollar" = '(1, 1)+type instance I.UnitScale "AUD" "cent" = '(100, 1)+-- | Aruban florin+type instance I.CurrencyScale "AWG" = I.UnitScale "AWG" "cent"+type instance I.UnitScale "AWG" "florin" = '(1, 1)+type instance I.UnitScale "AWG" "cent" = '(100, 1)+-- | Azerbaijani manat+type instance I.CurrencyScale "AZN" = I.UnitScale "AZN" "qapik"+type instance I.UnitScale "AZN" "manat" = '(1, 1)+type instance I.UnitScale "AZN" "qapik" = '(100, 1)+-- | Bosnia and Herzegovina convertible mark+type instance I.CurrencyScale "BAM" = I.UnitScale "BAM" "fenig"+type instance I.UnitScale "BAM" "mark" = '(1, 1)+type instance I.UnitScale "BAM" "fening" = '(100, 1)+-- | Barbadian dollar+type instance I.CurrencyScale "BBD" = I.UnitScale "BBD" "cent"+type instance I.UnitScale "BBD" "dollar" = '(1, 1)+type instance I.UnitScale "BBD" "cent" = '(100, 1)+-- | Bangladeshi taka+type instance I.CurrencyScale "BDT" = I.UnitScale "BDT" "paisa"+type instance I.UnitScale "BDT" "taka" = '(1, 1)+type instance I.UnitScale "BDT" "paisa" = '(100, 1)+-- | Bulgarian lev+type instance I.CurrencyScale "BGN" = I.UnitScale "BGN" "stotinka"+type instance I.UnitScale "BGN" "lev" = '(1, 1)+type instance I.UnitScale "BGN" "stotinka" = '(100, 1)+-- | Bahraini dinar+type instance I.CurrencyScale "BHD" = I.UnitScale "BHD" "fils"+type instance I.UnitScale "BHD" "dinar" = '(1, 1)+type instance I.UnitScale "BHD" "fils" = '(1000, 1)+-- | Burundi franc+type instance I.CurrencyScale "BIF" = I.UnitScale "BIF" "centime"+type instance I.UnitScale "BIF" "franc" = '(1, 1)+type instance I.UnitScale "BIF" "centime" = '(100, 1)+-- | Bermudian dollar+type instance I.CurrencyScale "BMD" = I.UnitScale "BMD" "cent"+type instance I.UnitScale "BMD" "dollar" = '(1, 1)+type instance I.UnitScale "BMD" "cent" = '(100, 1)+-- | Brunei dollar+type instance I.CurrencyScale "BND" = I.UnitScale "BND" "sen"+type instance I.UnitScale "BND" "dollar" = '(1, 1)+type instance I.UnitScale "BND" "sen" = '(100, 1)+-- | Bolivian boliviano+type instance I.CurrencyScale "BOB" = I.UnitScale "BOB" "centavo"+type instance I.UnitScale "BOB" "boliviano" = '(1, 1)+type instance I.UnitScale "BOB" "centavo" = '(100, 1)+-- | Bolivian Mvdol+type instance I.CurrencyScale "BOV" = '(100, 1)+-- | Brazilian real+type instance I.CurrencyScale "BRL" = I.UnitScale "BRL" "centavo"+type instance I.UnitScale "BRL" "real" = '(1, 1)+type instance I.UnitScale "BRL" "centavo" = '(100, 1)+-- | Bahamian dollar+type instance I.CurrencyScale "BSD" = I.UnitScale "BSD" "cent"+type instance I.UnitScale "BSD" "dollar" = '(1, 1)+type instance I.UnitScale "BSD" "cent" = '(1, 1)+-- | Bhutanese ngultrum+type instance I.CurrencyScale "BTN" = I.UnitScale "BTN" "chetrum"+type instance I.UnitScale "BTN" "ngultrum" = '(1, 1)+type instance I.UnitScale "BTN" "chetrum" = '(100, 1)+-- | Botswana pula+type instance I.CurrencyScale "BWP" = I.UnitScale "BWP" "thebe"+type instance I.UnitScale "BWP" "pula" = '(1, 1)+type instance I.UnitScale "BWP" "thebe" = '(100, 1)+-- | Belarusian ruble+type instance I.CurrencyScale "BYN" = I.UnitScale "BYN" "kapiejka"+type instance I.UnitScale "BYN" "ruble" = '(1, 1)+type instance I.UnitScale "BYN" "kapiejka" = '(100, 1)+-- | Belarusian ruble+type instance I.CurrencyScale "BYR" = I.UnitScale "BYR" "kapiejka"+type instance I.UnitScale "BYR" "ruble" = '(1, 1)+type instance I.UnitScale "BYR" "kapiejka" = '(100, 1)+-- | Belize dollar+type instance I.CurrencyScale "BZD" = I.UnitScale "BZD" "cent"+type instance I.UnitScale "BZD" "dollar" = '(1, 1)+type instance I.UnitScale "BZD" "cent" = '(100, 1)+-- | Canadian dollar+type instance I.CurrencyScale "CAD" = I.UnitScale "CAD" "cent"+type instance I.UnitScale "CAD" "dollar" = '(1, 1)+type instance I.UnitScale "CAD" "cent" = '(100, 1)+-- | Congolese franc+type instance I.CurrencyScale "CDF" = I.UnitScale "CDF" "centime"+type instance I.UnitScale "CDF" "franc" = '(1, 1)+type instance I.UnitScale "CDF" "centime" = '(100, 1)+-- | WIR euro+type instance I.CurrencyScale "CHE" = '(100, 1)+-- | Swiss franc+type instance I.CurrencyScale "CHF" = I.UnitScale "CHF" "rappen"+type instance I.UnitScale "CHF" "franc" = '(1, 1)+type instance I.UnitScale "CHF" "rappen" = '(100, 1)+-- | WIR franc+type instance I.CurrencyScale "CHW" = '(100, 1)+-- | Chilean unidad de fomento+type instance I.CurrencyScale "CLF" = '(10000, 1)+-- | Chilean peso+type instance I.CurrencyScale "CLP" = I.UnitScale "CLP" "centavo"+type instance I.UnitScale "CLP" "peso" = '(1, 1)+type instance I.UnitScale "CLP" "centavo" = '(100, 1)+-- | Chinese Renminbi+type instance I.CurrencyScale "CNY" = I.UnitScale "CNY" "fen"+type instance I.UnitScale "CNY" "yuan" = '(1, 1)+type instance I.UnitScale "CNY" "fen" = '(100, 1)+-- | Colombian peso+type instance I.CurrencyScale "COP" = I.UnitScale "COP" "centavo"+type instance I.UnitScale "COP" "peso" = '(1, 1)+type instance I.UnitScale "COP" "centavo" = '(100, 1)+-- | Colombian unidad de valor real+type instance I.CurrencyScale "COU" = '(100, 1)+-- | Costa Rican colon+type instance I.CurrencyScale "CRC" = I.UnitScale "CRC" "centimo"+type instance I.UnitScale "CRC" "colon" = '(1, 1)+type instance I.UnitScale "CRC" "centimo" = '(100, 1)+-- | Cuban peso convertible+type instance I.CurrencyScale "CUC" = I.UnitScale "CUC" "centavo"+type instance I.UnitScale "CUC" "peso" = '(1, 1)+type instance I.UnitScale "CUC" "centavo" = '(100, 1)+-- | Cuban peso+type instance I.CurrencyScale "CUP" = I.UnitScale "CUP" "centavo"+type instance I.UnitScale "CUP" "peso" = '(1, 1)+type instance I.UnitScale "CUP" "centavo" = '(100, 1)+-- | Cape Verdean escudo+type instance I.CurrencyScale "CVE" = I.UnitScale "CVE" "centavo"+type instance I.UnitScale "CVE" "escudo" = '(1, 1)+type instance I.UnitScale "CVE" "centavo" = '(100, 1)+-- | Czech koruna+type instance I.CurrencyScale "CZK" = I.UnitScale "CZK" "haler"+type instance I.UnitScale "CZK" "koruna" = '(1, 1)+type instance I.UnitScale "CZK" "haler" = '(100, 1)+-- | Djiboutian franc+type instance I.CurrencyScale "DJF" = I.UnitScale "DJF" "centime"+type instance I.UnitScale "DJF" "franc" = '(1, 1)+type instance I.UnitScale "DJF" "centime" = '(100, 1)+-- | Danish krone+type instance I.CurrencyScale "DKK" = I.UnitScale "DKK" "ore"+type instance I.UnitScale "DKK" "krone" = '(1, 1)+type instance I.UnitScale "DKK" "ore" = '(100, 1)+-- | Dominican peso+type instance I.CurrencyScale "DOP" = I.UnitScale "DOP" "centavo"+type instance I.UnitScale "DOP" "peso" = '(1, 1)+type instance I.UnitScale "DOP" "centavo" = '(100, 1)+-- | Algerian dinar+type instance I.CurrencyScale "DZD" = I.UnitScale "DZD" "santeem"+type instance I.UnitScale "DZD" "dinar" = '(1, 1)+type instance I.UnitScale "DZD" "santeem" = '(100, 1)+-- | Egyptian pound+type instance I.CurrencyScale "EGP" = I.UnitScale "EGP" "piastre"+type instance I.UnitScale "EGP" "pound" = '(1, 1)+type instance I.UnitScale "EGP" "piastre" = '(100, 1)+-- | Eritrean nakfa+type instance I.CurrencyScale "ERN" = I.UnitScale "ERN" "cent"+type instance I.UnitScale "ERN" "nafka" = '(1, 1)+type instance I.UnitScale "ERN" "cent" = '(100, 1)+-- | Ethiopian birr+type instance I.CurrencyScale "ETB" = I.UnitScale "ETB" "santim"+type instance I.UnitScale "ETB" "birr" = '(1, 1)+type instance I.UnitScale "ETB" "santim" = '(100, 1)+-- | European euro+type instance I.CurrencyScale "EUR" = I.UnitScale "EUR" "cent"+type instance I.UnitScale "EUR" "euro" = '(1, 1)+type instance I.UnitScale "EUR" "cent" = '(100, 1)+-- | Fijian dollar+type instance I.CurrencyScale "FJD" = I.UnitScale "FJD" "cent"+type instance I.UnitScale "FJD" "dollar" = '(1, 1)+type instance I.UnitScale "FJD" "cent" = '(100, 1)+-- | Falkland Islands pound+type instance I.CurrencyScale "FKP" = I.UnitScale "FKP" "penny"+type instance I.UnitScale "FKP" "pound" = '(1, 1)+type instance I.UnitScale "FKP" "penny" = '(100, 1)+-- | Pound sterling+type instance I.CurrencyScale "GBP" = I.UnitScale "GBP" "penny"+type instance I.UnitScale "GBP" "pound" = '(1, 1)+type instance I.UnitScale "GBP" "penny" = '(100, 1)+-- | Georgian lari+type instance I.CurrencyScale "GEL" = I.UnitScale "GEL" "tetri"+type instance I.UnitScale "GEL" "lari" = '(1, 1)+type instance I.UnitScale "GEL" "tetri" = '(100, 1)+-- | Ghanaian cedi+type instance I.CurrencyScale "GHS" = I.UnitScale "GHS" "pesewa"+type instance I.UnitScale "GHS" "cedi" = '(1, 1)+type instance I.UnitScale "GHS" "pesewa" = '(100, 1)+-- | Gibraltar pound+type instance I.CurrencyScale "GIP" = I.UnitScale "GIP" "penny"+type instance I.UnitScale "GIP" "pound" = '(1, 1)+type instance I.UnitScale "GIP" "penny" = '(100, 1)+-- | Gambian dalasi+type instance I.CurrencyScale "GMD" = I.UnitScale "GMD" "butut"+type instance I.UnitScale "GMD" "dalasi" = '(1, 1)+type instance I.UnitScale "GMD" "butut" = '(100, 1)+-- | Guinean franc+type instance I.CurrencyScale "GNF" = I.UnitScale "GNF" "centime"+type instance I.UnitScale "GNF" "franc" = '(1, 1)+type instance I.UnitScale "GNF" "centime" = '(100, 1)+-- | Guatemalan quetzal+type instance I.CurrencyScale "GTQ" = I.UnitScale "GTQ" "centavo"+type instance I.UnitScale "GTQ" "quetzal" = '(1, 1)+type instance I.UnitScale "GTQ" "centavo" = '(100, 1)+-- | Guyanese dollar+type instance I.CurrencyScale "GYD" = I.UnitScale "GYD" "cent"+type instance I.UnitScale "GYD" "dollar" = '(1, 1)+type instance I.UnitScale "GYD" "cent" = '(100, 1)+-- | Hong Kong dollar+type instance I.CurrencyScale "HKD" = I.UnitScale "HKD" "cent"+type instance I.UnitScale "HKD" "dollar" = '(1, 1)+type instance I.UnitScale "HKD" "cent" = '(100, 1)+-- | Honduran lempira+type instance I.CurrencyScale "HNL" = I.UnitScale "HNL" "centavo"+type instance I.UnitScale "HNL" "lempira" = '(1, 1)+type instance I.UnitScale "HNL" "centavo" = '(100, 1)+-- | Croatian kuna+type instance I.CurrencyScale "HRK" = I.UnitScale "HRK" "lipa"+type instance I.UnitScale "HRK" "kuna" = '(1, 1)+type instance I.UnitScale "HRK" "lipa" = '(100, 1)+-- | Haitian gourde+type instance I.CurrencyScale "HTG" = I.UnitScale "HTG" "centime"+type instance I.UnitScale "HTG" "gourde" = '(1, 1)+type instance I.UnitScale "HTG" "centime" = '(1, 1)+-- | Hungarian forint+type instance I.CurrencyScale "HUF" = I.UnitScale "HUF" "filler"+type instance I.UnitScale "HUF" "forint" = '(1, 1)+type instance I.UnitScale "HUF" "filler" = '(100, 1)+-- | Indonesian rupiah+type instance I.CurrencyScale "IDR" = I.UnitScale "IDR" "sen"+type instance I.UnitScale "IDR" "rupiah" = '(1, 1)+type instance I.UnitScale "IDR" "sen" = '(100, 1)+-- | Israeli new shekel+type instance I.CurrencyScale "ILS" = I.UnitScale "ILS" "agora"+type instance I.UnitScale "ILS" "shekel" = '(1, 1)+type instance I.UnitScale "ILS" "agora" = '(100, 1)+-- | Indian rupee+type instance I.CurrencyScale "INR" = I.UnitScale "INR" "paisa"+type instance I.UnitScale "INR" "rupee" = '(1, 1)+type instance I.UnitScale "INR" "paisa" = '(100, 1)+-- | Iraqi dinar+type instance I.CurrencyScale "IQD" = I.UnitScale "IQD" "fils"+type instance I.UnitScale "IQD" "dinar" = '(1, 1)+type instance I.UnitScale "IQD" "fils" = '(1000, 1)+-- | Iranian rial+type instance I.CurrencyScale "IRR" = I.UnitScale "IRR" "dinar"+type instance I.UnitScale "IRR" "rial" = '(1, 1)+type instance I.UnitScale "IRR" "dinar" = '(100, 1)+-- | Icelandic króna+type instance I.CurrencyScale "ISK" = I.UnitScale "ISK" "eyrir"+type instance I.UnitScale "ISK" "krona" = '(1, 1)+type instance I.UnitScale "ISK" "eyrir" = '(100, 1)+-- | Jamaican dollar+type instance I.CurrencyScale "JMD" = I.UnitScale "JMD" "cent"+type instance I.UnitScale "JMD" "dollar" = '(1, 1)+type instance I.UnitScale "JMD" "cent" = '(100, 1)+-- | Jordanian dinar+type instance I.CurrencyScale "JOD" = I.UnitScale "JOD" "piastre"+type instance I.UnitScale "JOD" "dinar" = '(1, 1)+type instance I.UnitScale "JOD" "piastre" = '(100, 1)+-- | Japanese yen+type instance I.CurrencyScale "JPY" = I.UnitScale "JPY" "sen"+type instance I.UnitScale "JPY" "yen" = '(1, 1)+type instance I.UnitScale "JPY" "sen" = '(100, 1)+-- | Kenyan shilling+type instance I.CurrencyScale "KES" = I.UnitScale "KES" "cent"+type instance I.UnitScale "KES" "shilling" = '(1, 1)+type instance I.UnitScale "KES" "cent" = '(100, 1)+-- | Kyrgyzstani som+type instance I.CurrencyScale "KGS" = I.UnitScale "KGS" "tyiyn"+type instance I.UnitScale "KGS" "som" = '(1, 1)+type instance I.UnitScale "KGS" "tyiyn" = '(100, 1)+-- | Cambodian riel+type instance I.CurrencyScale "KHR" = I.UnitScale "KHR" "sen"+type instance I.UnitScale "KHR" "riel" = '(1, 1)+type instance I.UnitScale "KHR" "sen" = '(100, 1)+-- | Comorian franc+type instance I.CurrencyScale "KMF" = I.UnitScale "KMF" "centime"+type instance I.UnitScale "KMF" "franc" = '(1, 1)+type instance I.UnitScale "KMF" "centime" = '(100, 1)+-- | North Korean won+type instance I.CurrencyScale "KPW" = I.UnitScale "KPW" "chon"+type instance I.UnitScale "KPW" "won" = '(1, 1)+type instance I.UnitScale "KPW" "chon" = '(100, 1)+-- | South Korean won+type instance I.CurrencyScale "KRW" = I.UnitScale "KRW" "jeon"+type instance I.UnitScale "KRW" "won" = '(1, 1)+type instance I.UnitScale "KRW" "jeon" = '(100, 1)+-- | Kuwaiti dinar+type instance I.CurrencyScale "KWD" = I.UnitScale "KWD" "fils"+type instance I.UnitScale "KWD" "dinar" = '(1, 1)+type instance I.UnitScale "KWD" "fils" = '(1000, 1)+-- | Cayman Islands dollar+type instance I.CurrencyScale "KYD" = I.UnitScale "KYD" "cent"+type instance I.UnitScale "KYD" "dollar" = '(1, 1)+type instance I.UnitScale "KYD" "cent" = '(100, 1)+-- | Kazakhstani tenge+type instance I.CurrencyScale "KZT" = I.UnitScale "KZT" "tiyin"+type instance I.UnitScale "KZT" "tenge" = '(1, 1)+type instance I.UnitScale "KZT" "tiyin" = '(100, 1)+-- | Lao kip+type instance I.CurrencyScale "LAK" = I.UnitScale "LAK" "att"+type instance I.UnitScale "LAK" "kip" = '(1, 1)+type instance I.UnitScale "LAK" "att" = '(100, 1)+-- | Lebanese pound+type instance I.CurrencyScale "LBP" = I.UnitScale "LBP" "piastre"+type instance I.UnitScale "LBP" "pound" = '(1, 1)+type instance I.UnitScale "LBP" "piastre" = '(100, 1)+-- | Sri Lankan rupee+type instance I.CurrencyScale "LKR" = I.UnitScale "LKR" "cent"+type instance I.UnitScale "LKR" "rupee" = '(1, 1)+type instance I.UnitScale "LKR" "cent" = '(100, 1)+-- | Liberian dollar+type instance I.CurrencyScale "LRD" = I.UnitScale "LRD" "cent"+type instance I.UnitScale "LRD" "dollar" = '(1, 1)+type instance I.UnitScale "LRD" "cent" = '(100, 1)+-- | Lesotho loti+type instance I.CurrencyScale "LSL" = I.UnitScale "LSL" "sente"+type instance I.UnitScale "LSL" "loti" = '(1, 1)+type instance I.UnitScale "LSL" "sente" = '(100, 1)+-- | Libyan dinar+type instance I.CurrencyScale "LYD" = I.UnitScale "LYD" "dirham"+type instance I.UnitScale "LYD" "dinar" = '(1, 1)+type instance I.UnitScale "LYD" "dirham" = '(1000, 1)+-- | Moroccan dirham+type instance I.CurrencyScale "MAD" = I.UnitScale "MAD" "centime"+type instance I.UnitScale "MAD" "dirham" = '(1, 1)+type instance I.UnitScale "MAD" "centime" = '(100, 1)+-- | Moldovan leu+type instance I.CurrencyScale "MDL" = I.UnitScale "MDL" "ban"+type instance I.UnitScale "MDL" "leu" = '(100, 1)+type instance I.UnitScale "MDL" "ban" = '(100, 1)+-- | Malagasy ariary+type instance I.CurrencyScale "MGA" = I.UnitScale "MGA" "iraimbilanja"+type instance I.UnitScale "MGA" "ariary" = '(1, 1)+type instance I.UnitScale "MGA" "iraimbilanja" = '(5, 1)+-- | Macedonian denar+type instance I.CurrencyScale "MKD" = I.UnitScale "MKD" "deni"+type instance I.UnitScale "MKD" "denar" = '(1, 1)+type instance I.UnitScale "MKD" "deni" = '(100, 1)+-- | Myanmar kyat+type instance I.CurrencyScale "MMK" = I.UnitScale "MMK" "pya"+type instance I.UnitScale "MMK" "kyat" = '(1, 1)+type instance I.UnitScale "MMK" "pya" = '(100, 1)+-- | Mongolian tugrik+type instance I.CurrencyScale "MNT" = I.UnitScale "MNT" "mongo"+type instance I.UnitScale "MNT" "tugrik" = '(1, 1)+type instance I.UnitScale "MNT" "mongo" = '(100, 1)+-- | Macanese pataca+type instance I.CurrencyScale "MOP" = I.UnitScale "MOP" "avo"+type instance I.UnitScale "MOP" "pataca" = '(1, 1)+type instance I.UnitScale "MOP" "avo" = '(100, 1)+-- | Mauritanian ouguiya+type instance I.CurrencyScale "MRO" = I.UnitScale "MRO" "khoums"+type instance I.UnitScale "MRO" "ouguiya" = '(1, 1)+type instance I.UnitScale "MRO" "khoums" = '(5, 1)+-- | Mauritian rupee+type instance I.CurrencyScale "MUR" = I.UnitScale "MUR" "cent"+type instance I.UnitScale "MUR" "rupee" = '(1, 1)+type instance I.UnitScale "MUR" "cent" = '(100, 1)+-- | Maldivian rufiyaa+type instance I.CurrencyScale "MVR" = I.UnitScale "MVR" "laari"+type instance I.UnitScale "MVR" "rufiyaa" = '(1, 1)+type instance I.UnitScale "MVR" "laari" = '(100, 1)+-- | Malawian kwacha+type instance I.CurrencyScale "MWK" = I.UnitScale "MWK" "tambala"+type instance I.UnitScale "MWK" "kwacha" = '(1, 1)+type instance I.UnitScale "MWK" "tambala" = '(100, 1)+-- | Mexican peso+type instance I.CurrencyScale "MXN" = I.UnitScale "MXN" "centavo"+type instance I.UnitScale "MXN" "peso" = '(1, 1)+type instance I.UnitScale "MXN" "centavo" = '(100, 1)+-- | Mexican unidad de inversion+type instance I.CurrencyScale "MXV" = '(100, 1)+-- | Malaysian ringgit+type instance I.CurrencyScale "MYR" = I.UnitScale "MYR" "sen"+type instance I.UnitScale "MYR" "ringgit" = '(1, 1)+type instance I.UnitScale "MYR" "sen" = '(100, 1)+-- | Mozambican metical+type instance I.CurrencyScale "MZN" = I.UnitScale "MZN" "centavo"+type instance I.UnitScale "MZN" "metical" = '(1, 1)+type instance I.UnitScale "MZN" "centavo" = '(100, 1)+-- | Namibian dollar+type instance I.CurrencyScale "NAD" = I.UnitScale "NAD" "cent"+type instance I.UnitScale "NAD" "dollar" = '(1, 1)+type instance I.UnitScale "NAD" "cent" = '(100, 1)+-- | Nigerian naira+type instance I.CurrencyScale "NGN" = I.UnitScale "NGN" "kobo"+type instance I.UnitScale "NGN" "naira" = '(1, 1)+type instance I.UnitScale "NGN" "kobo" = '(100, 1)+-- | Nicaraguan cordoba+type instance I.CurrencyScale "NIO" = I.UnitScale "NIO" "centavo"+type instance I.UnitScale "NIO" "cordoba" = '(1, 1)+type instance I.UnitScale "NIO" "centavo" = '(100, 1)+-- | Norwegian krone+type instance I.CurrencyScale "NOK" = I.UnitScale "NOK" "ore"+type instance I.UnitScale "NOK" "krone" = '(1, 1)+type instance I.UnitScale "NOK" "ore" = '(100, 1)+-- | Nepalese rupee+type instance I.CurrencyScale "NPR" = I.UnitScale "NPR" "paisa"+type instance I.UnitScale "NPR" "rupee" = '(1, 1)+type instance I.UnitScale "NPR" "paisa" = '(100, 1)+-- | New Zealand dollar+type instance I.CurrencyScale "NZD" = I.UnitScale "NZD" "cent"+type instance I.UnitScale "NZD" "dollar" = '(1, 1)+type instance I.UnitScale "NZD" "cent" = '(100, 1)+-- | Omani rial+type instance I.CurrencyScale "OMR" = I.UnitScale "OMR" "baisa"+type instance I.UnitScale "OMR" "rial" = '(1, 1)+type instance I.UnitScale "OMR" "baisa" = '(1000, 1)+-- | Panamenian balboa+type instance I.CurrencyScale "PAB" = I.UnitScale "PAB" "centesimo"+type instance I.UnitScale "PAB" "balboa" = '(1, 1)+type instance I.UnitScale "PAB" "centesimo" = '(100, 1)+-- | Peruvian sol+type instance I.CurrencyScale "PEN" = I.UnitScale "PEN" "centimo"+type instance I.UnitScale "PEN" "sol" = '(1, 1)+type instance I.UnitScale "PEN" "centimo" = '(100, 1)+-- | Papua New Guinean kina+type instance I.CurrencyScale "PGK" = I.UnitScale "PGK" "toea"+type instance I.UnitScale "PGK" "kina" = '(1, 1)+type instance I.UnitScale "PGK" "toea" = '(100, 1)+-- | Philippine peso+type instance I.CurrencyScale "PHP" = I.UnitScale "PHP" "centavo"+type instance I.UnitScale "PHP" "peso" = '(1, 1)+type instance I.UnitScale "PHP" "centavo" = '(100, 1)+-- | Pakistani rupee+type instance I.CurrencyScale "PKR" = I.UnitScale "PKR" "paisa"+type instance I.UnitScale "PKR" "rupee" = '(1, 1)+type instance I.UnitScale "PKR" "paisa" = '(100, 1)+-- | Polish zloty+type instance I.CurrencyScale "PLN" = I.UnitScale "PLN" "grosz"+type instance I.UnitScale "PLN" "zloty" = '(1, 1)+type instance I.UnitScale "PLN" "grosz" = '(100, 1)+-- | Paraguayan guarani+type instance I.CurrencyScale "PYG" = I.UnitScale "PYG" "centimo"+type instance I.UnitScale "PYG" "guarani" = '(1, 1)+type instance I.UnitScale "PYG" "centimo" = '(100, 1)+-- | Qatari riyal+type instance I.CurrencyScale "QAR" = I.UnitScale "QAR" "dirham"+type instance I.UnitScale "QAR" "riyal" = '(1, 1)+type instance I.UnitScale "QAR" "dirham" = '(100, 1)+-- | Romanian leu+type instance I.CurrencyScale "RON" = I.UnitScale "RON" "ban"+type instance I.UnitScale "RON" "leu" = '(1, 1)+type instance I.UnitScale "RON" "ban" = '(100, 1)+-- | Serbian dinar+type instance I.CurrencyScale "RSD" = I.UnitScale "RSD" "para"+type instance I.UnitScale "RSD" "dinar" = '(1, 1)+type instance I.UnitScale "RSD" "para" = '(100, 1)+-- | Russian ruble+type instance I.CurrencyScale "RUB" = I.UnitScale "RUB" "kopek"+type instance I.UnitScale "RUB" "ruble" = '(1, 1)+type instance I.UnitScale "RUB" "kopek" = '(100, 1)+-- | Rwandan franc+type instance I.CurrencyScale "RWF" = I.UnitScale "RWF" "centime"+type instance I.UnitScale "RWF" "franc" = '(1, 1)+type instance I.UnitScale "RWF" "centime" = '(100, 1)+-- | Saudi Arabian riyal+type instance I.CurrencyScale "SAR" = I.UnitScale "SAR" "halala"+type instance I.UnitScale "SAR" "riyal" = '(1, 1)+type instance I.UnitScale "SAR" "halala" = '(100, 1)+-- | Solomon Islands dollar+type instance I.CurrencyScale "SBD" = I.UnitScale "SBD" "cent"+type instance I.UnitScale "SBD" "dollar" = '(100, 1)+type instance I.UnitScale "SBD" "cent" = '(100, 1)+-- | Seychellois rupee+type instance I.CurrencyScale "SCR" = I.UnitScale "SCR" "cent"+type instance I.UnitScale "SCR" "rupee" = '(1, 1)+type instance I.UnitScale "SCR" "cent" = '(100, 1)+-- | Sudanese pound+type instance I.CurrencyScale "SDG" = I.UnitScale "SDG" "piastre"+type instance I.UnitScale "SDG" "pound" = '(1, 1)+type instance I.UnitScale "SDG" "piastre" = '(100, 1)+-- | Swedish krona+type instance I.CurrencyScale "SEK" = I.UnitScale "SEK" "ore"+type instance I.UnitScale "SEK" "krona" = '(1, 1)+type instance I.UnitScale "SEK" "ore" = '(100, 1)+-- | Singapore dollar+type instance I.CurrencyScale "SGD" = I.UnitScale "SGD" "cent"+type instance I.UnitScale "SGD" "dollar" = '(1, 1)+type instance I.UnitScale "SGD" "cent" = '(100, 1)+-- | Saint Helena pound+type instance I.CurrencyScale "SHP" = I.UnitScale "SHP" "penny"+type instance I.UnitScale "SHP" "pound" = '(1, 1)+type instance I.UnitScale "SHP" "penny" = '(100, 1)+-- | Sierra Leonean leone+type instance I.CurrencyScale "SLL" = I.UnitScale "SLL" "cent"+type instance I.UnitScale "SLL" "leone" = '(1, 1)+type instance I.UnitScale "SLL" "cent" = '(100, 1)+-- | Somali shilling+type instance I.CurrencyScale "SOS" = I.UnitScale "SOS" "cent"+type instance I.UnitScale "SOS" "shilling" = '(1, 1)+type instance I.UnitScale "SOS" "cent" = '(100, 1)+-- | Surinamese dollar+type instance I.CurrencyScale "SRD" = I.UnitScale "SRD" "cent"+type instance I.UnitScale "SRD" "dollar" = '(1, 1)+type instance I.UnitScale "SRD" "cent" = '(100, 1)+-- | South Sudanese pound+type instance I.CurrencyScale "SSP" = I.UnitScale "SSP" "piastre"+type instance I.UnitScale "SSP" "pound" = '(1, 1)+type instance I.UnitScale "SSP" "piastre" = '(100, 1)+-- | Sao Tome and Principe dobra+type instance I.CurrencyScale "STD" = I.UnitScale "STD" "centimo"+type instance I.UnitScale "STD" "dobra" = '(1, 1)+type instance I.UnitScale "STD" "centimo" = '(100, 1)+-- | Salvadoran colon+type instance I.CurrencyScale "SVC" = I.UnitScale "SVC" "centavo"+type instance I.UnitScale "SVC" "colon" = '(1, 1)+type instance I.UnitScale "SVC" "centavo" = '(100, 1)+-- | Syrian pound+type instance I.CurrencyScale "SYP" = I.UnitScale "SYP" "piastre"+type instance I.UnitScale "SYP" "pound" = '(1, 1)+type instance I.UnitScale "SYP" "piastre" = '(100, 1)+-- | Swazi lilangeni+type instance I.CurrencyScale "SZL" = I.UnitScale "SZL" "cent"+type instance I.UnitScale "SZL" "lilangeni" = '(1, 1)+type instance I.UnitScale "SZL" "cent" = '(100, 1)+-- | Thai baht+type instance I.CurrencyScale "THB" = I.UnitScale "THB" "satang"+type instance I.UnitScale "THB" "baht" = '(1, 1)+type instance I.UnitScale "THB" "satang" = '(100, 1)+-- | Tajikistani somoni+type instance I.CurrencyScale "TJS" = I.UnitScale "TJS" "diram"+type instance I.UnitScale "TJS" "somoni" = '(1, 1)+type instance I.UnitScale "TJS" "diram" = '(100, 1)+-- | Turkmen manat+type instance I.CurrencyScale "TMT" = I.UnitScale "TMT" "tennesi"+type instance I.UnitScale "TMT" "manat" = '(1, 1)+type instance I.UnitScale "TMT" "tennesi" = '(100, 1)+-- | Tunisian dinar+type instance I.CurrencyScale "TND" = I.UnitScale "TND" "millime"+type instance I.UnitScale "TND" "dinar" = '(1, 1)+type instance I.UnitScale "TND" "millime" = '(1000, 1)+-- | Tongan pa’anga+type instance I.CurrencyScale "TOP" = I.UnitScale "TOP" "seniti"+type instance I.UnitScale "TOP" "pa'anga" = '(1, 1)+type instance I.UnitScale "TOP" "seniti" = '(100, 1)+-- | Turkish lira+type instance I.CurrencyScale "TRY" = I.UnitScale "TRY" "kurus"+type instance I.UnitScale "TRY" "lira" = '(1, 1)+type instance I.UnitScale "TRY" "kurus" = '(100, 1)+-- | Tobago Trinidad and Tobago dollar+type instance I.CurrencyScale "TTD" = I.UnitScale "TTD" "cent"+type instance I.UnitScale "TTD" "dollar" = '(1, 1)+type instance I.UnitScale "TTD" "cent" = '(100, 1)+-- | New Taiwan dollar+type instance I.CurrencyScale "TWD" = I.UnitScale "TWD" "cent"+type instance I.UnitScale "TWD" "dollar" = '(1, 1)+type instance I.UnitScale "TWD" "cent" = '(100, 1)+-- | Tanzanian shilling+type instance I.CurrencyScale "TZS" = I.UnitScale "TZS" "cent"+type instance I.UnitScale "TZS" "shilling" = '(1, 1)+type instance I.UnitScale "TZS" "cent" = '(100, 1)+-- | Ukrainian hryvnia+type instance I.CurrencyScale "UAH" = I.UnitScale "UAH" "kopiyka"+type instance I.UnitScale "UAH" "hryvnia" = '(1, 1)+type instance I.UnitScale "UAH" "kopiyka" = '(100, 1)+-- | Ugandan shilling+type instance I.CurrencyScale "UGX" = I.UnitScale "UGX" "cent"+type instance I.UnitScale "UGX" "shilling" = '(1, 1)+type instance I.UnitScale "UGX" "cent" = '(100, 1)+-- | United States dollar+type instance I.CurrencyScale "USD" = I.UnitScale "USD" "cent"+type instance I.UnitScale "USD" "dollar" = '(1, 1)+type instance I.UnitScale "USD" "cent" = '(100, 1)+-- | United States dollar (next day)+type instance I.CurrencyScale "USN" = I.UnitScale "USN" "cent"+type instance I.UnitScale "USN" "dollar" = '(1, 1)+type instance I.UnitScale "USN" "cent" = '(100, 1)+-- | Uruguayan peso en unidades indexadas+type instance I.CurrencyScale "UYI" = '(1, 1)+-- | Uruguayan peso+type instance I.CurrencyScale "UYU" = I.UnitScale "UYU" "centesimo"+type instance I.UnitScale "UYU" "peso" = '(1, 1)+type instance I.UnitScale "UYU" "centesimo" = '(100, 1)+-- | Uruguayan unidad previsional+type instance I.CurrencyScale "UYW" = '(10000, 1)+-- | Uzbekistani som+type instance I.CurrencyScale "UZS" = I.UnitScale "UZS" "tiyin"+type instance I.UnitScale "UZS" "som" = '(1, 1)+type instance I.UnitScale "UZS" "tiyin" = '(100, 1)+-- | Venezuelan bolivar fuerte+type instance I.CurrencyScale "VEF" = I.UnitScale "VEF" "centimo"+type instance I.UnitScale "VEF" "bolivar" = '(1, 1)+type instance I.UnitScale "VEF" "centimo" = '(100, 1)+-- | Venezuelan bolivar soberano+type instance I.CurrencyScale "VES" = I.UnitScale "VES" "centimo"+type instance I.UnitScale "VES" "bolivar" = '(1, 1)+type instance I.UnitScale "VES" "centimo" = '(100, 1)+-- | Vietnamese dong+type instance I.CurrencyScale "VND" = I.UnitScale "VND" "hao"+type instance I.UnitScale "VND" "dong" = '(1, 1)+type instance I.UnitScale "VND" "hao" = '(10, 1)+-- | Vanuatu vatu+type instance I.CurrencyScale "VUV" = I.UnitScale "VUV" "vatu"+type instance I.UnitScale "VUV" "vatu" = '(1, 1)+-- | Samoan tālā+type instance I.CurrencyScale "WST" = I.UnitScale "WST" "sene"+type instance I.UnitScale "WST" "tala" = '(1, 1)+type instance I.UnitScale "WST" "sene" = '(100, 1)+-- | Central African CFA franc+type instance I.CurrencyScale "XAF" = I.UnitScale "XAF" "centime"+type instance I.UnitScale "XAF" "franc" = '(1, 1)+type instance I.UnitScale "XAF" "centime" = '(100, 1)+-- | East Caribbean dollar+type instance I.CurrencyScale "XCD" = I.UnitScale "XCD" "cent"+type instance I.UnitScale "XCD" "dollar" = '(1, 1)+type instance I.UnitScale "XCD" "cent" = '(100, 1)+-- | International Monetary Fund Special Drawing Right+type instance I.CurrencyScale "XDR" = '(1, 1)+-- | West African CFA franc+type instance I.CurrencyScale "XOF" = I.UnitScale "XOF" "centime"+type instance I.UnitScale "XOF" "franc" = '(1, 1)+type instance I.UnitScale "XOF" "centime" = '(100, 1)+-- | CFP franc+type instance I.CurrencyScale "XPF" = I.UnitScale "XPF" "centime"+type instance I.UnitScale "XPF" "franc" = '(1, 1)+type instance I.UnitScale "XPF" "centime" = '(100, 1)+-- | Sucre+type instance I.CurrencyScale "XSU" = '(1, 1)+-- | African Development Bank unit of account+type instance I.CurrencyScale "XUA" = '(1, 1)+-- | Yemeni rial+type instance I.CurrencyScale "YER" = I.UnitScale "YER" "fils"+type instance I.UnitScale "YER" "rial" = '(1, 1)+type instance I.UnitScale "YER" "fils" = '(100, 1)+-- | South African rand+type instance I.CurrencyScale "ZAR" = I.UnitScale "ZAR" "cent"+type instance I.UnitScale "ZAR" "rand" = '(1, 1)+type instance I.UnitScale "ZAR" "cent" = '(100, 1)+-- | Zambian kwacha+type instance I.CurrencyScale "ZMW" = I.UnitScale "ZMW" "ngwee"+type instance I.UnitScale "ZMW" "kwacha" = '(1, 1)+type instance I.UnitScale "ZMW" "ngwee" = '(100, 1)+-- | Zimbawe dollar+type instance I.CurrencyScale "ZWL" = I.UnitScale "ZWL" "cent"+type instance I.UnitScale "ZWL" "dollar" = '(1, 1)+type instance I.UnitScale "ZWL" "cent" = '(100, 1)+-- | Gold. No canonical smallest unit. Unusable instance.+type instance I.CurrencyScale "XAU" = I.ErrScaleNonCanonical "XAU"+type instance I.UnitScale "XAU" "troy-ounce" = '(1, 1)+type instance I.UnitScale "XAU" "grain" = '(480, 1)+type instance I.UnitScale "XAU" "milligrain" = '(480000, 1)+type instance I.UnitScale "XAU" "micrograin" = '(480000000, 1)+type instance I.UnitScale "XAU" "kilogram" = '(31103477, 1000000000)+type instance I.UnitScale "XAU" "gram" = '(31103477, 1000000)+type instance I.UnitScale "XAU" "milligram" = '(31103477, 1000)+type instance I.UnitScale "XAU" "microgram" = '(31103477, 1)+-- | Silver. No canonical smallest unit. Unusable instance.+type instance I.CurrencyScale "XAG" = I.ErrScaleNonCanonical "XAU"+type instance I.UnitScale "XAG" "troy-ounce" = '(1, 1)+type instance I.UnitScale "XAG" "grain" = '(480, 1)+type instance I.UnitScale "XAG" "milligrain" = '(480000, 1)+type instance I.UnitScale "XAG" "micrograin" = '(480000000, 1)+type instance I.UnitScale "XAG" "kilogram" = '(31103477, 1000000000)+type instance I.UnitScale "XAG" "gram" = '(31103477, 1000000)+type instance I.UnitScale "XAG" "milligram" = '(31103477, 1000)+type instance I.UnitScale "XAG" "microgram" = '(31103477, 1)+-- | Palladium. No canonical smallest unit. Unusable instance.+type instance I.CurrencyScale "XPD" = I.ErrScaleNonCanonical "XPD"+type instance I.UnitScale "XPD" "troy-ounce" = '(1, 1)+type instance I.UnitScale "XPD" "grain" = '(480, 1)+type instance I.UnitScale "XPD" "milligrain" = '(480000, 1)+type instance I.UnitScale "XPD" "micrograin" = '(480000000, 1)+type instance I.UnitScale "XPD" "kilogram" = '(31103477, 1000000000)+type instance I.UnitScale "XPD" "gram" = '(31103477, 1000000)+type instance I.UnitScale "XPD" "milligram" = '(31103477, 1000)+type instance I.UnitScale "XPD" "microgram" = '(31103477, 1)+-- | Platinum. No canonical smallest unit. Unusable instance.+type instance I.CurrencyScale "XPT" = I.ErrScaleNonCanonical "XPT"+type instance I.UnitScale "XPT" "troy-ounce" = '(1, 1)+type instance I.UnitScale "XPT" "grain" = '(480, 1)+type instance I.UnitScale "XPT" "milligrain" = '(480000, 1)+type instance I.UnitScale "XPT" "micrograin" = '(480000000, 1)+type instance I.UnitScale "XPT" "kilogram" = '(31103477, 1000000000)+type instance I.UnitScale "XPT" "gram" = '(31103477, 1000000)+type instance I.UnitScale "XPT" "milligram" = '(31103477, 1000)+type instance I.UnitScale "XPT" "microgram" = '(31103477, 1)+-- | Bitcoin+type instance I.CurrencyScale "BTC" = I.UnitScale "BTC" "satoshi"+type instance I.UnitScale "BTC" "bitcoin" = '(1, 1)+type instance I.UnitScale "BTC" "millibitcoin" = '(1000, 1)+type instance I.UnitScale "BTC" "satoshi" = '(100000000, 1)+-- | Bitcoin+type instance I.CurrencyScale "XBT" = I.CurrencyScale "BTC"+type instance I.UnitScale "XBT" "bitcoin" = I.UnitScale "BTC" "bitcoin"+type instance I.UnitScale "XBT" "millibitcoin" = I.UnitScale "BTC" "millibitcoin"+type instance I.UnitScale "XBT" "satoshi" = I.UnitScale "BTC" "satoshi"+-- | Ether+type instance I.CurrencyScale "ETH" = I.UnitScale "ETH" "wei"+type instance I.UnitScale "ETH" "ether"      = '(1, 1)+type instance I.UnitScale "ETH" "kwei"       = '(1000, 1)+type instance I.UnitScale "ETH" "babbage"    = '(1000, 1)+type instance I.UnitScale "ETH" "mwei"       = '(1000000, 1)+type instance I.UnitScale "ETH" "lovelace"   = '(1000000, 1)+type instance I.UnitScale "ETH" "gwei"       = '(1000000000, 1)+type instance I.UnitScale "ETH" "shannon"    = '(1000000000, 1)+type instance I.UnitScale "ETH" "microether" = '(1000000000000, 1)+type instance I.UnitScale "ETH" "szabo"      = '(1000000000000, 1)+type instance I.UnitScale "ETH" "finney"     = '(1000000000000000, 1)+type instance I.UnitScale "ETH" "milliether" = '(1000000000000000, 1)+type instance I.UnitScale "ETH" "wei"        = '(1000000000000000000, 1)+-- | Cardano+type instance I.CurrencyScale "ADA" = I.UnitScale "ADA" "lovelace"+type instance I.UnitScale "ADA" "ada" = '(1, 1)+type instance I.UnitScale "ADA" "lovelace" = '(1000000, 1)+-- | Litecoin+type instance I.CurrencyScale "LTC" = I.UnitScale "LTC" "photon"+type instance I.UnitScale "LTC" "litecoin" = '(1, 1)+type instance I.UnitScale "LTC" "lite" = '(1000, 1)+type instance I.UnitScale "LTC" "photon" = '(100000000, 1)+-- | Ripple+type instance I.CurrencyScale "XRP" = I.UnitScale "XRP" "drop"+type instance I.UnitScale "XRP" "ripple" = '(1, 1)+type instance I.UnitScale "XRP" "drop" = '(1000000, 1)+-- | Monero+type instance I.CurrencyScale "XMR" = I.UnitScale "XMR" "piconero"+type instance I.UnitScale "XMR" "monero" = '(1, 1)+type instance I.UnitScale "XMR" "decinero" = '(10, 1)+type instance I.UnitScale "XMR" "centinero" = '(100, 1)+type instance I.UnitScale "XMR" "millinero" = '(1000, 1)+type instance I.UnitScale "XMR" "micronero" = '(1000000, 1)+type instance I.UnitScale "XMR" "nanonero" = '(1000000000, 1)+type instance I.UnitScale "XMR" "piconero" = '(1000000000000, 1) 
src/Money/Internal.hs view
@@ -43,9 +43,13 @@  , discreteToDecimal    -- * Currency scales  , Scale+ , scaleFromRational+ , scaleToRational+ , scale+ , UnitScale+ , CurrencyScale  , GoodScale  , ErrScaleNonCanonical- , scale    -- * Currency exchange  , ExchangeRate  , exchangeRate@@ -85,10 +89,30 @@  , someExchangeRateDstCurrency  , someExchangeRateDstCurrency'  , someExchangeRateRate- -- * Misc- , Approximation(Round, Floor, Ceiling, Truncate)+ -- * Rationals  , rationalToDecimal  , rationalFromDecimal+ -- * Miscellaneous+ , Approximation(Round, Floor, Ceiling, Truncate, HalfEven)+ , approximate+ -- ** Decimal config+ , DecimalConf(..)+ , defaultDecimalConf+ -- *** Separators+ , Separators+ , mkSeparators+ , separatorsComma+ , separatorsCommaDot+ , separatorsCommaNarrownbsp+ , separatorsCommaNbsp+ , separatorsCommaThinsp+ , separatorsCommaSpace+ , separatorsDot+ , separatorsDotComma+ , separatorsDotNarrownbsp+ , separatorsDotThinsp+ , separatorsDotNbsp+ , separatorsDotSpace  ) where  import Control.Applicative ((<|>), empty)@@ -300,8 +324,8 @@ -- -- For example, if you want to represent @GBP 21.05@, where the smallest -- represetable unit for a GBP (United Kingdom Pound) is the /penny/, and 100--- /pennies/ equal 1 GBP (i.e., @'Scale' \"GBP\" ~ '(100, 1)@), then you can--- use:+-- /pennies/ equal 1 GBP (i.e., @'UnitScale' \"GBP\" \"penny\" ~ '(100, 1)@), then+-- you can use: -- -- @ -- 'discrete' 2105 :: 'Discrete' \"GBP\" \"penny\"@@ -309,7 +333,7 @@ -- -- Because @2015 / 100 == 20.15@. type Discrete (currency :: Symbol) (unit :: Symbol)-  = Discrete' currency (Scale currency unit)+  = Discrete' currency (UnitScale currency unit)  -- | 'Discrete'' represents a discrete monetary value for a @currency@ expresed -- as amount of @scale@, which is a rational number expressed as @(numerator,@@ -373,24 +397,24 @@   ( KnownSymbol currency, GoodScale scale   ) => Show (Discrete' currency scale) where   showsPrec n = \d0@(Discrete i0) ->-    let c = symbolVal (Proxy :: Proxy currency)-        s = scale d0+    let c = symbolVal (Proxy :: Proxy currency) :: String+        rs = scaleToRational (scale d0) :: Rational     in showParen (n > 10) $          showString "Discrete " .  showsPrec 0 c . showChar ' ' .-         showsPrec 0 (numerator s) . showChar '%' .-         showsPrec 0 (denominator s) . showChar ' ' .+         showsPrec 0 (numerator rs) . showChar '%' .+         showsPrec 0 (denominator rs) . showChar ' ' .          showsPrec 0 i0  instance forall currency scale.   ( KnownSymbol currency, GoodScale scale   ) => Read (Discrete' currency scale) where   readPrec = Read.parens $ do-    let c = symbolVal (Proxy :: Proxy currency)-        s = scale (Proxy :: Proxy scale)+    let c = symbolVal (Proxy :: Proxy currency) :: String+        rs = scaleToRational (scale (Proxy :: Proxy scale)) :: Rational     _ <- ReadPrec.lift (ReadP.string (concat            [ "Discrete ", show c, " "-           , show (numerator s), "%"-           , show (denominator s), " "+           , show (numerator rs), "%"+           , show (denominator rs), " "            ]))     fmap Discrete Read.readPrec @@ -428,7 +452,8 @@   :: GoodScale scale   => Discrete' currency scale   -> Dense currency -- ^-denseFromDiscrete = \c@(Discrete i) -> Dense (fromInteger i / scale c)+denseFromDiscrete = \c@(Discrete i) ->+  Dense (fromInteger i / scaleToRational (scale c)) {-# INLINE denseFromDiscrete #-}  -- | 'Discrete' currency identifier.@@ -464,9 +489,11 @@   -- ^ Approximate @x@ to the nearest integer greater than or equal to @x@.   | Truncate   -- ^ Approximate @x@ to the nearest integer betwen @0@ and @x@, inclusive.+  | HalfEven+  -- ^ Approximate @x@ to the nearest even integer, when equidistant from the+  -- nearest two integers. This is also known as “Bankers Rounding”.   deriving (Eq, Ord, Show, Read, GHC.Generic) - approximate :: Approximation -> Rational -> Integer {-# INLINE approximate #-} approximate = \case@@ -474,7 +501,20 @@   Floor -> floor   Ceiling -> ceiling   Truncate -> truncate+  HalfEven -> halfEven +-- | Approximate to the nearest even integer, when equidistant from the nearest+-- two integers. This is also known as “Bankers Rounding”.+halfEven :: Rational -> Integer+{-# INLINABLE halfEven #-}+halfEven = \r ->                          --    1.5    -1.5+  let tr  :: Integer  = truncate r        --    1.0    -1.0+      rr  :: Rational = toRational tr - r --   -0.5     0.5+  in if | abs rr /= 1%2 -> round r+        | even tr -> tr+        | otherwise -> tr + signum tr++ -- | Approximate a 'Dense' value @x@ to the nearest value fully representable a -- given @scale@. --@@ -497,7 +537,7 @@   -> (Discrete' currency scale, Dense currency) discreteFromDense a = \c0 ->   let !r0 = toRational c0 :: Rational-      !r1 = scale (Proxy :: Proxy scale)+      !r1 = scaleToRational (scale (Proxy :: Proxy scale)) :: Rational       !i2 = approximate a (r0 * r1) :: Integer       !r2 = fromInteger i2 / r1 :: Rational       !d2 = Discrete i2@@ -507,42 +547,55 @@  -------------------------------------------------------------------------------- --- | @'Scale' currency unit@ is an rational number (expressed as @'(numerator,+-- | This is the term-level representation of the “scale” we represent as+-- @('Nat', 'Nat')@ elsewhere in the type system (e.g., in 'GoodScale' or+-- 'UnitScale').+--+-- See 'UnitScale' for a detailed description.+newtype Scale = Scale Rational+  deriving (Eq, Ord, Show, GHC.Generic)++-- | Construct a 'Scale' from a positive, non-zero rational number.+scaleFromRational :: Rational -> Maybe Scale+{-# INLINE scaleFromRational #-}+scaleFromRational r = do+  guard (denominator r /= 0 && r > 0)+  Just (Scale (abs (numerator r) % abs (denominator r)))++-- | Obtain the 'Rational' representation of a 'Scale'.+scaleToRational :: Scale -> Rational+{-# INLINE scaleToRational #-}+scaleToRational (Scale r) = r++-- | @'UnitScale' currency unit@ is an rational number (expressed as @'(numerator, -- denominator)@) indicating how many pieces of @unit@ fit in @currency@. -- -- @currency@ is usually a ISO-4217 currency code, but not necessarily. ----- The 'Scale' will determine how to convert a 'Dense' value into a--- 'Discrete' value and vice-versa.+-- The resulting @('Nat', 'Nat')@, which is the type-level representation for+-- what at the term-level we call 'Scale', will determine how to convert a+-- 'Dense' value into a 'Discrete' value and vice-versa. -- -- For example, there are 100 USD cents in 1 USD, so the scale for this -- relationship is: -- -- @--- type instance 'Scale' \"USD\" \"cent\" = '(100, 1)+-- type instance 'UnitScale' \"USD\" \"cent\" = '(100, 1) -- @ -- -- As another example, there is 1 dollar in USD, so the scale for this -- relationship is: -- -- @--- type instance 'Scale' \"USD\" \"dollar\" = '(1, 1)+-- type instance 'UnitScale' \"USD\" \"dollar\" = '(1, 1) -- @ -- -- When using 'Discrete' values to represent money, it will be impossible to -- represent an amount of @currency@ smaller than @unit@. So, if you decide to--- use @Scale \"USD\" \"dollar\"@ as your scale, you will not be able to+-- use @UnitScale \"USD\" \"dollar\"@ as your scale, you will not be able to -- represent values such as USD 3.50 or USD 21.87 becacuse they are not exact -- multiples of a dollar. ----- If there exists a canonical smallest @unit@ that can fully represent the--- currency in all its denominations, then an instance @'Scale' currency--- currency@ exists.------ @--- type instance 'Scale' \"USD\" \"USD\" = 'Scale' \"USD\" \"cent\"--- @--- -- For some monetary values, such as precious metals, there is no smallest -- representable unit, since you can repeatedly split the precious metal many -- times before it stops being a precious metal. Nevertheless, for practical@@ -554,7 +607,7 @@ -- /troy ounce/ equals 480000 /milligrains/. -- -- @--- type instance 'Scale' \"XAU\" \"milligrain\" = '(480000, 1)+-- type instance 'UnitScale' \"XAU\" \"milligrain\" = '(480000, 1) -- @ -- -- You can use other units such as /milligrams/ for measuring XAU, for example.@@ -563,13 +616,28 @@ -- express it. -- -- @--- type instance 'Scale' \"XAU\" \"milligram\" = '(31103477, 1000)+-- type instance 'UnitScale' \"XAU\" \"milligram\" = '(31103477, 1000) -- @ ----- If you try to obtain the 'Scale' of a @currency@ without an obvious smallest+-- If you try to obtain the 'UnitScale' of a @currency@ without an obvious smallest -- representable @unit@, like XAU, you will get a compile error.-type family Scale (currency :: Symbol) (unit :: Symbol) :: (Nat, Nat)+type family UnitScale (currency :: Symbol) (unit :: Symbol) :: (Nat, Nat) +-- | If there exists a canonical smallest 'Scale' that can fully represent the+-- @currency@ in all its denominations, then @'CurrencyScale' currency@ will+-- return such 'Scale'. For example, @'CurrencyScale' \"USD\"@ evaluates to+-- @'UnitScale' \"USD\" \"cent\"@.+--+-- @+-- type instance 'CurrencyScale' \"USD\" = 'UnitScale' \"USD\" \"cent\"+-- @+--+-- If the @currency@ doesn't have a canonical smallest 'Scale', then+-- @'CurrencyScale' currency@ shall be left undefined or fail to compile with a+-- 'GHC.TypeError'. For example @'CurrencyScale' \"XAU\"@ fails with+-- @'ErrScaleNonCanonical' \"XAU\"@.+type family CurrencyScale (currency :: Symbol) :: (Nat, Nat)+ -- | A friendly 'GHC.TypeError' to use for a @currency@ that doesn't have a -- canonical small unit. type family ErrScaleNonCanonical (currency :: Symbol) :: k where@@ -578,7 +646,7 @@       'GHC.Text " is not a currency with a canonical smallest unit," 'GHC.:$$:       'GHC.Text "be explicit about the currency unit you want to use." ) --- | Constraints to a scale (like the one returned by @'Scale' currency unit@)+-- | Constraints to a scale (like the one returned by @'UnitScale' currency unit@) -- expected to always be satisfied. In particular, the scale is always -- guaranteed to be a positive rational number ('GHC.Real.infinity' and -- 'GHC.Real.notANumber' are forbidden by 'GoodScale').@@ -606,22 +674,28 @@  -- | Term-level representation of a currrency @scale@. ----- For example, the 'Scale' for @\"USD\"@ in @\"cent\"@s is @100/1@.+-- For example, the 'Scale' for @\"USD\"@ in @\"cent\"@s is @100/1@. We can+-- obtain a term-level representation for it using any of the following: -- -- @--- > 'scale' ('Proxy' :: 'Proxy' ('Scale' \"USD\" \"cent\"))--- 100 '%' 1+-- > 'scale' ('Proxy' :: 'Proxy' ('UnitScale' \"USD\" \"cent\"))+-- 'Scale' (100 '%' 1) -- @ -- -- @+-- > 'scale' ('Proxy' :: 'CurrencyScale' \"USD\")+-- 'Scale' (100 '%' 1)+-- @+--+-- @ -- > 'scale' (x :: 'Discrete' \"USD\" \"cent\")--- 100 '%' 1+-- 'Scale' (100 '%' 1) -- @ -- -- The returned 'Rational' is statically guaranteed to be a positive number.-scale :: forall proxy scale. GoodScale scale => proxy scale -> Rational -- ^-scale = \_ -> natVal (Proxy :: Proxy (Fst scale)) %-              natVal (Proxy :: Proxy (Snd scale))+scale :: forall proxy scale. GoodScale scale => proxy scale -> Scale -- ^+scale = \_ -> Scale (natVal (Proxy :: Proxy (Fst scale)) %+                     natVal (Proxy :: Proxy (Snd scale))) {-# INLINE scale #-}  --------------------------------------------------------------------------------@@ -710,6 +784,8 @@ {-# INLINE exchangeRateToRational #-}  -- | Safely construct an 'ExchangeRate' from a *positive* 'Rational' number.+--+-- If the given 'Rational' is non-positive, returns 'Nothing'. exchangeRate :: Rational -> Maybe (ExchangeRate src dst) exchangeRate = \r ->   if denominator r /= 0 && r > 0@@ -772,9 +848,9 @@   , _someDenseAmount            :: !Rational   } deriving (Eq, Show, GHC.Generic) --- | __WARNING__ This instance does not compare monetary amounts, it just helps--- you sort 'SomeDense' values in case you need to put them in a 'Data.Set.Set'--- or similar.+-- | __WARNING__ This instance does not compare monetary amounts across+-- different currencies, it just helps you sort 'SomeDense' values in case you+-- need to put them in a 'Data.Set.Set' or similar. deriving instance Ord SomeDense  -- | Currency name.@@ -799,7 +875,7 @@ -- operation on the monetary value. mkSomeDense   :: T.Text   -- ^ Currency. ('someDenseCurrency')-  -> Rational -- ^ Scale. ('someDenseAmount')+  -> Rational -- ^ Amount. ('someDenseAmount')   -> Maybe SomeDense {-# INLINE mkSomeDense #-} mkSomeDense = \c r -> mkSomeDense' (T.unpack c) r@@ -870,13 +946,13 @@     -- us to derive serialization instances maintaining backwards compatiblity     -- with pre-0.6 versions of this library, when 'String' was the prefered     -- string type, and not 'T.Text'.-  , _someDiscreteScale    :: !Rational -- ^ Positive, non-zero.+  , _someDiscreteScale    :: !Scale   , _someDiscreteAmount   :: !Integer  -- ^ Amount of unit.   } deriving (Eq, Show, GHC.Generic) --- | __WARNING__ This instance does not compare monetary amounts, it just helps--- you sort 'SomeDiscrete' values in case you need to put them in a--- 'Data.Set.Set' or similar.+-- | __WARNING__ This instance does not compare monetary amounts across+-- different currencies, it just helps you sort 'SomeDiscrete' values in case+-- you need to put them in a 'Data.Set.Set' or similar. deriving instance Ord SomeDiscrete  -- | Currency name.@@ -890,7 +966,7 @@ {-# INLINE someDiscreteCurrency' #-}  -- | Positive, non-zero.-someDiscreteScale :: SomeDiscrete -> Rational+someDiscreteScale :: SomeDiscrete -> Scale someDiscreteScale = _someDiscreteScale {-# INLINE someDiscreteScale #-} @@ -902,23 +978,20 @@ -- | Internal. Build a 'SomeDiscrete' from raw values. -- -- This function is intended for deserialization purposes. You need to convert--- this 'SomeDiscrete' value to a 'Discrete' vallue in order to do any arithmetic--- operation on the monetary value.+-- this 'SomeDiscrete' value to a 'Discrete' vallue in order to do any+-- arithmetic operation on the monetary value. mkSomeDiscrete   :: T.Text   -- ^ Currency name. ('someDiscreteCurrency')-  -> Rational -- ^ Scale. Positive, non-zero. ('someDiscreteScale')+  -> Scale    -- ^ Scale. Positive, non-zero. ('someDiscreteScale')   -> Integer  -- ^ Amount of unit. ('someDiscreteAmount')-  -> Maybe SomeDiscrete+  -> SomeDiscrete {-# INLINE mkSomeDiscrete #-}-mkSomeDiscrete = \c r a -> mkSomeDiscrete' (T.unpack c) r a+mkSomeDiscrete = \c s a -> mkSomeDiscrete' (T.unpack c) s a  -- | Like 'mkSomeDiscrete' but takes 'String' rather than 'T.Text'.-mkSomeDiscrete' :: String -> Rational -> Integer -> Maybe SomeDiscrete+mkSomeDiscrete' :: String -> Scale -> Integer -> SomeDiscrete {-# INLINABLE mkSomeDiscrete' #-}-mkSomeDiscrete' = \c r a ->-  if (denominator r /= 0) && (r > 0)-  then Just (SomeDiscrete c r a)-  else Nothing+mkSomeDiscrete' = SomeDiscrete  -- | Convert a 'Discrete' to a 'SomeDiscrete' for ease of serialization. toSomeDiscrete@@ -929,7 +1002,7 @@   let c = symbolVal (Proxy :: Proxy currency)       n = natVal (Proxy :: Proxy (Fst scale))       d = natVal (Proxy :: Proxy (Snd scale))-  in SomeDiscrete c (n % d) i0+  in SomeDiscrete c (Scale (n % d)) i0 {-# INLINABLE toSomeDiscrete #-}  -- | Attempt to convert a 'SomeDiscrete' to a 'Discrete', provided you know the@@ -965,19 +1038,20 @@            -> r )   -> r  -- ^ withSomeDiscrete dr = \f ->-  case someSymbolVal (_someDiscreteCurrency dr) of-    SomeSymbol (Proxy :: Proxy currency) ->-      case someNatVal (numerator (someDiscreteScale dr)) of-        Nothing -> error "withSomeDiscrete: impossible: numerator < 0"-        Just (SomeNat (Proxy :: Proxy num)) ->-          case someNatVal (denominator (someDiscreteScale dr)) of-            Nothing -> error "withSomeDiscrete: impossible: denominator < 0"-            Just (SomeNat (Proxy :: Proxy den)) ->-              case mkGoodScale of-                Nothing -> error "withSomeDiscrete: impossible: mkGoodScale"-                Just (Dict :: Dict (GoodScale '(num, den))) ->-                  f (Discrete (someDiscreteAmount dr)-                       :: Discrete' currency '(num, den))+  let rscale :: Rational = scaleToRational (someDiscreteScale dr)+  in case someSymbolVal (_someDiscreteCurrency dr) of+       SomeSymbol (Proxy :: Proxy currency) ->+         case someNatVal (numerator rscale) of+           Nothing -> error "withSomeDiscrete: impossible: numerator < 0"+           Just (SomeNat (Proxy :: Proxy num)) ->+             case someNatVal (denominator rscale) of+               Nothing -> error "withSomeDiscrete: impossible: denominator < 0"+               Just (SomeNat (Proxy :: Proxy den)) ->+                 case mkGoodScale of+                   Nothing -> error "withSomeDiscrete: impossible: mkGoodScale"+                   Just (Dict :: Dict (GoodScale '(num, den))) ->+                     f (Discrete (someDiscreteAmount dr)+                          :: Discrete' currency '(num, den)) {-# INLINABLE withSomeDiscrete #-}  --------------------------------------------------------------------------------@@ -1011,9 +1085,9 @@   , _someExchangeRateRate            :: !Rational -- ^ Positive, non-zero.   } deriving (Eq, Show, GHC.Generic) --- | __WARNING__ This instance does not compare monetary amounts, it just helps--- you sort 'SomeExchangeRate' values in case you need to put them in a--- 'Data.Set.Set' or similar.+-- | __WARNING__ This instance does not compare rates across different currency+-- pairs (whatever that means), it just helps you sort 'SomeExchangeRate' values+-- in case you need to put them in a 'Data.Set.Set' or similar. deriving instance Ord SomeExchangeRate  -- | Source currency name.@@ -1162,6 +1236,7 @@ instance Hashable SomeDiscrete instance Hashable (ExchangeRate src dst) instance Hashable SomeExchangeRate+instance Hashable Scale  -------------------------------------------------------------------------------- -- Extra instances: deepseq@@ -1172,6 +1247,7 @@ instance NFData SomeDiscrete instance NFData (ExchangeRate src dst) instance NFData SomeExchangeRate+instance NFData Scale  -------------------------------------------------------------------------------- -- Extra instances: binary@@ -1208,21 +1284,29 @@     when (d == 0) (fail "denominator is zero")     pure (mkSomeDense' c (n % d)) +instance Binary.Binary Scale where+  put = \s ->+    let r = scaleToRational s+    in Binary.put (numerator r) <>+       Binary.put (denominator r)+  get = maybe empty pure =<< do+    n :: Integer <- Binary.get+    d :: Integer <- Binary.get+    when (d == 0) (fail "denominator is zero")+    pure (scaleFromRational (n % d))+ -- | Compatible with 'Discrete'. instance Binary.Binary SomeDiscrete where-  put = \(SomeDiscrete c r a) ->+  put = \(SomeDiscrete c s a) ->     -- We go through String for backwards compatibility.     Binary.put c <>-    Binary.put (numerator r) <>-    Binary.put (denominator r) <>+    Binary.put s <>     Binary.put a-  get = maybe empty pure =<< do+  get = do     c :: String <- Binary.get-    n :: Integer <- Binary.get-    d :: Integer <- Binary.get-    when (d == 0) (fail "denominator is zero")+    s :: Scale <- Binary.get     a :: Integer <- Binary.get-    pure (mkSomeDiscrete' c (n % d) a)+    pure (mkSomeDiscrete' c s a)  -- | Compatible with 'ExchangeRate'. instance Binary.Binary SomeExchangeRate where@@ -1246,228 +1330,261 @@ -- manner. -- -- @--- > 'denseToDecimal' 'Round' 'True' ('Just' \',\') \'.\' 2 (1 '%' 1)---      ('dense'' (123456 '%' 100) :: 'Dense' \"USD\")--- Just \"+1,234.56\"--- @------ @--- > 'denseToDecimal' 'Round' 'True' ('Just' \',\') \'.\' 2 (100 '%' 1)+-- > 'denseToDecimal' 'defaultDecimalConf' 'Round' --      ('dense'' (123456 '%' 100) :: 'Dense' \"USD\")--- Just \"+123,456.00\"+-- \"1234.56\" -- @------ This function returns 'Nothing' if the scale is less than @1@, or if it's not--- possible to reliably render the decimal string due to a bad choice of--- separators. That is, if the separators are digits or equal among themselves,--- this function returns 'Nothing'. denseToDecimal-  :: Approximation+  :: DecimalConf+  -- ^ Config to use for rendering the decimal number.+  -> Approximation   -- ^ Approximation to use if necesary in order to fit the 'Dense' amount in   -- as many decimal numbers as requested.-  -> Bool-  -- ^ Whether to render a leading @\'+\'@ sign in case the amount is positive.-  -> Maybe Char-  -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in-  -- @1,234.56789@).-  ---  -- If the given separator is a digit, or if it is equal to the decimal-  -- separator, then this functions returns 'Nothing'.-  -> Char-  -- ^ Decimal separator (i.e., the @\'.\'@ in @1,234.56789@).-  ---  -- If the given separator is a digit, or if it is equal to the thousands-  -- separator, then this functions returns 'Nothing'.-  -> Word8-  -- ^ Number of decimal numbers to render, if any.-  -> Rational-  -- ^ Scale used to when rendering the decimal number. This is useful if you-  -- want to render a “number of cents” rather than a “number of dollars” when-  -- rendering a USD amount, for example.-  ---  -- Set this to @1 '%' 1@ if you don't care.-  ---  -- For example, when rendering render @'dense'' (123 '%' 100) :: 'Dense'-  -- "USD"@ as a decimal number with three decimal places, a scale of @1 '%' 1@-  -- (analogous to  @'Scale' \"USD\" \"dollar\"@) would render @1@ as the-  -- integer part and @230@ as the fractional part, whereas a scale of @100 '%'-  -- 1@ (analogous @'Scale' \"USD\" \"cent\"@) would render @123@ as the integer-  -- part and @000@ as the fractional part.-  ---  -- You can easily obtain the scale for a particular currency and unit-  -- combination using the 'scale' function. Otherwise, you are free to pass in-  -- any other /positive/ 'Rational' number. If a non-positive scale is given,-  -- then this function returns 'Nothing'.-  ---  -- Specifying scales other than @1 '%' 1@ is particularly useful for-  -- currencies whose main unit is too big. For example, the main unit of gold-  -- (XAU) is the troy-ounce, which is too big for day to day accounting, so-  -- using the gram or the grain as the unit when rendering decimal amounts-  -- could be useful.-  ---  -- Be careful when using a scale smaller than @1 '%' 1@, since it may become-  -- impossible to parse back a meaningful amount from the rendered decimal-  -- representation unless a big number of fractional digits is used.   -> Dense currency   -- ^ The dense monetary amount to render.-  -> Maybe T.Text+  -> T.Text   -- ^ Returns 'Nothing' is the given separators are not acceptable (i.e., they   -- are digits, or they are equal). {-# INLINABLE denseToDecimal #-}-denseToDecimal a plus ytsep dsep fdigs scal = \(Dense r0) -> do-  guard (scal > 0)-  rationalToDecimal a plus ytsep dsep fdigs (r0 * scal)+denseToDecimal ds a = \(Dense r0) -> do+  rationalToDecimal ds a r0 + -- | Render a 'Discrete'' monetary amount as a decimal number in a potentially -- lossy manner. -- -- This is simply a convenient wrapper around 'denseToDecimal': -- -- @--- 'discreteToDecimal' a b c d e f (dis :: 'Discrete'' currency scale)---     == 'denseToDecimal' a b c d e f ('denseFromDiscrete' dis :: 'Dense' currency)+-- 'discreteToDecimal' ds a (dis :: 'Discrete'' currency scale)+--     == 'denseToDecimal' ds a ('denseFromDiscrete' dis :: 'Dense' currency) -- @ -- -- In particular, the @scale@ in @'Discrete'' currency scale@ has no influence--- over the scale in which the decimal number is rendered. Use the 'Rational'--- parameter to this function for modifying that behavior.+-- over the scale in which the decimal number is rendered. Change the scale+-- with 'decimalConf_scale' in order to modify that behavior. -- -- Please refer to 'denseToDecimal' for further documentation.------ This function returns 'Nothing' if the scale is less than @1@, or if it's not--- possible to reliably render the decimal string due to a bad choice of--- separators. That is, if the separators are digits or equal among themselves,--- this function returns 'Nothing'. discreteToDecimal   :: GoodScale scale-  => Approximation+  => DecimalConf+  -- ^ Config to use for rendering the decimal number.+  -> Approximation   -- ^ Approximation to use if necesary in order to fit the 'Discrete' amount in   -- as many decimal numbers as requested.-  -> Bool-  -- ^ Whether to render a leading @\'+\'@ sign in case the amount is positive.-  -> Maybe Char-  -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in-  -- @1,234.56789@).-  ---  -- If the given separator is a digit, or if it is equal to the decimal-  -- separator, then this functions returns 'Nothing'.-  -> Char-  -- ^ Decimal separator (i.e., the @\'.\'@ in @1,234.56789@).-  ---  -- If the given separator is a digit, or if it is equal to the thousands-  -- separator, then this functions returns 'Nothing'.-  -> Word8-  -- ^ Number of decimal numbers to render, if any.-  -> Rational-  -- ^ Scale used to when rendering the decimal number. This is useful if you-  -- want to render a “number of cents” rather than a “number of dollars” when-  -- rendering a USD amount, for example.-  ---  -- Set this to @1 '%' 1@ if you don't care.-  ---  -- For example, when rendering render @'discrete' 123 :: 'Dense' \"USD\"-  -- \"cent\"@ as a decimal number with three decimal places, a scale of @1 '%'-  -- 1@ (analogous to  @'Scale' \"USD\" \"dollar\"@) would render @1@ as the-  -- integer part and @230@ as the fractional part, whereas a scale of @100 '%'-  -- 1@ (analogous @'Scale' \"USD\" \"cent\"@) would render @123@ as the integer-  -- part and @000@ as the fractional part.-  ---  -- You can easily obtain the scale for a particular currency and unit-  -- combination using the 'scale' function. Otherwise, you are free to pass in-  -- any other /positive/ 'Rational' number. If a non-positive scale is-  -- given, then this function returns 'Nothing'.-  ---  -- Specifying scales other than @1 '%' 1@ is particularly useful for-  -- currencies whose main unit is too big. For example, the main unit of gold-  -- (XAU) is the troy-ounce, which is too big for day to day accounting, so-  -- using the gram or the grain as the unit when rendering decimal amounts-  -- could be useful.-  ---  -- Be careful when using a scale smaller than @1 '%' 1@, since it may become-  -- impossible to parse back a meaningful amount from the rendered decimal-  -- representation unless a big number of fractional digits is used.   -> Discrete' currency scale   -- ^ The monetary amount to render.-  -> Maybe T.Text+  -> T.Text   -- ^ Returns 'Nothing' is the given separators are not acceptable (i.e., they   -- are digits, or they are equal). {-# INLINABLE discreteToDecimal #-}-discreteToDecimal a plus ytsep dsep fdigs scal = \dns ->-  denseToDecimal a plus ytsep dsep fdigs scal (denseFromDiscrete dns)+discreteToDecimal ds a = \dns ->+  denseToDecimal ds a (denseFromDiscrete dns)  -- | Render a 'ExchangeRate' as a decimal number in a potentially lossy manner. -- -- @--- > 'exchangeRateToDecimal' 'Round' 'True' ('Just' \',\') \'.\' 2---       '=<<' ('exchangeRate' (123456 '%' 100) :: 'Maybe' ('ExchangeRate' \"USD\" \"EUR\"))+-- > 'exchangeRateToDecimal' 'defaultDecimalConf' 'Round'+--       '<$>' ('exchangeRate' (123456 '%' 100) :: 'Maybe' ('ExchangeRate' \"USD\" \"EUR\")) -- Just \"1,234.56\" -- @------ This function returns 'Nothing' if it is not possible to reliably render the--- decimal string due to a bad choice of separators. That is, if the separators--- are digits or equal among themselves, this function returns 'Nothing'. exchangeRateToDecimal-  :: Approximation+  :: DecimalConf+  -- ^ Config to use for rendering the decimal number.+  -> Approximation   -- ^ Approximation to use if necesary in order to fit the 'Dense' amount in   -- as many decimal numbers as requested.-  -> Maybe Char-  -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in-  -- @1,234.56789@).-  -> Char-  -- ^ Decimal separator (i.e., the @\'.\'@ in @1,234.56789@)-  -> Word8-  -- ^ Number of decimal numbers to render, if any.   -> ExchangeRate src dst   -- ^ The 'ExchangeRate' to render.-  -> Maybe T.Text+  -> T.Text   -- ^ Returns 'Nothing' if the given separators are not acceptable (i.e., they   -- are digits, or they are equal). {-# INLINABLE exchangeRateToDecimal #-}-exchangeRateToDecimal a ytsep dsep fdigs0 = \(ExchangeRate r0) ->-  rationalToDecimal a False ytsep dsep fdigs0 r0+exchangeRateToDecimal ds a = \(ExchangeRate r0) ->+  rationalToDecimal ds a r0 --- | Render a 'Rational' number as a decimal approximation.+--------------------------------------------------------------------------------++-- | Decimal and thousands separators used when rendering or parsing a decimal+-- number. ----- This function returns 'Nothing' if it is not possible to reliably render the--- decimal string due to a bad choice of separators. That is, if the separators--- are digits or equal among themselves, this function returns 'Nothing'.-rationalToDecimal-  :: Approximation-  -- ^ Approximation to use if necesary in order to fit the 'Dense' amount in-  -- as many decimal numbers as requested.-  -> Bool-  -- ^ Whether to render a leading @\'+\'@ sign in case the amount is positive.+-- Use 'mkSeparators' to construct.+data Separators = Separators Char (Maybe Char)+  deriving (Eq, Show)++-- | Construct 'Separators' to use with in 'DecimalConf'.+--+-- The separatorsA can't be an ASCII digit nor control character, and they must+-- be different from each other.+mkSeparators+  :: Char+  -- ^ Decimal separator (i.e., the @\'.\'@ in @1,234.56789@)   -> Maybe Char   -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in   -- @1,234.56789@).-  -> Char-  -- ^ Decimal separator (i.e., the @\'.\'@ in @1,234.56789@)-  -> Word8+  -> Maybe Separators+mkSeparators ds yts = do+  guard (not (Char.isDigit ds || Char.isControl ds))+  for_ yts $ \ts ->+    guard (not (ts == ds || Char.isDigit ts || Char.isControl ts))+  pure (Separators ds yts)++-- | @1234567,89@+separatorsComma :: Separators+separatorsComma = Separators ',' Nothing++-- | @1.234.567,89@+separatorsCommaDot :: Separators+separatorsCommaDot = Separators ',' (Just '.')++-- | @1 234 567,89@+--+-- The whitespace is Unicode's /NARROW NO-BREAK SPACE/ (U+202f, 8239,+-- @'\8239'@).+separatorsCommaNarrownbsp :: Separators+separatorsCommaNarrownbsp = Separators ',' (Just '\8239')++-- | @1 234 567,89@+--+-- The whitespace is Unicode's /NO-BREAK SPACE/ (U+00a0, 160, @'\160'@).+separatorsCommaNbsp :: Separators+separatorsCommaNbsp = Separators ',' (Just '\160')++-- | @1 234 567,89@+--+-- The whitespace is Unicode's /THIN SPACE/ (U+2009, 8201, @'\8201'@).+separatorsCommaThinsp :: Separators+separatorsCommaThinsp = Separators ',' (Just '\8201')++-- | @1 234 567,89@+--+-- The whitespace is ASCII's /SPC/ (U+0020, 32, @'\32'@).+separatorsCommaSpace :: Separators+separatorsCommaSpace = Separators ',' (Just '\32')++-- | @1234567.89@+separatorsDot :: Separators+separatorsDot = Separators '.' Nothing++-- | @1,234,567.89@+separatorsDotComma :: Separators+separatorsDotComma = Separators '.' (Just ',')++-- | @1 234 567.89@+--+-- The whitespace is Unicode's /NARROW NO-BREAK SPACE/ (U+202f, 8239,+-- @'\8239'@).+separatorsDotNarrownbsp :: Separators+separatorsDotNarrownbsp = Separators '.' (Just '\8239')++-- | @1 234 567.89@+--+-- The whitespace is Unicode's /THIN SPACE/ (U+2009, 8201, @'\8201'@).+separatorsDotThinsp :: Separators+separatorsDotThinsp = Separators '.' (Just '\8201')++-- | @1 234 567.89@+--+-- The whitespace is Unicode's /NO-BREAK SPACE/ (U+00a0, 160, @'\160'@).+separatorsDotNbsp :: Separators+separatorsDotNbsp = Separators '.' (Just '\160')++-- | @1 234 567.89@+--+-- The whitespace is ASCII's /SPACE/ (U+0020, 32, @'\32'@).+separatorsDotSpace :: Separators+separatorsDotSpace = Separators '.' (Just '\32')++--------------------------------------------------------------------------------++-- | Config to use when rendering or parsing decimal numbers.+--+-- See 'defaultDecimalConf'.+data DecimalConf = DecimalConf+  { decimalConf_separators :: !Separators+  -- ^ Decimal and thousands separators to use when rendering the decimal number.+  -- Construct one with 'mkSeparators', or pick a ready made one like+  -- 'separatorsDot' or 'separatorsDotNarrownbsp'.+  , decimalConf_leadingPlus :: !Bool+  -- ^ Whether to render a leading @\'+\'@ sign in case the amount is positive.+  , decimalConf_digits :: !Word8   -- ^ Number of decimal numbers to render, if any.+  , decimalConf_scale :: !Scale+  -- ^ Scale used to when rendering the decimal number. This is useful if, for+  -- example, you want to render a “number of cents” rather than a “number of+  -- dollars” as the whole part of the decimal number when rendering a USD+  -- amount. It's particularly useful when rendering currencies such as XAU,+  -- where one might prefer to render amounts as a number of grams, rather than+  -- as a number of troy-ounces.+  --+  -- /Set this to @1@ if you don't care./+  --+  -- For example, when rendering render @'dense'' (123 '%' 100) :: 'Dense'+  -- \"USD\"@ as a decimal number with two decimal places, a scale of @1@+  -- (analogous to  @'UnitScale' \"USD\" \"dollar\"@) would render @1@ as the+  -- integer part and @23@ as the fractional part, whereas a scale of @100@+  -- (analogous @'UnitScale' \"USD\" \"cent\"@) would render @123@ as the integer+  -- part and @00@ as the fractional part.+  --+  -- You can easily obtain the scale for a particular currency and unit+  -- combination using the 'scale' function.+  --+  -- __Important:__ Generally, you will want this number to be @1@ or larger.+  -- This is because scales in the range @(0, 1)@ can be particularly lossy unless+  -- the number of decimal digits is sufficiently large.+  } deriving (Eq, Show)++-- | Default 'DecimalConf'.+--+-- * No leading @\'+\'@ sign+--+-- * No thousands separator+--+-- * Decimal separator is @\'.\'@+--+-- * @2@ decimal digits+--+-- * A scale of @1@+--+-- That is, something like @1.23@ or @-1234567.89@.+defaultDecimalConf :: DecimalConf+defaultDecimalConf = DecimalConf+  { decimalConf_separators = separatorsDot+  , decimalConf_leadingPlus = False+  , decimalConf_digits = 2+  , decimalConf_scale = Scale 1+  }++--------------------------------------------------------------------------------++-- | Render a 'Rational' number as a decimal approximation.+rationalToDecimal+  :: DecimalConf+  -- ^ Config to use for rendering the decimal number.+  -> Approximation+  -- ^ Approximation to use if necesary in order to fit the 'Dense' amount in+  -- as many decimal numbers as requested.   -> Rational   -- ^ The dense monetary amount to render.-  -> Maybe T.Text+  -> T.Text   -- ^ Returns 'Nothing' if the given separators are not acceptable (i.e., they   -- are digits, or they are equal). {-# INLINABLE rationalToDecimal #-}-rationalToDecimal a plus ytsep dsep fdigs0 = \r0 -> do-  guard (not (Char.isDigit dsep))-  for_ ytsep $ \tsep ->-     guard (tsep /= dsep && not (Char.isDigit tsep))+rationalToDecimal (DecimalConf (Separators ds yts) plus fdigs0 scal) a = \r0 -> do   -- this string-fu is not particularly efficient.-  let start = r0 * (10 ^ fdigs0) :: Rational+  let start = r0 * scaleToRational scal * (10 ^ fdigs0) :: Rational       parts = approximate a start :: Integer       ipart = fromInteger (abs parts) `div` (10 ^ fdigs0) :: Natural       ftext | ipart == 0 = show (abs parts) :: String             | otherwise = drop (length (show ipart)) (show (abs parts))-      itext = maybe (show ipart) (renderThousands ipart) ytsep :: String+      itext = maybe (show ipart) (renderThousands ipart) yts :: String       fpad0 = List.replicate (fromIntegral fdigs0 - length ftext) '0' :: String-  Just $ T.pack $ mconcat+  T.pack $ mconcat     [ if | parts < 0 -> "-"          | plus && parts > 0 -> "+"          | otherwise -> ""     , itext-    , if | fdigs0 > 0 -> dsep : if start < 1+    , if | fdigs0 > 0 -> ds : if start < 1                                    then fpad0 <> ftext                                    else ftext <> fpad0          | otherwise -> ""@@ -1497,114 +1614,87 @@ -- Decimal parsing  -- | Parses a decimal representation of a 'Dense'.------ Leading @\'-\'@ and @\'+\'@ characters are considered. denseFromDecimal-  :: Maybe Char-  -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in-  -- @-1,234.56789@).-  -> Char-  -- ^ Decimal separator (i.e., the @\'.\'@ in @-1,234.56789@)-  -> Rational-  -- ^ Scale used by the rendered decimal. It is important to get this number-  -- correctly, otherwise the resulting 'Dense' amount will be wrong. Please-  -- refer to the documentation for 'denseToDecimal' to understand the meaning-  -- of this scale.+  :: DecimalConf+  -- ^ Config to use for parsing the decimal number.   ---  -- In summary, this scale will have a value of @1@ unless the decimal amount-  -- represents a unit other than the main unit for the currency (e.g., cents-  -- rather than dollars for USD, or grams rather than troy-ounces for XAU, or-  -- millibitcoins rather than bitcoins for BTC).+  -- Notice that a leading @\'-\'@ or @\'+\'@ will always be correctly+  -- interpreted, notwithstanding what the “leading @\'+\'@” policy is on+  -- the given 'DecimalConf'.   -> T.Text   -- ^ The raw string containing the decimal representation (e.g.,   -- @"-1,234.56789"@).   -> Maybe (Dense currency)-denseFromDecimal yst sf scal str = do-  guard (scal > 0)-  r <- rationalFromDecimal yst sf str-  pure (Dense $! (r / scal))+denseFromDecimal ds t = do+  r <- rationalFromDecimal ds t+  pure (Dense $! r)  -- | Parses a decimal representation of a 'Discrete'. ----- Leading @\'-\'@ and @\'+\'@ characters are considered.--- -- Notice that parsing will fail unless the entire precision of the decimal -- number can be represented in the desired @scale@. discreteFromDecimal   :: GoodScale scale-  => Maybe Char-  -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in-  -- @-1,234.56789@).-  -> Char-  -- ^ Decimal separator (i.e., the @\'.\'@ in @-1,234.56789@)-  -> Rational-  -- ^ Scale used by the rendered decimal. It is important to get this number-  -- correctly, otherwise the resulting 'Dense' amount will be wrong. Please-  -- refer to the documentation for 'denseToDecimal' to understand the meaning-  -- of this scale.+  => DecimalConf+  -- ^ Config to use for parsing the decimal number.   ---  -- In summary, this scale will have a value of @1@ unless the decimal amount-  -- represents a unit other than the main unit for the currency (e.g., cents-  -- rather than dollars for USD, or grams rather than troy-ounces for XAU, or-  -- millibitcoins rather than bitcoins for BTC).+  -- Notice that a leading @\'-\'@ or @\'+\'@ will always be correctly+  -- interpreted, notwithstanding what the “leading @\'+\'@” policy is on+  -- the given 'DecimalConf'.   -> T.Text   -- ^ The raw string containing the decimal representation (e.g.,   -- @"-1,234.56789"@).   -> Maybe (Discrete' currency scale)-discreteFromDecimal yst sf scal = \str -> do-  dns <- denseFromDecimal yst sf scal str+discreteFromDecimal ds = \t -> do+  dns <- denseFromDecimal ds t   case discreteFromDense Truncate dns of     (x, 0) -> Just x     _ -> Nothing -- We fail for decimals that don't fit exactly in our scale.  -- | Parses a decimal representation of an 'ExchangeRate'. exchangeRateFromDecimal-  :: Maybe Char-  -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in-  -- @1,234.56789@).-  -> Char-  -- ^ Decimal separator (i.e., the @\'.\'@ in @1,234.56789@)+  :: DecimalConf+  -- ^ Config to use for parsing the decimal number.+  --+  -- Notice that a leading @\'-\'@ or @\'+\'@ will always be correctly+  -- interpreted, notwithstanding what the “leading @\'+\'@” policy is on+  -- the given 'DecimalConf'.   -> T.Text   -- ^ The raw string containing the decimal representation (e.g.,   -- @"1,234.56789"@).   -> Maybe (ExchangeRate src dst)-exchangeRateFromDecimal yst sf t+exchangeRateFromDecimal ds t   | T.isPrefixOf "-" t = Nothing-  | otherwise = exchangeRate =<< rationalFromDecimal yst sf t+  | otherwise = exchangeRate =<< rationalFromDecimal ds t +-- | Parses a decimal number representation as 'T.Text' into a 'Rational'. rationalFromDecimal-  :: Maybe Char-  -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in-  -- @-1,234.56789@).-  -> Char-  -- ^ Decimal separator (i.e., the @\'.\'@ in @-1,234.56789@)+  :: DecimalConf+  -- ^ Config to use for parsing the decimal number.+  --+  -- Notice that a leading @\'-\'@ or @\'+\'@ will always be correctly+  -- interpreted, notwithstanding what the “leading @\'+\'@” policy is on+  -- the given 'DecimalConf'.   -> T.Text   -- ^ The raw string containing the decimal representation (e.g.,   -- @"-1,234.56789"@).   -> Maybe Rational-rationalFromDecimal yst sf = \t ->-  case ReadP.readP_to_S (rationalFromDecimalP yst sf) (T.unpack t) of+rationalFromDecimal ds = \t ->+  case ReadP.readP_to_S (rationalFromDecimalP ds) (T.unpack t) of     [(x,"")] -> Just x     _ -> Nothing  -- TODO limit number of digits parsed to prevent DoS rationalFromDecimalP-  :: Maybe Char-  -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in-  -- @-1,234.56789@).-  ---  -- The separator can't be a digit or control character. If it is, then parsing-  -- will always fail.-  -> Char-  -- ^ Decimal separator (i.e., the @\'.\'@ in @-1,234.56789@).+  :: DecimalConf+  -- ^ Config to use for parsing the decimal number.   ---  -- The separator can't be a digit or control character. If it is, then parsing-  -- will always fail.+  -- Notice that a leading @\'-\'@ or @\'+\'@ will always be correctly+  -- interpreted, notwithstanding what the “leading @\'+\'@” policy is on+  -- the given 'DecimalConf'.   -> ReadP.ReadP Rational-rationalFromDecimalP ytsep dsep = do-   for_ ytsep $ \tsep ->-      guard (tsep /= dsep && not (Char.isDigit tsep))-   guard (not (Char.isDigit dsep))+rationalFromDecimalP ds = do+   let Separators dsep ytsep = decimalConf_separators ds    sig :: Rational -> Rational <-      (ReadP.char '-' $> negate) <|>      (ReadP.char '+' $> id) <|>@@ -1620,9 +1710,10 @@    yfpart :: Maybe String <-      (ReadP.char dsep *> fmap Just (ReadP.munch1 Char.isDigit) <* ReadP.eof) <|>      (ReadP.eof $> Nothing)-   pure $! sig $ case yfpart of-     Nothing -> fromInteger (read ipart)-     Just fpart -> read (ipart <> fpart) % (10 ^ length fpart)+   let r = sig $ case yfpart of+         Nothing -> fromInteger (read ipart)+         Just fpart -> read (ipart <> fpart) % (10 ^ length fpart)+   pure $! r / scaleToRational (decimalConf_scale ds)  -------------------------------------------------------------------------------- -- QuickCheck Arbitrary instances@@ -1634,12 +1725,9 @@   shrink = fmap fromInteger . QC.shrink . toInteger  instance QC.Arbitrary SomeDiscrete where-  arbitrary = do-    let md = mkSomeDiscrete-               <$> fmap T.pack QC.arbitrary-               <*> QC.arbitrary-               <*> QC.arbitrary-    fromJust <$> QC.suchThat md isJust+  arbitrary = mkSomeDiscrete <$> fmap T.pack QC.arbitrary+                             <*> QC.arbitrary+                             <*> QC.arbitrary   shrink = \x -> withSomeDiscrete x (map toSomeDiscrete . QC.shrink)  instance QC.Arbitrary (Dense currency) where@@ -1672,3 +1760,24 @@  instance QC.Arbitrary Approximation where   arbitrary = QC.oneof [ pure Round, pure Floor, pure Ceiling, pure Truncate ]++instance QC.Arbitrary Scale where+  arbitrary = do+    n <- QC.arbitrary+    d <- QC.arbitrary+    let r = (abs n + 1) % (abs d + 1)+    pure (Scale r)++instance QC.Arbitrary Separators where+  arbitrary = do+    let msep = QC.suchThat QC.arbitrary $ \c ->+          not (Char.isDigit c) && not (Char.isControl c)+    ds :: Char <- msep+    yts :: Maybe Char <- QC.oneof+      [ pure Nothing, fmap Just (QC.suchThat msep (/= ds)) ]+    let Just out = mkSeparators ds yts+    pure out++instance QC.Arbitrary DecimalConf where+  arbitrary = DecimalConf <$> QC.arbitrary <*> QC.arbitrary+                          <*> QC.arbitrary <*> QC.arbitrary
test/Main.hs view
@@ -17,6 +17,7 @@ import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import qualified Data.Char as Char+import Data.Constraint (Dict(Dict)) import Data.Maybe (catMaybes, fromJust, fromMaybe, isJust, isNothing) import Data.Proxy (Proxy(Proxy)) import Data.Ratio ((%), numerator, denominator)@@ -36,36 +37,27 @@  import qualified Money import qualified Money.Internal as MoneyI-  (rationalFromDecimal, rationalToDecimal)  -------------------------------------------------------------------------------- --- | Generates a valid 'MoneyI.rationalToDecimal' result. Returns the thousand--- and decimal separators as welland decimal separators as well.-genDecimal :: QC.Gen (T.Text, Maybe Char, Char)+-- | A wrapper around a decimal representation and its 'Money.DecimalConf',+-- mostly just to benefit from its 'QC.Arbitrary' instance.+data Decimal = Decimal Money.DecimalConf T.Text+  deriving (Show)++instance QC.Arbitrary Decimal where+  arbitrary = uncurry (flip Decimal) <$> genDecimal+  shrink (Decimal ds t) = Decimal <$> QC.shrink ds+                                  <*> fmap T.pack (QC.shrink (T.unpack t))++-- | Generates a valid 'MoneyI.rationalToDecimal' result using the returned+-- 'Money.DecimalConf'.+genDecimal :: QC.Gen (T.Text, Money.DecimalConf) genDecimal = do   aprox :: Money.Approximation <- QC.arbitrary-  plus :: Bool <- QC.arbitrary-  digs :: Word8 <- QC.arbitrary+  ds :: Money.DecimalConf <- QC.arbitrary   r :: Rational <- (%) <$> QC.arbitrary <*> QC.suchThat QC.arbitrary (/= 0)-  (yts, ds) <- genDecimalSeps-  let-      dec = fromMaybe-          (error "genDecimal failed because rationalToDecimal returned Nothing")-          (MoneyI.rationalToDecimal aprox plus yts ds digs r)-  pure (dec, yts, ds)---- | Generates valid separators for decimal representations (see genDecimal).-genDecimalSeps :: QC.Gen (Maybe Char, Char)-genDecimalSeps = do-  let msep = QC.suchThat QC.arbitrary (not . Char.isDigit)-  ds :: Char <- msep-  yts :: Maybe Char <- genMaybe (QC.suchThat msep (/= ds))-  pure (yts, ds)---genMaybe :: QC.Gen a -> QC.Gen (Maybe a)-genMaybe m = QC.oneof [pure Nothing, fmap Just m]+  pure (MoneyI.rationalToDecimal ds aprox r, ds)  -------------------------------------------------------------------------------- @@ -79,10 +71,12 @@ tests =   Tasty.testGroup "root"   [ testCurrencies+  , testHalfEvenRounding   , testCurrencyUnits   , testExchange   , testRationalToDecimal   , testRationalFromDecimal+  , testMkSeparators   , testDiscreteFromDecimal   , testRawSerializations   ]@@ -99,10 +93,8 @@ testCurrencyUnits :: Tasty.TestTree testCurrencyUnits =   Tasty.testGroup "Currency units"-  [ testDiscrete (Proxy :: Proxy "BTC") (Proxy :: Proxy "BTC")-  , testDiscrete (Proxy :: Proxy "BTC") (Proxy :: Proxy "satoshi")+  [ testDiscrete (Proxy :: Proxy "BTC") (Proxy :: Proxy "satoshi")   , testDiscrete (Proxy :: Proxy "BTC") (Proxy :: Proxy "bitcoin")-  , testDiscrete (Proxy :: Proxy "USD") (Proxy :: Proxy "USD")   , testDiscrete (Proxy :: Proxy "USD") (Proxy :: Proxy "cent")   , testDiscrete (Proxy :: Proxy "USD") (Proxy :: Proxy "dollar")   , testDiscrete (Proxy :: Proxy "VUV") (Proxy :: Proxy "vatu")@@ -202,6 +194,29 @@          , "0"       --  6          , "0"       --  7          ]+  , HU.testCase "Round: r5" $ do+       render Money.Round r5 @?=+         [ "0.50"   --  0+         , "0.50"   --  1+         , "+0.50"  --  2+         , "+0.50"  --  3+         , "0"      --  4+         , "0"      --  5+         , "0"      --  6+         , "0"      --  7+         ]+  , HU.testCase "Round: negate r5" $ do+       render Money.Round (negate r5) @?=+         [ "-0.50"   --  0+         , "-0.50"   --  1+         , "-0.50"   --  2+         , "-0.50"   --  3+         , "0"       --  4+         , "0"       --  5+         , "0"       --  6+         , "0"       --  7+         ]+   , HU.testCase "Floor: r1" $ do        render Money.Floor r1 @?=          [ "1023004567.89"        --  0@@ -290,6 +305,28 @@          , "-1"      --  6          , "-1"      --  7          ]+  , HU.testCase "Floor: r5" $ do+       render Money.Floor r5 @?=+         [ "0.50"   --  0+         , "0.50"   --  1+         , "+0.50"   --  2+         , "+0.50"   --  3+         , "0"      --  4+         , "0"      --  5+         , "0"      --  6+         , "0"      --  7+         ]+  , HU.testCase "Floor: negate r5" $ do+       render Money.Floor (negate r5) @?=+         [ "-0.50"   --  0+         , "-0.50"   --  1+         , "-0.50"   --  2+         , "-0.50"   --  3+         , "-1"      --  4+         , "-1"      --  5+         , "-1"      --  6+         , "-1"      --  7+         ]    , HU.testCase "Ceiling: r1" $ do        render Money.Ceiling r1 @?=@@ -379,6 +416,28 @@          , "0"      --  6          , "0"      --  7          ]+  , HU.testCase "Ceiling: r5" $ do+       render Money.Ceiling r5 @?=+         [ "0.50"   --  0+         , "0.50"   --  1+         , "+0.50"  --  2+         , "+0.50"  --  3+         , "1"      --  4+         , "1"      --  5+         , "+1"     --  6+         , "+1"     --  7+         ]+  , HU.testCase "Ceiling: negate r5" $ do+       render Money.Ceiling (negate r5) @?=+         [ "-0.50"   --  0+         , "-0.50"   --  1+         , "-0.50"   --  2+         , "-0.50"   --  3+         , "0"      --  4+         , "0"      --  5+         , "0"      --  6+         , "0"      --  7+         ]    , HU.testCase "Truncate: r1" $ do       render Money.Truncate r1 @?= render Money.Floor r1@@ -403,50 +462,114 @@    , HU.testCase "Truncate: negate r4" $ do       render Money.Truncate (negate r4) @?= render Money.Ceiling (negate r4)++  , HU.testCase "Truncate: r5" $ do+      render Money.Truncate r5 @?= render Money.Floor r5++  , HU.testCase "Truncate: negate r5" $ do+      render Money.Truncate (negate r5) @?= render Money.Ceiling (negate r5)++  , HU.testCase "HalfEven: r1" $ do+      render Money.HalfEven r1 @?= render Money.Round r1++  , HU.testCase "HalfEven: negate r1" $ do+      render Money.HalfEven (negate r1) @?= render Money.Round (negate r1)++  , HU.testCase "HalfEven: r2" $ do+      render Money.HalfEven r2 @?= render Money.Round r2++  , HU.testCase "HalfEven: negate r2" $ do+      render Money.HalfEven (negate r2) @?= render Money.Round (negate r2)++  , HU.testCase "HalfEven: r3" $ do+      render Money.HalfEven r3 @?= render Money.Round r3++  , HU.testCase "HalfEven: negate r3" $ do+      render Money.HalfEven (negate r3) @?= render Money.Round (negate r3)++  , HU.testCase "HalfEven: r4" $ do+      render Money.HalfEven r4 @?= render Money.Round r4++  , HU.testCase "HalfEven: negate r4" $ do+      render Money.HalfEven (negate r4) @?= render Money.Round (negate r4)++  , HU.testCase "HalfEven: r5" $ do+       render Money.HalfEven r5 @?=+         [ "0.50"   --  0+         , "0.50"   --  1+         , "+0.50"  --  2+         , "+0.50"  --  3+         , "0"      --  4+         , "0"      --  5+         , "0"      --  6+         , "0"      --  7+         ]+  , HU.testCase "HalfEven: negate r5" $ do+       render Money.HalfEven (negate r5) @?=+         [ "-0.50"   --  0+         , "-0.50"   --  1+         , "-0.50"   --  2+         , "-0.50"   --  3+         , "0"      --  4+         , "0"      --  5+         , "0"      --  6+         , "0"      --  7+         ]+   ]   where     r1 :: Rational = 1023004567895 % 1000     r2 :: Rational = 123 % 100     r3 :: Rational = 345 % 1000     r4 :: Rational = 7 % 1000+    r5 :: Rational = 1 % 2      render :: Money.Approximation -> Rational -> [T.Text]     render a r =-      [ fromJust $ MoneyI.rationalToDecimal a False Nothing    '.' 2 r  --  0-      , fromJust $ MoneyI.rationalToDecimal a False (Just ',') '.' 2 r  --  1-      , fromJust $ MoneyI.rationalToDecimal a True  Nothing    '.' 2 r  --  2-      , fromJust $ MoneyI.rationalToDecimal a True  (Just ',') '.' 2 r  --  3-      , fromJust $ MoneyI.rationalToDecimal a False Nothing    '.' 0 r  --  4-      , fromJust $ MoneyI.rationalToDecimal a False (Just ',') '.' 0 r  --  5-      , fromJust $ MoneyI.rationalToDecimal a True  Nothing    '.' 0 r  --  6-      , fromJust $ MoneyI.rationalToDecimal a True  (Just ',') '.' 0 r  --  7+      [ MoneyI.rationalToDecimal ds_dd_2 a r        -- 0+      , MoneyI.rationalToDecimal ds_tc_dd_2 a r     -- 1+      , MoneyI.rationalToDecimal ds_p_dd_2 a r      -- 2+      , MoneyI.rationalToDecimal ds_p_tc_dd_2 a r   -- 3+      , MoneyI.rationalToDecimal ds_dd_0 a r        -- 4+      , MoneyI.rationalToDecimal ds_tc_dd_0 a r     -- 5+      , MoneyI.rationalToDecimal ds_p_dd_0 a r      -- 6+      , MoneyI.rationalToDecimal ds_p_tc_dd_0 a r   -- 7       ] -testRationalFromDecimal :: Tasty.TestTree-testRationalFromDecimal =-  Tasty.testGroup "rationalFromDecimal"+testMkSeparators :: Tasty.TestTree+testMkSeparators =+  Tasty.testGroup "mkSeparators"   [ QC.testProperty "Unsupported separators" $-      let mbadsep :: QC.Gen Char = QC.suchThat QC.arbitrary Char.isDigit-          mgoodsep :: QC.Gen Char = QC.suchThat QC.arbitrary (not . Char.isDigit)+      let mbadsep :: QC.Gen Char = QC.suchThat QC.arbitrary $ \c ->+            Char.isDigit c || Char.isControl c+          mgoodsep :: QC.Gen Char = QC.suchThat QC.arbitrary $ \c ->+            not (Char.isDigit c || Char.isControl c)       in QC.forAll ((,,) <$> mbadsep <*> mbadsep <*> mgoodsep) $            \(s1 :: Char, s2 :: Char, s3 :: Char) ->-              let f = MoneyI.rationalFromDecimal-              in (f Nothing s1 (error "untouched") === Nothing) .&&.-                 (f (Just s1) s2 (error "untouched") === Nothing) .&&.-                 (f (Just s1) s1 (error "untouched") === Nothing) .&&.-                 (f (Just s3) s3 (error "untouched") === Nothing)+               (Money.mkSeparators s1 Nothing   === Nothing) .&&.+               (Money.mkSeparators s2 (Just s1) === Nothing) .&&.+               (Money.mkSeparators s1 (Just s1) === Nothing) .&&.+               (Money.mkSeparators s3 (Just s3) === Nothing)+  ] -  , Tasty.localOption (QC.QuickCheckTests 1000) $+testRationalFromDecimal :: Tasty.TestTree+testRationalFromDecimal =+  Tasty.testGroup "rationalFromDecimal"+  [ Tasty.localOption (QC.QuickCheckTests 1000) $     QC.testProperty "Lossy roundtrip" $       -- We check that the roundtrip results in a close amount with a fractional       -- difference of up to one.-      let gen = (,) <$> genDecimalSeps <*> QC.arbitrary-      in QC.forAll gen $ \( (yst :: Maybe Char, sd :: Char)-                          , (r :: Rational, plus :: Bool, digs :: Word8,-                             aprox :: Money.Approximation) ) ->-           let Just dec = MoneyI.rationalToDecimal aprox plus yst sd digs r-               Just r' = MoneyI.rationalFromDecimal yst sd dec-           in 1 > abs (abs r - abs r')+      QC.forAll QC.arbitrary $ \(ds0 :: Money.DecimalConf,+                                 aprox :: Money.Approximation,+                                 r :: Rational) ->+        let Just sr = Money.scaleFromRational+                    $ (+ 1) -- scales less than 1 can't reliably roundtrip.+                    $ Money.scaleToRational+                    $ Money.decimalConf_scale ds0+            ds1 = ds0 { Money.decimalConf_scale = sr }+            dec = MoneyI.rationalToDecimal ds1 aprox r+            Just r' = MoneyI.rationalFromDecimal ds1 dec+        in 1 > abs (abs r - abs r')   ]  testDense@@ -489,42 +612,39 @@         T.unpack (Money.denseCurrency x) === symbolVal pc    , QC.testProperty "denseToDecimal: Same as rationalToDecimal" $-      let gen = (,) <$> genDecimalSeps <*> QC.arbitrary-      in QC.forAll gen $ \( (yst :: Maybe Char, sd :: Char)-                          , (dns :: Money.Dense currency, plus :: Bool,-                             digs :: Word8, aprox :: Money.Approximation) ) ->-            let ydnsd1 = Money.denseToDecimal aprox plus yst sd digs 1 dns-                ydnsd100 = Money.denseToDecimal aprox plus yst sd digs 100 dns-                yrd1 = MoneyI.rationalToDecimal aprox plus yst sd digs (toRational dns)-                yrd100 = MoneyI.rationalToDecimal aprox plus yst sd digs (toRational dns * 100)-            in (ydnsd1 === yrd1) .&&. (ydnsd100 === yrd100)--  , QC.testProperty "denseFromDecimal: Same as rationalFromDecimal" $-      QC.forAll genDecimal $ \(dec :: T.Text, yts :: Maybe Char, ds :: Char) ->-         let Just r = MoneyI.rationalFromDecimal yts ds dec-             Just dns = Money.denseFromDecimal yts ds 1 dec-         in r === toRational (dns :: Money.Dense currency)+      QC.forAll QC.arbitrary $ \(ds :: Money.DecimalConf,+                                 dns :: Money.Dense currency,+                                 aprox :: Money.Approximation) ->+         let Just s100 = Money.scaleFromRational+                    (Money.scaleToRational (Money.decimalConf_scale ds) * 100)+             ds_s100 = ds { Money.decimalConf_scale = s100 }+             ydnsd1 = Money.denseToDecimal ds aprox dns+             ydnsd100 = Money.denseToDecimal ds_s100 aprox dns+             yrd1 = MoneyI.rationalToDecimal ds aprox (toRational dns)+             yrd100 = MoneyI.rationalToDecimal ds aprox (toRational dns * 100)+         in (ydnsd1 === yrd1) .&&. (ydnsd100 === yrd100)    , QC.testProperty "denseFromDecimal: Same as rationalFromDecimal" $-      QC.forAll genDecimal $ \(dec :: T.Text, yts :: Maybe Char, ds :: Char) ->-         let Just r = MoneyI.rationalFromDecimal yts ds dec-             Just dns = Money.denseFromDecimal yts ds 1 dec+      QC.forAll QC.arbitrary $ \(Decimal ds dec) ->+         let Just r = MoneyI.rationalFromDecimal ds dec+             Just dns = Money.denseFromDecimal ds dec          in r === toRational (dns :: Money.Dense currency) -   , Tasty.localOption (QC.QuickCheckTests 1000) $     QC.testProperty "denseToDecimal/denseFromDiscrete: Lossy roundtrip" $       -- We check that the roundtrip results in a close amount with a fractional       -- difference of up to one.-      let gen = (,) <$> genDecimalSeps <*> QC.arbitrary-      in QC.forAll gen $ \( (yst :: Maybe Char, sd :: Char)-                          , (sc0 :: Rational, plus :: Bool,-                             digs :: Word8, aprox :: Money.Approximation,-                             dns :: Money.Dense currency) ) ->-           let sc = abs sc0 + 1 -- smaller scales can't reliably be parsed back-               Just dec = Money.denseToDecimal aprox plus yst sd digs sc dns-               Just dns' = Money.denseFromDecimal yst sd sc dec-           in Money.dense' 1 > abs (abs dns - abs dns')+      QC.forAll QC.arbitrary $ \(ds0 :: Money.DecimalConf,+                                 aprox :: Money.Approximation,+                                 dns :: Money.Dense currency) ->+        let Just sr = Money.scaleFromRational+                    $ (+ 1) -- scales less than 1 can't reliably roundtrip.+                    $ Money.scaleToRational+                    $ Money.decimalConf_scale ds0+            ds1 = ds0 { Money.decimalConf_scale = sr }+            dec = Money.denseToDecimal ds1 aprox dns+            Just dns' = Money.denseFromDecimal ds1 dec+        in Money.dense' 1 > abs (abs dns - abs dns')    , HU.testCase "AdditiveGroup: zeroV" $       (AG.zeroV :: Money.Dense currency) @?= Money.dense' (0%1)@@ -587,7 +707,7 @@  testDiscrete   :: forall (currency :: Symbol) (unit :: Symbol)-  .  ( Money.GoodScale (Money.Scale currency unit)+  .  ( Money.GoodScale (Money.UnitScale currency unit)      , KnownSymbol currency      , KnownSymbol unit )   => Proxy currency@@ -615,7 +735,7 @@       QC.forAll QC.arbitrary $ \(dr :: Money.SomeDiscrete) ->         ((T.unpack (Money.someDiscreteCurrency dr) /= symbolVal pc) &&          (Money.someDiscreteScale dr /=-             Money.scale (Proxy :: Proxy (Money.Scale currency unit)))+             Money.scale (Proxy :: Proxy (Money.UnitScale currency unit)))         ) ==> isNothing (Money.fromSomeDiscrete dr                           :: Maybe (Money.Discrete currency unit))   , QC.testProperty "withSomeDiscrete" $@@ -633,16 +753,14 @@   , QC.testProperty "discreteToDecimal/discreteFromDecimal: Same as denseToDecimal/denseFromDecimal" $       -- We check that the roundtrip results in a close amount with a fractional       -- difference of up to one.-      let gen = (,) <$> genDecimalSeps <*> QC.arbitrary-      in QC.forAll gen $ \( (yst :: Maybe Char, sd :: Char)-                          , (sc0 :: Rational, plus :: Bool,-                             digs :: Word8, aprox :: Money.Approximation,-                             dis :: Money.Discrete currency unit) ) ->-           let sc = abs sc0 + 1  -- scale can't be less than 1-               dns = Money.denseFromDiscrete dis-               ydec = Money.discreteToDecimal aprox plus yst sd digs sc dis-               ydec' = Money.denseToDecimal aprox plus yst sd digs sc dns-           in ydec === ydec'+      QC.forAll QC.arbitrary $ \(ds :: Money.DecimalConf,+                                 sc0 :: Rational,+                                 aprox :: Money.Approximation,+                                 dis :: Money.Discrete currency unit) ->+        let sc = abs sc0 + 1  -- scales less than 1 can't reliably roundtrip.+            dns = Money.denseFromDiscrete dis+        in Money.discreteToDecimal ds aprox dis+             === Money.denseToDecimal ds aprox dns    , HU.testCase "AdditiveGroup: zeroV" $       (AG.zeroV :: Money.Discrete currency unit) @?= Money.discrete 0@@ -751,19 +869,17 @@              (show x, dr) === (show x', Money.toSomeExchangeRate x')    , QC.testProperty "exchangeRateToDecimal: Same as rationalToDecimal" $-      let gen = (,) <$> genDecimalSeps <*> QC.arbitrary-      in QC.forAll gen $ \( (yst :: Maybe Char, sd :: Char)-                          , (xr :: Money.ExchangeRate src dst, digs :: Word8,-                             aprox :: Money.Approximation ) ) ->-           let xrd = Money.exchangeRateToDecimal aprox yst sd digs xr-               rd = MoneyI.rationalToDecimal aprox False yst sd digs-                       (Money.exchangeRateToRational xr)-           in xrd === rd+      QC.forAll QC.arbitrary $ \(ds :: Money.DecimalConf,+                                 xr :: Money.ExchangeRate src dst,+                                 aprox :: Money.Approximation) ->+        Money.exchangeRateToDecimal ds aprox xr+          === MoneyI.rationalToDecimal ds aprox+                (Money.exchangeRateToRational xr)    , QC.testProperty "exchangeRateFromDecimal: Same as rationalFromDecimal" $-      QC.forAll genDecimal $ \(dec :: T.Text, yts :: Maybe Char, ds :: Char) ->-         let Just r = MoneyI.rationalFromDecimal yts ds dec-             yxr = Money.exchangeRateFromDecimal yts ds dec+      QC.forAll QC.arbitrary $ \(Decimal ds dec) ->+         let Just r = MoneyI.rationalFromDecimal ds dec+             yxr = Money.exchangeRateFromDecimal ds dec                       :: Maybe (Money.ExchangeRate src dst)          in (r > 0) ==> (Just r === fmap Money.exchangeRateToRational yxr) @@ -793,57 +909,60 @@ testDiscreteFromDecimal =   Tasty.testGroup "discreteFromDecimal"   [ HU.testCase "Too large" $ do-      Money.discreteFromDecimal Nothing '.' 1 "0.053"+      Money.discreteFromDecimal ds_dd_2 "0.053"         @?= (Nothing :: Maybe (Money.Discrete "USD" "cent"))-      Money.discreteFromDecimal (Just ',') '.' 1 "0.253"+      Money.discreteFromDecimal ds_tc_dd_2 "0.253"         @?= (Nothing :: Maybe (Money.Discrete "USD" "cent"))    , HU.testCase "USD cent, small, zero" $ do       let dis = 0 :: Money.Discrete "USD" "cent"-          f = Money.discreteFromDecimal-      f Nothing '.' 1 "0" @?= Just dis-      f Nothing '.' 1 "+0" @?= Just dis-      f Nothing '.' 1 "-0" @?= Just dis-      f (Just ',') '.' 1 "0" @?= Just dis-      f (Just ',') '.' 1 "+0" @?= Just dis-      f (Just ',') '.' 1 "-0" @?= Just dis+      Money.discreteFromDecimal ds_dd_2       "0" @?= Just dis+      Money.discreteFromDecimal ds_dd_2      "+0" @?= Just dis+      Money.discreteFromDecimal ds_dd_2      "-0" @?= Just dis+      Money.discreteFromDecimal ds_p_dd_2     "0" @?= Just dis+      Money.discreteFromDecimal ds_p_dd_2    "+0" @?= Just dis+      Money.discreteFromDecimal ds_p_dd_2    "-0" @?= Just dis    , HU.testCase "USD cent, small, positive" $ do       let dis = 25 :: Money.Discrete "USD" "cent"-          f = Money.discreteFromDecimal-      f Nothing '.' 1 "0.25" @?= Just dis-      f Nothing '.' 1 "+0.25" @?= Just dis-      f (Just ',') '.' 1 "0.25" @?= Just dis-      f (Just ',') '.' 1 "+0.25" @?= Just dis+      Money.discreteFromDecimal ds_dd_2      "0.25"  @?= Just dis+      Money.discreteFromDecimal ds_dd_2      "+0.25" @?= Just dis+      Money.discreteFromDecimal ds_tc_dd_2   "0.25"  @?= Just dis+      Money.discreteFromDecimal ds_tc_dd_2   "+0.25" @?= Just dis+      Money.discreteFromDecimal ds_p_dd_2    "0.25"  @?= Just dis+      Money.discreteFromDecimal ds_p_dd_2    "+0.25" @?= Just dis+      Money.discreteFromDecimal ds_p_tc_dd_2 "0.25"  @?= Just dis+      Money.discreteFromDecimal ds_p_tc_dd_2 "+0.25" @?= Just dis    , HU.testCase "USD cent, small, negative" $ do       let dis = -25 :: Money.Discrete "USD" "cent"-          f = Money.discreteFromDecimal-      f Nothing '.' 1 "-0.25" @?= Just dis-      f Nothing '.' 1 "-0.25" @?= Just dis-      f (Just ',') '.' 1 "-0.25" @?= Just dis-      f (Just ',') '.' 1 "-0.25" @?= Just dis+      Money.discreteFromDecimal ds_dd_2      "-0.25" @?= Just dis+      Money.discreteFromDecimal ds_p_dd_2    "-0.25" @?= Just dis+      Money.discreteFromDecimal ds_tc_dd_2   "-0.25" @?= Just dis+      Money.discreteFromDecimal ds_p_tc_dd_2 "-0.25" @?= Just dis    , HU.testCase "USD cent, big, positive" $ do       let dis = 102300456789 :: Money.Discrete "USD" "cent"-          f = Money.discreteFromDecimal-      f Nothing '.' 1 "1023004567.89" @?= Just dis-      f Nothing '.' 1 "+1023004567.89" @?= Just dis-      f (Just ',') '.' 1 "1,023,004,567.89" @?= Just dis-      f (Just ',') '.' 1 "+1,023,004,567.89" @?= Just dis+      Money.discreteFromDecimal ds_dd_2      "+1023004567.89"    @?= Just dis+      Money.discreteFromDecimal ds_dd_2      "1023004567.89"     @?= Just dis+      Money.discreteFromDecimal ds_p_dd_2    "+1023004567.89"    @?= Just dis+      Money.discreteFromDecimal ds_p_dd_2    "1023004567.89"     @?= Just dis+      Money.discreteFromDecimal ds_p_tc_dd_2 "+1,023,004,567.89" @?= Just dis+      Money.discreteFromDecimal ds_p_tc_dd_2 "1,023,004,567.89"  @?= Just dis+      Money.discreteFromDecimal ds_tc_dd_2   "+1,023,004,567.89" @?= Just dis+      Money.discreteFromDecimal ds_tc_dd_2   "1,023,004,567.89"  @?= Just dis    , HU.testCase "USD cent, big, negative" $ do       let dis = -102300456789 :: Money.Discrete "USD" "cent"-          f = Money.discreteFromDecimal-      f Nothing '.' 1 "-1023004567.89" @?= Just dis-      f Nothing '.' 1 "-1023004567.89" @?= Just dis-      f (Just ',') '.' 1 "-1,023,004,567.89" @?= Just dis-      f (Just ',') '.' 1 "-1,023,004,567.89" @?= Just dis+      Money.discreteFromDecimal ds_dd_2      "-1023004567.89" @?= Just dis+      Money.discreteFromDecimal ds_p_dd_2    "-1023004567.89" @?= Just dis+      Money.discreteFromDecimal ds_tc_dd_2   "-1,023,004,567.89" @?= Just dis+      Money.discreteFromDecimal ds_p_tc_dd_2 "-1,023,004,567.89" @?= Just dis   ]  testRounding   :: forall (currency :: Symbol) (unit :: Symbol)-  .  (Money.GoodScale (Money.Scale currency unit), KnownSymbol currency)+  .  (Money.GoodScale (Money.UnitScale currency unit), KnownSymbol currency)   => Proxy currency   -> Proxy unit   -> Tasty.TestTree@@ -853,24 +972,98 @@     , QC.testProperty "ceiling"  $ QC.forAll QC.arbitrary (g (Money.discreteFromDense Money.Ceiling))     , QC.testProperty "round"    $ QC.forAll QC.arbitrary (g (Money.discreteFromDense Money.Round))     , QC.testProperty "truncate" $ QC.forAll QC.arbitrary (g (Money.discreteFromDense Money.Truncate))+    , QC.testProperty "half-even" $ QC.forAll QC.arbitrary (g (Money.discreteFromDense Money.HalfEven))     , QC.testProperty "floor no reminder"    $ QC.forAll QC.arbitrary (h (Money.discreteFromDense Money.Floor))     , QC.testProperty "ceiling no reminder"  $ QC.forAll QC.arbitrary (h (Money.discreteFromDense Money.Ceiling))     , QC.testProperty "round no reminder"    $ QC.forAll QC.arbitrary (h (Money.discreteFromDense Money.Round))     , QC.testProperty "truncate no reminder" $ QC.forAll QC.arbitrary (h (Money.discreteFromDense Money.Truncate))+    , QC.testProperty "half-even no reminder " $ QC.forAll QC.arbitrary (h (Money.discreteFromDense Money.HalfEven))     ]   where-    g :: (Money.Dense currency -> (Money.Discrete' currency (Money.Scale currency unit), Money.Dense currency))+    g :: (Money.Dense currency -> (Money.Discrete' currency (Money.UnitScale currency unit), Money.Dense currency))       -> Money.Dense currency       -> QC.Property     g f = \x -> x === case f x of (y, z) -> Money.denseFromDiscrete y + z -    h :: (Money.Dense currency -> (Money.Discrete' currency (Money.Scale currency unit), Money.Dense currency))+    h :: (Money.Dense currency -> (Money.Discrete' currency (Money.UnitScale currency unit), Money.Dense currency))       -> Money.Discrete currency unit       -> QC.Property     h f = \x -> (Money.denseFromDiscrete x) === case f (Money.denseFromDiscrete x) of       (y, 0) -> Money.denseFromDiscrete y       (_, _) -> error "testRounding.h: unexpected" +testHalfEvenRounding :: Tasty.TestTree+testHalfEvenRounding =+    Tasty.testGroup "HalfEven rounding"+    [ HU.testCase "-2.8" $ -3 @=? f (-3152519739159347 % 1125899906842624)++    , HU.testCase "-2.5" $ -2 @=? f (-5 % 2)+    , HU.testCase "-2.2" $ -2 @=? f (-2476979795053773 % 1125899906842624)+    , HU.testCase "-2"   $ -2 @=? f (-2 % 1)+    , HU.testCase "-1.8" $ -2 @=? f (-8106479329266893 % 4503599627370496)+    , HU.testCase "-1.5" $ -2 @=? f (-3 % 2)++    , HU.testCase "-1.2" $ -1 @=? f (-5404319552844595 % 4503599627370496)+    , HU.testCase "-1"   $ -1 @=? f (-1 % 1)+    , HU.testCase "-0.8" $ -1 @=? f (-3602879701896397 % 4503599627370496)++    , HU.testCase "-0.5" $  0 @=? f (-1 % 2)+    , HU.testCase "-0.2" $  0 @=? f (-3602879701896397 % 18014398509481984)+    , HU.testCase  "0"   $  0 @=? f (0 % 1)+    , HU.testCase  "0.2" $  0 @=? f (3602879701896397 % 18014398509481984)+    , HU.testCase  "0.5" $  0 @=? f (1 % 2)++    , HU.testCase  "0.8" $  1 @=? f (3602879701896397 % 4503599627370496)+    , HU.testCase  "1"   $  1 @=? f (1 % 1)+    , HU.testCase  "1.2" $  1 @=? f (5404319552844595 % 4503599627370496)++    , HU.testCase  "1.5" $  2 @=? f (3 % 2)+    , HU.testCase  "1.8" $  2 @=? f (8106479329266893 % 4503599627370496)+    , HU.testCase  "2"   $  2 @=? f (2 % 1)+    , HU.testCase  "2.2" $  2 @=? f (2476979795053773 % 1125899906842624)+    , HU.testCase  "2.5" $  2 @=? f (5 % 2)++    , HU.testCase  "2.8" $  3 @=? f (3152519739159347 % 1125899906842624)+    ]+  where+    f :: Rational -> Integer+    f = MoneyI.approximate MoneyI.HalfEven++++-- | Decimal dot, 2 decimals+ds_dd_2 :: Money.DecimalConf+ds_dd_2 = Money.defaultDecimalConf++-- | Leading plus, decimal dot, 2 decimals+ds_p_dd_2 :: Money.DecimalConf+ds_p_dd_2 = ds_dd_2 { Money.decimalConf_leadingPlus = True }++-- | Thousands comma, decimal dot, 2 decimals+ds_tc_dd_2 :: Money.DecimalConf+ds_tc_dd_2 = ds_dd_2 { Money.decimalConf_separators = Money.separatorsDotComma }++-- | Leading plus, thousands comma, decimal dot, 2 decimals+ds_p_tc_dd_2 :: Money.DecimalConf+ds_p_tc_dd_2 = ds_tc_dd_2 { Money.decimalConf_leadingPlus = True }++-- | Decimal dot, 0 decimals+ds_dd_0 :: Money.DecimalConf+ds_dd_0 = ds_dd_2 { Money.decimalConf_digits = 0 }++-- | Leading plus, decimal dot, 0 decimals+ds_p_dd_0 :: Money.DecimalConf+ds_p_dd_0 = ds_p_dd_2 { Money.decimalConf_digits = 0 }++-- | Thousands comma, decimal dot, 0 decimals+ds_tc_dd_0 :: Money.DecimalConf+ds_tc_dd_0 = ds_tc_dd_2 { Money.decimalConf_digits = 0 }++-- | Leading plus, thousands comma, decimal dot, 0 decimals+ds_p_tc_dd_0 :: Money.DecimalConf+ds_p_tc_dd_0 = ds_p_tc_dd_2 { Money.decimalConf_digits = 0 }++ -------------------------------------------------------------------------------- -- Raw parsing "golden tests" @@ -913,6 +1106,16 @@ rawDis0_binary = "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\ETXUSD\NUL\NUL\NUL\NULd\NUL\NUL\NUL\NUL\SOH\NUL\NUL\NUL\NUL\EOT" rawXr0_binary :: BL.ByteString rawXr0_binary = "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\ETXUSD\NUL\NUL\NUL\NUL\NUL\NUL\NUL\ETXBTC\NUL\NUL\NUL\NUL\ETX\NUL\NUL\NUL\NUL\STX"++--------------------------------------------------------------------------------++-- Just checking that this compiles.+_testCurrencyScale+  :: Dict (Money.CurrencyScale "USD" ~ Money.UnitScale "USD" "cent",+           Money.CurrencyScale "BTC" ~ Money.UnitScale "BTC" "satoshi",+           Money.CurrencyScale "VUV" ~ Money.UnitScale "VUV" "vatu",+           Money.CurrencyScale "XAU" ~ Money.ErrScaleNonCanonical "XAU")+_testCurrencyScale = Dict  -------------------------------------------------------------------------------- -- Misc