packages feed

ordinal 0.5.0.0 → 0.6.0.0

raw patch · 14 files changed

+1205/−820 lines, 14 filesdep +unordered-containersnew-component:exe:ordinalPVP ok

version bump matches the API change (PVP)

Dependencies added: unordered-containers

API changes (from Hackage documentation)

- Text.Numerals.Class: type ClockText = ClockSegment " The 'ClockSegment' that describes the state of minutes within an hour." -> DaySegment " The 'DaySegment' that describes the state of hours within a day." -> Int " The number of hours." -> Int " The number of minutes." -> Text " A 'Text' object that describes the given time."
+ Text.Numerals.Class: type ClockText = -- | The 'ClockSegment' that describes the state of minutes within an hour. ClockSegment -> -- | The 'DaySegment' that describes the state of hours within a day. DaySegment -> -- | The number of hours. Int -> -- | The number of minutes. Int -> -- | A 'Text' object that describes the given time. Text

Files

CHANGELOG.md view
@@ -2,6 +2,10 @@  For a full list of changes, see the history on [GitHub](https://github.com/hapytex/ordinal). +## Version 0.6.0.0++Added an executable to make conversions more convenient.+ ## Version 0.5.0.0  Use `data-default-class` instead of `data-default` to reduce the number of dependencies.
+ app/Main.hs view
@@ -0,0 +1,86 @@+{-# LANGUAGE OverloadedStrings #-}++module Main (main) where++import Data.Char (digitToInt, isDigit)+import Data.Default.Class (Default (def))+import Data.HashMap.Strict (HashMap, fromList, lookup)+import Data.Maybe (fromMaybe)+import Data.Text (Text, pack, toCaseFold)+import qualified Data.Text.IO as TI+import System.Console.GetOpt (ArgDescr (NoArg, ReqArg), ArgOrder (RequireOrder), OptDescr (Option), getOpt, usageInfo)+import System.Environment (getArgs)+import Text.Numerals.Algorithm (NumeralsAlgorithm)+import Text.Numerals.Class (toCardinal, toOrdinal, toShortOrdinal, toTimeText')+import Text.Numerals.Languages (dutch, english, french, german)+import Prelude hiding (lookup)++languages_ :: [([Text], NumeralsAlgorithm)]+languages_ =+  [ (["nl", "nld", "dut", "dutch"], dutch),+    (["en", "eng", "english"], english),+    (["fr", "fra", "fre", "french"], french),+    (["de", "deu", "ger", "german"], german)+  ]++languages :: HashMap Text NumeralsAlgorithm+languages = fromList [(toCaseFold k, v) | (ks, v) <- languages_, k <- ks]++data OrdinalMode = Cardinal | Ordinal | ShortOrdinal | Time deriving (Eq, Ord, Read, Show)++data OrdinalConfig = OrdinalConfig {lang :: NumeralsAlgorithm, mode :: OrdinalMode, help :: Bool}++instance Default OrdinalMode where+  def = Cardinal++instance Default OrdinalConfig where+  def = OrdinalConfig english def False++determine :: OrdinalMode -> NumeralsAlgorithm -> Integer -> Text+determine Cardinal = toCardinal+determine Ordinal = toOrdinal+determine ShortOrdinal = toShortOrdinal+determine Time = go+  where+    go lng x = toTimeText' lng (fromIntegral (x `div` 60)) (fromIntegral (x `mod` 60))++findLanguage :: String -> IO NumeralsAlgorithm+findLanguage k =+  case lookup (toCaseFold (pack k)) languages of+    Just x -> pure x+    _ -> fail ("Can not find language \"" ++ k ++ "\".")++options :: [OptDescr (OrdinalConfig -> IO OrdinalConfig)]+options =+  [ Option "l" ["lang", "language"] (ReqArg (\l' v -> (\l -> v {lang = l}) <$> findLanguage l') "lang") "specify the language, English by default",+    Option "c" ["cardinal"] (NoArg (\v -> pure (v {mode = Cardinal}))) "set the number mode to cardinal numbers, which is the deault",+    Option "o" ["ordinal"] (NoArg (\v -> pure (v {mode = Ordinal}))) "set the number mode to ordinal numbers",+    Option "s" ["short-ordinal"] (NoArg (\v -> pure (v {mode = ShortOrdinal}))) "set the number mode to short ordinal numbers",+    Option "t" ["time"] (NoArg (\v -> pure (v {mode = Time}))) "set the number mode to time",+    Option "?h" ["help"] (NoArg (\v -> pure (v {help = True}))) "show this help page"+  ]++header :: String+header = "Usage: ordinal [OPTION...] numbers..."++compilerOpts :: [String] -> IO ([OrdinalConfig -> IO OrdinalConfig], [String])+compilerOpts argv =+  case getOpt RequireOrder options argv of+    (o, n, []) -> return (o, n)+    (_, _, errs) -> ioError (userError (concat errs ++ usageInfo header options))++readInt :: String -> Integer+readInt v = fromMaybe (read v) (go 0 v)+  where+    go h (':' : xs) = Just (60 * h + read xs)+    go h (x : xs) | isDigit x = go (h * 10 + fromIntegral (digitToInt x)) xs+    go _ _ = Nothing++main :: IO ()+main = do+  args <- getArgs+  ~(opts, xs) <- compilerOpts args+  opt <- foldl (>>=) def opts+  if help opt+    then putStrLn (usageInfo header options)+    else mapM_ (TI.putStrLn . determine (mode opt) (lang opt) . readInt) xs
ordinal.cabal view
@@ -1,5 +1,5 @@ name:                ordinal-version:             0.5.0.0+version:             0.6.0.0 synopsis:            Convert numbers to words in different languages. description:     A package based on Python's num2words package that converts numbers@@ -48,6 +48,18 @@     , template-haskell >=2.2.0.0     , vector >= 0.7   default-language:    Haskell2010++executable ordinal+  hs-source-dirs: app+  main-is: Main.hs+  build-depends:+      base >=4.7 && <5+    , ordinal+    , data-default-class >=0.0.1+    , text >= 0.1+    , unordered-containers >=0.1.0+  default-language: Haskell2010+  test-suite             cardinal   type:                exitcode-stdio-1.0
src/Text/Numerals.hs view
@@ -1,19 +1,18 @@-{-|-Module      : Text.Numerals-Description : The main module that converts numbers to words. This module re-exports a set of modules.-Maintainer  : hapytexeu+gh@gmail.com-Stability   : experimental-Portability : POSIX--The main module of the @ordinal@ package. This module re-exports the most important modules of the package to convert numbers to words in the supported languages and functions to construct algorithmic transformations.--}--module Text.Numerals (-    module Text.Numerals.Algorithm-  , module Text.Numerals.Class-  , module Text.Numerals.Languages-  , module Text.Numerals.Prefix-  ) where+-- |+-- Module      : Text.Numerals+-- Description : The main module that converts numbers to words. This module re-exports a set of modules.+-- Maintainer  : hapytexeu+gh@gmail.com+-- Stability   : experimental+-- Portability : POSIX+--+-- The main module of the @ordinal@ package. This module re-exports the most important modules of the package to convert numbers to words in the supported languages and functions to construct algorithmic transformations.+module Text.Numerals+  ( module Text.Numerals.Algorithm,+    module Text.Numerals.Class,+    module Text.Numerals.Languages,+    module Text.Numerals.Prefix,+  )+where  import Text.Numerals.Algorithm import Text.Numerals.Class
src/Text/Numerals/Algorithm.hs view
@@ -1,92 +1,116 @@-{-# LANGUAGE CPP, DeriveDataTypeable, DeriveGeneric, OverloadedStrings, RankNTypes, TupleSections #-}--{-|-Module      : Text.Numerals.Algorithm-Description : A module that contains functions to construct algorithmic conversions from numbers to words.-Maintainer  : hapytexeu+gh@gmail.com-Stability   : experimental-Portability : POSIX+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TupleSections #-} -A module that contains data types and functions to automatically convert a number to words. It has tooling for a 'NumeralsAlgorithm'-as well as a 'HighNumberAlgorithm' that is used to generate a 'ShortScale' or 'LongScale'.--}+-- |+-- Module      : Text.Numerals.Algorithm+-- Description : A module that contains functions to construct algorithmic conversions from numbers to words.+-- Maintainer  : hapytexeu+gh@gmail.com+-- Stability   : experimental+-- Portability : POSIX+--+-- A module that contains data types and functions to automatically convert a number to words. It has tooling for a 'NumeralsAlgorithm'+-- as well as a 'HighNumberAlgorithm' that is used to generate a 'ShortScale' or 'LongScale'.+module Text.Numerals.Algorithm+  ( -- * Data types for number algorithms+    NumeralsAlgorithm,+    numeralsAlgorithm, -module Text.Numerals.Algorithm (-    -- * Data types for number algorithms-    NumeralsAlgorithm-  , numeralsAlgorithm     -- * Large number algorithms-  , HighNumberAlgorithm(ShortScale, LongScale)-  , shortScale, longScale-  , shortScaleTitle, longScaleTitle-  , valueSplit'+    HighNumberAlgorithm (ShortScale, LongScale),+    shortScale,+    longScale,+    shortScaleTitle,+    longScaleTitle,+    valueSplit',+     -- * Conversion to a 'NumberSegment'-  , toSegments-  , toSegmentLow, toSegmentMid, toSegmentHigh-    -- * Segment compression-  , compressSegments-  ) where+    toSegments,+    toSegmentLow,+    toSegmentMid,+    toSegmentHigh, -import Control.DeepSeq(NFData)+    -- * Segment compression+    compressSegments,+  )+where -import Data.Data(Data)-import Data.Default.Class(Default(def))-import Data.Foldable(toList)-import Data.List(sortOn)+import Control.DeepSeq (NFData)+import Data.Data (Data)+import Data.Default.Class (Default (def))+import Data.Foldable (toList)+import Data.List (sortOn) #if __GLASGOW_HASKELL__ < 803 import Data.Semigroup((<>)) #endif-import Data.Text(Text, cons, toTitle)-import Data.Vector(Vector, (!), (!?), fromList)+import Data.Text (Text, cons, toTitle)+import Data.Vector (Vector, fromList, (!), (!?)) import qualified Data.Vector as V--import GHC.Generics(Generic)--import Test.QuickCheck(oneof)-import Test.QuickCheck.Arbitrary(Arbitrary(arbitrary, shrink))--import Text.Numerals.Class(-    NumToWord(toCardinal, toOrdinal, toShortOrdinal, toTimeText')-  , FreeMergerFunction, FreeNumberToWords, FreeValueSplitter-  , MergerFunction, MNumberSegment-  , NumberSegment(NumberSegment), NumberSegmenting-  , ValueSplit(valueSplit), ValueSplitter-  , ClockText-  , toClockSegment, toDaySegment+import GHC.Generics (Generic)+import Test.QuickCheck (oneof)+import Test.QuickCheck.Arbitrary (Arbitrary (arbitrary, shrink))+import Text.Numerals.Class+  ( ClockText,+    FreeMergerFunction,+    FreeNumberToWords,+    FreeValueSplitter,+    MNumberSegment,+    MergerFunction,+    NumToWord (toCardinal, toOrdinal, toShortOrdinal, toTimeText'),+    NumberSegment (NumberSegment),+    NumberSegmenting,+    ValueSplit (valueSplit),+    ValueSplitter,+    toClockSegment,+    toDaySegment,   )-import Text.Numerals.Internal(_genText, _shrinkText, _thousand, _iLogFloor)-import Text.Numerals.Prefix(latinPrefixes)+import Text.Numerals.Internal (_genText, _iLogFloor, _shrinkText, _thousand)+import Text.Numerals.Prefix (latinPrefixes)  -- | A data type for algorithmic number to word conversions. Most western -- languages /likely/ can work with this data type.-data NumeralsAlgorithm = NumeralsAlgorithm {-    minusWord :: Text  -- ^ The word used as prefix to denote negative numbers.-  , oneWord :: Text  -- ^ The word used to denote /one/ in the language.-  , lowWords :: Vector Text  -- ^ A 'Vector' of small numbers, the first item is the word for /two/ and each successor is the word for the next number.-  , midWords :: [(Integer, Text)]  -- ^ A list of 2-tuples where the first item contains the value, and the second the corresponding word, the values are ordered in descending value order.-  , highWords :: FreeValueSplitter  -- ^ A function that is used to generate words for large values (greater than or equal to one /million/), often constructed with the /short scale/ or /long scale/.-  , mergeFunction :: FreeMergerFunction  -- ^ A function that specifies how to merge words based on the grammar of that specific language.-  , ordinize :: Text -> Text  -- ^ A function to conver the /cardinal/ form of a number in an /ordinal/ one.-  , shortOrdinal :: FreeNumberToWords -- ^ A function that converts a number to its /short ordinal/ form.-  , clockText :: ClockText  -- ^ A function that converts the clock segment and day segment to a /Text/ that describes the time of the day in words.+data NumeralsAlgorithm = NumeralsAlgorithm+  { -- | The word used as prefix to denote negative numbers.+    minusWord :: Text,+    -- | The word used to denote /one/ in the language.+    oneWord :: Text,+    -- | A 'Vector' of small numbers, the first item is the word for /two/ and each successor is the word for the next number.+    lowWords :: Vector Text,+    -- | A list of 2-tuples where the first item contains the value, and the second the corresponding word, the values are ordered in descending value order.+    midWords :: [(Integer, Text)],+    -- | A function that is used to generate words for large values (greater than or equal to one /million/), often constructed with the /short scale/ or /long scale/.+    highWords :: FreeValueSplitter,+    -- | A function that specifies how to merge words based on the grammar of that specific language.+    mergeFunction :: FreeMergerFunction,+    -- | A function to conver the /cardinal/ form of a number in an /ordinal/ one.+    ordinize :: Text -> Text,+    -- | A function that converts a number to its /short ordinal/ form.+    shortOrdinal :: FreeNumberToWords,+    -- | A function that converts the clock segment and day segment to a /Text/ that describes the time of the day in words.+    clockText :: ClockText   }  instance NumToWord NumeralsAlgorithm where-    toCardinal NumeralsAlgorithm { minusWord=_minusWord, oneWord=_oneWord, lowWords=_lowWords, midWords=_midWords, highWords=_highWords, mergeFunction=_mergeFunction } = cardinal-       where cardinal i-                  | i < 0 = _minusWord <> cons ' ' (go (-j))-                  | otherwise = go j-                  where go = compressSegments _oneWord _mergeFunction . toSegments _lowWords _midWords _highWords-                        j = fromIntegral i :: Integer--    toOrdinal na@NumeralsAlgorithm { ordinize=_ordinize } = _ordinize . toCardinal na-    toShortOrdinal = shortOrdinal-    toTimeText' alg h m = clockText alg (toClockSegment m) (toDaySegment h) h m+  toCardinal NumeralsAlgorithm {minusWord = _minusWord, oneWord = _oneWord, lowWords = _lowWords, midWords = _midWords, highWords = _highWords, mergeFunction = _mergeFunction} = cardinal+    where+      cardinal i+        | i < 0 = _minusWord <> cons ' ' (go (-j))+        | otherwise = go j+        where+          go = compressSegments _oneWord _mergeFunction . toSegments _lowWords _midWords _highWords+          j = fromIntegral i :: Integer +  toOrdinal na@NumeralsAlgorithm {ordinize = _ordinize} = _ordinize . toCardinal na+  toShortOrdinal = shortOrdinal+  toTimeText' alg h m = clockText alg (toClockSegment m) (toDaySegment h) h m  _toNumberScale :: (Integral i, Integral j) => i -> (j, i) _toNumberScale i = (l, k)-    where ~(_, l, k) = _iLogFloor _thousand i+  where+    ~(_, l, k) = _iLogFloor _thousand i  -- | A data type used for to map larger numbers to words. This data type -- supports the /short scale/ and /long scale/ with /Latin/ prefixes, and@@ -105,7 +129,7 @@   shrink (LongScale ta tb) = ((`LongScale` tb) <$> _shrinkText ta) <> (LongScale ta <$> _shrinkText tb)  instance Default HighNumberAlgorithm where-    def = ShortScale "illion"+  def = ShortScale "illion"  -- | Construct a 'FreeValueSplitter' function for the given suffix for a /short scale/. shortScale :: Text -> FreeValueSplitter@@ -123,28 +147,32 @@ longScaleTitle :: Text -> Text -> FreeValueSplitter longScaleTitle suf1 = valueSplit' toTitle . LongScale suf1 - _highWithSuffix :: Text -> Int -> Maybe Text _highWithSuffix suf = fmap (<> suf) . (latinPrefixes !?)  _highToText :: HighNumberAlgorithm -> Int -> Maybe Text _highToText (ShortScale suf) j = _highWithSuffix suf j _highToText (LongScale suf1 suf2) j-    | even j = _highWithSuffix suf1 k-    | otherwise = _highWithSuffix suf2 k-    where k = div j 2+  | even j = _highWithSuffix suf1 k+  | otherwise = _highWithSuffix suf2 k+  where+    k = div j 2  -- | Generate a /value splitter/ for a 'HighNumberAlgorithm' but where the result -- is post-processed by a function.-valueSplit'-  :: (Text -> Text)  -- ^ The post-processing function.-  -> HighNumberAlgorithm  -- ^ The 'HighNumberAlgorithm' that is used.-  -> FreeValueSplitter  -- ^ The 'FreeValueSplitter' result.-valueSplit' f vs i = (m,) . f <$> _highToText vs (j-2)-    where ~(j, m) = _toNumberScale i+valueSplit' ::+  -- | The post-processing function.+  (Text -> Text) ->+  -- | The 'HighNumberAlgorithm' that is used.+  HighNumberAlgorithm ->+  -- | The 'FreeValueSplitter' result.+  FreeValueSplitter+valueSplit' f vs i = (m,) . f <$> _highToText vs (j - 2)+  where+    ~(j, m) = _toNumberScale i  instance ValueSplit HighNumberAlgorithm where-    valueSplit = valueSplit' id+  valueSplit = valueSplit' id  -- | A /smart constructor/ for the 'NumeralsAlgorithm' type. This constructor -- allows one to use an arbitrary 'Foldable' type for the low words and mid@@ -154,77 +182,112 @@  _maybeSegment :: Integral i => (i -> NumberSegment i) -> i -> MNumberSegment i _maybeSegment f = go-    where go 0 = Nothing-          go i = Just (f i)+  where+    go 0 = Nothing+    go i = Just (f i)  -- | Convert the given number to a 'NumberSegment' with the given 'Vector' of -- low numbers. Mid words and large numbers are not taken into account. This -- is often the next step after the 'toSegmentMid'.-toSegmentLow :: Integral i-  => Vector Text  -- ^ A 'Vector' of low words.-  -> NumberSegmenting i  -- ^ The function that maps the number to the 'NumberSegment'.+toSegmentLow ::+  Integral i =>+  -- | A 'Vector' of low words.+  Vector Text ->+  -- | The function that maps the number to the 'NumberSegment'.+  NumberSegmenting i toSegmentLow vs = go-    where go i | i >= nvs = NumberSegment (Just (go dv)) nvs lv (tl md)-               | otherwise = NumberSegment Nothing i (vs ! fromIntegral i) Nothing-               where (dv, md) = divMod i nvs-          lv = V.last vs-          nvs = fromIntegral (V.length vs) - 1-          tl = _maybeSegment go+  where+    go i+      | i >= nvs = NumberSegment (Just (go dv)) nvs lv (tl md)+      | otherwise = NumberSegment Nothing i (vs ! fromIntegral i) Nothing+      where+        (dv, md) = divMod i nvs+    lv = V.last vs+    nvs = fromIntegral (V.length vs) - 1+    tl = _maybeSegment go  _splitRecurse :: Integral i => (i -> NumberSegment i) -> (i -> NumberSegment i) -> i -> Text -> i -> NumberSegment i _splitRecurse f g im v j = NumberSegment hd im v (_maybeSegment g md)-    where hd | dv == 1 = Nothing-             | otherwise = Just (f dv)-          ~(dv, md) = divMod j im+  where+    hd+      | dv == 1 = Nothing+      | otherwise = Just (f dv)+    ~(dv, md) = divMod j im  -- | Convert the given number to a 'NumberSegment' with the given 'Vector' of -- low numbers, and the /sorted/ list of mid numbers. Large numbers are not -- taken into account. This is often the next step after the 'toSegmentHigh'.-toSegmentMid :: Integral i-  => Vector Text  -- ^ A 'Vector' of low words.-  -> [(Integer, Text)]  -- ^ The list of name and the names of these numbers in /descending/ order for the mid words.-  -> NumberSegmenting i  -- ^ The function that maps the number to the 'NumberSegment'.+toSegmentMid ::+  Integral i =>+  -- | A 'Vector' of low words.+  Vector Text ->+  -- | The list of name and the names of these numbers in /descending/ order for the mid words.+  [(Integer, Text)] ->+  -- | The function that maps the number to the 'NumberSegment'.+  NumberSegmenting i toSegmentMid lows = go-    where go [] n = toSegmentLow lows n-          go ma@((m, v) : ms) n-              | im > n = goms n-              | otherwise = _splitRecurse (go ma) goms im v n-              where im = fromIntegral m-                    goms = go ms+  where+    go [] n = toSegmentLow lows n+    go ma@((m, v) : ms) n+      | im > n = goms n+      | otherwise = _splitRecurse (go ma) goms im v n+      where+        im = fromIntegral m+        goms = go ms  -- | Convert the given number to a 'NumberSegment' with the given 'Vector' of -- low numbers, the /sorted/ list of mid numbers, and a 'FreeValueSplitter' for -- large numbers.-toSegmentHigh :: Integral i-  => Vector Text  -- ^ A 'Vector' of low words.-  -> [(Integer, Text)]  -- ^ The list of name and the names of these numbers in /descending/ order for the mid words.-  -> ValueSplitter i  -- ^ The 'ValueSplitter' used for large numbers, likely a splitter from a /short scale/ or /long scale/.-  -> NumberSegmenting i  -- ^ The function that maps the number to the 'NumberSegment'.+toSegmentHigh ::+  Integral i =>+  -- | A 'Vector' of low words.+  Vector Text ->+  -- | The list of name and the names of these numbers in /descending/ order for the mid words.+  [(Integer, Text)] ->+  -- | The 'ValueSplitter' used for large numbers, likely a splitter from a /short scale/ or /long scale/.+  ValueSplitter i ->+  -- | The function that maps the number to the 'NumberSegment'.+  NumberSegmenting i toSegmentHigh lows mids highs = go-    where go v | Just (i, t) <- highs v = _splitRecurse go go i t v-               | otherwise = toSegmentMid lows mids v+  where+    go v+      | Just (i, t) <- highs v = _splitRecurse go go i t v+      | otherwise = toSegmentMid lows mids v  -- | Convert the given number to a 'NumberSegment' with the given 'Vector' of -- low numbers, the /sorted/ list of mid numbers, and a 'FreeValueSplitter' for -- large numbers.-toSegments :: Integral i-  => Vector Text  -- ^ A 'Vector' of low words.-  -> [(Integer, Text)]  -- ^ The list of name and the names of these numbers in /descending/ order for the mid words.-  -> ValueSplitter i  -- ^ The 'ValueSplitter' used for large numbers, likely a splitter from a /short scale/ or /long scale/.-  -> NumberSegmenting i  -- ^ The function that maps the number to the 'NumberSegment'.+toSegments ::+  Integral i =>+  -- | A 'Vector' of low words.+  Vector Text ->+  -- | The list of name and the names of these numbers in /descending/ order for the mid words.+  [(Integer, Text)] ->+  -- | The 'ValueSplitter' used for large numbers, likely a splitter from a /short scale/ or /long scale/.+  ValueSplitter i ->+  -- | The function that maps the number to the 'NumberSegment'.+  NumberSegmenting i toSegments = toSegmentHigh  -- | Use the given 'MergerFunction' to compress the 'NumberSegment' to a single -- 'Text' object that represents the given number.-compressSegments :: Integral i-  => Text  -- ^ The value used for /one/ in the specific language.-  -> MergerFunction i  -- ^ The 'MergerFunction' for the specific language that implements the grammar rules how to merge values.-  -> NumberSegment i  -- ^ The given 'NumberSegment' value to turn into a 'Text' object.-  -> Text  -- ^ The 'Text' object that contains the name of the number stored in the 'NumberSegment'.+compressSegments ::+  Integral i =>+  -- | The value used for /one/ in the specific language.+  Text ->+  -- | The 'MergerFunction' for the specific language that implements the grammar rules how to merge values.+  MergerFunction i ->+  -- | The given 'NumberSegment' value to turn into a 'Text' object.+  NumberSegment i ->+  -- | The 'Text' object that contains the name of the number stored in the 'NumberSegment'.+  Text compressSegments one' merger = snd . go-    where go (NumberSegment dv' i t md') = _mergeTail md' (dvi * i, merger dvi i dv t)-              where (dvi, dv) = _unwrap dv'-          _unwrap = maybe (1, one') go-          _mergeTail Nothing r = r-          _mergeTail (Just md') (vi, v) = (vi + mdi, merger vi mdi v md)-              where (mdi, md) = go md'+  where+    go (NumberSegment dv' i t md') = _mergeTail md' (dvi * i, merger dvi i dv t)+      where+        (dvi, dv) = _unwrap dv'+    _unwrap = maybe (1, one') go+    _mergeTail Nothing r = r+    _mergeTail (Just md') (vi, v) = (vi + mdi, merger vi mdi v md)+      where+        (mdi, md) = go md'
src/Text/Numerals/Algorithm/Template.hs view
@@ -1,36 +1,34 @@-{-# LANGUAGE CPP, TemplateHaskellQuotes #-}--{-|-Module      : Text.Numerals.Algorithm.Template-Description : A module that constructs template Haskell to make defining an ordinize function more convenient.-Maintainer  : hapytexeu+gh@gmail.com-Stability   : experimental-Portability : POSIX--The module is designed to construct an 'Exp' based on the mapping data provided. It will check if the text object-ends with the given suffix, and replace the suffix with another suffix. It aims to compile this into an efficient function.--}+{-# LANGUAGE CPP #-}+{-# LANGUAGE TemplateHaskellQuotes #-} -module Text.Numerals.Algorithm.Template (-    ordinizeFromDict-  ) where+-- |+-- Module      : Text.Numerals.Algorithm.Template+-- Description : A module that constructs template Haskell to make defining an ordinize function more convenient.+-- Maintainer  : hapytexeu+gh@gmail.com+-- Stability   : experimental+-- Portability : POSIX+--+-- The module is designed to construct an 'Exp' based on the mapping data provided. It will check if the text object+-- ends with the given suffix, and replace the suffix with another suffix. It aims to compile this into an efficient function.+module Text.Numerals.Algorithm.Template+  ( ordinizeFromDict,+  )+where -import Data.Map.Strict(Map, elems, fromListWith)+import Data.Map.Strict (Map, elems, fromListWith) #if __GLASGOW_HASKELL__ < 803 import Data.Semigroup((<>)) #endif-import Data.Text(Text, isSuffixOf, pack, snoc)--import Text.Numerals.Internal(_replaceSuffix)--import Language.Haskell.TH(Body(GuardedB), Clause(Clause), Dec(FunD, SigD), Exp(AppE, ConE, LitE, VarE), Guard(NormalG), Lit(CharL, IntegerL, StringL), Name, Pat(VarP), Type(ConT, AppT, ArrowT), mkName)+import Data.Text (Text, isSuffixOf, pack, snoc)+import Language.Haskell.TH (Body (GuardedB), Clause (Clause), Dec (FunD, SigD), Exp (AppE, ConE, LitE, VarE), Guard (NormalG), Lit (CharL, IntegerL, StringL), Name, Pat (VarP), Type (AppT, ArrowT, ConT), mkName)+import Text.Numerals.Internal (_replaceSuffix)  _getPrefix :: [Char] -> [Char] -> (Int, [Char]) _getPrefix [] bs = (0, bs) _getPrefix as [] = (length as, [])-_getPrefix aa@(a:as) ba@(b:bs)-     | a == b = _getPrefix as bs-     | otherwise = (length aa, ba)+_getPrefix aa@(a : as) ba@(b : bs)+  | a == b = _getPrefix as bs+  | otherwise = (length aa, ba)  _orCondition :: [Exp] -> Guard _orCondition [] = NormalG (ConE 'False)@@ -47,11 +45,13 @@  _ordinizeSingle :: Exp -> String -> String -> ((Int, String), ([Exp], Exp)) _ordinizeSingle nm sa sb = (p, ([AppE (AppE (VarE 'isSuffixOf) (_packText sa)) nm], _packExp l sc nm))-    where p@(l, sc) = _getPrefix sa sb+  where+    p@(l, sc) = _getPrefix sa sb  _ordinizeMap :: Exp -> [(String, String)] -> Map (Int, String) ([Exp], Exp) _ordinizeMap n = fromListWith f . map (uncurry (_ordinizeSingle n))-    where f (as, a) (bs, _) = (bs ++ as, a)+  where+    f (as, a) (bs, _) = (bs ++ as, a)  _toGuard :: ([Exp], Exp) -> (Guard, Exp) _toGuard (gs, es) = (_orCondition gs, es)@@ -59,13 +59,18 @@ -- | Construct a function with the given name that maps suffixes in the first -- item of the 2-tuples to the second item of the 2-tuples. It turns this into a -- declaration.-ordinizeFromDict-  :: String  -- ^ The name of the function, often this is just @ordinize'@-  -> [(String, String)]  -- ^ The list of suffixes and their corresponding mapping, the suffixes should be non-overlapping.-  -> Name  -- ^ The name of the post-processing function in case there was no match, one can for example use 'id'.-  -> [Dec]  -- ^ The corresponding declaration.+ordinizeFromDict ::+  -- | The name of the function, often this is just @ordinize'@+  String ->+  -- | The list of suffixes and their corresponding mapping, the suffixes should be non-overlapping.+  [(String, String)] ->+  -- | The name of the post-processing function in case there was no match, one can for example use 'id'.+  Name ->+  -- | The corresponding declaration.+  [Dec] ordinizeFromDict nm ts pp = [SigD nnm (tText (tText ArrowT)), FunD nnm [Clause [VarP t] (GuardedB (map _toGuard (elems (_ordinizeMap t' ts)) ++ [(NormalG (ConE 'True), AppE (VarE pp) t')])) []]]-    where t = mkName "t"-          t' = VarE t-          nnm = mkName nm-          tText = (`AppT` ConT ''Text)+  where+    t = mkName "t"+    t' = VarE t+    nnm = mkName nm+    tText = (`AppT` ConT ''Text)
src/Text/Numerals/Class.hs view
@@ -1,70 +1,84 @@-{-# LANGUAGE CPP, DeriveDataTypeable, DeriveFunctor, DeriveFoldable, DeriveGeneric, RankNTypes, Safe #-}--{-|-Module      : Text.Numerals.Class-Description : A module that contains the typeclasses on which the rest of the module works.-Maintainer  : hapytexeu+gh@gmail.com-Stability   : experimental-Portability : POSIX+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveFoldable #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE Safe #-} -A module that defines the typeclasses that are used in the rest of the module. The 'NumToWord' class-is the typeclass that is used by all algorithmic conversion tools.--}+-- |+-- Module      : Text.Numerals.Class+-- Description : A module that contains the typeclasses on which the rest of the module works.+-- Maintainer  : hapytexeu+gh@gmail.com+-- Stability   : experimental+-- Portability : POSIX+--+-- A module that defines the typeclasses that are used in the rest of the module. The 'NumToWord' class+-- is the typeclass that is used by all algorithmic conversion tools.+module Text.Numerals.Class+  ( -- * Typeclasses+    NumToWord (toCardinal, toOrdinal, toShortOrdinal, toWords, toTimeText, toTimeText'),+    ValueSplit (valueSplit), -module Text.Numerals.Class (-    -- * Typeclasses-    NumToWord(toCardinal, toOrdinal, toShortOrdinal, toWords, toTimeText, toTimeText')-  , ValueSplit(valueSplit)     -- * Types of numbers-  , NumberType(Cardinal, Ordinal, ShortOrdinal)+    NumberType (Cardinal, Ordinal, ShortOrdinal),+     -- * Segmenting a number-  , NumberSegment(NumberSegment, segmentDivision, segmentValue, segmentText, segmentRemainder)-  , MNumberSegment+    NumberSegment (NumberSegment, segmentDivision, segmentValue, segmentText, segmentRemainder),+    MNumberSegment,+     -- * Segments of time-  , ClockSegment(OClock, Past, QuarterPast, ToHalf, Half, PastHalf, QuarterTo, To)-  , DayPart(Night, Morning, Afternoon, Evening)-  , DaySegment(DaySegment, dayPart, dayHour)-  , toDayPart, toDaySegment, toClockSegment-  , hourCorrection+    ClockSegment (OClock, Past, QuarterPast, ToHalf, Half, PastHalf, QuarterTo, To),+    DayPart (Night, Morning, Afternoon, Evening),+    DaySegment (DaySegment, dayPart, dayHour),+    toDayPart,+    toDaySegment,+    toClockSegment,+    hourCorrection,+     -- * Convert the current time to words-  , currentTimeText, currentTimeText'-    -- * Utility type synonyms-  , NumberToWords,  FreeNumberToWords-  , MergerFunction, FreeMergerFunction, ValueSplitter, FreeValueSplitter, NumberSegmenting-  , ClockText-  ) where+    currentTimeText,+    currentTimeText', -import Control.DeepSeq(NFData, NFData1)+    -- * Utility type synonyms+    NumberToWords,+    FreeNumberToWords,+    MergerFunction,+    FreeMergerFunction,+    ValueSplitter,+    FreeValueSplitter,+    NumberSegmenting,+    ClockText,+  )+where -import Data.Data(Data)-import Data.Default.Class(Default(def))+import Control.DeepSeq (NFData, NFData1)+import Data.Data (Data)+import Data.Default.Class (Default (def)) #if __GLASGOW_HASKELL__ < 803 import Data.Semigroup((<>)) #endif-import Data.Text(Text)-import Data.Time.Clock(getCurrentTime, utctDayTime)-import Data.Time.LocalTime(TimeOfDay(TimeOfDay), TimeZone, timeToTimeOfDay, utcToLocalTimeOfDay)--import GHC.Generics(Generic, Generic1)--import Test.QuickCheck(choose)-import Test.QuickCheck.Arbitrary(Arbitrary(arbitrary, shrink), Arbitrary1(liftArbitrary), arbitrary1, arbitraryBoundedEnum)--import Text.Numerals.Internal(_genText, _shrinkText)+import Data.Text (Text)+import Data.Time.Clock (getCurrentTime, utctDayTime)+import Data.Time.LocalTime (TimeOfDay (TimeOfDay), TimeZone, timeToTimeOfDay, utcToLocalTimeOfDay)+import GHC.Generics (Generic, Generic1)+import Test.QuickCheck (choose)+import Test.QuickCheck.Arbitrary (Arbitrary (arbitrary, shrink), Arbitrary1 (liftArbitrary), arbitrary1, arbitraryBoundedEnum)+import Text.Numerals.Internal (_genText, _shrinkText)  -- | A type alias for a function that maps a number to a 'Text' object. type NumberToWords i = i -> Text  -- | A type alias for a 'NumberToWords' function, with a free 'Integral' -- variable.-type FreeNumberToWords = forall i . Integral i => NumberToWords i+type FreeNumberToWords = forall i. Integral i => NumberToWords i  -- | A type alias of a function that is used to merge the names of two numbers according -- to gramatical rules. The type parameter is the type of the numbers to merge. type MergerFunction i = i -> i -> Text -> Text -> Text  -- | A type alias of a 'MergerFunction' function with a free 'Integral' variable.-type FreeMergerFunction = forall i . Integral i => MergerFunction i+type FreeMergerFunction = forall i. Integral i => MergerFunction i  -- | A type alias of a function that maps a number to a 2-tuple that contains a -- number and the word for that number. This number is normally the largest@@ -74,7 +88,7 @@  -- | A type alias of a 'ValueSplitter' function, with a free 'Integral' -- variable.-type FreeValueSplitter = forall i . Integral i => ValueSplitter i+type FreeValueSplitter = forall i. Integral i => ValueSplitter i  -- | A type alias of a function that converts a number to a 'NumberSegment' for that number. type NumberSegmenting i = i -> NumberSegment i@@ -82,12 +96,17 @@ -- | A data type used to convert a number into segments. Each segment has an -- optional division and remainder part, together with a value and the name of -- that value in a language.-data NumberSegment i = NumberSegment {-    segmentDivision :: MNumberSegment i  -- ^ The optional division part. 'Nothing' if the division is equal to one.-  , segmentValue :: i  -- ^ The value of the given segment.-  , segmentText :: Text  -- ^ The name of the value of the given segment, in a specific language.-  , segmentRemainder ::  MNumberSegment i  -- ^ The optional remainder part. 'Nothing' if the remainder is equal to zero.-  } deriving (Data, Eq, Foldable, Functor, Generic, Generic1, Ord, Read, Show)+data NumberSegment i = NumberSegment+  { -- | The optional division part. 'Nothing' if the division is equal to one.+    segmentDivision :: MNumberSegment i,+    -- | The value of the given segment.+    segmentValue :: i,+    -- | The name of the value of the given segment, in a specific language.+    segmentText :: Text,+    -- | The optional remainder part. 'Nothing' if the remainder is equal to zero.+    segmentRemainder :: MNumberSegment i+  }+  deriving (Data, Eq, Foldable, Functor, Generic, Generic1, Ord, Read, Show)  instance NFData a => NFData (NumberSegment a) @@ -95,16 +114,16 @@  instance Arbitrary1 NumberSegment where   liftArbitrary gen = go-      where go = NumberSegment <$> liftArbitrary go <*> gen <*> _genText <*> liftArbitrary go+    where+      go = NumberSegment <$> liftArbitrary go <*> gen <*> _genText <*> liftArbitrary go  instance Arbitrary i => Arbitrary (NumberSegment i) where   arbitrary = arbitrary1   shrink (NumberSegment dv val txt rm) =-    ((\x -> NumberSegment x val txt rm) <$> shrink dv) <>-    ((\x -> NumberSegment dv x txt rm) <$> shrink val) <>-    ((\x -> NumberSegment dv val x rm) <$> _shrinkText txt) <>-    (NumberSegment dv val txt <$> shrink rm)-+    ((\x -> NumberSegment x val txt rm) <$> shrink dv)+      <> ((\x -> NumberSegment dv x txt rm) <$> shrink val)+      <> ((\x -> NumberSegment dv val x rm) <$> _shrinkText txt)+      <> (NumberSegment dv val txt <$> shrink rm)  -- | A 'Maybe' variant of the 'NumberSegment' data type. This is used since the -- division part can be one, or the remainder part can be zero.@@ -113,9 +132,12 @@ -- | A data type that specifies the different types of numbers. These can be -- used to specify the "target format". The 'Default' number type is 'Cardinal'. data NumberType-  = Cardinal  -- ^ /Cardinal/ numbers like one, two, three, etc.-  | Ordinal  -- ^ /Ordinal/ numbers like first, second, third, etc.-  | ShortOrdinal -- ^ /Short ordinal/ numbers like 1st, 2nd, 3rd, etc.+  = -- | /Cardinal/ numbers like one, two, three, etc.+    Cardinal+  | -- | /Ordinal/ numbers like first, second, third, etc.+    Ordinal+  | -- | /Short ordinal/ numbers like 1st, 2nd, 3rd, etc.+    ShortOrdinal   deriving (Bounded, Data, Enum, Eq, Generic, Ord, Read, Show)  instance Arbitrary NumberType where@@ -125,23 +147,36 @@  -- | The type of a function that converts time to its description. The first -- two parameters are used to make conversion more convenient.-type ClockText-  =  ClockSegment  -- ^ The 'ClockSegment' that describes the state of minutes within an hour.-  -> DaySegment  -- ^ The 'DaySegment' that describes the state of hours within a day.-  -> Int  -- ^ The number of hours.-  -> Int  -- ^ The number of minutes.-  -> Text  -- ^ A 'Text' object that describes the given time.+type ClockText =+  -- | The 'ClockSegment' that describes the state of minutes within an hour.+  ClockSegment ->+  -- | The 'DaySegment' that describes the state of hours within a day.+  DaySegment ->+  -- | The number of hours.+  Int ->+  -- | The number of minutes.+  Int ->+  -- | A 'Text' object that describes the given time.+  Text  -- | A data type that describes the state of the minutes within an hour. data ClockSegment-  = OClock  -- ^ The number of minutes is zero.-  | Past Int  -- ^ The parameter is the number of minutes past the hour, this is between @1@ and @14@.-  | QuarterPast  -- ^ It is a quarter past the hour.-  | ToHalf Int  -- ^ The parameter is the number of minutes until half, this is between @1@ and @14@.-  | Half  -- ^ It is half past an hour.-  | PastHalf Int  -- ^ The parameter is the number of minutes past half, this is between @1@ and @14@.-  | QuarterTo  -- ^ It is a quarter to an hour.-  | To Int  -- ^ The parameter is the number of minutes to the next hour, this is between @1@ and @14@.+  = -- | The number of minutes is zero.+    OClock+  | -- | The parameter is the number of minutes past the hour, this is between @1@ and @14@.+    Past Int+  | -- | It is a quarter past the hour.+    QuarterPast+  | -- | The parameter is the number of minutes until half, this is between @1@ and @14@.+    ToHalf Int+  | -- | It is half past an hour.+    Half+  | -- | The parameter is the number of minutes past half, this is between @1@ and @14@.+    PastHalf Int+  | -- | It is a quarter to an hour.+    QuarterTo+  | -- | The parameter is the number of minutes to the next hour, this is between @1@ and @14@.+    To Int   deriving (Data, Eq, Generic, Ord, Read, Show)  instance NFData ClockSegment@@ -151,10 +186,14 @@  -- | A data type that describes the state of the hours within a day. data DayPart-  = Night  -- ^ It is night, this means that it is between @0:00@ and @5:59@.-  | Morning  -- ^ It is morning, this means that it is between @6:00@ and @11:59@.-  | Afternoon  -- ^ It is afternoon, this means it is between @12:00@ and @17:59@.-  | Evening  -- ^ It is evening, this means it is between @18:00@ and @23:59@.+  = -- | It is night, this means that it is between @0:00@ and @5:59@.+    Night+  | -- | It is morning, this means that it is between @6:00@ and @11:59@.+    Morning+  | -- | It is afternoon, this means it is between @12:00@ and @17:59@.+    Afternoon+  | -- | It is evening, this means it is between @18:00@ and @23:59@.+    Evening   deriving (Bounded, Data, Enum, Eq, Generic, Ord, Read, Show)  instance Arbitrary DayPart where@@ -164,11 +203,12 @@  -- | A data type that describes the part of the day, and the number of hours on -- a 12-hour clock.-data DaySegment-  = DaySegment {-        dayPart :: DayPart  -- ^ The part of the day.-      , dayHour :: Int  -- ^ The number of hours, between @1@ and @12@ (both inclusive).-      }+data DaySegment = DaySegment+  { -- | The part of the day.+    dayPart :: DayPart,+    -- | The number of hours, between @1@ and @12@ (both inclusive).+    dayHour :: Int+  }   deriving (Data, Eq, Generic, Ord, Read, Show)  instance Arbitrary DaySegment where@@ -177,119 +217,159 @@ instance NFData DaySegment  -- | Convert the given number of minutes to the corresponding 'ClockSegment'.-toClockSegment-  :: Int  -- ^ The number of minutes.-  -> ClockSegment  -- ^ The corresponding 'ClockSegment'.+toClockSegment ::+  -- | The number of minutes.+  Int ->+  -- | The corresponding 'ClockSegment'.+  ClockSegment toClockSegment 0 = OClock toClockSegment 15 = QuarterPast toClockSegment 30 = Half toClockSegment 45 = QuarterTo toClockSegment n-    | n <= 15 = Past n-    | n <= 30 = ToHalf (30-n)-    | n <= 45 = PastHalf (n-30)-    | otherwise = To (60-n)+  | n <= 15 = Past n+  | n <= 30 = ToHalf (30 - n)+  | n <= 45 = PastHalf (n - 30)+  | otherwise = To (60 - n)  -- | Convert the given number of hours to the corresponding 'DayPart'.-toDayPart-  :: Int  -- ^ The given number of hours.-  -> DayPart  -- ^ The corresponding 'DayPart'.+toDayPart ::+  -- | The given number of hours.+  Int ->+  -- | The corresponding 'DayPart'.+  DayPart toDayPart n-    | n <= 5 = Night-    | n <= 11 = Morning-    | n <= 17 = Afternoon-    | otherwise = Evening+  | n <= 5 = Night+  | n <= 11 = Morning+  | n <= 17 = Afternoon+  | otherwise = Evening  -- | Convert the given number of hours to the corresponding 'DaySegment'.-toDaySegment-  :: Int  -- ^ The given number of hours.-  -> DaySegment  -- ^ The corresponding 'DaySegment'.+toDaySegment ::+  -- | The given number of hours.+  Int ->+  -- | The corresponding 'DaySegment'.+  DaySegment toDaySegment n = DaySegment (toDayPart n) (hourCorrection n)  -- | Correct the hour to a 12 number segment. -- The input can be any Int number, whereas the -- result will be in the @1 .. 12@ range.-hourCorrection-  :: Int  -- ^ The value for the number of hours.-  -> Int  -- ^ The hours in the @1 .. 12@ range.+hourCorrection ::+  -- | The value for the number of hours.+  Int ->+  -- | The hours in the @1 .. 12@ range.+  Int hourCorrection h = ((h - 1) `mod` 12) + 1  instance Default NumberType where-    def = Cardinal+  def = Cardinal  -- | A type class used for num to word algorithms. It maps an 'Integral' type -- @i@ to 'Text'. class NumToWord a where-    -- | Convert the given number to a 'Text' object that is the given number in-    -- words in /cardinal/ form.-    toCardinal :: Integral i-      => a  -- ^ The conversion algorithm that transforms the number into words.-      -> i  -- ^ The number to transform into a /cardinal/ form.-      -> Text  -- ^ The number in words in a /cardinal/ form.-    toCardinal = toWords Cardinal+  -- | Convert the given number to a 'Text' object that is the given number in+  -- words in /cardinal/ form.+  toCardinal ::+    Integral i =>+    -- | The conversion algorithm that transforms the number into words.+    a ->+    -- | The number to transform into a /cardinal/ form.+    i ->+    -- | The number in words in a /cardinal/ form.+    Text+  toCardinal = toWords Cardinal -    -- | Convert the given number to a 'Text' object that is the given number in-    -- words in /cardinal/ form.-    toOrdinal :: Integral i-      => a  -- ^ The conversion algorithm that transforms the number into words.-      -> i  -- ^ The number to transform into a /ordinal/ form.-      -> Text  -- ^ The number in words in a /ordinal/ form.-    toOrdinal = toWords Ordinal+  -- | Convert the given number to a 'Text' object that is the given number in+  -- words in /cardinal/ form.+  toOrdinal ::+    Integral i =>+    -- | The conversion algorithm that transforms the number into words.+    a ->+    -- | The number to transform into a /ordinal/ form.+    i ->+    -- | The number in words in a /ordinal/ form.+    Text+  toOrdinal = toWords Ordinal -    -- | Convert the given number to a 'Text' object that is the given number-    -- in words in /short cardinal/ form.-    toShortOrdinal :: Integral i-      => a  -- ^ The conversion algorithm that transforms the number into words.-      -> i  -- ^ The number to transform into a /ordinal/ form.-      -> Text  -- ^ The number in words in a /ordinal/ form.-    toShortOrdinal = toWords Ordinal+  -- | Convert the given number to a 'Text' object that is the given number+  -- in words in /short cardinal/ form.+  toShortOrdinal ::+    Integral i =>+    -- | The conversion algorithm that transforms the number into words.+    a ->+    -- | The number to transform into a /ordinal/ form.+    i ->+    -- | The number in words in a /ordinal/ form.+    Text+  toShortOrdinal = toWords Ordinal -    -- | Convert the given number to a 'Text' object that is the given number in-    -- words in the given 'NumberType'.-    toWords :: Integral i-      => NumberType  -- ^ The given format to convert the number to.-      -> a  -- ^ The conversion algorithm that transforms the number into words.-      -> i  -- ^ The number to transform into the given form.-      -> Text  -- ^ The number in words in the given form.-    toWords Cardinal = toCardinal-    toWords Ordinal = toOrdinal-    toWords ShortOrdinal = toShortOrdinal+  -- | Convert the given number to a 'Text' object that is the given number in+  -- words in the given 'NumberType'.+  toWords ::+    Integral i =>+    -- | The given format to convert the number to.+    NumberType ->+    -- | The conversion algorithm that transforms the number into words.+    a ->+    -- | The number to transform into the given form.+    i ->+    -- | The number in words in the given form.+    Text+  toWords Cardinal = toCardinal+  toWords Ordinal = toOrdinal+  toWords ShortOrdinal = toShortOrdinal -    -- | Convert the given time of the day to text describing that time.-    toTimeText-      :: a  -- ^ The conversion algorithm to transform numbers into words.-      -> TimeOfDay  -- ^ The time of the day to convert to words.-      -> Text  -- ^ The time as /text/.-    toTimeText gen (TimeOfDay h m _) = toTimeText' gen h m+  -- | Convert the given time of the day to text describing that time.+  toTimeText ::+    -- | The conversion algorithm to transform numbers into words.+    a ->+    -- | The time of the day to convert to words.+    TimeOfDay ->+    -- | The time as /text/.+    Text+  toTimeText gen (TimeOfDay h m _) = toTimeText' gen h m -    -- | Convert the given hours and minutes to text that describes the time.-    toTimeText'-      :: a  -- ^ The conversion algorithm to transform numbers into words.-      -> Int  -- ^ The number of hours, between 0 and 23 (both inclusive)-      -> Int  -- ^ The number of minutes, beween 0 and 59 (both inclusive)-      -> Text  -- ^ The time as /text/.-    toTimeText' gen h m = toTimeText gen (TimeOfDay h m 0)-    {-# MINIMAL ((toCardinal, toOrdinal, toShortOrdinal) | toWords), (toTimeText | toTimeText') #-}+  -- | Convert the given hours and minutes to text that describes the time.+  toTimeText' ::+    -- | The conversion algorithm to transform numbers into words.+    a ->+    -- | The number of hours, between 0 and 23 (both inclusive)+    Int ->+    -- | The number of minutes, beween 0 and 59 (both inclusive)+    Int ->+    -- | The time as /text/.+    Text+  toTimeText' gen h m = toTimeText gen (TimeOfDay h m 0) +  {-# MINIMAL ((toCardinal, toOrdinal, toShortOrdinal) | toWords), (toTimeText | toTimeText') #-}+ -- | Convert the current time in the given 'TimeZone' to the time in words with the given 'NumToWord' -- algorithm.-currentTimeText :: NumToWord a-  => TimeZone -- ^ The given 'TimeZone'.-  -> a  -- ^ The 'NumToWord' algorithm that converts time to words.-  -> IO Text  -- ^ An 'IO' that will generate a 'Text' object that describes the current time in words.+currentTimeText ::+  NumToWord a =>+  -- | The given 'TimeZone'.+  TimeZone ->+  -- | The 'NumToWord' algorithm that converts time to words.+  a ->+  -- | An 'IO' that will generate a 'Text' object that describes the current time in words.+  IO Text currentTimeText tz alg = toTimeText alg . snd . utcToLocalTimeOfDay tz . timeToTimeOfDay . utctDayTime <$> getCurrentTime  -- | Convert the current time to the time in words with the given 'NumToWord' -- algorithm as UTC time.-currentTimeText' :: NumToWord a-  => a  -- ^ The 'NumToWord' algorithm that converts time to words.-  -> IO Text  -- ^ An 'IO' that will generate a 'Text' object that describes the current time in words.+currentTimeText' ::+  NumToWord a =>+  -- | The 'NumToWord' algorithm that converts time to words.+  a ->+  -- | An 'IO' that will generate a 'Text' object that describes the current time in words.+  IO Text currentTimeText' alg = toTimeText alg . timeToTimeOfDay . utctDayTime <$> getCurrentTime  -- | A type class used to split a value, based on the name of a number in a -- specific language. The value that is used to split, is often, depending on -- the language, the largest value smaller than the given number. class ValueSplit a where-    -- | A function that takes an 'Integral' value, and based on the object-    -- splits it with a value and the name of the number in a specific language.-    valueSplit :: a -> FreeValueSplitter+  -- | A function that takes an 'Integral' value, and based on the object+  -- splits it with a value and the name of the number in a specific language.+  valueSplit :: a -> FreeValueSplitter
src/Text/Numerals/Internal.hs view
@@ -1,46 +1,62 @@-{-# LANGUAGE CPP, Safe #-}--module Text.Numerals.Internal (-    _div10, _rem10, _divisableBy, _divisable100-  , _pluralize, _pluralize'-  , _showText-  , _mergeWith, _mergeWithSpace, _mergeWithHyphen, _mergeWith', _replaceSuffix-  , _hundred, _thousand, _million, _billion, _trillion-  , _iLog, _iLogFloor-  , _stripLastIf-  , _showIntegral-  , _showPositive-  , _genText, _shrinkText-  ) where+{-# LANGUAGE CPP #-}+{-# LANGUAGE Safe #-} -import Control.Applicative(liftA2)+module Text.Numerals.Internal+  ( _div10,+    _rem10,+    _divisableBy,+    _divisable100,+    _pluralize,+    _pluralize',+    _showText,+    _mergeWith,+    _mergeWithSpace,+    _mergeWithHyphen,+    _mergeWith',+    _replaceSuffix,+    _hundred,+    _thousand,+    _million,+    _billion,+    _trillion,+    _iLog,+    _iLogFloor,+    _stripLastIf,+    _showIntegral,+    _showPositive,+    _genText,+    _shrinkText,+  )+where -import Data.Char(intToDigit)+import Control.Applicative (liftA2)+import Data.Char (intToDigit) #if __GLASGOW_HASKELL__ < 803 import Data.Semigroup((<>)) #endif-import Data.Text(Text, cons, dropEnd, inits, isSuffixOf, singleton, tails, pack)+import Data.Text (Text, cons, dropEnd, inits, isSuffixOf, pack, singleton, tails) import qualified Data.Text as T--import Test.QuickCheck(listOf)-import Test.QuickCheck.Arbitrary(Arbitrary(arbitrary))-import Test.QuickCheck.Gen(Gen)+import Test.QuickCheck (listOf)+import Test.QuickCheck.Arbitrary (Arbitrary (arbitrary))+import Test.QuickCheck.Gen (Gen)  _pluralize :: a -> a -> Int -> a _pluralize sing plur = go-    where go 1 = sing-          go (-1) = sing-          go _ = plur+  where+    go 1 = sing+    go (-1) = sing+    go _ = plur  _pluralize' :: a -> a -> Int -> a _pluralize' sing plur = go-    where go 1 = sing-          go _ = plur+  where+    go 1 = sing+    go _ = plur  _stripLastIf :: Char -> Text -> Text _stripLastIf c t-    | singleton c `isSuffixOf` t = T.init t-    | otherwise = t+  | singleton c `isSuffixOf` t = T.init t+  | otherwise = t  _mergeWith' :: Char -> Text -> Text -> Text _mergeWith' m = (. cons m) . (<>)@@ -89,35 +105,43 @@  _iLogFloor :: (Integral i, Integral j) => i -> i -> (i, j, i) _iLogFloor b m = go b-  where go i | m < i = (m, 0, 1)-             | q < i = (q, 2 * e, j)-             | otherwise = (div q i, 2 * e + 1, j * i)-            where ~(q, e, j) = go (i*i)+  where+    go i+      | m < i = (m, 0, 1)+      | q < i = (q, 2 * e, j)+      | otherwise = (div q i, 2 * e + 1, j * i)+      where+        ~(q, e, j) = go (i * i)  _iLog :: (Integral i, Integral j) => i -> i -> Maybe j _iLog b m = snd <$> go b-  where go i | m < i = Just (m, 0)-             | Just (q, e) <- go (i*i) = go' i q e-             | otherwise = Nothing-        go' i q e | q < i = Just (q, 2 * e)-                  | md == 0 = Just (d, 2 * e + 1)-                  | otherwise = Nothing-            where (d, md) = divMod q i+  where+    go i+      | m < i = Just (m, 0)+      | Just (q, e) <- go (i * i) = go' i q e+      | otherwise = Nothing+    go' i q e+      | q < i = Just (q, 2 * e)+      | md == 0 = Just (d, 2 * e + 1)+      | otherwise = Nothing+      where+        (d, md) = divMod q i  _replaceSuffix :: Int -> Text -> Text -> Text _replaceSuffix n s = (<> s) . dropEnd n  _showIntegral :: Integral i => i -> String -> String _showIntegral n s-    | n < 0 = '-' : _showPositive (-(fromIntegral n :: Integer)) s-    | otherwise = _showPositive n s+  | n < 0 = '-' : _showPositive (-(fromIntegral n :: Integer)) s+  | otherwise = _showPositive n s  _showPositive :: Integral i => i -> String -> String _showPositive n s-    | q == 0 = tl-    | otherwise = _showPositive q tl-    where (q, r) = quotRem n 10-          tl = intToDigit (fromIntegral r) : s+  | q == 0 = tl+  | otherwise = _showPositive q tl+  where+    (q, r) = quotRem n 10+    tl = intToDigit (fromIntegral r) : s  _genText :: Gen Text _genText = pack <$> listOf arbitrary
src/Text/Numerals/Languages.hs view
@@ -1,18 +1,20 @@-{-|-Module      : Text.Numerals.Languages-Description : A module that re-exports the algorithms to convert numbers to words in the supported languages.-Maintainer  : hapytexeu+gh@gmail.com-Stability   : experimental-Portability : POSIX--This module imports the /num to word/ algorithms and re-exports these algorithms. The module thus can be used to conveniently import algorithms for all supported languages.--}--module Text.Numerals.Languages (-    dutch, english, french, german-  ) where+-- |+-- Module      : Text.Numerals.Languages+-- Description : A module that re-exports the algorithms to convert numbers to words in the supported languages.+-- Maintainer  : hapytexeu+gh@gmail.com+-- Stability   : experimental+-- Portability : POSIX+--+-- This module imports the /num to word/ algorithms and re-exports these algorithms. The module thus can be used to conveniently import algorithms for all supported languages.+module Text.Numerals.Languages+  ( dutch,+    english,+    french,+    german,+  )+where -import Text.Numerals.Languages.Dutch(dutch)-import Text.Numerals.Languages.English(english)-import Text.Numerals.Languages.French(french)-import Text.Numerals.Languages.German(german)+import Text.Numerals.Languages.Dutch (dutch)+import Text.Numerals.Languages.English (english)+import Text.Numerals.Languages.French (french)+import Text.Numerals.Languages.German (german)
src/Text/Numerals/Languages/Dutch.hs view
@@ -1,61 +1,76 @@-{-# LANGUAGE CPP, OverloadedLists, OverloadedStrings, TemplateHaskell #-}--{-|-Module      : Text.Numerals.Languages.Dutch-Description : A module to convert numbers to words in the /Dutch/ language.-Maintainer  : hapytexeu+gh@gmail.com-Stability   : experimental-Portability : POSIX+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-} -This module contains logic to convert numbers to words in the /Dutch/ language.--}+-- |+-- Module      : Text.Numerals.Languages.Dutch+-- Description : A module to convert numbers to words in the /Dutch/ language.+-- Maintainer  : hapytexeu+gh@gmail.com+-- Stability   : experimental+-- Portability : POSIX+--+-- This module contains logic to convert numbers to words in the /Dutch/ language.+module Text.Numerals.Languages.Dutch+  ( -- * Num to word algorithm+    dutch, -module Text.Numerals.Languages.Dutch (-    -- * Num to word algorithm-    dutch     -- * Convert a cardinal number to text-  , toCardinal'+    toCardinal',+     -- * Convert to ordinal-  , ordinize'+    ordinize',+     -- * Constant words-  , negativeWord', zeroWord', oneWord'+    negativeWord',+    zeroWord',+    oneWord',+     -- * Names for numbers-  , lowWords', midWords', highWords'+    lowWords',+    midWords',+    highWords',+     -- * Merge function-  , merge'-  ) where+    merge',+  )+where  #if __GLASGOW_HASKELL__ < 803 import Data.Semigroup((<>)) #endif-import Data.Text(Text, pack, snoc)-import Data.Vector(Vector)--import Text.Numerals.Algorithm(HighNumberAlgorithm(LongScale), NumeralsAlgorithm, numeralsAlgorithm)-import Text.Numerals.Algorithm.Template(ordinizeFromDict)-import Text.Numerals.Class(ClockSegment(OClock, Past, QuarterPast, ToHalf, Half, PastHalf, QuarterTo, To), DayPart(Night, Morning, Afternoon, Evening), DaySegment(dayPart, dayHour), ClockText, hourCorrection, toCardinal, valueSplit)-import Text.Numerals.Internal(_million, _mergeWithSpace, _pluralize', _showIntegral)+import Data.Text (Text, pack, snoc)+import Data.Vector (Vector)+import Text.Numerals.Algorithm (HighNumberAlgorithm (LongScale), NumeralsAlgorithm, numeralsAlgorithm)+import Text.Numerals.Algorithm.Template (ordinizeFromDict)+import Text.Numerals.Class (ClockSegment (Half, OClock, Past, PastHalf, QuarterPast, QuarterTo, To, ToHalf), ClockText, DayPart (Afternoon, Evening, Morning, Night), DaySegment (dayHour, dayPart), hourCorrection, toCardinal, valueSplit)+import Text.Numerals.Internal (_mergeWithSpace, _million, _pluralize', _showIntegral) -$(pure (ordinizeFromDict "_ordinize'" [-    ("nul", "nuld")-  , ("één", "eerst")-  , ("twee", "tweed")-  , ("drie", "derd")-  , ("vier", "vierd")-  , ("vijf", "vijfd")-  , ("zes", "zesd")-  , ("zeven", "zevend")-  , ("acht", "achtst")-  , ("negen", "negend")-  , ("tien", "tiend")-  , ("elf", "elfd")-  , ("twaalf", "twaalfd")-  , ("ig", "igst")-  , ("erd", "erdst")-  , ("end", "endst")-  , ("joen", "joenst")-  , ("rd", "rdst")-  ] 'id))+$( pure+     ( ordinizeFromDict+         "_ordinize'"+         [ ("nul", "nuld"),+           ("één", "eerst"),+           ("twee", "tweed"),+           ("drie", "derd"),+           ("vier", "vierd"),+           ("vijf", "vijfd"),+           ("zes", "zesd"),+           ("zeven", "zevend"),+           ("acht", "achtst"),+           ("negen", "negend"),+           ("tien", "tiend"),+           ("elf", "elfd"),+           ("twaalf", "twaalfd"),+           ("ig", "igst"),+           ("erd", "erdst"),+           ("end", "endst"),+           ("joen", "joenst"),+           ("rd", "rdst")+         ]+         'id+     )+ )  -- | A function that converts a number in words in /cardinal/ form to /ordinal/ -- form according to the /Dutch/ language rules.@@ -63,13 +78,18 @@ ordinize' = (`snoc` 'e') . _ordinize'  -- | A 'NumeralsAlgorithm' to convert numbers to words in the /Dutch/ language.-dutch :: NumeralsAlgorithm  -- ^ A 'NumeralsAlgorithm' that can be used to convert numbers to different formats.+dutch ::+  -- | A 'NumeralsAlgorithm' that can be used to convert numbers to different formats.+  NumeralsAlgorithm dutch = numeralsAlgorithm negativeWord' zeroWord' oneWord' lowWords' midWords' (valueSplit highWords') merge' ordinize' shortOrdinal' clockText'  -- | Convert numers to their cardinal counterpart in /Dutch/.-toCardinal' :: Integral i-  => i  -- ^ The number to convert to text.-  -> Text  -- ^ The cardinal counterpart in /Dutch/.+toCardinal' ::+  Integral i =>+  -- | The number to convert to text.+  i ->+  -- | The cardinal counterpart in /Dutch/.+  Text toCardinal' = toCardinal dutch  -- | The words used to mark a negative number in the /Dutch/ language.@@ -86,41 +106,41 @@  -- | A 'Vector' that contains the word used for the numbers /two/ to /twenty/ in the /Dutch/ language. lowWords' :: Vector Text-lowWords' = [-    "twee"-  , "drie"-  , "vier"-  , "vijf"-  , "zes"-  , "zeven"-  , "acht"-  , "negen"-  , "tien"-  , "elf"-  , "twaalf"-  , "dertien"-  , "veertien"-  , "vijftien"-  , "zestien"-  , "zeventien"-  , "achttien"-  , "negentien"-  , "twintig"+lowWords' =+  [ "twee",+    "drie",+    "vier",+    "vijf",+    "zes",+    "zeven",+    "acht",+    "negen",+    "tien",+    "elf",+    "twaalf",+    "dertien",+    "veertien",+    "vijftien",+    "zestien",+    "zeventien",+    "achttien",+    "negentien",+    "twintig"   ]  -- | A list of 2-tuples that contains the names of values between /thirty/ and -- /thousand/ in the /Dutch/ language. midWords' :: [(Integer, Text)]-midWords' = [-    (1000, "duizend")-  , (100, "honderd")-  , (90, "negentig")-  , (80, "tachtig")-  , (70, "zeventig")-  , (60, "zestig")-  , (50, "vijftig")-  , (40, "veertig")-  , (30, "dertig")+midWords' =+  [ (1000, "duizend"),+    (100, "honderd"),+    (90, "negentig"),+    (80, "tachtig"),+    (70, "zeventig"),+    (60, "zestig"),+    (50, "vijftig"),+    (40, "veertig"),+    (30, "dertig")   ]  _rightAnd :: Integral i => i -> Text -> Text@@ -129,27 +149,30 @@  _leftAnd :: Integral i => i -> Text -> Text _leftAnd 1 = const "eenen"-_leftAnd n | 2 <- n = addE-           | 3 <- n = addE-           | otherwise = (<> "en")-           where addE = (<> "ën")+_leftAnd n+  | 2 <- n = addE+  | 3 <- n = addE+  | otherwise = (<> "en")+  where+    addE = (<> "ën")  -- | A merge function that is used to combine the names of words together to -- larger words, according to the /Dutch/ grammar rules. merge' :: Integral i => i -> i -> Text -> Text -> Text merge' 1 r-    | r < _million = const id-    | otherwise = const (_merge' 1 r "een")+  | r < _million = const id+  | otherwise = const (_merge' 1 r "een") merge' l r = _merge' l r  _merge' :: Integral i => i -> i -> Text -> Text -> Text _merge' l r-    | r > l && r >= _million = _mergeWithSpace-    | r > l = (<>)-    | r < 10 && 10 < l && l < 100 = go-    | l >= _million = _mergeWithSpace-    | otherwise = (<>)-    where go tl tr = _leftAnd r tr <> _rightAnd l tl+  | r > l && r >= _million = _mergeWithSpace+  | r > l = (<>)+  | r < 10 && 10 < l && l < 100 = go+  | l >= _million = _mergeWithSpace+  | otherwise = (<>)+  where+    go tl tr = _leftAnd r tr <> _rightAnd l tl  -- | An algorithm to obtain the names of /large/ numbers (one million or larger) -- in /Dutch/. Dutch uses a /long scale/ with the @iljoen@ and @iljard@@@ -158,9 +181,12 @@ highWords' = LongScale "iljoen" "iljard"  -- | A function to convert a number to its /short ordinal/ form in /Dutch/.-shortOrdinal' :: Integral i-  => i  -- ^ The number to convert to /short ordinal/ form.-  -> Text  -- ^ The equivalent 'Text' specifying the number in /short ordinal/ form.+shortOrdinal' ::+  Integral i =>+  -- | The number to convert to /short ordinal/ form.+  i ->+  -- | The equivalent 'Text' specifying the number in /short ordinal/ form.+  Text shortOrdinal' = pack . (`_showIntegral` "e")  _dayPartText :: DayPart -> Text@@ -182,9 +208,9 @@ clockText' :: ClockText clockText' OClock ds _ _ = _dayComponent " uur " 0 ds clockText' (Past m) ds _ _ = toCardinal' m <> _mins m <> "na " <> _dayComponent' 0 ds-clockText' QuarterPast ds _ _ = "kwart na "  <> _dayComponent' 0 ds+clockText' QuarterPast ds _ _ = "kwart na " <> _dayComponent' 0 ds clockText' (ToHalf m) ds _ _ = toCardinal' m <> _mins m <> "voor half " <> _dayComponent' 1 ds clockText' Half ds _ _ = "half " <> _dayComponent' 1 ds clockText' (PastHalf m) ds _ _ = toCardinal' m <> _mins m <> "na half " <> _dayComponent' 1 ds-clockText' QuarterTo ds _ _ = "kwart voor "  <> _dayComponent' 1 ds+clockText' QuarterTo ds _ _ = "kwart voor " <> _dayComponent' 1 ds clockText' (To m) ds _ _ = toCardinal' m <> _mins m <> "voor " <> _dayComponent' 1 ds
src/Text/Numerals/Languages/English.hs view
@@ -1,77 +1,97 @@-{-# LANGUAGE CPP, OverloadedLists, OverloadedStrings, TemplateHaskell #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -Wno-orphans #-} -{-|-Module      : Text.Numerals.Languages.English-Description : A module to convert numbers to words in the /English/ language.-Maintainer  : hapytexeu+gh@gmail.com-Stability   : experimental-Portability : POSIX--This module contains logic to convert numbers to words in the /English/ language.--}+-- |+-- Module      : Text.Numerals.Languages.English+-- Description : A module to convert numbers to words in the /English/ language.+-- Maintainer  : hapytexeu+gh@gmail.com+-- Stability   : experimental+-- Portability : POSIX+--+-- This module contains logic to convert numbers to words in the /English/ language.+module Text.Numerals.Languages.English+  ( -- * Num to word algorithm+    english, -module Text.Numerals.Languages.English (-    -- * Num to word algorithm-    english     -- * Convert a cardinal number to text-  , toCardinal'+    toCardinal',+     -- * Convert to ordinal-  , ordinize'+    ordinize',+     -- * Constant words-  , negativeWord', zeroWord', oneWord'+    negativeWord',+    zeroWord',+    oneWord',+     -- * Names for numbers-  , lowWords', midWords', highWords'+    lowWords',+    midWords',+    highWords',+     -- * Merge function-  , merge'-  ) where+    merge',+  )+where -import Data.Default.Class(Default(def))+import Data.Default.Class (Default (def)) #if __GLASGOW_HASKELL__ < 803 import Data.Semigroup((<>)) #endif-import Data.Text(Text, isSuffixOf, pack)+import Data.Text (Text, isSuffixOf, pack) import qualified Data.Text as T-import Data.Vector(Vector)--import Text.Numerals.Algorithm(HighNumberAlgorithm, NumeralsAlgorithm, numeralsAlgorithm)-import Text.Numerals.Algorithm.Template(ordinizeFromDict)-import Text.Numerals.Class(ClockSegment(OClock, Past, QuarterPast, ToHalf, Half, PastHalf, QuarterTo, To), DayPart(Night, Morning, Afternoon, Evening), DaySegment(dayPart, dayHour), ClockText, hourCorrection, valueSplit, toCardinal)-import Text.Numerals.Internal(_div10, _mergeWith, _mergeWithSpace, _mergeWithHyphen, _rem10, _showIntegral)+import Data.Vector (Vector)+import Text.Numerals.Algorithm (HighNumberAlgorithm, NumeralsAlgorithm, numeralsAlgorithm)+import Text.Numerals.Algorithm.Template (ordinizeFromDict)+import Text.Numerals.Class (ClockSegment (Half, OClock, Past, PastHalf, QuarterPast, QuarterTo, To, ToHalf), ClockText, DayPart (Afternoon, Evening, Morning, Night), DaySegment (dayHour, dayPart), hourCorrection, toCardinal, valueSplit)+import Text.Numerals.Internal (_div10, _mergeWith, _mergeWithHyphen, _mergeWithSpace, _rem10, _showIntegral)  _ordinizepp :: Text -> Text _ordinizepp t-    | "y" `isSuffixOf` t = T.init t <> "ieth"-    | otherwise = t <> "th"+  | "y" `isSuffixOf` t = T.init t <> "ieth"+  | otherwise = t <> "th"  -- | A function that converts a number in words in /cardinal/ form to /ordinal/ -- form according to the /English/ language rules.-$(pure (ordinizeFromDict "ordinize'" [-    ("one", "first")-  , ("two", "second")-  , ("three", "third")-  , ("four", "fourth")-  , ("five", "fifth")-  , ("six", "sixth")-  , ("seven", "seventh")-  , ("eight", "eighth")-  , ("nine", "ninth")-  , ("ten", "tenth")-  , ("eleven", "eleventh")-  , ("twelve", "twelfth")-  ] '_ordinizepp))+$( pure+     ( ordinizeFromDict+         "ordinize'"+         [ ("one", "first"),+           ("two", "second"),+           ("three", "third"),+           ("four", "fourth"),+           ("five", "fifth"),+           ("six", "sixth"),+           ("seven", "seventh"),+           ("eight", "eighth"),+           ("nine", "ninth"),+           ("ten", "tenth"),+           ("eleven", "eleventh"),+           ("twelve", "twelfth")+         ]+         '_ordinizepp+     )+ )  -- | A 'NumeralsAlgorithm' to convert numbers to words in the /English/ language.-english :: NumeralsAlgorithm  -- ^ A 'NumeralsAlgorithm' that can be used to convert numbers to different formats.+english ::+  -- | A 'NumeralsAlgorithm' that can be used to convert numbers to different formats.+  NumeralsAlgorithm english = numeralsAlgorithm negativeWord' zeroWord' oneWord' lowWords' midWords' (valueSplit highWords') merge' ordinize' shortOrdinal' clockText'  instance Default NumeralsAlgorithm where   def = english  -- | Convert numers to their cardinal counterpart in /English/.-toCardinal' :: Integral i-  => i  -- ^ The number to convert to text.-  -> Text  -- ^ The cardinal counterpart in /English/.+toCardinal' ::+  Integral i =>+  -- | The number to convert to text.+  i ->+  -- | The cardinal counterpart in /English/.+  Text toCardinal' = toCardinal english  -- | The words used to mark a negative number in the /English/ language.@@ -88,50 +108,51 @@  -- | A 'Vector' that contains the word used for the numbers /two/ to /twenty/ in the /English/ language. lowWords' :: Vector Text-lowWords' = [-    "two"-  , "three"-  , "four"-  , "five"-  , "six"-  , "seven"-  , "eight"-  , "nine"-  , "ten"-  , "eleven"-  , "twelve"-  , "thirteen"-  , "fourteen"-  , "fifteen"-  , "sixteen"-  , "seventeen"-  , "eighteen"-  , "nineteen"-  , "twenty"+lowWords' =+  [ "two",+    "three",+    "four",+    "five",+    "six",+    "seven",+    "eight",+    "nine",+    "ten",+    "eleven",+    "twelve",+    "thirteen",+    "fourteen",+    "fifteen",+    "sixteen",+    "seventeen",+    "eighteen",+    "nineteen",+    "twenty"   ]  -- | A list of 2-tuples that contains the names of values between /thirty/ and -- /thousand/ in the /English/ language. midWords' :: [(Integer, Text)]-midWords' = [-    (1000, "thousand")-  , (100, "hundred")-  , (90, "ninety")-  , (80, "eighty")-  , (70, "seventy")-  , (60, "sixty")-  , (50, "fifty")-  , (40, "forty")-  , (30, "thirty")+midWords' =+  [ (1000, "thousand"),+    (100, "hundred"),+    (90, "ninety"),+    (80, "eighty"),+    (70, "seventy"),+    (60, "sixty"),+    (50, "fifty"),+    (40, "forty"),+    (30, "thirty")   ]  -- | A merge function that is used to combine the names of words together to -- larger words, according to the /English/ grammar rules. merge' :: Integral i => i -> i -> Text -> Text -> Text merge' 1 r | r < 100 = const id-merge' l r | 100 > l && l > r = _mergeWithHyphen-           | l >= 100 && 100 > r = _mergeWith " and "-           | r > l = _mergeWithSpace+merge' l r+  | 100 > l && l > r = _mergeWithHyphen+  | l >= 100 && 100 > r = _mergeWith " and "+  | r > l = _mergeWithSpace merge' _ _ = _mergeWith ", "  -- | An algorithm to obtain the names of /large/ numbers (one million or larger)@@ -140,17 +161,21 @@ highWords' = def  -- | A function to convert a number to its /short ordinal/ form in /English/.-shortOrdinal' :: Integral i-  => i  -- ^ The number to convert to /short ordinal/ form.-  -> Text  -- ^ The equivalent 'Text' specifying the number in /short ordinal/ form.+shortOrdinal' ::+  Integral i =>+  -- | The number to convert to /short ordinal/ form.+  i ->+  -- | The equivalent 'Text' specifying the number in /short ordinal/ form.+  Text shortOrdinal' i = pack (_showIntegral i (_shortOrdinalSuffix i))-    where _shortOrdinalSuffix n-              | _rem10 (_div10 n) == 1 = "th"-              | otherwise = go' (_rem10 n)-          go' 1 = "st"-          go' 2 = "nd"-          go' 3 = "rd"-          go' _ = "th"+  where+    _shortOrdinalSuffix n+      | _rem10 (_div10 n) == 1 = "th"+      | otherwise = go' (_rem10 n)+    go' 1 = "st"+    go' 2 = "nd"+    go' 3 = "rd"+    go' _ = "th"  _dayPartText :: DayPart -> Text _dayPartText Night = "at night"@@ -168,9 +193,9 @@ clockText' :: ClockText clockText' OClock ds _ _ = _dayComponent " o'clock " 0 ds clockText' (Past m) ds _ _ = toCardinal' m <> " past " <> _dayComponent' 0 ds-clockText' QuarterPast ds _ _ = "quarter past "  <> _dayComponent' 0 ds+clockText' QuarterPast ds _ _ = "quarter past " <> _dayComponent' 0 ds clockText' (ToHalf _) ds _ m = toCardinal' m <> " past " <> _dayComponent' 0 ds clockText' Half ds _ _ = "half past " <> _dayComponent' 0 ds clockText' (PastHalf _) ds _ m = toCardinal' (60 - m) <> " to " <> _dayComponent' 1 ds-clockText' QuarterTo ds _ _ = "quarter to "  <> _dayComponent' 1 ds+clockText' QuarterTo ds _ _ = "quarter to " <> _dayComponent' 1 ds clockText' (To m) ds _ _ = toCardinal' m <> " to " <> _dayComponent' 1 ds
src/Text/Numerals/Languages/French.hs view
@@ -1,54 +1,74 @@-{-# LANGUAGE CPP, OverloadedLists, OverloadedStrings, TemplateHaskell #-}--{-|-Module      : Text.Numerals.Languages.French-Description : A module to convert numbers to words in the /French/ language.-Maintainer  : hapytexeu+gh@gmail.com-Stability   : experimental-Portability : POSIX+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-} -This module contains logic to convert numbers to words in the /French/ language.--}+-- |+-- Module      : Text.Numerals.Languages.French+-- Description : A module to convert numbers to words in the /French/ language.+-- Maintainer  : hapytexeu+gh@gmail.com+-- Stability   : experimental+-- Portability : POSIX+--+-- This module contains logic to convert numbers to words in the /French/ language.+module Text.Numerals.Languages.French+  ( -- * Num to word algorithm+    french, -module Text.Numerals.Languages.French (-    -- * Num to word algorithm-    french     -- * Convert a cardinal number to text-  , toCardinal'+    toCardinal',+     -- * Convert to ordinal-  , ordinize'+    ordinize',+     -- * Constant words-  , negativeWord', zeroWord', oneWord'+    negativeWord',+    zeroWord',+    oneWord',+     -- * Names for numbers-  , lowWords', midWords', highWords'+    lowWords',+    midWords',+    highWords',+     -- * Merge function-  , merge'-  ) where+    merge',+  )+where  #if __GLASGOW_HASKELL__ < 803 import Data.Semigroup((<>)) #endif-import Data.Text(Text, isSuffixOf, pack, snoc)-import Data.Vector(Vector)--import Text.Numerals.Algorithm(HighNumberAlgorithm(LongScale), NumeralsAlgorithm, numeralsAlgorithm)-import Text.Numerals.Algorithm.Template(ordinizeFromDict)-import Text.Numerals.Class(ClockSegment(OClock, QuarterPast, Half, QuarterTo), DayPart(Night, Morning, Afternoon, Evening), DaySegment(dayPart, dayHour), ClockText, FreeMergerFunction, valueSplit, toCardinal)-import Text.Numerals.Internal(_divisable100, _mergeWith, _mergeWithSpace, _mergeWithHyphen, _million, _pluralize', _showIntegral, _stripLastIf, _thousand)+import Data.Text (Text, isSuffixOf, pack, snoc)+import Data.Vector (Vector)+import Text.Numerals.Algorithm (HighNumberAlgorithm (LongScale), NumeralsAlgorithm, numeralsAlgorithm)+import Text.Numerals.Algorithm.Template (ordinizeFromDict)+import Text.Numerals.Class (ClockSegment (Half, OClock, QuarterPast, QuarterTo), ClockText, DayPart (Afternoon, Evening, Morning, Night), DaySegment (dayHour, dayPart), FreeMergerFunction, toCardinal, valueSplit)+import Text.Numerals.Internal (_divisable100, _mergeWith, _mergeWithHyphen, _mergeWithSpace, _million, _pluralize', _showIntegral, _stripLastIf, _thousand) -$(pure (ordinizeFromDict "_ordinize'" [-    ("cinq", "cinqu")-  , ("neuf", "neuv")-  ] 'id))+$( pure+     ( ordinizeFromDict+         "_ordinize'"+         [ ("cinq", "cinqu"),+           ("neuf", "neuv")+         ]+         'id+     )+ )  -- | A 'NumeralsAlgorithm' to convert numbers to words in the /French/ language.-french :: NumeralsAlgorithm  -- ^ A 'NumeralsAlgorithm' that can be used to convert numbers to different formats.+french ::+  -- | A 'NumeralsAlgorithm' that can be used to convert numbers to different formats.+  NumeralsAlgorithm french = numeralsAlgorithm negativeWord' zeroWord' oneWord' lowWords' midWords' (valueSplit highWords') merge' ordinize' shortOrdinal' clockText'  -- | Convert numers to their cardinal counterpart in /French/.-toCardinal' :: Integral i-  => i  -- ^ The number to convert to text.-  -> Text  -- ^ The cardinal counterpart in /French/.+toCardinal' ::+  Integral i =>+  -- | The number to convert to text.+  i ->+  -- | The cardinal counterpart in /French/.+  Text toCardinal' = toCardinal french  -- | The words used to mark a negative number in the /French/ language.@@ -65,62 +85,64 @@  -- | A 'Vector' that contains the word used for the numbers /two/ to /twenty/ in the /French/ language. lowWords' :: Vector Text-lowWords' = [-    "deux"-  , "trois"-  , "quatre"-  , "cinq"-  , "six"-  , "sept"-  , "huit"-  , "neuf"-  , "dix"-  , "onze"-  , "douze"-  , "treize"-  , "quatorze"-  , "quinze"-  , "seize"-  , "dix-sept"-  , "dix-huit"-  , "dix-neuf"-  , "vingt"+lowWords' =+  [ "deux",+    "trois",+    "quatre",+    "cinq",+    "six",+    "sept",+    "huit",+    "neuf",+    "dix",+    "onze",+    "douze",+    "treize",+    "quatorze",+    "quinze",+    "seize",+    "dix-sept",+    "dix-huit",+    "dix-neuf",+    "vingt"   ]  -- | A list of 2-tuples that contains the names of values between /thirty/ and -- /thousand/ in the /French/ language. midWords' :: [(Integer, Text)]-midWords' = [-    (1000, "mille")-  , (100, "cent")-  , (80, "quatre-vingts")-  , (60, "soixante")-  , (50, "cinquante")-  , (40, "quarante")-  , (30, "trente")+midWords' =+  [ (1000, "mille"),+    (100, "cent"),+    (80, "quatre-vingts"),+    (60, "soixante"),+    (50, "cinquante"),+    (40, "quarante"),+    (30, "trente")   ]  -- | A merge function that is used to combine the names of words together to -- larger words, according to the /French/ grammar rules. merge' :: FreeMergerFunction-merge' 1 r | r < _million = const id-           | otherwise = _merge' 1 r+merge' 1 r+  | r < _million = const id+  | otherwise = _merge' 1 r merge' l r = \ta tb -> _merge' l r (_firstWithoutS l r ta) (_secondWithS l r tb)  _firstWithoutS :: Integral i => i -> i -> Text -> Text _firstWithoutS l r t-    | (_divisable100 (l + 20) || (_divisable100 l && l < _thousand)) && r < _million = _stripLastIf 's' t-    | otherwise = t+  | (_divisable100 (l + 20) || (_divisable100 l && l < _thousand)) && r < _million = _stripLastIf 's' t+  | otherwise = t  _secondWithS :: Integral i => i -> i -> Text -> Text _secondWithS l r t-    | l < _thousand && r /= _thousand && _divisable100 r && not ("s" `isSuffixOf` t) = snoc t 's'-    | otherwise = t+  | l < _thousand && r /= _thousand && _divisable100 r && not ("s" `isSuffixOf` t) = snoc t 's'+  | otherwise = t  _merge' :: Integral i => i -> i -> Text -> Text -> Text-_merge' l r | r >= l || l >= 100 = _mergeWithSpace-            | r `mod` 10 == 1 && l /= 80 = _mergeWith " et "-            | otherwise = _mergeWithHyphen+_merge' l r+  | r >= l || l >= 100 = _mergeWithSpace+  | r `mod` 10 == 1 && l /= 80 = _mergeWith " et "+  | otherwise = _mergeWithHyphen  -- | A function that converts a number in words in /cardinal/ form to /ordinal/ -- form according to the /French/ language rules.@@ -132,12 +154,15 @@ -- in /French/. French uses a /long scale/ with the @illion@ and @illiard@ -- suffixes. highWords' :: HighNumberAlgorithm-highWords' =  LongScale "illion" "illiard"+highWords' = LongScale "illion" "illiard"  -- | A function to convert a number to its /short ordinal/ form in /French/.-shortOrdinal' :: Integral i-  => i  -- ^ The number to convert to /short ordinal/ form.-  -> Text  -- ^ The equivalent 'Text' specifying the number in /short ordinal/ form.+shortOrdinal' ::+  Integral i =>+  -- | The number to convert to /short ordinal/ form.+  i ->+  -- | The equivalent 'Text' specifying the number in /short ordinal/ form.+  Text shortOrdinal' = pack . (`_showIntegral` "e")  _dayPartText :: DayPart -> Text@@ -148,7 +173,8 @@  _heures :: Text -> Int -> DaySegment -> Text _heures sep dh ds = toCardinal' h <> _pluralize' "e heure" " heures" h <> sep <> _dayPartText (dayPart ds)-    where h = dayHour ds + dh+  where+    h = dayHour ds + dh  -- | Converting the time to a text that describes that time in /French/. clockText' :: ClockText@@ -159,5 +185,5 @@ clockText' Half ds _ _ = _heures " et demie" 0 ds clockText' QuarterTo ds _ _ = _heures " moins le quart" 1 ds clockText' _ ds _ m-    | m <= 30 = _heures (" " <> toCardinal' m) 0 ds-    | otherwise = _heures (" moins " <> toCardinal' (60 - m)) 1 ds+  | m <= 30 = _heures (" " <> toCardinal' m) 0 ds+  | otherwise = _heures (" moins " <> toCardinal' (60 - m)) 1 ds
src/Text/Numerals/Languages/German.hs view
@@ -1,65 +1,86 @@-{-# LANGUAGE CPP, OverloadedLists, OverloadedStrings, QuasiQuotes, TemplateHaskell #-}--{-|-Module      : Text.Numerals.Languages.German-Description : A module to convert numbers to words in the /German/ language.-Maintainer  : hapytexeu+gh@gmail.com-Stability   : experimental-Portability : POSIX+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-} -This module contains logic to convert numbers to words in the /German/ language.--}+-- |+-- Module      : Text.Numerals.Languages.German+-- Description : A module to convert numbers to words in the /German/ language.+-- Maintainer  : hapytexeu+gh@gmail.com+-- Stability   : experimental+-- Portability : POSIX+--+-- This module contains logic to convert numbers to words in the /German/ language.+module Text.Numerals.Languages.German+  ( -- * Num to word algorithm+    german, -module Text.Numerals.Languages.German (-    -- * Num to word algorithm-    german     -- * Convert a cardinal number to text-  , toCardinal'+    toCardinal',+     -- * Convert to ordinal-  , ordinize'+    ordinize',+     -- * Constant words-  , negativeWord', zeroWord', oneWord'+    negativeWord',+    zeroWord',+    oneWord',+     -- * Names for numbers-  , lowWords', midWords', highWords'+    lowWords',+    midWords',+    highWords',+     -- * Merge function-  , merge'-  ) where+    merge',+  )+where -import Data.Bool(bool)+import Data.Bool (bool) #if __GLASGOW_HASKELL__ < 803 import Data.Semigroup((<>)) #endif-import Data.Text(Text, isSuffixOf, pack, toLower, toTitle)-import Data.Vector(Vector)--import Text.Numerals.Algorithm(HighNumberAlgorithm(LongScale), NumeralsAlgorithm, numeralsAlgorithm, valueSplit')-import Text.Numerals.Algorithm.Template(ordinizeFromDict)-import Text.Numerals.Class(ClockText, FreeMergerFunction, toCardinal)-import Text.Numerals.Internal(_mergeWith, _mergeWithSpace, _million, _showIntegral)-import Text.RE.TDFA.Text(RE, SearchReplace, (*=~/), ed)+import Data.Text (Text, isSuffixOf, pack, toLower, toTitle)+import Data.Vector (Vector)+import Text.Numerals.Algorithm (HighNumberAlgorithm (LongScale), NumeralsAlgorithm, numeralsAlgorithm, valueSplit')+import Text.Numerals.Algorithm.Template (ordinizeFromDict)+import Text.Numerals.Class (ClockText, FreeMergerFunction, toCardinal)+import Text.Numerals.Internal (_mergeWith, _mergeWithSpace, _million, _showIntegral)+import Text.RE.TDFA.Text (RE, SearchReplace, ed, (*=~/)) -$(pure (ordinizeFromDict "_ordinize'" [-    ("eins", "ers")-  , ("drei", "drit")-  , ("acht", "ach")-  , ("sieben", "sieb")-  , ("ig", "igs")-  , ("ert", "erts")-  , ("end", "ends")-  , ("ion", "ions")-  , ("nen", "ns")-  , ("rde", "rds")-  , ("rden", "rds")-  ] 'id))+$( pure+     ( ordinizeFromDict+         "_ordinize'"+         [ ("eins", "ers"),+           ("drei", "drit"),+           ("acht", "ach"),+           ("sieben", "sieb"),+           ("ig", "igs"),+           ("ert", "erts"),+           ("end", "ends"),+           ("ion", "ions"),+           ("nen", "ns"),+           ("rde", "rds"),+           ("rden", "rds")+         ]+         'id+     )+ )  -- | A 'NumeralsAlgorithm' to convert numbers to words in the /German/ language.-german :: NumeralsAlgorithm  -- ^ A 'NumeralsAlgorithm' that can be used to convert numbers to different formats.+german ::+  -- | A 'NumeralsAlgorithm' that can be used to convert numbers to different formats.+  NumeralsAlgorithm german = numeralsAlgorithm negativeWord' zeroWord' oneWord' lowWords' midWords' (valueSplit' toTitle highWords') merge' ordinize' shortOrdinal' clockText'  -- | Convert numers to their cardinal counterpart in /German/.-toCardinal' :: Integral i-  => i  -- ^ The number to convert to text.-  -> Text  -- ^ The cardinal counterpart in /German/.+toCardinal' ::+  Integral i =>+  -- | The number to convert to text.+  i ->+  -- | The cardinal counterpart in /German/.+  Text toCardinal' = toCardinal german  -- | The words used to mark a negative number in the /German/ language.@@ -76,41 +97,41 @@  -- | A 'Vector' that contains the word used for the numbers /two/ to /twenty/ in the /German/ language. lowWords' :: Vector Text-lowWords' = [-    "zwei"-  , "drei"-  , "vier"-  , "fünf"-  , "sechs"-  , "sieben"-  , "acht"-  , "neun"-  , "zehn"-  , "elf"-  , "zwölf"-  , "dreizehn"-  , "vierzehn"-  , "fünfzehn"-  , "sechzehn"-  , "siebzehn"-  , "achtzehn"-  , "neunzehn"-  , "zwanzig"+lowWords' =+  [ "zwei",+    "drei",+    "vier",+    "fünf",+    "sechs",+    "sieben",+    "acht",+    "neun",+    "zehn",+    "elf",+    "zwölf",+    "dreizehn",+    "vierzehn",+    "fünfzehn",+    "sechzehn",+    "siebzehn",+    "achtzehn",+    "neunzehn",+    "zwanzig"   ]  -- | A list of 2-tuples that contains the names of values between /thirty/ and -- /thousand/ in the /German/ language. midWords' :: [(Integer, Text)]-midWords' = [-    (1000, "tausend")-  , (100, "hundert")-  , (90, "neunzig")-  , (80, "achtzig")-  , (70, "siebzig")-  , (60, "sechzig")-  , (50, "fünfzig")-  , (40, "vierzig")-  , (30, "dreißig")+midWords' =+  [ (1000, "tausend"),+    (100, "hundert"),+    (90, "neunzig"),+    (80, "achtzig"),+    (70, "siebzig"),+    (60, "sechzig"),+    (50, "fünfzig"),+    (40, "vierzig"),+    (30, "dreißig")   ]  -- | A merge function that is used to combine the names of words together to@@ -124,18 +145,18 @@  _pluralize :: Text -> Text _pluralize t-    | "e" `isSuffixOf` t = t <> "n"-    | otherwise = t <> "en"+  | "e" `isSuffixOf` t = t <> "n"+  | otherwise = t <> "en"  _merge' :: FreeMergerFunction _merge' l r-    | r > l && r >= _million = (. bool id _pluralize (l > 1)) . _mergeWithSpace-    | r > l = (<>)-_merge' l 1 | 10 < l && l < 100 = const . ("einund" <> )+  | r > l && r >= _million = (. bool id _pluralize (l > 1)) . _mergeWithSpace+  | r > l = (<>)+_merge' l 1 | 10 < l && l < 100 = const . ("einund" <>) _merge' l r-    | r < 10 && 10 < l && l < 100 = flip (_mergeWith "und")-    | l >= _million = _mergeWithSpace-    | otherwise = (<>)+  | r < 10 && 10 < l && l < 100 = flip (_mergeWith "und")+  | l >= _million = _mergeWithSpace+  | otherwise = (<>)  _ordinalSuffixRe :: SearchReplace RE Text _ordinalSuffixRe = [ed|(eine)? ([a-z]+(illion|illiard)ste)$///${2}|]@@ -144,20 +165,24 @@ -- form according to the /German/ language rules. ordinize' :: Text -> Text ordinize' = postprocess . (<> "te") . _ordinize' . toLower-    where postprocess "eintausendste" = "tausendste"-          postprocess "einhundertste" = "hundertste"-          postprocess t = t *=~/ _ordinalSuffixRe+  where+    postprocess "eintausendste" = "tausendste"+    postprocess "einhundertste" = "hundertste"+    postprocess t = t *=~/ _ordinalSuffixRe  -- | An algorithm to obtain the names of /large/ numbers (one million or larger) -- in /German/. German uses a /long scale/ with the @illion@ and @illiard@ -- suffixes. highWords' :: HighNumberAlgorithm-highWords' =  LongScale "illion" "illiarde"+highWords' = LongScale "illion" "illiarde"  -- | A function to convert a number to its /short ordinal/ form in /German/.-shortOrdinal' :: Integral i-  => i  -- ^ The number to convert to /short ordinal/ form.-  -> Text  -- ^ The equivalent 'Text' specifying the number in /short ordinal/ form.+shortOrdinal' ::+  Integral i =>+  -- | The number to convert to /short ordinal/ form.+  i ->+  -- | The equivalent 'Text' specifying the number in /short ordinal/ form.+  Text shortOrdinal' = pack . (`_showIntegral` ".")  -- | Converting the time to a text that describes that time in /German/.
src/Text/Numerals/Prefix.hs view
@@ -1,75 +1,83 @@ {-# LANGUAGE OverloadedStrings #-} -{-|-Module      : Text.Numerals.Prefix-Description : A module used to define /numeric prefixes/ for /long/ and /short scales/.-Maintainer  : hapytexeu+gh@gmail.com-Stability   : experimental-Portability : POSIX--A module that defines /Latin/ prefixes. These prefixes are used to construct names for the /long/ and /short scales/.-So the /m/, /b/, /tr/ in /million/, /billion/, /trillion/.--}--module Text.Numerals.Prefix (-    -- * Latin prefixes-    latinPrefixes, latinPrefixes', latinPrefix-  ) where+-- |+-- Module      : Text.Numerals.Prefix+-- Description : A module used to define /numeric prefixes/ for /long/ and /short scales/.+-- Maintainer  : hapytexeu+gh@gmail.com+-- Stability   : experimental+-- Portability : POSIX+--+-- A module that defines /Latin/ prefixes. These prefixes are used to construct names for the /long/ and /short scales/.+-- So the /m/, /b/, /tr/ in /million/, /billion/, /trillion/.+module Text.Numerals.Prefix+  ( -- * Latin prefixes+    latinPrefixes,+    latinPrefixes',+    latinPrefix,+  )+where -import Data.Text(Text)-import Data.Vector(Vector, (!?), fromList)+import Data.Text (Text)+import Data.Vector (Vector, fromList, (!?))  -- | A list of /Latin/ prefixes, used for the /long/ and /short scale/.-latinPrefixes' :: [Text]  -- ^ A list of 'Text' objects. This makes explicit recursion more convenient.-latinPrefixes' = [-    "m"-  , "b"-  , "tr"-  , "quadr"-  , "quint"-  , "sext"-  , "sept"-  , "oct"-  , "non"-  , "dec"-  , "undec"-  , "duodec"-  , "tredec"-  , "quattuordec"-  , "quindec"-  , "sedec"-  , "septendec"-  , "octodec"-  , "novendec"-  , "vigint"-  , "unvigint"-  , "duovigint"-  , "tresvigint"-  , "quattuorvigint"-  , "quinvigint"-  , "sesvigint"-  , "septemvigint"-  , "octovigint"-  , "novemvigint"-  , "trigint"-  , "untrigint"-  , "duotrigint"-  , "trestrigint"-  , "quattuortrigint"-  , "quintrigint"-  , "sestrigint"-  , "septentrigint"-  , "octotrigint"-  , "noventrigint"-  , "quadragint"+latinPrefixes' ::+  -- | A list of 'Text' objects. This makes explicit recursion more convenient.+  [Text]+latinPrefixes' =+  [ "m",+    "b",+    "tr",+    "quadr",+    "quint",+    "sext",+    "sept",+    "oct",+    "non",+    "dec",+    "undec",+    "duodec",+    "tredec",+    "quattuordec",+    "quindec",+    "sedec",+    "septendec",+    "octodec",+    "novendec",+    "vigint",+    "unvigint",+    "duovigint",+    "tresvigint",+    "quattuorvigint",+    "quinvigint",+    "sesvigint",+    "septemvigint",+    "octovigint",+    "novemvigint",+    "trigint",+    "untrigint",+    "duotrigint",+    "trestrigint",+    "quattuortrigint",+    "quintrigint",+    "sestrigint",+    "septentrigint",+    "octotrigint",+    "noventrigint",+    "quadragint"   ]  -- | The /Latin/ prefixes in a 'Vector' for /O(1)/ lookup.-latinPrefixes :: Vector Text  -- ^ A 'Vector' of 'Text' objects to allow fast lookup.+latinPrefixes ::+  -- | A 'Vector' of 'Text' objects to allow fast lookup.+  Vector Text latinPrefixes = fromList latinPrefixes'  -- | Lookup the given /Latin/ prefix for the given value.-latinPrefix :: Integral i-  => i  -- ^ The value to map on a Latin prefix.-  -> Maybe Text  -- ^ The corresponding Latin prefix, given this exists.+latinPrefix ::+  Integral i =>+  -- | The value to map on a Latin prefix.+  i ->+  -- | The corresponding Latin prefix, given this exists.+  Maybe Text latinPrefix n = latinPrefixes !? (fromIntegral n - 1)