-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.AR.Helpers
  ( digitsMap
  , numeralToStringMap
  , parseArabicDoubleAsText
  , parseArabicDoubleFromText
  , parseArabicIntAsText
  , parseArabicIntegerFromText
  ) where

import Control.Monad (join)
import Data.HashMap.Strict (HashMap)
import Data.Maybe (mapMaybe)
import Data.String
import Data.Text (Text)
import Duckling.Numeral.Helpers
  ( parseDouble
  , parseInteger
  )
import Prelude

import qualified Data.HashMap.Strict as HashMap
import qualified Data.Text as Text

numeralToStringMap :: HashMap Char String
numeralToStringMap =
  HashMap.fromList
    [ ('Ù ', "0")
    , ('Ù¡', "1")
    , ('Ù¢', "2")
    , ('Ù£', "3")
    , ('Ù¤', "4")
    , ('Ù¥', "5")
    , ('Ù¦', "6")
    , ('Ù§', "7")
    , ('Ù¨', "8")
    , ('Ù©', "9")
    ]

digitsMap :: HashMap Text Integer
digitsMap =
  HashMap.fromList
    [ ("Ø¹Ø´Ø±", 2)
    , ("Ø«Ù„Ø§Ø«", 3)
    , ("Ø§Ø±Ø¨Ø¹", 4)
    , ("Ø£Ø±Ø¨Ø¹", 4)
    , ("Ø®Ù…Ø³", 5)
    , ("Ø³Øª", 6)
    , ("Ø³Ø¨Ø¹", 7)
    , ("Ø«Ù…Ø§Ù†", 8)
    , ("ØªØ³Ø¹", 9)
    ]

parseArabicIntAsText :: Text -> Text
parseArabicIntAsText =
  Text.pack
    . join
    . mapMaybe (`HashMap.lookup` numeralToStringMap)
    . Text.unpack

parseArabicIntegerFromText :: Text -> Maybe Integer
parseArabicIntegerFromText = parseInteger . parseArabicIntAsText

parseArabicDoubleAsText :: Text -> Text
parseArabicDoubleAsText =
  Text.pack
    . join
    . mapMaybe (`HashMap.lookup` HashMap.insert 'Ù«' "." numeralToStringMap)
    . Text.unpack

parseArabicDoubleFromText :: Text -> Maybe Double
parseArabicDoubleFromText = parseDouble . parseArabicDoubleAsText


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.AR.EG.Helpers
  ( digitsMap
  , numeralToStringMap
  , parseArabicDoubleAsText
  , parseArabicDoubleFromText
  , parseArabicIntAsText
  , parseArabicIntegerFromText
  ) where

import Control.Monad (join)
import Data.HashMap.Strict (HashMap)
import Data.Maybe (mapMaybe)
import Data.String
import Data.Text (Text)
import Duckling.Numeral.Helpers
  ( parseDouble
  , parseInteger
  )
import Prelude

import qualified Data.HashMap.Strict as HashMap
import qualified Data.Text as Text

numeralToStringMap :: HashMap Char String
numeralToStringMap =
  HashMap.fromList
    [ ('Ù ', "0")
    , ('Ù¡', "1")
    , ('Ù¢', "2")
    , ('Ù£', "3")
    , ('Ù¤', "4")
    , ('Ù¥', "5")
    , ('Ù¦', "6")
    , ('Ù§', "7")
    , ('Ù¨', "8")
    , ('Ù©', "9")
    ]

digitsMap :: HashMap Text Integer
digitsMap =
  HashMap.fromList
    [ ("Ø§ØªÙ†ÙŠÙ†", 2)
    , ("ØªÙ„Ø§Øª", 3)
    , ("Ø§Ø±Ø¨Ø¹", 4)
    , ("Ø£Ø±Ø¨Ø¹", 4)
    , ("Ø®Ù…Ø³", 5)
    , ("Ø³Øª", 6)
    , ("Ø³Ø¨Ø¹", 7)
    , ("ØªÙ…Ø§Ù†", 8)
    , ("ØªØ³Ø¹", 9)
    ]

parseArabicIntAsText :: Text -> Text
parseArabicIntAsText =
  Text.pack
    . join
    . mapMaybe (`HashMap.lookup` numeralToStringMap)
    . Text.unpack

parseArabicIntegerFromText :: Text -> Maybe Integer
parseArabicIntegerFromText = parseInteger . parseArabicIntAsText

parseArabicDoubleAsText :: Text -> Text
parseArabicDoubleAsText =
  Text.pack
    . join
    . mapMaybe (`HashMap.lookup` HashMap.insert 'Ù«' "." numeralToStringMap)
    . Text.unpack

parseArabicDoubleFromText :: Text -> Maybe Double
parseArabicDoubleFromText = parseDouble . parseArabicDoubleAsText


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE OverloadedStrings #-}

module Duckling.Numeral.CA.Corpus (corpus) where

import Data.String
import Prelude

import Duckling.Locale
import Duckling.Numeral.Types
import Duckling.Resolve
import Duckling.Testing.Types

corpus :: Corpus
corpus =
  (testContext { locale = makeLocale CA Nothing }, testOptions, allExamples)

allExamples :: [Example]
allExamples =
  concat
    [ examples (NumeralValue 1)
        [ "1"
        , "u"
        , "un"
        , "una"
        ]
    , examples (NumeralValue 11)
        [ "onze"
        ]
    , examples (NumeralValue 12)
        [ "dotze"
        ]
    , examples (NumeralValue 13)
        [ "tretze"
        ]
    , examples (NumeralValue 14)
        [ "catorze"
        ]
    , examples (NumeralValue 15)
        [ "quinze"
        ]
    , examples (NumeralValue 16)
        [ "setze"
        ]
    , examples (NumeralValue 17)
        [ "disset"
        , "dÃ¨sset"
        ]
    , examples (NumeralValue 18)
        [ "divuit"
        , "dihuit"
        , "devuit"
        ]
    , examples (NumeralValue 19)
        [ "dinou"
        , "dÃ¨nou"
        , "denou"
        ]
    , examples (NumeralValue 20)
        [ "vint"
        ]
    , examples (NumeralValue 21)
        [ "vint-i-un"
        , "vint i un"
        ]
    , examples (NumeralValue 22)
        [ "vint-i-dos"
        , "vint i dos"
        ]
    , examples (NumeralValue 23)
        [ "vint-i-tres"
        , "vint i tres"
        ]
    , examples (NumeralValue 37)
        [ "trenta-set"
        ]
    , examples (NumeralValue 40)
        [ "quaranta"
        ]
    , examples (NumeralValue 70)
        [ "setanta"
        ]
    , examples (NumeralValue 78)
        [ "Setanta-vuit"
        ]
    , examples (NumeralValue 80)
        [ "vuitanta"
        ]
    , examples (NumeralValue 33)
        [ "33"
        , "trenta-tres"
        , "trenta-3"
        ]
    , examples (NumeralValue 100000)
        [ "100000"
        , "100K"
        , "100k"
        ]
    , examples (NumeralValue 300)
        [ "tres-cents"
        ]
    , examples (NumeralValue 243)
        [ "243"
        ]
    , examples (NumeralValue 85)
        [ "vuitanta-cinc"
        ]
    , examples (NumeralValue 3000000)
        [ "3M"
        , "3000K"
        , "3000000"
        ]
    , examples (NumeralValue 1200000)
        [ "1200000"
        , "1200K"
        ]
    , examples (NumeralValue (-1200000))
        [ "-1200000"
        , "-1200K"
        ]
    , examples (NumeralValue 1.5)
        [ "1 coma cinc"
        , "una coma cinc"
        , "u coma cinc"
        ]
    , examples (NumeralValue 1)
        [ "zero u"
        , "zero un"
        ]
    , examples (NumeralValue 2)
        [ "zero dos"
        ]
    , examples (NumeralValue 3)
        [ "zero tres"
        ]
    , examples (NumeralValue 4)
        [ "zero quatre"
        ]
    , examples (NumeralValue 5)
        [ "zero cinc"
        ]
    , examples (NumeralValue 6)
        [ "zero sis"
        ]
    , examples (NumeralValue 7)
        [ "zero set"
        ]
    , examples (NumeralValue 8)
        [ "zero vuit"
        ]
    , examples (NumeralValue 9)
        [ "zero nou"
        ]
    ]

-- Ull, revisar la xifra amb decimals


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

module Duckling.Numeral.CA.Rules (rules) where

import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import Data.Maybe
import Data.String
import Data.Text (Text)
import qualified Data.Text as Text
import Prelude

import Duckling.Dimensions.Types
import Duckling.Numeral.Helpers
import Duckling.Numeral.Types (NumeralData(..))
import qualified Duckling.Numeral.Types as TNumeral
import Duckling.Regex.Types
import Duckling.Types

zeroToFifteenMap :: HashMap Text Integer
zeroToFifteenMap =
  HashMap.fromList
    [ ("zero", 0)
    , ("u", 1)
    , ("un", 1)
    , ("una", 1)
    , ("dos", 2)
    , ("dues", 2)
    , ("tres", 3)
    , ("quatre", 4)
    , ("cinc", 5)
    , ("sis", 6)
    , ("set", 7)
    , ("vuit", 8)
    , ("nou", 9)
    , ("deu", 10)
    , ("onze", 11)
    , ("dotze", 12)
    , ("tretze", 13)
    , ("catorze", 14)
    , ("quinze", 15)
    ]

ruleZeroToFifteen :: Rule
ruleZeroToFifteen = Rule
  { name = "number (0..15)"
  , pattern =
      [ regex
          "(zero|u(na|n)?|d(o|ue)s|tres|quatre|cinc|sis|set|vuit|nou|deu|onze|dotze|tretze|catorze|quinze)"
      ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) zeroToFifteenMap >>= integer
      _ -> Nothing
  }

ruleNumeralsPrefixWithNegativeOrMinus :: Rule
ruleNumeralsPrefixWithNegativeOrMinus = Rule
  { name = "numbers prefix with -, negative or minus"
  , pattern = [regex "-|menys", Predicate isPositive]
  , prod = \case
      (_ : Token Numeral NumeralData { TNumeral.value = v } : _) ->
        double $ negate v
      _ -> Nothing
  }

tensMap :: HashMap Text Integer
tensMap =
  HashMap.fromList
    [ ("vint", 20)
    , ("trenta", 30)
    , ("quaranta", 40)
    , ("cinquanta", 50)
    , ("seixanta", 60)
    , ("setanta", 70)
    , ("vuitanta", 80)
    , ("noranta", 90)
    ]

ruleTens :: Rule
ruleTens = Rule
  { name = "number (20..90)"
  , pattern =
      [ regex
          "(vint|(tre|quara|cinqua|seixa|seta|vuita|nora)nta)"
      ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)) : _) ->
        HashMap.lookup (Text.toLower match) tensMap >>= integer
      _ -> Nothing
  }

sixteenToTwentyNineMap :: HashMap Text Integer
sixteenToTwentyNineMap =
  HashMap.fromList
    [ ("setze", 16)
    , ("disset", 17)
    , ("dÃ¨sset", 17)
    , ("devuit", 18)
    , ("divuit", 18)
    , ("dihuit", 18)
    , ("dinou", 19)
    , ("dÃ¨nou", 19)
    , ("denou", 19)
    , ("vint-i-u", 21)
    , ("vint-i-una", 21)
    , ("vint-i-dos", 22)
    , ("vint-i-tres", 23)
    , ("vint-i-quatre", 24)
    , ("vint-i-cinc", 25)
    , ("vint-i-sis", 26)
    , ("vint-i-set", 27)
    , ("vint-i-vuit", 28)
    , ("vint-i-nou", 29)
    ]

ruleLowerTensWithOnes :: Rule
ruleLowerTensWithOnes = Rule
  { name = "number (16..19 21..29)"
  , pattern =
      [ regex
          "(setze|d(i|e|Ã¨)sset|d(e|i)(v|h)uit|d(i|e|Ã¨)nou|vint-i-u(na)?|vint-i-dos|vint-i-tres|vint-i-quatre|vint-i-cinc|vint-i-sis|vint-i-set|vint-i-vuit|vint-i-nou)"
      ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) sixteenToTwentyNineMap >>= integer
      _ -> Nothing
  }

ruleHigherTensWithOnes :: Rule
ruleHigherTensWithOnes = Rule
  { name = "number (31..39 41..49 51..59 61..69 71..79 81..89 91..99)"
  , pattern =
      [ oneOf [30, 40, 50, 60, 70, 80, 90]
      , regex "-"
      , Predicate $ numberBetween 1 9
      ]
  , prod = \case
      (Token Numeral NumeralData{TNumeral.value = v1}:
       _:
       Token Numeral NumeralData{TNumeral.value = v2}:
       _) -> double $ v1 + v2
      _ -> Nothing
  }

ruleNumeralsSuffixesKMG :: Rule
ruleNumeralsSuffixesKMG = Rule
  { name = "numbers suffixes (K, M, G)"
  , pattern = [dimension Numeral, regex "([kmg])(?=[\\W\\$â‚¬]|$)"]
  , prod = \case
      (Token Numeral NumeralData{TNumeral.value = v}:
       Token RegexMatch (GroupMatch (match:_)):
       _) ->
        case Text.toLower match of
          "k" -> double $ v * 1e3
          "m" -> double $ v * 1e6
          "g" -> double $ v * 1e9
          _ -> Nothing
      _ -> Nothing
  }

oneHundredToThousandMap :: HashMap Text Integer
oneHundredToThousandMap =
  HashMap.fromList
    [ ("cent", 100)
    , ("cents", 100)
    , ("dos-cents", 200)
    , ("tres-cents", 300)
    , ("quatre-cents", 400)
    , ("cinc-cents", 500)
    , ("sis-cents", 600)
    , ("set-cents", 700)
    , ("vuit-cents", 800)
    , ("nou-cents", 900)
    , ("mil", 1000)
    ]

ruleTwenties :: Rule
ruleTwenties = Rule
  { name = "number (21..29)"
  , pattern =
      [ oneOf [20]
      , regex "(-i-| i )"
      , Predicate $ numberBetween 1 10
      ]
  , prod = \case
      (Token Numeral NumeralData{TNumeral.value = v1}:
       _:
       Token Numeral NumeralData{TNumeral.value = v2}:
       _) -> double $ v1 + v2
      _ -> Nothing
  }

ruleHundreds :: Rule
ruleHundreds = Rule
  { name = "number 100..1000 "
  , pattern =
      [ regex
          "(cent(s)?|dos-cents|tres-cents|quatre-cents|cinc-cents|sis-cents|set-cents|vuit-cents|nou-cents|mil)"
      ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) oneHundredToThousandMap >>= integer
      _ -> Nothing
  }

-- Afegeixo regex "-" perque les centenes s'escriuen dos-cent, tres-cent
ruleNumerals :: Rule
ruleNumerals = Rule
  { name = "numbers 200..999"
  , pattern =
      [ Predicate $ numberBetween 2 10
      , regex "-"
      , numberWith TNumeral.value (== 100)
      , Predicate $ numberBetween 0 100
      ]
  , prod = \case
      (Token Numeral NumeralData{TNumeral.value = v1}:
       _:
       Token Numeral NumeralData{TNumeral.value = v2}:
       _) -> double $ 100 * v1 + v2
      _ -> Nothing
  }

ruleNumeralDotNumeral :: Rule
ruleNumeralDotNumeral = Rule
  { name = "number dot number"
  , pattern = [dimension Numeral, regex "coma", Predicate $ not . hasGrain]
  , prod = \case
      (Token Numeral NumeralData{TNumeral.value = v1}:
       _:
       Token Numeral NumeralData{TNumeral.value = v2}:
       _) -> double $ v1 + decimalsToDouble v2
      _ -> Nothing
  }

ruleBelowTenWithTwoDigits :: Rule
ruleBelowTenWithTwoDigits = Rule
  { name = "integer (0-9) with two digits"
  , pattern =
      [ regex "zero|0"
      , Predicate $ numberBetween 1 10
      ]
  , prod = \case
      (_:Token Numeral NumeralData{TNumeral.value = v}:_) -> double v
      _ -> Nothing
  }

ruleDecimalWithThousandsSeparator :: Rule
ruleDecimalWithThousandsSeparator = Rule
  { name = "decimal with thousands separator ."
  , pattern = [regex "(\\d+(\\.\\d\\d\\d)+,\\d+)"]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        let fmt = Text.replace "," "." . Text.replace "." Text.empty $ match
        in parseDouble fmt >>= double
      _ -> Nothing
  }

ruleDecimalNumeral :: Rule
ruleDecimalNumeral = Rule
  { name = "decimal number ,"
  , pattern = [regex "(\\d*,\\d+)"]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        parseDecimal False match
      _ -> Nothing
  }

ruleIntegerWithThousandsSeparator :: Rule
ruleIntegerWithThousandsSeparator = Rule
  { name = "integer with thousands separator ."
  , pattern = [regex "(\\d{1,3}(\\.\\d\\d\\d){1,5})"]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        parseDouble (Text.replace "." Text.empty match) >>= double
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleBelowTenWithTwoDigits
  , ruleZeroToFifteen
  , ruleTens
  , ruleTwenties
  , ruleLowerTensWithOnes
  , ruleHigherTensWithOnes
  , ruleHundreds

  , ruleNumeralDotNumeral
  , ruleNumerals
  , ruleNumeralsPrefixWithNegativeOrMinus
  , ruleNumeralsSuffixesKMG
  , ruleDecimalNumeral
  , ruleDecimalWithThousandsSeparator
  , ruleIntegerWithThousandsSeparator
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE OverloadedStrings #-}

module Duckling.Numeral.DA.Corpus
  ( corpus ) where

import Prelude
import Data.String

import Duckling.Locale
import Duckling.Numeral.Types
import Duckling.Resolve
import Duckling.Testing.Types

corpus :: Corpus
corpus = (testContext {locale = makeLocale DA Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (NumeralValue 0)
             [ "0"
             , "nul"
             ]
  , examples (NumeralValue 1)
             [ "1"
             , "Ã©n"
             , "en"
             , "Ã©t"
             , "et"
             ]
  , examples (NumeralValue 2)
             [ "2"
             , "to"
             , "et par"
             ]
  , examples (NumeralValue 14)
             [ "14"
             , "fjorten"
             ]
  , examples (NumeralValue 16)
             [ "16"
             , "seksten"
             ]
  , examples (NumeralValue 17)
             [ "17"
             , "sytten"
             ]
  , examples (NumeralValue 18)
             [ "18"
             , "Atten"
             ]
  , examples (NumeralValue 1.1)
             [ "1,1"
             , "1,10"
             , "01,10"
             ]
  , examples (NumeralValue 0.77)
             [ "0,77"
             , ",77"
             ]
  , examples (NumeralValue 100000)
             [ "100.000"
             , "100000"
             , "100K"
             , "100k"
             ]
  , examples (NumeralValue 3000000)
             [ "3M"
             , "3000K"
             , "3000000"
             , "3.000.000"
             ]
  , examples (NumeralValue 1200000)
             [ "1.200.000"
             , "1200000"
             , "1,2M"
             , "1200K"
             , ",0012G"
             ]
  , examples (NumeralValue (-1200000))
             [ "- 1.200.000"
             , "-1200000"
             , "minus 1.200.000"
             , "negativ 1200000"
             , "-1,2M"
             , "-1200K"
             , "-,0012G"
             ]
  , examples (NumeralValue 5000)
             [ "5 tusind"
             , "fem tusinde"
             , "fem Tusind"
             ]
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}

module Duckling.Numeral.DA.Rules
  ( rules
  ) where

import Data.HashMap.Strict (HashMap)
import Data.Maybe
import Data.String
import Data.Text (Text)
import Prelude
import qualified Data.HashMap.Strict as HashMap
import qualified Data.Text as Text

import Duckling.Dimensions.Types
import Duckling.Numeral.Helpers
import Duckling.Numeral.Types (NumeralData (..))
import Duckling.Regex.Types
import Duckling.Types
import qualified Duckling.Numeral.Types as TNumeral

ruleNumeralsPrefixWithNegativeOrMinus :: Rule
ruleNumeralsPrefixWithNegativeOrMinus = Rule
  { name = "numbers prefix with -, negative or minus"
  , pattern =
    [ regex "-|minus|negativ"
    , Predicate isPositive
    ]
  , prod = \tokens -> case tokens of
      (_:Token Numeral NumeralData{TNumeral.value = v}:_) ->
        double $ v * (-1)
      _ -> Nothing
  }

ruleFew :: Rule
ruleFew = Rule
  { name = "few"
  , pattern =
    [ regex "(nogle )?fÃ¥"
    ]
  , prod = \_ -> integer 3
  }

ruleTen :: Rule
ruleTen = Rule
  { name = "ten"
  , pattern =
    [ regex "ti"
    ]
  , prod = \_ -> integer 10 >>= withGrain 1
  }

ruleDecimalWithThousandsSeparator :: Rule
ruleDecimalWithThousandsSeparator = Rule
  { name = "decimal with thousands separator"
  , pattern =
    [ regex "(\\d+(\\.\\d\\d\\d)+\\,\\d+)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        parseDouble (Text.replace "." Text.empty match) >>= double
      _ -> Nothing
  }

ruleDecimalNumeral :: Rule
ruleDecimalNumeral = Rule
  { name = "decimal number"
  , pattern =
    [ regex "(\\d*,\\d+)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        parseDecimal False match
      _ -> Nothing
  }

ruleInteger3 :: Rule
ruleInteger3 = Rule
  { name = "integer 21..99"
  , pattern =
    [ oneOf [20, 30 .. 90]
    , Predicate $ numberBetween 1 10
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v1}:
       Token Numeral NumeralData{TNumeral.value = v2}:
       _) -> double $ v1 + v2
      _ -> Nothing
  }

ruleSingle :: Rule
ruleSingle = Rule
  { name = "single"
  , pattern =
    [ regex "enkelt"
    ]
  , prod = \_ -> integer 1 >>= withGrain 1
  }

ruleIntersect :: Rule
ruleIntersect = Rule
  { name = "intersect"
  , pattern =
    [ Predicate hasGrain
    , Predicate $ and . sequence [not . isMultipliable, isPositive]
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = val1, TNumeral.grain = Just g}:
       Token Numeral NumeralData{TNumeral.value = val2}:
       _) | (10 ** fromIntegral g) > val2 -> double $ val1 + val2
      _ -> Nothing
  }

ruleNumeralsSuffixesKMG :: Rule
ruleNumeralsSuffixesKMG = Rule
  { name = "numbers suffixes (K, M, G)"
  , pattern =
    [ dimension Numeral
    , regex "([kmg])(?=[\\W\\$â‚¬]|$)"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:
       Token RegexMatch (GroupMatch (match:_)):
       _) -> case Text.toLower match of
          "k" -> double $ v * 1e3
          "m" -> double $ v * 1e6
          "g" -> double $ v * 1e9
          _   -> Nothing
      _ -> Nothing
  }

rulePowersOfTen :: Rule
rulePowersOfTen = Rule
  { name = "powers of tens"
  , pattern =
    [ regex "(hundrede?|tohundrede|tusinde?|totusinde|million(er)?)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) -> case Text.toLower match of
        "hundred"   -> double 1e2 >>= withGrain 2 >>= withMultipliable
        "hundrede"  -> double 1e2 >>= withGrain 2 >>= withMultipliable
        "tohundrede"  -> double (2 * 1e2) >>= withGrain 2 >>= withMultipliable
        "tusind"    -> double 1e3 >>= withGrain 3 >>= withMultipliable
        "tusinde"   -> double 1e3 >>= withGrain 3 >>= withMultipliable
        "totusinde"   -> double (2 * 1e3) >>= withGrain 3 >>= withMultipliable
        "million"   -> double 1e6 >>= withGrain 6 >>= withMultipliable
        "millioner" -> double 1e6 >>= withGrain 6 >>= withMultipliable
        _           -> Nothing
      _ -> Nothing
  }

ruleAPair :: Rule
ruleAPair = Rule
  { name = "a pair"
  , pattern =
    [ regex "et par"
    ]
  , prod = \_ -> integer 2 >>= withGrain 1
  }

ruleNumeralsOg :: Rule
ruleNumeralsOg = Rule
  { name = "numbers og"
  , pattern =
    [ Predicate $ numberBetween 1 10
    , regex "og"
    , oneOf [70, 20, 60, 50, 40, 90, 30, 80]
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v1}:
       _:
       Token Numeral NumeralData{TNumeral.value = v2}:
       _) -> double $ v1 + v2
      _ -> Nothing
  }

ruleDozen :: Rule
ruleDozen = Rule
  { name = "dozen"
  , pattern =
    [ regex "dusin"
    ]
  , prod = \_ -> integer 12 >>= withGrain 1 >>= withMultipliable
  }

ruleMultiply :: Rule
ruleMultiply = Rule
  { name = "compose by multiplication"
  , pattern =
    [ dimension Numeral
    , Predicate isMultipliable
    ]
  , prod = \tokens -> case tokens of
      (token1:token2:_) -> multiply token1 token2
      _ -> Nothing
  }

zeroNineteenMap :: HashMap Text Integer
zeroNineteenMap = HashMap.fromList
  [ ("ingen", 0)
  , ("nul", 0)
  , ("intet", 0)
  , ("en", 1)
  , ("et", 1)
  , ("Ã©n", 1)
  , ("Ã©t", 1)
  , ("to", 2)
  , ("tre", 3)
  , ("fire", 4)
  , ("fem", 5)
  , ("seks", 6)
  , ("syv", 7)
  , ("otte", 8)
  , ("ni", 9)
  , ("ti", 10)
  , ("elleve", 11)
  , ("tolv", 12)
  , ("tretten", 13)
  , ("fjorten", 14)
  , ("femten", 15)
  , ("seksten", 16)
  , ("sytten", 17)
  , ("atten", 18)
  , ("nitten", 19)
  ]

ruleInteger :: Rule
ruleInteger = Rule
  { name = "integer (0..19)"
  , pattern =
    [ regex "(intet|ingen|nul|en|et|Ã©n|Ã©t|to|tretten|tre|fire|femten|fem|seksten|seks|syv|otte|nitten|ni|ti|elleve|tolv|fjorten|sytten|atten)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) zeroNineteenMap >>= integer
      _ -> Nothing
  }

ruleInteger2 :: Rule
ruleInteger2 = Rule
  { name = "integer (20..90)"
  , pattern =
    [ regex "(tyve|tredive|fyrre|halvtreds|tres|halvfjerds|firs|halvfems)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) -> case Text.toLower match of
        "tyve" -> integer 20
        "tredive" -> integer 30
        "fyrre" -> integer 40
        "halvtreds" -> integer 50
        "tres" -> integer 60
        "halvfjerds" -> integer 70
        "firs" -> integer 80
        "halvfems" -> integer 90
        _ -> Nothing
      _ -> Nothing
  }

ruleNumeralDotNumeral :: Rule
ruleNumeralDotNumeral = Rule
  { name = "number dot number"
  , pattern =
    [ dimension Numeral
    , regex "komma"
    , Predicate $ not . hasGrain
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v1}:
       _:
       Token Numeral NumeralData{TNumeral.value = v2}:
       _) -> double $ v1 + decimalsToDouble v2
      _ -> Nothing
  }

ruleIntegerWithThousandsSeparator :: Rule
ruleIntegerWithThousandsSeparator = Rule
  { name = "integer with thousands separator ."
  , pattern =
    [ regex "(\\d{1,3}(\\.\\d\\d\\d){1,5})"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        parseDouble (Text.replace "." Text.empty match) >>= double
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleAPair
  , ruleDecimalNumeral
  , ruleDecimalWithThousandsSeparator
  , ruleDozen
  , ruleFew
  , ruleInteger
  , ruleInteger2
  , ruleInteger3
  , ruleIntegerWithThousandsSeparator
  , ruleIntersect
  , ruleMultiply
  , ruleNumeralDotNumeral
  , ruleNumeralsOg
  , ruleNumeralsPrefixWithNegativeOrMinus
  , ruleNumeralsSuffixesKMG
  , rulePowersOfTen
  , ruleSingle
  , ruleTen
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE OverloadedStrings #-}

module Duckling.Numeral.DE.Corpus
  ( corpus
  ) where

import Data.String
import Prelude

import Duckling.Locale
import Duckling.Numeral.Types
import Duckling.Resolve
import Duckling.Testing.Types

corpus :: Corpus
corpus = (testContext {locale = makeLocale DE Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (NumeralValue 0)
             [ "0"
             , "null"
             , "keiner"
             , "keinen"
             ]
  , examples (NumeralValue 1)
             [ "1"
             , "eins"
             , "Eine"
             , "einen"
             ]
  , examples (NumeralValue 3)
             [ "3"
             , "Drei"
             ]
  , examples (NumeralValue 8)
             [ "acht"
             ]
  , examples (NumeralValue 18)
             [ "achtzehn"
             ]
  , examples (NumeralValue 30)
             [ "30"
             , "dreiÃŸig"
             , "dreissig"
             ]
  , examples (NumeralValue 33)
             [ "33"
             , "dreiunddreiÃŸig"
             , "dreiunddreissig"
             , "0033"
             ]
  , examples (NumeralValue 14)
             [ "14"
             , "vierzehn"
             ]
  , examples (NumeralValue 15)
             [ "15"
             , "fÃ¼nfzehn"
             ]
  , examples (NumeralValue 16)
             [ "16"
             , "sechzehn"
             ]
  , examples (NumeralValue 17)
             [ "17"
             , "Siebzehn"
             ]
  , examples (NumeralValue 18)
             [ "18"
             , "achtzehn"
             ]
  , examples (NumeralValue 200)
             [ "200"
             , "zweihundert"
             ]
  , examples (NumeralValue 102)
             [ "102"
             , "Hundertzwei"
             ]
  , examples (NumeralValue 1.1)
             [ "1,1"
             , "1 komma 1"
             , "1,10"
             , "01,10"
             ]
  , examples (NumeralValue 0.77)
             [ "0,77"
             , ",77"
             ]
  , examples (NumeralValue 100000)
             [ "100.000"
             , "100000"
             , "100K"
             , "100k"
             , "einhunderttausend"
             , "hunderttausend"
             ]
  , examples (NumeralValue 3000000)
             [ "3M"
             , "3000K"
             , "3000000"
             , "3.000.000"
             , "drei Millionen"
             ]
  , examples (NumeralValue 1200000)
             [ "1.200.000"
             , "1200000"
             , "1,2M"
             , "1200K"
             , ",0012G"
             ]
  , examples (NumeralValue (-1200000))
             [ "- 1.200.000"
             , "-1200000"
             , "minus 1.200.000"
             , "negativ 1200000"
             , "-1,2M"
             , "-1200K"
             , "-,0012G"
             ]
  , examples (NumeralValue 1852)
             [ "eintausendachthundertzweiundfÃ¼nfzig"
             , "tausendachthundertzweiundfÃ¼nfzig"
             , "achtzehnhundertzweiundfÃ¼nfzig"]
  , examples (NumeralValue 5000)
             [ "5 tausend"
             , "fÃ¼nftausend"
             ]
  , examples (NumeralValue 200000)
             [ "zweihunderttausend"
             ]
  , examples (NumeralValue 721012)
             [ "siebenhunderteinundzwanzigtausendzwÃ¶lf"
             , "siebenhunderteinundzwanzigtausendundzwÃ¶lf"
             ]
  , examples (NumeralValue 31256721)
             [ "einunddreissig millionen zweihundertsechsundfÃ¼nfzigtausendsiebenhunderteinundzwanzig"
             , "einunddreiÃŸig Millionen zweihundertsechsundfÃ¼nfzigtausendundsiebenhunderteinundzwanzig"
             ]
  , examples (NumeralValue 1416.15)
             [ "1416,15"
             ,  "tausendvierhundertsechzehn Komma fÃ¼nfzehn"
             ]
  , examples (NumeralValue 1000000.0)
             [ "1.000.000,00",
               "eine million"
             ]
  , examples (NumeralValue 2771090092000000.0)
             [ "zwei billiarden siebenhunderteinundsiebzig billionen neunzig milliarden zweiundneunzig millionen"
             ]
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.
{-# LANGUAGE DeriveFunctor #-}

module Duckling.Numeral.DE.NumParser (parseNumeral) where

import Control.Applicative
import Data.Char
import Data.Foldable
import Data.List
import Data.String
import Prelude

newtype Parser a = Parser {runParser :: String -> Maybe (a, String)}
  deriving (Functor)

char :: Char -> Parser Char
char c = Parser p
  where
    p [] = Nothing
    p (x : xs)
      | x == c = Just (x, xs)
      | otherwise = Nothing

instance Applicative Parser where
  pure a = Parser (\s -> Just (a, s))
  (Parser fp) <*> xp = Parser $ \s ->
    case fp s of
      Nothing -> Nothing
      Just (f, s') -> runParser (f <$> xp) s'

instance Alternative Parser where
  empty = Parser (const Nothing)
  Parser p1 <|> Parser p2 = Parser $ liftA2 (<|>) p1 p2

type NumParser = Parser Integer

(.+.) :: NumParser -> NumParser -> NumParser
p .+. p' = (+) <$> p <*> p'

(.*.) :: NumParser -> NumParser -> NumParser
p .*. p' = (*) <$> p <*> p'

infixl 6 .+.

infixl 7 .*.

opt :: NumParser -> NumParser
opt p = p <|> Parser p'
  where
    p' s = Just (0, s)

data NumItem = NumItem
  { base :: NumParser
  , plus10 :: NumParser
  , times10 :: [NumParser]
  }

defaultNumItem :: Integer -> String -> NumItem
defaultNumItem value form =
  NumItem
    { base = p
    , plus10 = p .+. ten
    , times10 = [p .*. ty]
    }
  where
    p = assign value form

type Assignment = Integer -> String -> NumParser

assign :: Assignment
assign value = foldr (\c p -> (1 <$ char c) .*. p) (pure value)

ten :: NumParser
ten = assign 10 "zehn"

ty :: NumParser
ty = assign 10 "zig"

hundred :: NumParser
hundred = assign 100 "hundert"

thousand :: NumParser
thousand = assign 1000 "tausend"

und :: NumParser
und = assign 0 "und"

one :: NumItem
one =
  (defaultNumItem 1 "ein")
    { plus10 = assign 11 "elf"
    , times10 = [ten]
    }

two :: NumItem
two =
  (defaultNumItem 2 "zwei")
    { plus10 = assign 12 "zwÃ¶lf"
    , times10 = [assign 20 "zwanzig"]
    }

three :: NumItem
three =
  (defaultNumItem 3 "drei")
    { times10 =
        [ assign 30 "dreiÃŸig"
        , assign 30 "dreissig"
        ]
    }

four :: NumItem
four = defaultNumItem 4 "vier"

five :: NumItem
five = defaultNumItem 5 "fÃ¼nf"

six :: NumItem
six =
  (defaultNumItem 6 "sechs")
    { plus10 = assign 16 "sechzehn"
    , times10 = [assign 60 "sechzig"]
    }

seven :: NumItem
seven =
  (defaultNumItem 7 "sieben")
    { plus10 = assign 17 "siebzehn"
    , times10 = [assign 70 "siebzig"]
    }

eight :: NumItem
eight = defaultNumItem 8 "acht"

nine :: NumItem
nine = defaultNumItem 9 "neun"

digitLexicon :: [NumItem]
digitLexicon = [one, two, three, four, five, six, seven, eight, nine]

from1to9 :: NumParser
from1to9 = foldr ((<|>) . base) empty digitLexicon

tensFrom20 :: NumParser
tensFrom20 = asum (concatMap times10 (tail digitLexicon))

from1to99 :: NumParser
from1to99 =
  opt (from1to9 .+. und) .+. tensFrom20
    <|> foldr ((<|>) . plus10) empty digitLexicon
    <|> ten
    <|> from1to9

from1to999 :: NumParser
from1to999 = opt (from1to9 .*. hundred .+. opt und) .+. opt from1to99

from1to999999 :: NumParser
from1to999999 = opt (from1to999 .*. thousand .+. opt und) .+. opt from1to999

from1to999999' :: NumParser
from1to999999' = Parser p
  where
    p s
      | isPrefixOf "hundert" s || isPrefixOf "tausend" s =
        runParser from1to999999 ("ein" ++ s)
      | otherwise =
        runParser from1to999999 s

fromYear1100to1999 :: NumParser
fromYear1100to1999 =
  asum ((\n -> plus10 n .*. hundred) <$> digitLexicon)
    .+. opt (opt und .+. from1to99)

allNumerals :: NumParser
allNumerals =
  fromYear1100to1999
    <|> from1to999999'

removeInflection :: (Integer, String) -> Maybe Integer
removeInflection (n, suffix)
  | n `mod` 10 == 1 && suffix `elem` inflection = Just n
  where
    inflection = ["s", "e", "em", "en", "er", "es"]
removeInflection (n, "") = Just n
removeInflection _ = Nothing

parseNumeral :: String -> Maybe Integer
parseNumeral s = removeInflection =<< runParser allNumerals s


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE NoRebindableSyntax #-}
{-# LANGUAGE OverloadedStrings #-}

module Duckling.Numeral.DE.Rules
  ( rules
  ) where

import Data.HashMap.Strict (HashMap)
import Data.Maybe
import Data.Text (Text)
import Prelude
import Data.String
import qualified Data.HashMap.Strict as HashMap
import qualified Data.Text as Text

import Duckling.Dimensions.Types
import Duckling.Numeral.Helpers
import Duckling.Numeral.Types (NumeralData (..))
import Duckling.Regex.Types
import Duckling.Types
import qualified Duckling.Numeral.Types as TNumeral
import Duckling.Numeral.DE.NumParser

ruleNumeralsPrefixWithNegativeOrMinus :: Rule
ruleNumeralsPrefixWithNegativeOrMinus = Rule
  { name = "numbers prefix with -, negative or minus"
  , pattern =
    [ regex "-|minus|negativ"
    , Predicate isPositive
    ]
  , prod = \tokens -> case tokens of
      (_:Token Numeral NumeralData{TNumeral.value = v}:_) ->
        double $ v * (- 1)
      _ -> Nothing
  }

ruleFew :: Rule
ruleFew = Rule
  { name = "few"
  , pattern =
    [ regex "mehrere"
    ]
  , prod = \_ -> integer 3
  }

ruleDecimalWithThousandsSeparator :: Rule
ruleDecimalWithThousandsSeparator = Rule
  { name = "decimal with thousands separator"
  , pattern =
    [ regex "(\\d+(\\.\\d\\d\\d)+\\,\\d+)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):
       _) -> let fmt = Text.replace "," "." $ Text.replace "." Text.empty match
        in parseDouble fmt >>= double
      _ -> Nothing
  }

ruleDecimalNumeral :: Rule
ruleDecimalNumeral = Rule
  { name = "decimal number"
  , pattern =
    [ regex "(\\d*,\\d+)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        parseDecimal False match
      _ -> Nothing
  }

ruleNumeralsUnd :: Rule
ruleNumeralsUnd = Rule
  { name = "numbers und"
  , pattern =
    [ Predicate $ numberBetween 1 10
    , regex "und"
    , oneOf [20, 30 .. 90]
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v1}:
       _:
       Token Numeral NumeralData{TNumeral.value = v2}:
       _) -> double $ v1 + v2
      _ -> Nothing
  }

ruleMultiply :: Rule
ruleMultiply = Rule
  { name = "compose by multiplication"
  , pattern =
    [ dimension Numeral
    , Predicate isMultipliable
    ]
  , prod = \tokens -> case tokens of
      (token1:token2:_) -> multiply token1 token2
      _ -> Nothing
  }

ruleIntersect :: Rule
ruleIntersect = Rule
  { name = "intersect"
  , pattern =
    [ Predicate hasGrain
    , Predicate $ and . sequence [not . isMultipliable, isPositive]
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = val1, TNumeral.grain = Just g}:
       Token Numeral NumeralData{TNumeral.value = val2}:
       _) | (10 ** fromIntegral g) > val2 -> double $ val1 + val2
      _ -> Nothing
  }

ruleNumeralsSuffixesKMG :: Rule
ruleNumeralsSuffixesKMG = Rule
  { name = "numbers suffixes (K, M, G)"
  , pattern =
    [ dimension Numeral
    , regex "([kmg])(?=[\\W\\$â‚¬]|$)"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:
       Token RegexMatch (GroupMatch (match:_)):
       _) -> case Text.toLower match of
         "k" -> double $ v * 1e3
         "m" -> double $ v * 1e6
         "g" -> double $ v * 1e9
         _ -> Nothing
      _ -> Nothing
  }

ruleCouple :: Rule
ruleCouple = Rule
  { name = "couple"
  , pattern =
    [ regex "(ein )?paar"
    ]
  , prod = \_ -> integer 2
  }

ruleDozen :: Rule
ruleDozen = Rule
  { name = "dozen"
  , pattern =
    [ regex "dutzend"
    ]
  , prod = \_ -> integer 12 >>= withGrain 1 >>= withMultipliable
  }

rulePowersOfTen :: Rule
rulePowersOfTen = Rule
  { name = "powers of tens"
  , pattern =
    [ regex "(hunderte?|tausende?|million(en)?|milliarde(n)?|billion(en)?|billiarde(n)?)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) -> case Text.toLower match of
        "hundert"    -> double 1e2  >>= withGrain 2  >>= withMultipliable
        "hunderte"   -> double 1e2  >>= withGrain 2  >>= withMultipliable
        "tausend"    -> double 1e3  >>= withGrain 3  >>= withMultipliable
        "tausende"   -> double 1e3  >>= withGrain 3  >>= withMultipliable
        "million"    -> double 1e6  >>= withGrain 6  >>= withMultipliable
        "millionen"  -> double 1e6  >>= withGrain 6  >>= withMultipliable
        "milliarde"  -> double 1e9  >>= withGrain 9  >>= withMultipliable
        "milliarden" -> double 1e9  >>= withGrain 9  >>= withMultipliable
        "billion"    -> double 1e12 >>= withGrain 12 >>= withMultipliable
        "billionen"  -> double 1e12 >>= withGrain 12 >>= withMultipliable
        "billiarde"  -> double 1e15 >>= withGrain 15 >>= withMultipliable
        "billiarden" -> double 1e15 >>= withGrain 15 >>= withMultipliable
        _            -> Nothing
      _ -> Nothing
  }

ruleZero :: Rule
ruleZero = Rule
  { name = "integer 0"
  , pattern =
    [ regex "(keine(m|n|r|s)?|keins?|null|nichts)"
    ]
  , prod = \_ -> integer 0
  }

ruleNumeralDotNumeral :: Rule
ruleNumeralDotNumeral = Rule
  { name = "number dot number"
  , pattern =
    [ dimension Numeral
    , regex "komma"
    , Predicate $ not . hasGrain
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v1}:
       _:
       Token Numeral NumeralData{TNumeral.value = v2}:
       _) -> double $ v1 + decimalsToDouble v2
      _ -> Nothing
  }

ruleIntegerWithThousandsSeparator :: Rule
ruleIntegerWithThousandsSeparator = Rule
  { name = "integer with thousands separator ."
  , pattern =
    [ regex "(\\d{1,3}(\\.\\d\\d\\d){1,5})"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        parseDouble (Text.replace "." Text.empty match) >>= double
      _ -> Nothing
  }

ruleAllNumeralWords :: Rule
ruleAllNumeralWords = Rule
  { name = "simple and complex numerals written as one word"
  , pattern = [regex "(ein|zwei|drei|vier|fÃ¼nf|sech|sieb|acht|neun|zehn|elf|zwÃ¶lf|hundert|tausend)?([^\\s]+)?(eine[m|n|r|s]?|eins?|zwei|drei|vier|fÃ¼nf|sechs|sieben|acht|neun|zehn|elf|zwÃ¶lf|[s|ÃŸ|z]ig|hundert|tausend)"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch matches) : _) ->
        (parseNumeral $ concat $ Text.unpack . Text.toLower <$> matches)
        >>= integer
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleCouple
  , ruleDecimalNumeral
  , ruleDecimalWithThousandsSeparator
  , ruleDozen
  , ruleFew
  , ruleIntegerWithThousandsSeparator
  , ruleIntersect
  , ruleMultiply
  , ruleNumeralDotNumeral
  , ruleNumeralsPrefixWithNegativeOrMinus
  , ruleNumeralsSuffixesKMG
  , ruleNumeralsUnd
  , rulePowersOfTen
  , ruleZero
  , ruleAllNumeralWords
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE OverloadedStrings #-}

module Duckling.Numeral.EN.Corpus
  ( corpus ) where

import Prelude
import Data.String

import Duckling.Numeral.Types
import Duckling.Testing.Types

corpus :: Corpus
corpus = (testContext, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (NumeralValue 0)
             [ "0"
             , "naught"
             , "nought"
             , "zero"
             , "nil"
             ]
  , examples (NumeralValue 1)
             [ "1"
             , "one"
             , "single"
             ]
  , examples (NumeralValue 2)
             [ "2"
             , "two"
             , "a pair"
             , "a couple"
             , "a couple of"
             ]
  , examples (NumeralValue 3)
             [ "3"
             , "three"
             , "a few"
             , "few"
             ]
  , examples (NumeralValue 10)
             [ "10"
             , "ten"
             ]
  , examples (NumeralValue 12)
             [ "12"
             , "twelve"
             , "a dozen"
             , "a dozen of"
             ]
  , examples (NumeralValue 14)
             [ "14"
             , "fourteen"
             ]
  , examples (NumeralValue 16)
             [ "16"
             , "sixteen"
             ]
  , examples (NumeralValue 17)
             [ "17"
             , "seventeen"
             ]
  , examples (NumeralValue 18)
             [ "18"
             , "eighteen"
             ]
  , examples (NumeralValue 33)
             [ "33"
             , "thirty three"
             , "0033"
             ]
  , examples (NumeralValue 24)
             [ "24"
             , "2 dozens"
             , "two dozen"
             , "Two dozen"
             ]
  , examples (NumeralValue 1.1)
             [ "1.1"
             , "1.10"
             , "01.10"
             , "1 point 1"
             ]
  , examples (NumeralValue 0.77)
             [ ".77"
             , "0.77"
             , "point 77"
             ]
  , examples (NumeralValue 100000)
             [ "100,000"
             , "100,000.0"
             , "100000"
             , "100K"
             , "100k"
             , "one hundred thousand"
             ]
  , examples (NumeralValue 0.2)
             [ "1/5"
             , "2/10"
             , "3/15"
             , "20/100"
             ]
  , examples (NumeralValue 3e6)
             [ "3M"
             , "3000K"
             , "3000000"
             , "3,000,000"
             , "3 million"
             , "30 lakh"
             , "30 lkh"
             , "30 l"
             ]
  , examples (NumeralValue 1.2e6)
             [ "1,200,000"
             , "1200000"
             , "1.2M"
             , "1200k"
             , ".0012G"
             , "12 lakhs"
             , "12 lkhs"
             ]
  , examples (NumeralValue 5000)
             [ "5 thousand"
             , "five thousand"
             ]
  , examples (NumeralValue (-504))
             [ "-504"
             , "negative five hundred and four"
             ]
  , examples (NumeralValue (-1.2e6))
             [ "- 1,200,000"
             , "-1200000"
             , "minus 1,200,000"
             , "negative 1200000"
             , "-1.2M"
             , "-1200K"
             , "-.0012G"
             ]
  , examples (NumeralValue (-3200000))
             [ "-3,200,000"
             , "-3200000"
             , "minus three million two hundred thousand"
             ]
  , examples (NumeralValue 122)
             [ "one twenty two"
             , "ONE TwentY tWO"
             ]
  , examples (NumeralValue 2e5)
             [ "two Hundred thousand"
             ]
  , examples (NumeralValue 21011)
             [ "twenty-one thousand Eleven"
             ]
  , examples (NumeralValue 721012)
             [ "seven hundred twenty-one thousand twelve"
             , "seven hundred twenty-one thousand and twelve"
             ]
  , examples (NumeralValue 31256721)
             [ "thirty-one million two hundred fifty-six thousand seven hundred twenty-one"
             , "three crore twelve lakh fifty-six thousand seven hundred twenty-one"
             , "three cr twelve lac fifty-six thousand seven hundred twenty-one"
             ]
  , examples (NumeralValue 2400)
             [ "two hundred dozens"
             , "200 dozens"
             ]
  , examples (NumeralValue 2200000)
             [ "two point two million"
             ]
  , examples (NumeralValue 3000000000)
             [ "three billions"
             , "three thousand millions"
             , "three hundred crores"
             , "three hundred Cr"
             , "three hundred koti"
             , "three hundred krores"
             , "three hundred Kr"
             ]
  , examples (NumeralValue 45)
             [ "forty-five (45)"
             , "45 (forty five)"
             ]
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NoRebindableSyntax #-}
{-# LANGUAGE OverloadedStrings #-}

module Duckling.Numeral.EN.Rules
  ( rules
  ) where

import Control.Applicative ((<|>))
import Data.HashMap.Strict (HashMap)
import Data.Maybe
import Data.Text (Text)
import Prelude
import qualified Data.HashMap.Strict as HashMap
import qualified Data.Text as Text

import Duckling.Dimensions.Types
import Duckling.Numeral.Helpers
import Duckling.Numeral.Types (NumeralData (..), isInteger)
import Duckling.Regex.Types
import Duckling.Types
import qualified Duckling.Numeral.Types as TNumeral

ruleDozen :: Rule
ruleDozen = Rule
  { name = "a dozen of"
  , pattern =
    [ regex "(a )?dozens?( of)?"
    ]
  , prod = \_ -> integer 12 >>= withMultipliable >>= notOkForAnyTime
  }

zeroNineteenMap :: HashMap Text Integer
zeroNineteenMap = HashMap.fromList
  [ ( "naught"   , 0 )
  , ( "nil"      , 0 )
  , ( "nought"   , 0 )
  , ( "none"     , 0 )
  , ( "zero"     , 0 )
  , ( "zilch"    , 0 )
  , ( "one"      , 1 )
  , ( "two"      , 2 )
  , ( "three"    , 3 )
  , ( "four"     , 4 )
  , ( "five"     , 5 )
  , ( "six"      , 6 )
  , ( "seven"    , 7 )
  , ( "eight"    , 8 )
  , ( "nine"     , 9 )
  , ( "ten"      , 10 )
  , ( "eleven"   , 11 )
  , ( "twelve"   , 12 )
  , ( "thirteen" , 13 )
  , ( "fourteen" , 14 )
  , ( "fifteen"  , 15 )
  , ( "sixteen"  , 16 )
  , ( "seventeen", 17 )
  , ( "eighteen" , 18 )
  , ( "nineteen" , 19 )
  ]

informalMap :: HashMap Text Integer
informalMap = HashMap.fromList
  [ ( "single"     , 1 )
  , ( "a couple"   , 2 )
  , ( "a couple of", 2 )
  , ( "couple"     , 2 )
  , ( "couples"    , 2 )
  , ( "couple of"  , 2 )
  , ( "couples of" , 2 )
  , ( "a pair"     , 2 )
  , ( "a pair of"  , 2 )
  , ( "pair"       , 2 )
  , ( "pairs"      , 2 )
  , ( "pair of"    , 2 )
  , ( "pairs of"   , 2 )
  , ( "a few"      , 3 )
  , ( "few"        , 3 )
  ]

ruleToNineteen :: Rule
ruleToNineteen = Rule
  { name = "integer (0..19)"
  -- e.g. fourteen must be before four, otherwise four will always shadow fourteen
  , pattern =
    [ regex "(none|zilch|naught|nought|nil|zero|one|single|two|(a )?(pair|couple)s?( of)?|three|(a )?few|fourteen|four|fifteen|five|sixteen|six|seventeen|seven|eighteen|eight|nineteen|nine|ten|eleven|twelve|thirteen)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        let x = Text.toLower match in
        (HashMap.lookup x zeroNineteenMap >>= integer) <|>
        (HashMap.lookup x informalMap >>= integer >>= notOkForAnyTime)
      _ -> Nothing
  }

tensMap :: HashMap Text Integer
tensMap = HashMap.fromList
  [ ( "twenty"  , 20 )
  , ( "thirty"  , 30 )
  , ( "forty"   , 40 )
  , ( "fourty"  , 40 )
  , ( "fifty"   , 50 )
  , ( "sixty"   , 60 )
  , ( "seventy" , 70 )
  , ( "eighty"  , 80 )
  , ( "ninety"  , 90 )
  ]

ruleTens :: Rule
ruleTens = Rule
  { name = "integer (20..90)"
  , pattern =
    [ regex "(twenty|thirty|fou?rty|fifty|sixty|seventy|eighty|ninety)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) tensMap >>= integer
      _ -> Nothing
  }

powersOfTensMap :: HashMap Text Int
powersOfTensMap = HashMap.fromList
  [ ("hundred", 2)
  , ("thousand", 3)
  , ("lakh", 5)
  , ("lkh", 5)
  , ("l", 5)
  , ("lac", 5)
  , ("million", 6)
  , ("cr", 7)
  , ("crore", 7)
  , ("krore", 7)
  , ("kr", 7)
  , ("koti", 7)
  , ("billion", 9)
  , ("trillion", 12)
  ]

rulePowersOfTen :: Rule
rulePowersOfTen = Rule
  { name = "powers of tens"
  , pattern = [regex "(hundred|thousand|l(ac|(a?kh)?)|million|((k|c)r(ore)?|koti)|billion)s?"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) -> do
        grain <- HashMap.lookup (Text.toLower match) powersOfTensMap
        double (10 ^ grain) >>= withGrain grain >>= withMultipliable
      _ -> Nothing
  }

ruleCompositeTens :: Rule
ruleCompositeTens = Rule
  { name = "integer 21..99"
  , pattern =
    [ oneOf [20,30..90]
    , regex "[\\s\\-]+"
    , Predicate $ numberBetween 1 10
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = tens}:
       _:
       Token Numeral NumeralData{TNumeral.value = units}:
       _) -> double $ tens + units
      _ -> Nothing
  }

ruleSkipHundreds1 :: Rule
ruleSkipHundreds1 = Rule
  { name = "one eleven"
  , pattern =
    [ regex "(one|two|three|four|five|six|seven|eight|nine)"
    , regex "(ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty|thirty|fou?rty|fifty|sixty|seventy|eighty|ninety)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (m1:_)):
       Token RegexMatch (GroupMatch (m2:_)):
       _) -> do
       let x1 = Text.toLower m1
       let x2 = Text.toLower m2
       hundreds <- HashMap.lookup x1 zeroNineteenMap
       rest <- HashMap.lookup x2 zeroNineteenMap <|> HashMap.lookup x2 tensMap
       integer (hundreds * 100 + rest)
      _ -> Nothing
  }

ruleSkipHundreds2 :: Rule
ruleSkipHundreds2 = Rule
  { name = "one twenty two"
  , pattern =
    [ regex "(one|two|three|four|five|six|seven|eight|nine)"
    , regex "(twenty|thirty|fou?rty|fifty|sixty|seventy|eighty|ninety)"
    , regex "(one|two|three|four|five|six|seven|eight|nine)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (m1:_)):
       Token RegexMatch (GroupMatch (m2:_)):
       Token RegexMatch (GroupMatch (m3:_)):
       _) -> do
       let x1 = Text.toLower m1
       let x2 = Text.toLower m2
       let x3 = Text.toLower m3
       hundreds <- HashMap.lookup x1 zeroNineteenMap
       tens <- HashMap.lookup x2 tensMap
       rest <- HashMap.lookup x3 zeroNineteenMap
       integer (hundreds * 100 + tens + rest)
      _ -> Nothing
  }

ruleDotSpelledOut :: Rule
ruleDotSpelledOut = Rule
  { name = "one point 2"
  , pattern =
    [ dimension Numeral
    , regex "point|dot"
    , Predicate $ not . hasGrain
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral nd1:_:Token Numeral nd2:_) ->
        double $ TNumeral.value nd1 + decimalsToDouble (TNumeral.value nd2)
      _ -> Nothing
  }

ruleLeadingDotSpelledOut :: Rule
ruleLeadingDotSpelledOut = Rule
  { name = "point 77"
  , pattern =
    [ regex "point|dot"
    , Predicate $ not . hasGrain
    ]
  , prod = \tokens -> case tokens of
      (_:Token Numeral nd:_) -> double . decimalsToDouble $ TNumeral.value nd
      _ -> Nothing
  }

ruleDecimals :: Rule
ruleDecimals = Rule
  { name = "decimal number"
  , pattern =
    [ regex "(\\d*\\.\\d+)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) -> parseDecimal True match
      _ -> Nothing
  }

ruleCommas :: Rule
ruleCommas = Rule
  { name = "comma-separated numbers"
  , pattern =
    [ regex "(\\d+(,\\d\\d\\d)+(\\.\\d+)?)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        parseDouble (Text.replace "," Text.empty match) >>= double
      _ -> Nothing
  }

ruleSuffixes :: Rule
ruleSuffixes = Rule
  { name = "suffixes (K,M,G))"
  , pattern =
    [ dimension Numeral
    , regex "(k|m|g)(?=[\\W$â‚¬Â¢Â£]|$)"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral nd : Token RegexMatch (GroupMatch (match : _)):_) -> do
        x <- case Text.toLower match of
          "k" -> Just 1e3
          "m" -> Just 1e6
          "g" -> Just 1e9
          _ -> Nothing
        double $ TNumeral.value nd * x
      _ -> Nothing
  }

ruleNegative :: Rule
ruleNegative = Rule
  { name = "negative numbers"
  , pattern =
    [ regex "(-|minus|negative)(?!\\s*-)"
    , Predicate isPositive
    ]
  , prod = \tokens -> case tokens of
      (_:Token Numeral nd:_) -> double (TNumeral.value nd * (-1))
      _ -> Nothing
  }

ruleSum :: Rule
ruleSum = Rule
  { name = "intersect 2 numbers"
  , pattern =
    [ Predicate $ and . sequence [hasGrain, isPositive]
    , Predicate $ and . sequence [not . isMultipliable, isPositive]
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = val1, TNumeral.grain = Just g}:
       Token Numeral NumeralData{TNumeral.value = val2}:
       _) | (10 ** fromIntegral g) > val2 -> double $ val1 + val2
      _ -> Nothing
  }

ruleSumAnd :: Rule
ruleSumAnd = Rule
  { name = "intersect 2 numbers (with and)"
  , pattern =
    [ Predicate $ and . sequence [hasGrain, isPositive]
    , regex "and"
    , Predicate $ and . sequence [not . isMultipliable, isPositive]
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = val1, TNumeral.grain = Just g}:
       _:
       Token Numeral NumeralData{TNumeral.value = val2}:
       _) | (10 ** fromIntegral g) > val2 -> double $ val1 + val2
      _ -> Nothing
  }

ruleMultiply :: Rule
ruleMultiply = Rule
  { name = "compose by multiplication"
  , pattern =
    [ Predicate isPositive
    , Predicate isMultipliable
    ]
  , prod = \tokens -> case tokens of
      (token1:token2:_) -> multiply token1 token2
      _ -> Nothing
  }

ruleLegalParentheses :: Rule
ruleLegalParentheses = Rule
  { name = "<integer> '('<integer>')'"
  , pattern =
    [ numberWith TNumeral.value $ and . sequence [isInteger, (>0)]
    , regex "\\("
    , numberWith TNumeral.value $ and . sequence [isInteger, (>0)]
    , regex "\\)"
    ]
  , prod = \case
    (Token Numeral NumeralData{TNumeral.value = n1}:
     _:
     Token Numeral NumeralData{TNumeral.value = n2}:
     _:
     _) | n1 == n2 -> double n1
    _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleToNineteen
  , ruleTens
  , rulePowersOfTen
  , ruleCompositeTens
  , ruleSkipHundreds1
  , ruleSkipHundreds2
  , ruleDotSpelledOut
  , ruleLeadingDotSpelledOut
  , ruleDecimals
  , ruleCommas
  , ruleSuffixes
  , ruleNegative
  , ruleSum
  , ruleSumAnd
  , ruleMultiply
  , ruleDozen
  , ruleLegalParentheses
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NumericUnderscores #-}
module Duckling.Numeral.ES.Corpus (corpus) where

import Data.String
import Prelude

import Duckling.Locale
import Duckling.Numeral.Types
import Duckling.Resolve
import Duckling.Testing.Types

corpus :: Corpus
corpus =
  (testContext { locale = makeLocale ES Nothing }, testOptions, allExamples)

allExamples :: [Example]
allExamples =
  concat
    [ examples (NumeralValue 1) ["1", "uno", "una"]
    , examples (NumeralValue (-1)) ["negativo 1", "menos 1", "negativo uno"]
    , examples (NumeralValue 11) ["once"]
    , examples
        (NumeralValue 16)
        ["diecisÃ©is", "dieciseis", "Diesiseis", "diez y seis"]
    , examples (NumeralValue 21) ["veintiuno", "veinte y uno"]
    , examples (NumeralValue 22) ["veintidÃ³s"]
    , examples (NumeralValue 23) ["veintitrÃ©s", "veinte y tres"]
    , examples (NumeralValue 70) ["setenta"]
    , examples (NumeralValue 78) ["Setenta y ocho"]
    , examples (NumeralValue 80) ["ochenta"]
    , examples (NumeralValue 33) ["33", "treinta y tres", "treinta y 3"]
    , examples (NumeralValue 100_000) ["100000", "100K", "100k"]
    , examples (NumeralValue 300) ["trescientos"]
    , examples (NumeralValue 243) ["243"]
    , examples (NumeralValue 3_000_000) ["3M", "3000K", "3000000"]
    , examples (NumeralValue 1_200_000) ["1200000", "1200K"]
    , examples (NumeralValue (-1_200_000)) ["-1200000", "-1200K"]
    , examples (NumeralValue 1.5) ["1 punto cinco", "una punto cinco", "1 con cinco", "1 con 5", "uno coma 5"]
    , examples (NumeralValue 0.5) ["punto cinco", "coma 5"]
    , examples (NumeralValue 1) ["cero uno", "zero uno"]
    , examples (NumeralValue 2) ["cero dos", "zero dos"]
    , examples (NumeralValue 3)
               [
                 "cero tres",
                 "cero trÃ©s",
                 "zero tres",
                 "zero trÃ©s"
               ]
    , examples (NumeralValue 4) ["cero cuatro", "zero cuatro"]
    , examples (NumeralValue 5) ["cero cinco", "zero cinco"]
    , examples (NumeralValue 6)
               [
                 "cero seis",
                 "cero sÃ©is",
                 "zero seis",
                 "zero sÃ©is"
               ]
    , examples (NumeralValue 7) ["cero siete", "zero siete"]
    , examples (NumeralValue 8) ["cero ocho", "zero ocho"]
    , examples (NumeralValue 9) ["cero nueve", "zero nueve"]
    , examples (NumeralValue 203) ["dos cientos tres", "doscientos tres"]
    , examples (NumeralValue 203_000) ["doscientos tres mil", "203 mil"]
    , examples (NumeralValue 4_000_000) ["cuatro millones", "4 millones"]
    , examples (NumeralValue 4_002_003) ["cuatro millones dos mil tres", "4 millones 2 mil 3"]
    ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-}

module Duckling.Numeral.ES.Rules (rules) where

import qualified Data.HashMap.Strict as HashMap
import Data.Maybe
import Data.String
import qualified Data.Text as Text
import Prelude

import Duckling.Dimensions.Types
import Duckling.Numeral.Helpers
import Duckling.Numeral.Types (NumeralData(..))
import qualified Duckling.Numeral.Types as TNumeral
import Duckling.Regex.Types
import Duckling.Types

zeroToFifteenMap :: HashMap.HashMap Text.Text Integer
zeroToFifteenMap =
  HashMap.fromList
    [ ("zero", 0)
    , ("cero", 0)
    , ("un", 1)
    , ("una", 1)
    , ("uno", 1)
    , ("dos", 2)
    , ("trÃ©s", 3)
    , ("tres", 3)
    , ("cuatro", 4)
    , ("cinco", 5)
    , ("seis", 6)
    , ("sÃ©is", 6)
    , ("siete", 7)
    , ("ocho", 8)
    , ("nueve", 9)
    , ("diez", 10)
    , ("dies", 10)
    , ("once", 11)
    , ("doce", 12)
    , ("trece", 13)
    , ("catorce", 14)
    , ("quince", 15)
    ]

ruleNumeralZeroToFifteen :: Rule
ruleNumeralZeroToFifteen = Rule
  { name = "number (0..15)"
  , pattern =
      [ regex
          "((c|z)ero|un(o|a)?|dos|tr(Ã©|e)s|cuatro|cinco|s(e|Ã©)is|siete|ocho|nueve|die(z|s)|once|doce|trece|catorce|quince)"
      ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        HashMap.lookup (Text.toLower match) zeroToFifteenMap >>= integer
      _ -> Nothing
  }

ruleBelowTenWithTwoDigits :: Rule
ruleBelowTenWithTwoDigits = Rule
  {
    name = "integer (0-9) with two digits"
  , pattern =
      [ regex "((c|z)ero)|0"
      , Predicate $ numberBetween 1 10
      ]
  , prod = \case
      (
        _:
        Token Numeral NumeralData { TNumeral.value = v }:
        _
        ) -> double v
      _ -> Nothing
  }

sixteenToTwentyNineMap :: HashMap.HashMap Text.Text Integer
sixteenToTwentyNineMap =
  HashMap.fromList
    [ ("dieciseis", 16)
    , ("diesisÃ©is", 16)
    , ("diesiseis", 16)
    , ("diecisÃ©is", 16)
    , ("diecisiete", 17)
    , ("dieciocho", 18)
    , ("diecinueve", 19)
    , ("veintiuno", 21)
    , ("veintiuna", 21)
    , ("veintidos", 22)
    , ("veintidÃ³s", 22)
    , ("veintitrÃ©s", 23)
    , ("veintitres", 23)
    , ("veinticuatro", 24)
    , ("veinticinco", 25)
    , ("veintisÃ©is", 26)
    , ("veintiseis", 26)
    , ("veintisiete", 27)
    , ("veintiocho", 28)
    , ("veintinueve", 29)
    ]

ruleNumeralSixteenToTwentyNine :: Rule
ruleNumeralSixteenToTwentyNine = Rule
  { name = "number (16..19 21..29)"
  , pattern =
      [ regex
          "(die(c|s)is(Ã©|e)is|diecisiete|dieciocho|diecinueve|veintiun(o|a)|veintid(o|Ã³)s|veintitr(Ã©|e)s|veinticuatro|veinticinco|veintis(Ã©|e)is|veintisiete|veintiocho|veintinueve|treinta)"
      ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        HashMap.lookup (Text.toLower match) sixteenToTwentyNineMap >>= integer
      _ -> Nothing
  }

ruleNumeralSixteenToNineteenWithDiez :: Rule
ruleNumeralSixteenToNineteenWithDiez = Rule
  { name = "number (16..19, two words)"
  , pattern =
      [ numberWith TNumeral.value (== 10)
      , regex "y"
      , Predicate $ numberBetween 6 10
      ]
  , prod = \case
      (_ : _ : Token Numeral NumeralData { TNumeral.value = v } : _) ->
        double $ 10 + v
      _ -> Nothing
  }

byTensMap :: HashMap.HashMap Text.Text Integer
byTensMap =
  HashMap.fromList
    [ ("veinte", 20)
    , ("treinta", 30)
    , ("cuarenta", 40)
    , ("cincuenta", 50)
    , ("sesenta", 60)
    , ("setenta", 70)
    , ("ochenta", 80)
    , ("noventa", 90)
    ]

ruleNumeralTwentyToNinetyTens :: Rule
ruleNumeralTwentyToNinetyTens = Rule
  { name = "number (20..90)"
  , pattern =
      [ regex
          "(veinte|treinta|cuarenta|cincuenta|sesenta|setenta|ochenta|noventa)"
      ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        HashMap.lookup (Text.toLower match) byTensMap >>= integer
      _ -> Nothing
  }

ruleNumeralTwentyOneToNinetyNine :: Rule
ruleNumeralTwentyOneToNinetyNine = Rule
  { name = "number (21..29 31..39 41..49 51..59 61..69 71..79 81..89 91..99)"
  , pattern =
      [ oneOf [20, 30 .. 90]
      , regex "y"
      , Predicate $ numberBetween 1 10
      ]
  , prod = \case
      (Token Numeral NumeralData { TNumeral.value = v1 } : _ : Token Numeral NumeralData { TNumeral.value = v2 } : _) ->
        double $ v1 + v2
      _ -> Nothing
  }

bigNumbersMap :: HashMap.HashMap Text.Text Integer
bigNumbersMap =
  HashMap.fromList
    [ ("cien", 100)
    , ("cientos", 100)
    , ("ciento", 100)
    , ("doscientos", 200)
    , ("trescientos", 300)
    , ("cuatrocientos", 400)
    , ("quinientos", 500)
    , ("seiscientos", 600)
    , ("setecientos", 700)
    , ("ochocientos", 800)
    , ("novecientos", 900)
    , ("mil", 1000)
    , ("millon", 1000000)
    , ("millÃ³n", 1000000)
    , ("un millon", 1000000)
    , ("un millÃ³n", 1000000)
    , ("millones", 1000000)
    -- Note: billion and larger is ambiguous becaouse of long vs short scale
    ]

ruleBigNumeral :: Rule
ruleBigNumeral = Rule
  { name = "big number 100 to 1K"
  , pattern =
      [ regex
          "(cien(to|tos)?|doscientos|trescientos|cuatrocientos|quinientos|seiscientos|setecientos|ochocientos|novecientos|(un )?mill(o|Ã³)n)"
      ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        HashMap.lookup (Text.toLower match) bigNumbersMap >>= integer
      _ -> Nothing
  }

ruleBigNumeralMultipliable :: Rule
ruleBigNumeralMultipliable = Rule
  { name = "1K or 1M in multipliable form"
  , pattern =
      [ regex
          "(mil(lones)?)"
      ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        HashMap.lookup (Text.toLower match) bigNumbersMap >>= integer >>= withMultipliable
      _ -> Nothing
  }

ruleTwoPartHundreds :: Rule
ruleTwoPartHundreds = Rule
 { name = "2..9 cientos"
 , pattern =
     [ Predicate $ numberBetween 2 10
     , regex "cientos"
     ]
  , prod = \case
      (Token Numeral NumeralData { TNumeral.value = v1 } : _ : _) ->
        double $ 100 * v1
      _ -> Nothing
  }

ruleNumeralHundredsAndSmaller :: Rule
ruleNumeralHundredsAndSmaller = Rule
  { name = "<hundreds> 0..99"
  , pattern =
      [ numberWith TNumeral.value (TNumeral.isMultiple 100)
      , Predicate $ numberBetween 0 100
      ]
  , prod = \case
      (Token Numeral NumeralData { TNumeral.value = v1 } : Token Numeral NumeralData { TNumeral.value = v2 } : _)
        | v1 > 0 && v1 < 1000 -> double $ v1 + v2
      _ -> Nothing
  }

ruleNumeralMultiply :: Rule
ruleNumeralMultiply = Rule
  { name = "2..999 <multipliable>"
  , pattern =
      [ Predicate $ numberBetween 2 1000
      , Predicate isMultipliable
      ]
  , prod = \case
      (Token Numeral NumeralData { TNumeral.value = v1 } : Token Numeral NumeralData { TNumeral.value = v2 } : _) ->
        double $ v1 * v2
      _ -> Nothing
  }

ruleNumeralThousandsAnd :: Rule
ruleNumeralThousandsAnd = Rule
  { name = "<thousands> 0..999"
  , pattern =
      [ numberWith TNumeral.value (TNumeral.isMultiple 1000)
      , Predicate $ numberBetween 0 999
      ]
  , prod = \case
      (Token Numeral NumeralData { TNumeral.value = v1 } : Token Numeral NumeralData { TNumeral.value = v2 } : _)
       | 0 < v1 && v1 < 1000000 -> double $ v1 + v2
      _ -> Nothing
  }

ruleNumeralMillionsAnd :: Rule
ruleNumeralMillionsAnd = Rule
  { name = "<millions> 0..999999"
  , pattern =
      [ numberWith TNumeral.value (TNumeral.isMultiple 1000000)
      , Predicate $ numberBetween 0 999999
      ]
  , prod = \case
      (Token Numeral NumeralData { TNumeral.value = v1 } : Token Numeral NumeralData { TNumeral.value = v2 } : _)
       | 0 < v1 -> double $ v1 + v2
      _ -> Nothing
  }

ruleNumeralDotNumeral :: Rule
ruleNumeralDotNumeral = Rule
  { name = "number dot number"
  , pattern = [dimension Numeral, regex "(co(n|ma)|punto)", Predicate $ not . hasGrain]
  , prod = \case
      (Token Numeral NumeralData { TNumeral.value = v1 } : _ : Token Numeral NumeralData { TNumeral.value = v2 } : _) ->
        double $ v1 + decimalsToDouble v2
      _ -> Nothing
  }

ruleLeadingDotNumeral :: Rule
ruleLeadingDotNumeral = Rule
  { name = "dot number"
  , pattern = [regex "coma|punto", Predicate $ not . hasGrain]
  , prod = \case
      (_:Token Numeral NumeralData{TNumeral.value = v}:_) ->
        double $ decimalsToDouble v
      _ -> Nothing
  }

ruleNumeralsSuffixesKMG :: Rule
ruleNumeralsSuffixesKMG = Rule
  { name = "numbers suffixes (K, M, G)"
  , pattern = [dimension Numeral, regex "([kmg])(?=[\\W\\$â‚¬]|$)"]
  , prod = \case
      (Token Numeral NumeralData { TNumeral.value = v } : Token RegexMatch (GroupMatch (match : _)) : _) ->
        case Text.toLower match of
          "k" -> double $ v * 1e3
          "m" -> double $ v * 1e6
          "g" -> double $ v * 1e9
          _ -> Nothing
      _ -> Nothing
  }

ruleNumeralsPrefixWithNegativeOrMinus :: Rule
ruleNumeralsPrefixWithNegativeOrMinus = Rule
  { name = "numbers prefix with -, negative or minus"
  , pattern = [regex "-|menos|negativ(o|a)", Predicate isPositive]
  , prod = \case
      (_ : Token Numeral NumeralData { TNumeral.value = v } : _) ->
        double $ v * (-1)
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleNumeralZeroToFifteen
  , ruleBelowTenWithTwoDigits
  , ruleNumeralSixteenToTwentyNine
  , ruleNumeralSixteenToNineteenWithDiez
  , ruleNumeralTwentyToNinetyTens
  , ruleNumeralTwentyOneToNinetyNine
  , ruleBigNumeral
  , ruleBigNumeralMultipliable
  , ruleNumeralMultiply
  , ruleNumeralThousandsAnd
  , ruleNumeralMillionsAnd
  , ruleTwoPartHundreds
  , ruleNumeralHundredsAndSmaller
  , ruleNumeralDotNumeral
  , ruleLeadingDotNumeral
  , ruleNumeralsSuffixesKMG
  , ruleNumeralsPrefixWithNegativeOrMinus
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.ES.AR.Corpus (allExamples) where

import Data.String
import Prelude

import Duckling.Numeral.Types
import Duckling.Testing.Types

allExamples :: [Example]
allExamples =
  concat
    [ examples (NumeralValue 1) ["1"]
    , examples (NumeralValue 33) ["33"]
    , examples (NumeralValue 1.1) ["1,1", "1,10", "01,10"]
    , examples (NumeralValue 0.77) ["0,77", ",77"]
    , examples (NumeralValue 100000) ["100.000", "100000"]
    , examples (NumeralValue 243) ["243"]
    , examples (NumeralValue 3000000) ["3000000", "3.000.000"]
    , examples (NumeralValue 1200000) ["1.200.000", "1200000"]
    , examples
        (NumeralValue (-1200000))
        ["- 1.200.000", "menos 1.200.000", "-1,2M", "-,0012G"]
    , examples (NumeralValue 1.5) ["1,5"]
    ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.ES.AR.Rules (rules) where

import Data.Maybe
import Data.String
import qualified Data.Text as Text
import Prelude

import Duckling.Dimensions.Types
import Duckling.Numeral.Helpers
import Duckling.Regex.Types
import Duckling.Types

ruleDecimalWithThousandsSeparator :: Rule
ruleDecimalWithThousandsSeparator = Rule
  { name = "decimal with thousands separator ."
  , pattern = [regex "(\\d+(\\.\\d\\d\\d)+,\\d+)"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        let fmt = Text.replace "," "." $ Text.replace "." Text.empty match
        in parseDouble fmt >>= double
      _ -> Nothing
  }

ruleDecimalNumeral :: Rule
ruleDecimalNumeral = Rule
  { name = "decimal number ,"
  , pattern = [regex "(\\d*,\\d+)"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        parseDecimal False match
      _ -> Nothing
  }

ruleIntegerWithThousandsSeparator :: Rule
ruleIntegerWithThousandsSeparator = Rule
  { name = "integer with thousands separator ."
  , pattern = [regex "(\\d{1,3}(\\.\\d\\d\\d){1,5})"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        parseDouble (Text.replace "." Text.empty match) >>= double
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDecimalNumeral
  , ruleDecimalWithThousandsSeparator
  , ruleIntegerWithThousandsSeparator
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.ES.CL.Corpus (allExamples) where

import Data.String
import Prelude

import Duckling.Numeral.Types
import Duckling.Testing.Types

allExamples :: [Example]
allExamples =
  concat
    [ examples (NumeralValue 1) ["1"]
    , examples (NumeralValue 33) ["33"]
    , examples (NumeralValue 1.1) ["1,1", "1,10", "01,10"]
    , examples (NumeralValue 0.77) ["0,77", ",77"]
    , examples (NumeralValue 100000) ["100.000", "100000"]
    , examples (NumeralValue 243) ["243"]
    , examples (NumeralValue 3000000) ["3000000", "3.000.000"]
    , examples (NumeralValue 1200000) ["1.200.000", "1200000"]
    , examples
        (NumeralValue (-1200000))
        ["- 1.200.000", "menos 1.200.000", "-1,2M", "-,0012G"]
    , examples (NumeralValue 1.5) ["1,5"]
    ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.ES.CL.Rules (rules) where

import Data.Maybe
import Data.String
import qualified Data.Text as Text
import Prelude

import Duckling.Dimensions.Types
import Duckling.Numeral.Helpers
import Duckling.Regex.Types
import Duckling.Types

ruleDecimalWithThousandsSeparator :: Rule
ruleDecimalWithThousandsSeparator = Rule
  { name = "decimal with thousands separator ."
  , pattern = [regex "(\\d+(\\.\\d\\d\\d)+,\\d+)"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        let fmt = Text.replace "," "." $ Text.replace "." Text.empty match
        in parseDouble fmt >>= double
      _ -> Nothing
  }

ruleDecimalNumeral :: Rule
ruleDecimalNumeral = Rule
  { name = "decimal number ,"
  , pattern = [regex "(\\d*,\\d+)"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        parseDecimal False match
      _ -> Nothing
  }

ruleIntegerWithThousandsSeparator :: Rule
ruleIntegerWithThousandsSeparator = Rule
  { name = "integer with thousands separator ."
  , pattern = [regex "(\\d{1,3}(\\.\\d\\d\\d){1,5})"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        parseDouble (Text.replace "." Text.empty match) >>= double
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDecimalNumeral
  , ruleDecimalWithThousandsSeparator
  , ruleIntegerWithThousandsSeparator
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.ES.CO.Corpus (allExamples) where

import Data.String
import Prelude

import Duckling.Numeral.Types
import Duckling.Testing.Types

allExamples :: [Example]
allExamples =
  concat
    [ examples (NumeralValue 1) ["1"]
    , examples (NumeralValue 33) ["33"]
    , examples (NumeralValue 1.1) ["1,1", "1,10", "01,10"]
    , examples (NumeralValue 0.77) ["0,77", ",77"]
    , examples (NumeralValue 100000) ["100.000", "100000"]
    , examples (NumeralValue 243) ["243"]
    , examples (NumeralValue 3000000) ["3000000", "3.000.000"]
    , examples (NumeralValue 1200000) ["1.200.000", "1200000"]
    , examples
        (NumeralValue (-1200000))
        ["- 1.200.000", "menos 1.200.000", "-1,2M", "-,0012G"]
    , examples (NumeralValue 1.5) ["1,5"]
    ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.ES.CO.Rules (rules) where

import Data.Maybe
import Data.String
import qualified Data.Text as Text
import Prelude

import Duckling.Dimensions.Types
import Duckling.Numeral.Helpers
import Duckling.Regex.Types
import Duckling.Types

ruleDecimalWithThousandsSeparator :: Rule
ruleDecimalWithThousandsSeparator = Rule
  { name = "decimal with thousands separator ."
  , pattern = [regex "(\\d+(\\.\\d\\d\\d)+,\\d+)"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        let fmt = Text.replace "," "." $ Text.replace "." Text.empty match
        in parseDouble fmt >>= double
      _ -> Nothing
  }

ruleDecimalNumeral :: Rule
ruleDecimalNumeral = Rule
  { name = "decimal number ,"
  , pattern = [regex "(\\d*,\\d+)"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        parseDecimal False match
      _ -> Nothing
  }

ruleIntegerWithThousandsSeparator :: Rule
ruleIntegerWithThousandsSeparator = Rule
  { name = "integer with thousands separator ."
  , pattern = [regex "(\\d{1,3}(\\.\\d\\d\\d){1,5})"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        parseDouble (Text.replace "." Text.empty match) >>= double
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDecimalNumeral
  , ruleDecimalWithThousandsSeparator
  , ruleIntegerWithThousandsSeparator
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.ES.ES.Corpus (allExamples) where

import Data.String
import Prelude

import Duckling.Numeral.Types
import Duckling.Testing.Types

allExamples :: [Example]
allExamples =
  concat
    [ examples (NumeralValue 1) ["1"]
    , examples (NumeralValue 33) ["33"]
    , examples (NumeralValue 1.1) ["1,1", "1,10", "01,10"]
    , examples (NumeralValue 0.77) ["0,77", ",77"]
    , examples (NumeralValue 100000) ["100.000", "100000"]
    , examples (NumeralValue 243) ["243"]
    , examples (NumeralValue 3000000) ["3000000", "3.000.000"]
    , examples (NumeralValue 1200000) ["1.200.000", "1200000"]
    , examples
        (NumeralValue (-1200000))
        ["- 1.200.000", "menos 1.200.000", "-1,2M", "-,0012G"]
    , examples (NumeralValue 1.5) ["1,5"]
    ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.ES.ES.Rules
  ( rules
  , rulesBackwardCompatible
  ) where

import qualified Data.HashMap.Strict as HashMap
import Data.Maybe
import Data.String
import qualified Data.Text as Text
import Prelude

import Duckling.Dimensions.Types
import Duckling.Numeral.Helpers
import Duckling.Numeral.Types (NumeralData(..))
import qualified Duckling.Numeral.Types as TNumeral
import Duckling.Regex.Types
import Duckling.Types

ruleDecimalWithThousandsSeparator :: Rule
ruleDecimalWithThousandsSeparator = Rule
  { name = "decimal with thousands separator ."
  , pattern = [regex "(\\d+(\\.\\d\\d\\d)+,\\d+)"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        let fmt = Text.replace "," "." $ Text.replace "." Text.empty match
        in parseDouble fmt >>= double
      _ -> Nothing
  }

ruleDecimalNumeral :: Rule
ruleDecimalNumeral = Rule
  { name = "decimal number ,"
  , pattern = [regex "(\\d*,\\d+)"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        parseDecimal False match
      _ -> Nothing
  }

ruleIntegerWithThousandsSeparator :: Rule
ruleIntegerWithThousandsSeparator = Rule
  { name = "integer with thousands separator ."
  , pattern = [regex "(\\d{1,3}(\\.\\d\\d\\d){1,5})"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        parseDouble (Text.replace "." Text.empty match) >>= double
      _ -> Nothing
  }

rulesBackwardCompatible :: [Rule]
rulesBackwardCompatible =
  [ ruleDecimalNumeral
  , ruleDecimalWithThousandsSeparator
  , ruleIntegerWithThousandsSeparator
  ]

rules :: [Rule]
rules = rulesBackwardCompatible


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.ES.MX.Corpus (allExamples) where

import Data.String
import Prelude

import Duckling.Numeral.Types
import Duckling.Testing.Types

allExamples :: [Example]
allExamples =
  concat
    [ examples (NumeralValue 1) ["1"]
    , examples (NumeralValue 33) ["33"]
    , examples (NumeralValue 1.1) ["1.1", "1.10", "01.10"]
    , examples (NumeralValue 0.77) ["0.77", ".77"]
    , examples (NumeralValue 100000) ["100,000", "100000"]
    , examples (NumeralValue 243) ["243"]
    , examples (NumeralValue 3000000) ["3000000", "3,000,000"]
    , examples (NumeralValue 1200000) ["1,200,000", "1200000"]
    , examples
        (NumeralValue (-1200000))
        ["- 1,200,000", "menos 1,200,000", "-1.2M", "-.0012G"]
    , examples (NumeralValue 1.5) ["1.5"]
    ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.ES.MX.Rules (rules) where

import qualified Data.HashMap.Strict as HashMap
import Data.Maybe
import Data.String
import qualified Data.Text as Text
import Prelude

import Duckling.Dimensions.Types
import Duckling.Numeral.Helpers
import Duckling.Regex.Types
import Duckling.Types

ruleDecimalWithThousandsSeparator :: Rule
ruleDecimalWithThousandsSeparator = Rule
  { name = "decimal with thousands separator"
  , pattern = [regex "(\\d+(\\,\\d\\d\\d)+\\.\\d+)"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        parseDouble (Text.replace "," Text.empty match) >>= double
      _ -> Nothing
  }

ruleDecimalNumeral :: Rule
ruleDecimalNumeral = Rule
  { name = "decimal number ."
  , pattern = [regex "(\\d*\\.\\d+)"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        parseDecimal False match
      _ -> Nothing
  }

ruleIntegerWithThousandsSeparator :: Rule
ruleIntegerWithThousandsSeparator = Rule
  { name = "integer with thousands separator ,"
  , pattern = [regex "(\\d{1,3}(\\,\\d\\d\\d){1,5})"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        parseDouble (Text.replace "," Text.empty match) >>= double
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDecimalNumeral
  , ruleDecimalWithThousandsSeparator
  , ruleIntegerWithThousandsSeparator
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.ES.PE.Corpus (allExamples) where

import Data.String
import Prelude

import Duckling.Numeral.Types
import Duckling.Testing.Types

allExamples :: [Example]
allExamples =
  concat
    [ examples (NumeralValue 1) ["1"]
    , examples (NumeralValue 33) ["33"]
    , examples (NumeralValue 1.1) ["1,1", "1,10", "01,10"]
    , examples (NumeralValue 0.77) ["0,77", ",77"]
    , examples (NumeralValue 100000) ["100.000", "100000"]
    , examples (NumeralValue 243) ["243"]
    , examples (NumeralValue 3000000) ["3000000", "3.000.000"]
    , examples (NumeralValue 1200000) ["1.200.000", "1200000"]
    , examples
        (NumeralValue (-1200000))
        ["- 1.200.000", "menos 1.200.000", "-1,2M", "-,0012G"]
    , examples (NumeralValue 1.5) ["1,5"]
    ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.ES.PE.Rules (rules) where

import Data.Maybe
import Data.String
import qualified Data.Text as Text
import Prelude

import Duckling.Dimensions.Types
import Duckling.Numeral.Helpers
import Duckling.Regex.Types
import Duckling.Types

ruleDecimalWithThousandsSeparator :: Rule
ruleDecimalWithThousandsSeparator = Rule
  { name = "decimal with thousands separator ."
  , pattern = [regex "(\\d+(\\.\\d\\d\\d)+,\\d+)"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        let fmt = Text.replace "," "." $ Text.replace "." Text.empty match
        in parseDouble fmt >>= double
      _ -> Nothing
  }

ruleDecimalNumeral :: Rule
ruleDecimalNumeral = Rule
  { name = "decimal number ,"
  , pattern = [regex "(\\d*,\\d+)"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        parseDecimal False match
      _ -> Nothing
  }

ruleIntegerWithThousandsSeparator :: Rule
ruleIntegerWithThousandsSeparator = Rule
  { name = "integer with thousands separator ."
  , pattern = [regex "(\\d{1,3}(\\.\\d\\d\\d){1,5})"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        parseDouble (Text.replace "." Text.empty match) >>= double
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDecimalNumeral
  , ruleDecimalWithThousandsSeparator
  , ruleIntegerWithThousandsSeparator
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.ES.VE.Corpus (allExamples) where

import Data.String
import Prelude

import Duckling.Numeral.Types
import Duckling.Testing.Types

allExamples :: [Example]
allExamples =
  concat
    [ examples (NumeralValue 1) ["1"]
    , examples (NumeralValue 33) ["33"]
    , examples (NumeralValue 1.1) ["1,1", "1,10", "01,10"]
    , examples (NumeralValue 0.77) ["0,77", ",77"]
    , examples (NumeralValue 100000) ["100.000", "100000"]
    , examples (NumeralValue 243) ["243"]
    , examples (NumeralValue 3000000) ["3000000", "3.000.000"]
    , examples (NumeralValue 1200000) ["1.200.000", "1200000"]
    , examples
        (NumeralValue (-1200000))
        ["- 1.200.000", "menos 1.200.000", "-1,2M", "-,0012G"]
    , examples (NumeralValue 1.5) ["1,5"]
    ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.ES.VE.Rules (rules) where

import Data.Maybe
import Data.String
import qualified Data.Text as Text
import Prelude

import Duckling.Dimensions.Types
import Duckling.Numeral.Helpers
import Duckling.Regex.Types
import Duckling.Types

ruleDecimalWithThousandsSeparator :: Rule
ruleDecimalWithThousandsSeparator = Rule
  { name = "decimal with thousands separator ."
  , pattern = [regex "(\\d+(\\.\\d\\d\\d)+,\\d+)"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        let fmt = Text.replace "," "." $ Text.replace "." Text.empty match
        in parseDouble fmt >>= double
      _ -> Nothing
  }

ruleDecimalNumeral :: Rule
ruleDecimalNumeral = Rule
  { name = "decimal number ,"
  , pattern = [regex "(\\d*,\\d+)"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        parseDecimal False match
      _ -> Nothing
  }

ruleIntegerWithThousandsSeparator :: Rule
ruleIntegerWithThousandsSeparator = Rule
  { name = "integer with thousands separator ."
  , pattern = [regex "(\\d{1,3}(\\.\\d\\d\\d){1,5})"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match : _)) : _) ->
        parseDouble (Text.replace "." Text.empty match) >>= double
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDecimalNumeral
  , ruleDecimalWithThousandsSeparator
  , ruleIntegerWithThousandsSeparator
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE OverloadedStrings #-}

module Duckling.Numeral.ET.Corpus
  ( corpus ) where

import Prelude
import Data.String

import Duckling.Locale
import Duckling.Numeral.Types
import Duckling.Resolve
import Duckling.Testing.Types

corpus :: Corpus
corpus = (testContext {locale = makeLocale ET Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (NumeralValue 0)
             [ "0"
             , "null"
             ]
  , examples (NumeralValue 1)
             [ "1"
             , "Ã¼ks"
             ]
  , examples (NumeralValue 33)
             [ "33"
             , "KolmkÃ¼mmend kolm"
             ]
  , examples (NumeralValue 14)
             [ "14"
             , "neliteist"
             ]
  , examples (NumeralValue 16)
             [ "16"
             , "kuusteist"
             ]
  , examples (NumeralValue 17)
             [ "17"
             , "Seitseteist"
             ]
  , examples (NumeralValue 18)
             [ "18"
             , "kaheksateist"
             ]
  , examples (NumeralValue 1.1)
             [ "1.1"
             , "1.10"
             , "01.10"
             ]
  , examples (NumeralValue 0.77)
             [ "0.77"
             , ".77"
             ]
  , examples (NumeralValue 100000)
             [ "100,000"
             , "100000"
             , "100K"
             , "100k"
             , "100 000"
             ]
  , examples (NumeralValue 3000000)
             [ "3M"
             , "3000K"
             , "3000000"
             , "3,000,000"
             , "3 000 000"
             ]
  , examples (NumeralValue 1200000)
             [ "1,200,000"
             , "1200000"
             , "1.2M"
             , "1200K"
             , ".0012G"
             , "1 200 000"
             ]
  , examples (NumeralValue (-1200000))
             [ "- 1,200,000"
             , "-1200000"
             , "miinus 1,200,000"
             , "-1.2M"
             , "-1200K"
             , "-.0012G"
             ]
  , examples (NumeralValue 5000)
             [ "viis tuhat"
             ]
  , examples (NumeralValue 200000)
             [ "kakssada tuhat"
             ]
  , examples (NumeralValue 21011)
             [ "kakskÃ¼mmend Ã¼ks Tuhat Ã¼ksteist"
             ]
  , examples (NumeralValue 721012)
             [ "seitsesada kakskÃ¼mmend Ã¼ks tuhat kaksteist"
             ]
  , examples (NumeralValue 31256721)
             [ "kolmkÃ¼mmend Ã¼ks miljonit kakssada viiskÃ¼mmend kuus tuhat seitsesada kakskÃ¼mmend Ã¼ks"
             ]
  ]


-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.

{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}

module Duckling.Numeral.ET.Rules
  ( rules
  ) where

import Data.HashMap.Strict (HashMap)
import Data.Maybe
import Data.String
import Data.Text (Text)
import Prelude
import qualified Data.HashMap.Strict as HashMap
import qualified Data.Text as Text

import Duckling.Dimensions.Types
import Duckling.Numeral.Helpers
import Duckling.Numeral.Types (NumeralData (..))
import Duckling.Regex.Types
import Duckling.Types
import qualified Duckling.Numeral.Types as TNumeral

ruleNumeralsPrefixWithNegativeOrMinus :: Rule
ruleNumeralsPrefixWithNegativeOrMinus = Rule
  { name = "numbers prefix with -, negative or minus"
  , pattern =
    [ regex "-|miinus|negatiivne"
    , Predicate isPositive
    ]
  , prod = \tokens -> case tokens of
      (_:
       Token Numeral NumeralData{TNumeral.value = v}:
       _) -> double $ v * (-1)
      _ -> Nothing
  }

ruleACoupleOf :: Rule
ruleACoupleOf = Rule
  { name = "a couple of"
  , pattern =
    [ regex "paar"
    ]
  , prod = \_ -> integer 2
  }

ruleTen :: Rule
ruleTen = Rule
  { name = "ten"
  , pattern =
    [ regex "kÃ¼mme"
    ]
  , prod = \_ -> integer 10 >>= withGrain 1
  }

ruleDecimalWithThousandsSeparator :: Rule
ruleDecimalWithThousandsSeparator = Rule
  { name = "decimal with thousands separator"
  , pattern =
    [ regex "(\\d+(,\\d\\d\\d)+\\.\\d+)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        parseDouble (Text.replace (Text.singleton ',') Text.empty match) >>= double
      _ -> Nothing
  }

ruleDecimalNumeral :: Rule
ruleDecimalNumeral = Rule
  { name = "decimal number"
  , pattern =
    [ regex "(\\d*\\.\\d+)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) -> parseDecimal True match
      _ -> Nothing
  }

ruleInteger3 :: Rule
ruleInteger3 = Rule
  { name = "integer 21..99"
  , pattern =
    [ oneOf [70, 20, 60, 50, 40, 90, 30, 80]
    , Predicate $ numberBetween 1 10
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v1}:
       Token Numeral NumeralData{TNumeral.value = v2}:_) -> double $ v1 + v2
      _ -> Nothing
  }

ruleAFew :: Rule
ruleAFew = Rule
  { name = "(a )?few"
  , pattern =
    [ regex "mÃµni"
    ]
  , prod = \_ -> integer 3
  }

rulePowersOfTen :: Rule
rulePowersOfTen = Rule
  { name = "powers of tens"
  , pattern = [regex "(sada|tuhat|miljoni?t?)"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) -> case Text.toLower match of
        "sada"  -> double 1e2 >>= withGrain 2 >>= withMultipliable
        "tuhat" -> double 1e3 >>= withGrain 3 >>= withMultipliable
        _       -> double 1e6 >>= withGrain 6 >>= withMultipliable
      _ -> Nothing
  }

ruleIntersect :: Rule
ruleIntersect = Rule
  { name = "intersect"
  , pattern =
    [ Predicate hasGrain
    , Predicate $ and . sequence [not . isMultipliable, isPositive]
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = val1, TNumeral.grain = Just g}:
       Token Numeral NumeralData{TNumeral.value = val2}:
       _) | (10 ** fromIntegral g) > val2 -> double $ val1 + val2
      _ -> Nothing
  }

ruleNumeralsSuffixesKMG :: Rule
ruleNumeralsSuffixesKMG = Rule
  { name = "numbers suffixes (K, M, G)"
  , pattern =
    [ dimension Numeral
    , regex "([kmg])(?=[\\W\\$â‚¬]|$)"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:
       Token RegexMatch (GroupMatch (match:_)):
       _) -> case Text.toLower match of
          "k" -> double $ v * 1e3
          "m" -> double $ v * 1e6
          "g" -> double $ v * 1e9
          _   -> Nothing
      _ -> Nothing
  }

ruleMultiply :: Rule
ruleMultiply = Rule
  { name = "compose by multiplication"
  , pattern =
    [ dimension Numeral
    , Predicate isMultipliable
    ]
  , prod = \tokens -> case tokens of
      (token1:token2:_) -> multiply token1 token2
      _ -> Nothing
  }

zeroNineteenMap :: HashMap Text Integer
zeroNineteenMap = HashMap.fromList
  [ ("null", 0)
  , ("Ã¼ks", 1)
  , ("kaks", 2)
  , ("kolm", 3)
  , ("neli", 4)
  , ("viis", 5)
  , ("kuus", 6)
  , ("seitse", 7)
  , ("kaheksa", 8)
  , ("Ã¼heksa", 9)
  , ("kÃ¼mme", 10)
  , ("Ã¼ksteist", 11)
  , ("kaksteist", 12)
  , ("kolmteist", 13)
  , ("neliteist", 14)
  , ("viisteist", 15)
  , ("kuusteist", 16)
  , ("seitseteist", 17)
  , ("kaheksateist", 18)
  , ("Ã¼heksateist", 19)
  ]

ruleInteger :: Rule
ruleInteger = Rule
  { name = "integer (0..19)"
  , pattern =
    [ regex "(null|Ã¼ksteist|Ã¼ks|kaksteist|kaks|kolmteist|kolm|neliteist|neli|viisteist|viis|kuusteist|kuus|seitseteist|seitse|kaheksateist|kaheksa|Ã¼heksateist|Ã¼heksa|kÃ¼mme)"
    ]
  , prod = \tokens -> case tokens of
    (Token RegexMatch (GroupMatch (match:_)):_) ->
      HashMap.lookup (Text.toLower match) zeroNineteenMap >>= integer
    _ -> Nothing
  }

ruleInteger4 :: Rule
ruleInteger4 = Rule
  { name = "integer (200..900)"
  , pattern =
    [ regex "(kakssada|kolmsada|nelisada|viissada|kuussada|seitsesada|kaheksasada|Ã¼heksasada)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) -> case Text.toLower match of
        "kakssada"        -> integer 200 >>= withGrain 2 >>= withMultipliable
        "kolmsada"        -> integer 300 >>= withGrain 2 >>= withMultipliable
        "nelisada"        -> integer 400 >>= withGrain 2 >>= withMultipliable
        "viissada"        -> integer 500 >>= withGrain 2 >>= withMultipliable
        "kuussada"        -> integer 600 >>= withGrain 2 >>= withMultipliable
        "seitsesada"      -> integer 700 >>= withGrain 2 >>= withMultipliable
        "kaheksasada"     -> integer 800 >>= withGrain 2 >>= withMultipliable
        "Ã¼heksasada" -> integer 900 >>= withGrain 2 >>= withMultipliable
        _ -> Nothing
      _ -> Nothing
  }

twentyNinetyMap :: HashMap Text Integer
twentyNinetyMap = HashMap.fromList
  [ ("kakskÃ¼mmend", 20)
  , ("kolmkÃ¼mmend", 30)
  , ("nelikÃ¼mmend", 40)
  , ("viiskÃ¼mmend", 50)
  , ("kuuskÃ¼mmend", 60)
  , ("seitsekÃ¼mmend", 70)
  , ("kaheksakÃ¼mmend", 80)
  , ("Ã¼heksakÃ¼mmend", 90)
  ]

ruletwentyNinety :: Rule
ruletwentyNinety = Rule
  { name = "integer (20..90)"
  , pattern =
    [ regex "((kaks|kolm|neli|viis|kuus|seitse|kaheksa|(Ã¼)heksa)k(Ã¼)mmend)"
    ]
    , prod = \tokens -> case tokens of
        (Token RegexMatch (GroupMatch (match:_)):_) ->
          HashMap.lookup (Text.toLower match) twentyNinetyMap >>= integer
        _ -> Nothing
  }

ruleNumeralDotNumeral :: Rule
ruleNumeralDotNumeral = Rule
  { name = "number dot number"
  , pattern =
    [ dimension Numeral
    , regex "dot|point"
    , Predicate $ not . hasGrain
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v1}:
       _:
       Token Numeral NumeralData{TNumeral.value = v2}:
       _) -> double $ v1 + decimalsToDouble v2
      _ -> Nothing
  }

ruleIntegerWithThousandsSeparator :: Rule
ruleIntegerWithThousandsSeparator = Rule
  { name = "integer with thousands separator ,"
  , pattern =
    [ regex "(\\d{1,3}(([, ])\\d\\d\\d){1,5})"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_:sep:_)):_) ->
        parseDouble (Text.replace sep Text.empty match) >>= double
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleACoupleOf
  , ruleAFew
  , ruleDecimalNumeral
  , ruleDecimalWithThousandsSeparator
  , ruleInteger
  , ruletwentyNinety
  , ruleInteger3
  , ruleInteger4
  , ruleIntegerWithThousandsSeparator
  , ruleIntersect
  , ruleMultiply
  , ruleNumeralDotNumeral
  , ruleNumeralsPrefixWithNegativeOrMinus
  , ruleNumeralsSuffixesKMG
  , rulePowersOfTen
  , ruleTen
  ]