-- 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.FI.Corpus
  ( corpus
  ) where

import Data.String
import Prelude

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

context :: Context
context = testContext {locale = makeLocale FI Nothing}

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

allExamples :: [Example]
allExamples = concat
  [ examples (NumeralValue 0)
             [ "0"
             , "nolla"
             ]
  , examples (NumeralValue 1)
             [ "1"
             , "yksi"
             ]
  , examples (NumeralValue 2)
             [ "kaksi"
             ]
  , examples (NumeralValue 3)
             [ "kolme"
             ]
  , examples (NumeralValue 4)
             [ "neljÃ¤"
             ]
  , examples (NumeralValue 5)
             [ "viisi"
             ]
  , examples (NumeralValue 6)
             [ "kuusi"
             ]
  , examples (NumeralValue 7)
             [ "seitsemÃ¤n"
             ]
  , examples (NumeralValue 8)
             [ "kahdeksan"
             ]
  , examples (NumeralValue 9)
             [ "yhdeksÃ¤n"
             ]
  , examples (NumeralValue 10)
             [ "10"
             , "kymmenen"
             ]
  , examples (NumeralValue 11)
             [ "yksitoista"
             ]
  , examples (NumeralValue 12)
             [ "kaksitoista"
             ]
  , examples (NumeralValue 13)
             [ "kolmetoista"
             ]
  , examples (NumeralValue 14)
             [ "neljÃ¤toista"
             ]
  , examples (NumeralValue 15)
             [ "viisitoista"
             ]
  , examples (NumeralValue 16)
             [ "kuusitoista"
             ]
  , examples (NumeralValue 17)
             [ "seitsemÃ¤ntoista"
             ]
  , examples (NumeralValue 18)
             [ "kahdeksantoista"
             ]
  , examples (NumeralValue 19)
             [ "yhdeksÃ¤ntoista"
             ]
  , examples (NumeralValue 20)
             [ "20"
             , "kaksikymmentÃ¤"
             ]
  , examples (NumeralValue 24)
             [ "24"
             , "kaksikymmentÃ¤neljÃ¤"
             ]
  , examples (NumeralValue 35)
             [ "kolmekymmentÃ¤viisi"
             ]
  , examples (NumeralValue 42)
             [ "neljÃ¤kymmentÃ¤kaksi"
             ]
  , examples (NumeralValue 52)
             [ "viisikymmentÃ¤kaksi"
             ]
  , examples (NumeralValue 62)
             [ "kuusikymmentÃ¤kaksi"
             ]
  , examples (NumeralValue 72)
             [ "seitsemÃ¤nkymmentÃ¤kaksi"
             ]
  , examples (NumeralValue 82)
             [ "kahdeksankymmentÃ¤kaksi"
             ]
  , examples (NumeralValue 99)
             [ "99"
             , "yhdeksÃ¤nkymmentÃ¤yhdeksÃ¤n"
             ]
  ]


-- 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 #-}
{-# LANGUAGE NoRebindableSyntax #-}

module Duckling.Numeral.FI.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

ruleIntegerNumeric :: Rule
ruleIntegerNumeric = Rule
  { name = "integer (numeric)"
  , pattern =
    [ regex "(\\d{1,18})"
    ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) -> do
        v <- parseInt match
        integer $ toInteger v
      _ -> Nothing
  }

numeralMap :: HashMap Text Integer
numeralMap = HashMap.fromList
  [ ( "nolla", 0 )
  , ( "yksi", 1 )
  , ( "kaksi", 2 )
  , ( "kolme", 3 )
  , ( "neljÃ¤", 4 )
  , ( "viisi", 5 )
  , ( "kuusi", 6 )
  , ( "seitsemÃ¤n", 7 )
  , ( "kahdeksan", 8 )
  , ( "yhdeksÃ¤n", 9 )
  , ( "kymmenen", 10 )
  ]

ruleNumeral :: Rule
ruleNumeral = Rule
  { name = "number (0..10)"
  , pattern =
    [ regex "(nolla|yksi|kaksi|kolme|neljÃ¤|viisi|kuusi|seitsemÃ¤n|kahdeksan|yhdeksÃ¤n|kymmenen)"
    ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) numeralMap >>= integer
      _ -> Nothing
  }

elevenToNineteenMap :: HashMap Text Integer
elevenToNineteenMap = HashMap.fromList
  [ ( "yksitoista", 11 )
  , ( "kaksitoista", 12 )
  , ( "kolmetoista", 13 )
  , ( "neljÃ¤toista", 14 )
  , ( "viisitoista", 15 )
  , ( "kuusitoista", 16 )
  , ( "seitsemÃ¤ntoista", 17 )
  , ( "kahdeksantoista", 18 )
  , ( "yhdeksÃ¤ntoista", 19 )
  ]

ruleElevenToNineteen :: Rule
ruleElevenToNineteen = Rule
  { name = "number (11..19)"
  , pattern =
    [ regex "(yksitoista|kaksitoista|kolmetoista|neljÃ¤toista|viisitoista|kuusitoista|seitsemÃ¤ntoista|kahdeksantoista|yhdeksÃ¤ntoista)"
    ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) elevenToNineteenMap >>= integer
      _ -> Nothing
  }

tensMap :: HashMap Text Integer
tensMap = HashMap.fromList
  [ ( "kaksikymmentÃ¤", 20 )
  , ( "kolmekymmentÃ¤", 30 )
  , ( "neljÃ¤kymmentÃ¤", 40 )
  , ( "viisikymmentÃ¤", 50 )
  , ( "kuusikymmentÃ¤", 60 )
  , ( "seitsemÃ¤nkymmentÃ¤", 70 )
  , ( "kahdeksankymmentÃ¤", 80 )
  , ( "yhdeksÃ¤nkymmentÃ¤", 90 )
  ]

ruleTens :: Rule
ruleTens = Rule
  { name = "integer (20,30..90)"
  , pattern =
    [ regex "(kaksikymmentÃ¤|kolmekymmentÃ¤|neljÃ¤kymmentÃ¤|viisikymmentÃ¤|kuusikymmentÃ¤|seitsemÃ¤nkymmentÃ¤|kahdeksankymmentÃ¤|yhdeksÃ¤nkymmentÃ¤)"
    ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) tensMap >>= integer
      _ -> Nothing
  }

ruleCompositeTens :: Rule
ruleCompositeTens = Rule
  { name = "integer ([2-9][1-9])"
  , pattern =
    [ regex "(kaksikymmentÃ¤|kolmekymmentÃ¤|neljÃ¤kymmentÃ¤|viisikymmentÃ¤|kuusikymmentÃ¤|seitsemÃ¤nkymmentÃ¤|kahdeksankymmentÃ¤|yhdeksÃ¤nkymmentÃ¤)(yksi|kaksi|kolme|neljÃ¤|viisi|kuusi|seitsemÃ¤n|kahdeksan|yhdeksÃ¤n)"
    ]
  , prod = \case
      (Token RegexMatch (GroupMatch (m1:m2:_)):_) -> do
        v1 <- HashMap.lookup (Text.toLower m1) tensMap
        v2 <- HashMap.lookup (Text.toLower m2) numeralMap
        integer $ v1 + v2
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleIntegerNumeric
  , ruleNumeral
  , ruleElevenToNineteen
  , ruleTens
  , ruleCompositeTens
  ]


-- 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.FR.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 FR Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (NumeralValue 0)
             [ "0"
             , "zero"
             , "zÃ©ro"
             ]
  , examples (NumeralValue 1)
             [ "1"
             , "un"
             , "une"
             ]
  , examples (NumeralValue 11)
             [ "onze"
             ]
  , examples (NumeralValue 17)
             [ "dix sept"
             , "dix-sept"
             ]
  , examples (NumeralValue 21)
             [ "vingt et un"
             , "vingt-et-un"
             ]
  , examples (NumeralValue 23)
             [ "vingt trois"
             , "vingt-trois"
             ]
  , examples (NumeralValue 70)
             [ "soixante dix"
             ]
  , examples (NumeralValue 71)
             [ "soixante onze"
             ]
  , examples (NumeralValue 78)
             [ "soixante dix huit"
             ]
  , examples (NumeralValue 73)
             [ "soixante treize"
             ]
  , examples (NumeralValue 80)
             [ "quatre vingt"
             ]
  , examples (NumeralValue 81)
             [ "quatre vingt un"
             ]
  , examples (NumeralValue 82)
             [ "quatre vingt deux"
             ]
  , examples (NumeralValue 90)
             [ "quatre vingt dix"
             ]
  , examples (NumeralValue 91)
             [ "quatre vingt onze"
             ]
  , examples (NumeralValue 92)
             [ "quatre vingt douze"
             ]
  , examples (NumeralValue 99)
             [ "quatre vingt dix neuf"
             ]
  , examples (NumeralValue 33)
             [ "33"
             , "trente trois"
             , "trente-trois"
             , "trente 3"
             ]
  , examples (NumeralValue 118)
             [ "cent dix-huit"
             ]
  , examples (NumeralValue 4020)
             [ "quatre mille vingt"
             ]
  , examples (NumeralValue 100000)
             [ "100.000"
             , "100000"
             , "100K"
             , "100k"
             , "cent mille"
             , "100 000"
             ]
  , examples (NumeralValue 3000000)
             [ "3M"
             , "3000K"
             , "3000000"
             , "3.000.000"
             , "trois millions"
             ]
  , examples (NumeralValue 1200000)
             [ "1.200.000"
             , "1200000"
             , "1,2M"
             , "1200K"
             , ",0012G"
             , "un million deux cent mille"
             ]
  , examples (NumeralValue (-1200000))
             [ "- 1.200.000"
             , "-1200000"
             , "moins 1200000"
             , "-1,2M"
             , "-1200K"
             , "-,0012G"
             ]
  , examples (NumeralValue 6.7)
             [ "6,7"
             ]
  , examples (NumeralValue 6700.54)
             [ "6.700,54"
             , "6 700,54"
             ]
  ]


-- 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.FR.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

ruleNumerals4 :: Rule
ruleNumerals4 = Rule
  { name = "numbers 81"
  , pattern =
    [ numberWith TNumeral.value (== 80)
    , numberWith TNumeral.value (== 1)
    ]
  , prod = \_ -> integer 81
  }

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

ruleNumerals2 :: Rule
ruleNumerals2 = Rule
  { name = "numbers 22..29 32..39 .. 52..59"
  , pattern =
    [ oneOf [20, 50, 40, 30]
    , regex "[\\s\\-]+"
    , Predicate $ numberBetween 2 10
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v1}:
       _:
       Token Numeral NumeralData{TNumeral.value = v2}:
       _) -> double $ v1 + v2
      _ -> Nothing
  }

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:_:sep:_)):
       _) -> let fmt = Text.replace "," "." $ Text.replace sep 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
  }

ruleNumeral2Map :: HashMap Text Integer
ruleNumeral2Map = HashMap.fromList
  [ ( "vingt"    , 20 )
  , ( "trente"   , 30 )
  , ( "quarante" , 40 )
  , ( "cinquante", 50 )
  , ( "soixante" , 60 )
  ]

ruleNumeral2 :: Rule
ruleNumeral2 = Rule
  { name = "number (20..60)"
  , pattern =
    [ regex "(vingt|trente|quarante|cinquante|soixante)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) ruleNumeral2Map >>= integer
      _ -> Nothing
  }

ruleNumerals5 :: Rule
ruleNumerals5 = Rule
  { name = "numbers 62..69 .. 92..99"
  , pattern =
    [ oneOf [60, 80]
    , regex "[\\s\\-]+"
    , Predicate $ numberBetween 2 20
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v1}:
       _:
       Token Numeral NumeralData{TNumeral.value = v2}:
       _) -> double $ v1 + v2
      _ -> Nothing
  }

ruleNumeralMap :: HashMap Text Integer
ruleNumeralMap = HashMap.fromList
  [ ( "zero"     , 0 )
  , ( "zÃ©ro"     , 0 )
  , ( "un"       , 1 )
  , ( "une"      , 1 )
  , ( "deux"     , 2 )
  , ( "trois"    , 3 )
  , ( "quatre"   , 4 )
  , ( "cinq"     , 5 )
  , ( "six"      , 6 )
  , ( "sept"     , 7 )
  , ( "huit"     , 8 )
  , ( "neuf"     , 9 )
  , ( "dix"      , 10 )
  , ( "onze"     , 11 )
  , ( "douze"    , 12 )
  , ( "treize"   , 13 )
  , ( "quatorze" , 14 )
  , ( "quinze"   , 15 )
  , ( "seize"    , 16 )
  ]

ruleNumeral :: Rule
ruleNumeral = Rule
  { name = "number (0..16)"
  , pattern =
    [ regex "(z(e|Ã©)ro|une?|deux|trois|quatre|cinq|six|sept|huit|neuf|dix|onze|douze|treize|quatorze|quinze|seize)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) ruleNumeralMap >>= integer
      _ -> Nothing
  }

ruleNumeral3 :: Rule
ruleNumeral3 = Rule
  { name = "number (17..19)"
  , pattern =
    [ numberWith TNumeral.value (== 10)
    , regex "[\\s\\-]+"
    , Predicate $ numberBetween 7 10
    ]
  , prod = \tokens -> case tokens of
      (_:
       _:
       Token Numeral NumeralData{TNumeral.value = v}:
       _) -> double $ 10 + v
      _ -> Nothing
  }

ruleNumerals3 :: Rule
ruleNumerals3 = Rule
  { name = "numbers 61 71"
  , pattern =
    [ numberWith TNumeral.value (== 60)
    , regex "-?et-?"
    , oneOf [1, 11]
    ]
  , prod = \tokens -> case tokens of
      (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 = \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
  }

ruleNumeral4 :: Rule
ruleNumeral4 = Rule
  { name = "number 80"
  , pattern =
    [ regex "quatre"
    , regex "vingts?"
    ]
  , prod = \_ -> integer 80
  }

ruleNumerals :: Rule
ruleNumerals = Rule
  { name = "numbers 21 31 41 51"
  , pattern =
    [ oneOf [20, 50, 40, 30]
    , regex "-?et-?"
    , numberWith TNumeral.value (== 1)
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v1}:
       _:
       Token Numeral NumeralData{TNumeral.value = v2}:
       _) -> double $ v1 + 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:_)):
       _) -> let fmt = Text.replace sep Text.empty match
        in parseDouble fmt >>= double
      _ -> Nothing
  }

rulePowersOfTen :: Rule
rulePowersOfTen = Rule
  { name = "powers of tens"
  , pattern =
    [ regex "(cent|mille|millions?|milliards?)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) -> case Text.toLower match of
        "cent"      -> double 1e2 >>= withGrain 2 >>= withMultipliable
        "mille"     -> double 1e3 >>= withGrain 3 >>= withMultipliable
        "million"   -> double 1e6 >>= withGrain 6 >>= withMultipliable
        "millions"  -> double 1e6 >>= withGrain 6 >>= withMultipliable
        "milliard"  -> double 1e9 >>= withGrain 9 >>= withMultipliable
        "milliards" -> double 1e9 >>= withGrain 9 >>= withMultipliable
        _           -> Nothing
      _ -> Nothing
  }

ruleSum :: Rule
ruleSum = Rule
  { name = "intersect 2 numbers"
  , 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
  }

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

rules :: [Rule]
rules =
  [ ruleDecimalNumeral
  , ruleDecimalWithThousandsSeparator
  , ruleIntegerWithThousandsSeparator
  , ruleNumeral
  , ruleNumeral2
  , ruleNumeral3
  , ruleNumeral4
  , ruleNumerals
  , ruleNumerals2
  , ruleNumerals3
  , ruleNumerals4
  , ruleNumerals5
  , ruleNumeralsPrefixWithNegativeOrMinus
  , ruleNumeralsSuffixesKMG
  , rulePowersOfTen
  , ruleSum
  , ruleMultiply
  ]


-- 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.GA.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 GA Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (NumeralValue 0)
             [ "0"
             , "a nÃ¡id"
             ]
  , examples (NumeralValue 1)
             [ "1"
             , "aon"
             , "a haon"
             , "AmhÃ¡in"
             ]
  , examples (NumeralValue 20)
             [ "20"
             , "Fiche"
             ]
  , examples (NumeralValue 30)
             [ "dÃ©ag is fiche"
             ]
  , examples (NumeralValue 40)
             [ "is dha fhichead"
             , "is dÃ¡ fhichead"
             ]
  , examples (NumeralValue 50)
             [ "deag is dha fhichead"
             , "deag is dÃ¡ fhichead"
             ]
  ]


-- 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.GA.Rules
  ( rules
  ) where

import Data.HashMap.Strict (HashMap)
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 "-|m(Ã|i)neas(\\sa)?"
    , Predicate isPositive
    ]
  , prod = \tokens -> case tokens of
      (_:
       Token Numeral NumeralData{TNumeral.value = v}:
       _) -> double $ v * (-1)
      _ -> Nothing
  }

oneToTenMap :: HashMap Text Integer
oneToTenMap = HashMap.fromList
  [ ("aon", 1)
  , ("dha", 2)
  , ("dhÃ¡", 2)
  , ("trÃ", 3)
  , ("tri", 3)
  , ("ceithre", 4)
  , ("cuig", 5)
  , ("cÃºig", 5)
  , ("sÃ©", 6)
  , ("se", 6)
  , ("seacht", 7)
  , ("ocht", 8)
  , ("naoi", 9)
  , ("deich", 10)
  ]

ruleNumerals2 :: Rule
ruleNumerals2 = Rule
  { name = "numbers, 1-10"
  , pattern =
    [ regex "(aon|dh(Ã¡|a)|tr(Ã|i)|ceithre|c(Ãº|u)ig|seacht|s(Ã©|e)|ocht|naoi|deich)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) oneToTenMap >>= integer
      _ -> Nothing
  }

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 True match
      _ -> Nothing
  }

ruleDag :: Rule
ruleDag = Rule
  { name = "dÃ©ag"
  , pattern =
    [ regex "d(Ã©|e)ag"
    ]
  , prod = \_ -> integer 10
  }

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
  }

oldVigNumeralsSMap :: HashMap Text Integer
oldVigNumeralsSMap = HashMap.fromList
  [ ("dÃ¡ fhichead", 40)
  , ("da fhichead", 40)
  , ("dhÃ¡ fhichead", 40)
  , ("dha fhichead", 40)
  , ("trÃ fichid", 60)
  , ("tri fichid", 60)
  , ("ceithre fichid", 80)
  ]

ruleOldVigesimalNumeralsS :: Rule
ruleOldVigesimalNumeralsS = Rule
  { name = "old vigesimal numbers, 20s"
  , pattern =
    [ regex "(d[Ã©e]ag )?is (dh?(Ã¡|a) fhichead|tr(Ã|i) fichid|ceithre fichid)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (ten:match:_)):_) -> do
        x <- HashMap.lookup (Text.toLower match) oldVigNumeralsSMap
        integer $ if Text.null ten then x else x + 10
      _ -> Nothing
  }

ruleOldVigesimalFiche :: Rule
ruleOldVigesimalFiche = Rule
  { name = "old vigesimal 20 + 10"
  , pattern =
    [ regex "d[Ã©e]ag is fiche"
    ]
  , prod = const $ integer 30
  }

ruleAmhin :: Rule
ruleAmhin = Rule
  { name = "amhÃ¡in"
  , pattern =
    [ regex "amh(Ã¡|a)in"
    ]
  , prod = \_ -> integer 1
  }

twentyToNinetyMap :: HashMap Text Integer
twentyToNinetyMap = HashMap.fromList
 [ ("fiche", 20)
 , ("triocha", 30)
 , ("trÃocha", 30)
 , ("daichead", 40)
 , ("caoga", 50)
 , ("seasca", 60)
 , ("seachto", 70)
 , ("seachtÃ³", 70)
 , ("ochto", 80)
 , ("ochtÃ³", 80)
 , ("nÃ³cha", 90)
 , ("nocha", 90)
 ]

ruleNumerals :: Rule
ruleNumerals = Rule
  { name = "numbers, 20-90"
  , pattern =
    [ regex "(fiche|tr(Ã|i)ocha|daichead|caoga|seasca|seacht(Ã³|o)|ocht(Ã³|o)|n(Ã³|o)cha)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) twentyToNinetyMap >>= integer
      _ -> 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
  }

countNumeralsMap :: HashMap Text Integer
countNumeralsMap = HashMap.fromList
  [ ("naid", 0)
  , ("nÃ¡id", 0)
  , ("haon", 1)
  , ("dÃ³", 2)
  , ("do", 2)
  , ("trÃ", 3)
  , ("tri", 3)
  , ("ceathair", 4)
  , ("cuig", 5)
  , ("cÃºig", 5)
  , ("sÃ©", 6)
  , ("se", 6)
  , ("seacht", 7)
  , ("hocht", 8)
  , ("naoi", 9)
  , ("deich", 10)
  ]

ruleCountNumerals :: Rule
ruleCountNumerals = Rule
  { name = "count numbers"
  , pattern =
    [ regex "a (n(Ã¡|a)id|haon|d(Ã³|o)|tr(Ã|i)|ceathair|c(Ãº|u)ig|s(Ã©|e)|seacht|hocht|naoi|deich)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) countNumeralsMap >>= integer
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleAmhin
  , ruleCountNumerals
  , ruleDag
  , ruleDecimalNumeral
  , ruleDecimalWithThousandsSeparator
  , ruleIntegerWithThousandsSeparator
  , ruleNumerals
  , ruleNumerals2
  , ruleNumeralsPrefixWithNegativeOrMinus
  , ruleNumeralsSuffixesKMG
  , ruleOldVigesimalNumeralsS
  , ruleOldVigesimalFiche
  ]


-- 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.HU.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 HU Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (NumeralValue 0)
             [ "0"
             , "nulla"
             , "zÃ©rÃ³"
             ]
  , examples (NumeralValue 1)
             [ "1"
             , "egy"
             ]
  , examples (NumeralValue 2)
             [ "kettÅ‘"
             ]
  , examples (NumeralValue 3)
             [ "hÃ¡rom"
             ]
  , examples (NumeralValue 4)
             [ "nÃ©gy"
             ]
  , examples (NumeralValue 5)
             [ "Ã¶t"
             ]
  , examples (NumeralValue 6)
             [ "hat"
             ]
  , examples (NumeralValue 7)
             [ "hÃ©t"
             ]
  , examples (NumeralValue 8)
             [ "nyolc"
             ]
  , examples (NumeralValue 9)
             [ "kilenc"
             ]
  , examples (NumeralValue 11)
             [ "tizenegy"
             ]
  , examples (NumeralValue 15)
             [ "tizenÃ¶t"
             ]
  , examples (NumeralValue 17)
             [ "tizenhÃ©t"
             ]
  , examples (NumeralValue 20)
             [ "20"
             , "hÃºsz"
             ]
  , examples (NumeralValue 22)
             [ "huszonkettÅ‘"
             ]
  , examples (NumeralValue 24)
             [ "24"
             , "huszonnÃ©gy"
             ]
  , examples (NumeralValue 26)
             [ "huszonhat"
             ]
  , examples (NumeralValue 28)
             [ "huszonnyolc"
             ]
  , examples (NumeralValue 10)
             [ "tÃz"
             ]
  , examples (NumeralValue 20)
             [ "hÃºsz"
             ]
  , examples (NumeralValue 50)
             [ "Ã¶tven"
             ]
  , examples (NumeralValue 34)
             [ "harmincnÃ©gy"
             ]
  ]


-- 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 NoRebindableSyntax #-}

module Duckling.Numeral.HU.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

ruleNumeralMap :: HashMap Text Integer
ruleNumeralMap = HashMap.fromList
  [ ( "nulla", 0 )
  , ( "z\x00E9r\x00F3", 0 )
  , ( "egy", 1 )
  , ( "kett\x0151", 2 )
  , ( "h\x00E1rom", 3 )
  , ( "n\x00E9gy", 4 )
  , ( "\x00F6t", 5)
  , ( "hat", 6)
  , ( "h\x00E9t", 7)
  , ( "nyolc", 8)
  , ( "kilenc", 9)
  , ( "t\x00EDz", 10)
  ]

ruleNumeral :: Rule
ruleNumeral = Rule
  { name = "number (0..10)"
  , pattern =
    [ regex "(nulla|z\x00E9r\x00F3|egy|kett\x0151|h\x00E1rom|n\x00E9gy|\x00F6t|hat|h\x00E9t|nyolc|kilenc|t\x00EDz)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) ruleNumeralMap >>= integer
      _ -> Nothing
  }

elevenToNineteenMap :: HashMap Text Integer
elevenToNineteenMap = HashMap.fromList
  [ ( "tizenegy", 11 )
  , ( "tizenkett\x0151", 12 )
  , ( "tizenh\x00E1rom", 13 )
  , ( "tizenn\x00E9gy", 14 )
  , ( "tizen\x00F6t", 15 )
  , ( "tizenhat", 16 )
  , ( "tizenh\x00E9t", 17 )
  , ( "tizennyolc", 18 )
  , ( "tizenkilenc", 19 )
  ]

ruleElevenToNineteen :: Rule
ruleElevenToNineteen = Rule
  { name = "number (11..19)"
  , pattern =
    [ regex "(tizenegy|tizenkett\x0151|tizenh\x00E1rom|tizenn\x00E9gy|tizen\x00F6t|tizenhat|tizenh\x00E9t|tizennyolc|tizenkilenc)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) elevenToNineteenMap >>= integer
      _ -> Nothing
  }

twentyoneToTwentynineMap :: HashMap Text Integer
twentyoneToTwentynineMap = HashMap.fromList
  [ ( "huszonegy", 21 )
  , ( "huszonkett\x0151", 22 )
  , ( "huszonh\x00E1rom", 23 )
  , ( "huszonn\x00E9gy", 24 )
  , ( "huszon\x00F6t", 25 )
  , ( "huszonhat", 26 )
  , ( "huszonh\x00E9t", 27 )
  , ( "huszonnyolc", 28 )
  , ( "huszonkilenc", 29 )
  ]

ruleTwentyoneToTwentynine :: Rule
ruleTwentyoneToTwentynine = Rule
  { name = "number (21..29)"
  , pattern =
    [ regex "(huszonegy|huszonkett\x0151|huszonh\x00E1rom|huszonn\x00E9gy|huszon\x00F6t|huszonhat|huszonh\x00E9t|huszonnyolc|huszonkilenc)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) twentyoneToTwentynineMap >>= integer
      _ -> Nothing
  }

tensMap :: HashMap Text Integer
tensMap = HashMap.fromList
  [ ( "h\x00FAsz", 20 )
  , ( "harminc", 30 )
  , ( "negyven", 40 )
  , ( "\x00F6tven", 50 )
  , ( "hatvan", 60 )
  , ( "hetven", 70 )
  , ( "nyolcvan", 80 )
  , ( "kilencven", 90 )
  ]

ruleTens :: Rule
ruleTens = Rule
  { name = "integer (20,30..90)"
  , pattern =
    [ regex "(h\x00FAsz|harminc|negyven|Ã¶tven|hatvan|hetven|nyolcvan|kilencven)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) tensMap >>= integer
      _ -> Nothing
  }

ruleCompositeTens :: Rule
ruleCompositeTens = Rule
  { name = "integer ([3-9][1-9])"
  , pattern =
    [ regex "(harminc|negyven|\x00F6tven|hatvan|hetven|nyolcvan|kilencven)(egy|kett\x0151|h\x00E1rom|n\x00E9gy|\x00F6t|hat|h\x00E9t|nyolc|kilenc)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (m1:m2:_)):_) -> do
        v1 <- HashMap.lookup (Text.toLower m1) tensMap
        v2 <- HashMap.lookup (Text.toLower m2) ruleNumeralMap
        integer $ v1 + v2
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleNumeral
  , ruleElevenToNineteen
  , ruleTwentyoneToTwentynine
  , ruleTens
  , ruleCompositeTens
  ]


-- 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.ID.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 ID Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (NumeralValue 0)
             [ "0"
             , "nol"
             , "kosong"
             ]
  , examples (NumeralValue 1)
             [ "1"
             , "satu"
             ]
  , examples (NumeralValue 2)
             [ "2"
             , "Dua"
             ]
  , examples (NumeralValue 10)
             [ "10"
             , "sepuluh"
             ]
  , examples (NumeralValue 33)
             [ "33"
             , "tiga puluh tiga"
             , "0033"
             ]
  , examples (NumeralValue 100)
             [ "100"
             , "seratus"
             ]
  , examples (NumeralValue 17)
             [ "17"
             , "tujuh belas"
             ]
  , examples (NumeralValue 28)
             [ "28"
             , "dua puluh delapan"
             ]
  , 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"
             , "negatif 1200000"
             , "-1,2M"
             , "-1200K"
             , "-,0012G"
             , "-0,0012G"
             ]
  , examples (NumeralValue 5000)
             [ "5 ribu"
             , "lima ribu"
             ]
  , examples (NumeralValue 122)
             [ "seratus dua puluh dua"
             ]
  , examples (NumeralValue 200000)
             [ "dua ratus ribu"
             ]
  , examples (NumeralValue 10011)
             [ "sepuluh ribu sebelas"
             ]
  , examples (NumeralValue 721012)
             [ "tujuh ratus dua puluh satu ribu dua belas"
             ]
  , examples (NumeralValue 31256721)
             [ "tiga puluh satu juta dua ratus lima puluh enam ribu tujuh ratus dua puluh satu"
             ]
  ]


-- 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.ID.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

ruleIntegerMap :: HashMap Text Integer
ruleIntegerMap = HashMap.fromList
  [ ( "kosong"  , 0 )
  , ( "nol"     , 0 )
  , ( "satu"    , 1 )
  , ( "dua"     , 2 )
  , ( "tiga"    , 3 )
  , ( "empat"   , 4 )
  , ( "lima"    , 5 )
  , ( "enam"    , 6 )
  , ( "tujuh"   , 7 )
  , ( "delapan" , 8 )
  , ( "sembilan", 9 )
  , ( "sebelas" , 11 )
  ]

ruleTeen :: Rule
ruleTeen = Rule
  { name = "teen"
  , pattern =
    [ Predicate $ numberBetween 2 10
    , regex "belas"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:_) -> double $ v + 10
      _ -> Nothing
  }

ruleNumeralCommaNumeral :: Rule
ruleNumeralCommaNumeral = Rule
  { name = "number comma number"
  , pattern =
    [ dimension Numeral
    , regex "koma"
    , Predicate $ not . hasGrain
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral nd1:_:Token Numeral nd2:_) ->
        double $ TNumeral.value nd1 + decimalsToDouble (TNumeral.value nd2)
      _ -> Nothing
  }

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

ruleTen :: Rule
ruleTen = Rule
  { name = "ten"
  , pattern =
    [ regex "(se)?puluh"
    ]
  , 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:_)):
       _) -> let fmt = Text.replace "," "." $ Text.replace "." Text.empty match
        in parseDouble fmt >>= double
      _ -> 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
  }

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 [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
  }

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
  }

ruleSomefewcouple :: Rule
ruleSomefewcouple = Rule
  { name = "some/few/couple"
  , pattern =
    [ regex "beberapa"
    ]
  , prod = \_ -> integer 3
  }

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 "(se)?(ratus|ribu|juta)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (_:match:_)):_) -> case Text.toLower match of
        "ratus"-> double 1e2 >>= withGrain 2 >>= withMultipliable
        "ribu" -> double 1e3 >>= withGrain 3 >>= withMultipliable
        "juta" -> double 1e6 >>= withGrain 6 >>= withMultipliable
        _      -> Nothing
      _ -> Nothing
  }

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

ruleInteger :: Rule
ruleInteger = Rule
  { name = "integer (0..9 11)"
  , pattern =
    [ regex "(kosong|nol|satu|dua|tiga|empat|lima|enam|tujuh|delapan|sembilan|sebelas)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) ruleIntegerMap >>= integer
      _ -> Nothing
  }

ruleInteger2 :: Rule
ruleInteger2 = Rule
  { name = "integer 20..90"
  , pattern =
    [ Predicate $ numberBetween 2 10
    , numberWith TNumeral.value (== 10)
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v1}:
       Token Numeral NumeralData{TNumeral.value = v2, TNumeral.grain = Just g}:
       _) -> double (v1 * v2) >>= withGrain g
      _ -> 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
  , ruleDozen
  , ruleInteger
  , ruleInteger2
  , ruleInteger3
  , ruleIntegerWithThousandsSeparator
  , ruleIntersect
  , ruleMultiply
  , ruleNumeralCommaNumeral
  , ruleNumeralsPrefixWithNegativeOrMinus
  , ruleNumeralsSuffixesKMG
  , rulePowersOfTen
  , ruleSomefewcouple
  , ruleTeen
  , 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.IS.Corpus
  ( corpus
  ) where

import Data.String
import Prelude

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

context :: Context
context = testContext{locale = makeLocale IS Nothing}

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

allExamples :: [Example]
allExamples = concat
  [ examples (NumeralValue 0)
             [ "0"
             , "nÃºll"
             , "null"
             ]
  , examples (NumeralValue 1)
             [ "1"
             , "einn"
             ]
  , examples (NumeralValue 2)
             [ "tveir"
             ]
  , examples (NumeralValue 3)
             [ "Ã¾rÃr"
             ]
  , examples (NumeralValue 4)
             [ "fjÃ³rir"
             ]
  , examples (NumeralValue 5)
             [ "fimm"
             ]
  , examples (NumeralValue 6)
             [ "sex"
             ]
  , examples (NumeralValue 7)
             [ "sjÃ¶"
             ]
  , examples (NumeralValue 8)
             [ "Ã¡tta"
             ]
  , examples (NumeralValue 9)
             [ "nÃu"
             ]
  , examples (NumeralValue 10)
             [ "tÃu"
             ]
  , examples (NumeralValue 11)
             [ "ellefu"
             ]
  , examples (NumeralValue 15)
             [ "fimmtÃ¡n"
             ]
  , examples (NumeralValue 17)
             [ "sautjÃ¡n"
             ]
  , examples (NumeralValue 20)
             [ "20"
             , "tuttugu"
             ]
  ]


-- 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 #-}
{-# LANGUAGE NoRebindableSyntax #-}

module Duckling.Numeral.IS.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

zeroToTwentyMap:: HashMap Text Integer
zeroToTwentyMap = HashMap.fromList
  [ ( "nÃºll", 0 )
  , ( "null", 0 )
  , ( "einn", 1 )
  , ( "tveir", 2 )
  , ( "Ã¾rÃr", 3 )
  , ( "fjÃ³rir", 4 )
  , ( "fimm", 5 )
  , ( "sex", 6 )
  , ( "sjÃ¶", 7 )
  , ( "Ã¡tta", 8 )
  , ( "nÃu", 9 )
  , ( "tÃu", 10 )
  , ( "ellefu", 11 )
  , ( "tÃ³lf", 12 )
  , ( "Ã¾rettÃ¡n", 13 )
  , ( "fjÃ³rtÃ¡n", 14 )
  , ( "fimmtÃ¡n", 15 )
  , ( "sextÃ¡n", 16 )
  , ( "sautjÃ¡n", 17 )
  , ( "Ã¡tjÃ¡n", 18 )
  , ( "nÃtjÃ¡n", 19 )
  , ( "tuttugu", 20 )
  ]

ruleZeroToTwenty :: Rule
ruleZeroToTwenty = Rule
  { name = "number (0..20)"
  , pattern =
    [ regex "(n[Ãºu]ll|einn|tveir|Ã¾rÃr|fjÃ³rir|fimm(tÃ¡n)?|sex(tÃ¡n)?|sjÃ¶|Ã¡tta|nÃu|tÃu|ellefu|tÃ³lf|Ã¾rettÃ¡n|fjÃ³rtÃ¡n|sautjÃ¡n|Ã¡tjÃ¡n|nÃtjÃ¡n|tuttugu)"
    ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) zeroToTwentyMap >>= integer
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleZeroToTwenty
  ]


-- 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.IT.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 IT Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (NumeralValue 0)
             [ "0"
             , "nulla"
             , "zero"
             ]
  , examples (NumeralValue 1)
             [ "1"
             , "uno"
             , "Un"
             ]
  , examples (NumeralValue 2)
             [ "2"
             , "due"
             ]
  , examples (NumeralValue 3)
             [ "3"
             , "tre"
             ]
  , examples (NumeralValue 4)
             [ "4"
             , "quattro"
             ]
  , examples (NumeralValue 5)
             [ "5"
             , "cinque"
             ]
  , examples (NumeralValue 6)
             [ "6"
             , "sei"
             ]
  , examples (NumeralValue 7)
             [ "7"
             , "sette"
             ]
  , examples (NumeralValue 8)
             [ "8"
             , "otto"
             ]
  , examples (NumeralValue 9)
             [ "9"
             , "nove"
             ]
  , examples (NumeralValue 10)
             [ "10"
             , "dieci"
             ]
  , examples (NumeralValue 33)
             [ "33"
             , "trentatrÃ©"
             , "0033"
             ]
  , examples (NumeralValue 11)
             [ "11"
             , "Undici"
             ]
  , examples (NumeralValue 12)
             [ "12"
             , "dodici"
             ]
  , examples (NumeralValue 13)
             [ "13"
             , "tredici"
             ]
  , examples (NumeralValue 14)
             [ "14"
             , "quattordici"
             ]
  , examples (NumeralValue 15)
             [ "15"
             , "quindici"
             ]
  , examples (NumeralValue 16)
             [ "16"
             , "sedici"
             ]
  , examples (NumeralValue 17)
             [ "17"
             , "diciassette"
             ]
  , examples (NumeralValue 18)
             [ "18"
             , "diciotto"
             ]
  , examples (NumeralValue 19)
             [ "19"
             , "diciannove"
             ]
  , examples (NumeralValue 20)
             [ "20"
             , "venti"
             ]
  , 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"
             ]
  , examples (NumeralValue (-1200000))
             [ "- 1.200.000"
             , "-1200000"
             , "meno 1.200.000"
             , "negativo 1200000"
             , "-1,2M"
             , "-1200K"
             , "-,0012G"
             ]
  , examples (NumeralValue 6.7)
             [ "6,7"
             ]
  , examples (NumeralValue 6700.54)
             [ "6.700,54"
             , "6 700,54"
             ]
  ]


-- 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.IT.Rules
  ( rules
  ) where

import Data.HashMap.Strict (HashMap)
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 "-|meno|negativo"
    , Predicate isPositive
    ]
  , prod = \tokens -> case tokens of
      (_:Token Numeral nd:_) -> double (TNumeral.value nd * (-1))
      _ -> Nothing
  }

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:_:sep:_)):
       _) -> let fmt = Text.replace "," "." $ Text.replace sep 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
  }

tensMap :: HashMap Text Integer
tensMap = HashMap.fromList
  [ ("venti", 20)
  , ("trenta", 30)
  , ("quaranta", 40)
  , ("cinquanta", 50)
  , ("sessanta", 60)
  , ("settanta", 70)
  , ("ottanta", 80)
  , ("novanta", 90)
  ]

ruleNumeral2 :: Rule
ruleNumeral2 = Rule
  { name = "number (20..90)"
  , pattern =
    [ regex "(venti|trenta|quaranta|cinquanta|sessanta|settanta|ottanta|novanta)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) tensMap >>= integer
      _ -> Nothing
  }

zeroNineteenMap :: HashMap Text Integer
zeroNineteenMap = HashMap.fromList
  [ ("zero", 0)
  , ("niente", 0)
  , ("nulla", 0)
  , ("un", 1)
  , ("uno", 1)
  , ("due", 2)
  , ("tre", 3)
  , ("quattro", 4)
  , ("cinque", 5)
  , ("sei", 6)
  , ("sette", 7)
  , ("otto", 8)
  , ("nove", 9)
  , ("dieci", 10)
  , ("undici", 11)
  , ("dodici", 12)
  , ("tredici", 13)
  , ("quattordici", 14)
  , ("quindici", 15)
  , ("sedici", 16)
  , ("diciassette", 17)
  , ("diciotto", 18)
  , ("diciannove", 19)
  ]

ruleNumeral :: Rule
ruleNumeral = Rule
  { name = "number (0..19)"
  , pattern =
    [ regex "(zero|nulla|niente|uno|due|tredici|tre|quattro|cinque|sei|sette|otto|nove|dieci|undici|dodici|quattordici|quindici|sedici|diciassette|diciotto|diciannove|un)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) zeroNineteenMap >>= integer
      _ -> Nothing
  }

hundredsMap :: HashMap Text Integer
hundredsMap = HashMap.fromList
  [ ("cento", 100)
  , ("duecento", 200)
  , ("trecento", 300)
  , ("quattrocento", 400)
  , ("cinquecento", 500)
  , ("seicento", 600)
  , ("settecento", 700)
  , ("ottocento", 800)
  , ("novecento", 900)
  , ("mila", 1000)
  , ("mille", 1000)
  ]

ruleNumeral5 :: Rule
ruleNumeral5 = Rule
  { name = "number 100..1000 "
  , pattern =
    [ regex "(due|tre|quattro|cinque|sei|sette|otto|nove)?cento|mil(a|le)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) hundredsMap >>= integer
      _ -> Nothing
  }

ruleNumeral3 :: Rule
ruleNumeral3 = Rule
  { name = "number (21..29 31..39 41..49 51..59 61..69 71..79 81..89 91..99)"
  , pattern =
    [ oneOf [70, 20, 60, 50, 40, 90, 30, 80]
    , regex "e"
    , 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
  }

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
  }

twentyoneNinetynineMap :: HashMap Text Integer
twentyoneNinetynineMap = HashMap.fromList
  [ ("ventuno", 21)
  , ("ventidue", 22)
  , ("ventitre", 23)
  , ("ventitrÃ©", 23)
  , ("ventiquattro", 24)
  , ("venticinque", 25)
  , ("ventisei", 26)
  , ("ventisette", 27)
  , ("ventotto", 28)
  , ("ventinove", 29)
  , ("trentuno", 31)
  , ("trentadue", 32)
  , ("trentatre", 33)
  , ("trentatrÃ©", 33)
  , ("trentaquattro", 34)
  , ("trentacinque", 35)
  , ("trentasei", 36)
  , ("trentasette", 37)
  , ("trentotto", 38)
  , ("trentanove", 39)
  , ("quarantuno", 41)
  , ("quarantadue", 42)
  , ("quarantatre", 43)
  , ("quarantatrÃ©", 43)
  , ("quarantaquattro", 44)
  , ("quarantacinque", 45)
  , ("quarantasei", 46)
  , ("quarantasette", 47)
  , ("quarantotto", 48)
  , ("quarantanove", 49)
  , ("cinquantuno", 51)
  , ("cinquantadue", 52)
  , ("cinquantatre", 53)
  , ("cinquantatrÃ©", 53)
  , ("cinquantaquattro", 54)
  , ("cinquantacinque", 55)
  , ("cinquantasei", 56)
  , ("cinquantasette", 57)
  , ("cinquantotto", 58)
  , ("cinquantanove", 59)
  , ("sessantuno", 61)
  , ("sessantadue", 62)
  , ("sessantatrÃ©", 63)
  , ("sessantatre", 63)
  , ("sessantaquattro", 64)
  , ("sessantacinque", 65)
  , ("sessantasei", 66)
  , ("sessantasette", 67)
  , ("sessantotto", 68)
  , ("sessantanove", 69)
  , ("settantuno", 71)
  , ("settantadue", 72)
  , ("settantatrÃ©", 73)
  , ("settantatre", 73)
  , ("settantaquattro", 74)
  , ("settantacinque", 75)
  , ("settantasei", 76)
  , ("settantasette", 77)
  , ("settantotto", 78)
  , ("settantanove", 79)
  , ("ottantuno", 81)
  , ("ottantadue", 82)
  , ("ottantatrÃ©", 83)
  , ("ottantatre", 83)
  , ("ottantaquattro", 84)
  , ("ottantacinque", 85)
  , ("ottantasei", 86)
  , ("ottantasette", 87)
  , ("ottantotto", 88)
  , ("ottantanove", 89)
  , ("novantuno", 91)
  , ("novantadue", 92)
  , ("novantatre", 93)
  , ("novantatrÃ©", 93)
  , ("novantaquattro", 94)
  , ("novantacinque", 95)
  , ("novantasei", 96)
  , ("novantasette", 97)
  , ("novantotto", 98)
  , ("novantanove", 99)
  ]

ruleNumeral4 :: Rule
ruleNumeral4 = Rule
  { name = "number (21..29 31..39 41..49 51..59 61..69 71..79 81..89 91..99)"
  , pattern =
    [ regex "((venti|trenta|quaranta|cinquanta|sessanta|settanta|ottanta|novanta)(due|tre|trÃ©|quattro|cinque|sei|sette|nove))|((vent|trent|quarant|cinquant|sessant|settant|ottant|novant)(uno|otto))"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) twentyoneNinetynineMap >>= integer
      _ -> Nothing
  }

ruleNumerals :: Rule
ruleNumerals = Rule
  { name = "numbers 200..999"
  , pattern =
    [ Predicate $ numberBetween 2 10
    , numberWith TNumeral.value (== 100)
    , Predicate $ numberBetween 0 100
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v1}:
       Token Numeral NumeralData{TNumeral.value = v2}:
       Token Numeral NumeralData{TNumeral.value = v3}:
       _) -> double $ v1 * v2 + v3
      _ -> 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:_)):
       _) -> let fmt = Text.replace sep Text.empty match
        in parseDouble fmt >>= double
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDecimalNumeral
  , ruleDecimalWithThousandsSeparator
  , ruleIntegerWithThousandsSeparator
  , ruleNumeral
  , ruleNumeral2
  , ruleNumeral3
  , ruleNumeral4
  , ruleNumeral5
  , ruleNumerals
  , ruleNumeralsPrefixWithNegativeOrMinus
  , ruleNumeralsSuffixesKMG
  ]


-- 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.NB.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 NB Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (NumeralValue 0)
             [ "0"
             , "null"
             ]
  , examples (NumeralValue 1)
             [ "1"
             , "Ã©n"
             , "en"
             , "Ett"
             ]
  , examples (NumeralValue 2)
             [ "2"
             , "to"
             , "et par"
             ]
  , examples (NumeralValue 7)
             [ "7"
             , "syv"
             , "sju"
             ]
  , examples (NumeralValue 14)
             [ "14"
             , "fjorten"
             ]
  , examples (NumeralValue 16)
             [ "16"
             , "seksten"
             ]
  , examples (NumeralValue 17)
             [ "17"
             , "sytten"
             , "sÃ¸tten"
             ]
  , examples (NumeralValue 18)
             [ "18"
             , "atten"
             ]
  , examples (NumeralValue 20)
             [ "20"
             , "tyve"
             , "Tjue"
             ]
  , examples (NumeralValue 30)
             [ "30"
             , "tretti"
             , "tredve"
             ]
  , examples (NumeralValue 70)
             [ "70"
             , "sÃ¸tti"
             , "sytti"
             ]
  , 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"
             , "minus 1.200.000"
             , "negativ 1200000"
             , "-1,2M"
             , "-1200K"
             , "-,0012G"
             , "-1 200 000"
             ]
  , examples (NumeralValue 5000)
             [ "5 tusen"
             , "fem tusen"
             , "5 000"
             ]
  , examples (NumeralValue 100)
             [ "hundre"
             ]
  , examples (NumeralValue 5020)
             [ "fem tusen og tjue"
             ]
  , examples (NumeralValue 1e9)
             [ "en milliard"
             ]
  , examples (NumeralValue 1e12)
             [ "en billion"
             ]
  , examples (NumeralValue 1e15)
             [ "en billiard"
             ]
  , examples (NumeralValue 1e18)
             [ "en trillion"
             ]
  , examples (NumeralValue 1e21)
             [ "en trilliard"
             ]
  , examples (NumeralValue 1e24)
             [ "en kvadrillion"
             ]
  , examples (NumeralValue 1e27)
             [ "en kvadrilliard"
             ]
  , examples (NumeralValue 6e10)
             [ "seksti milliarder"
             , "60 milliarder"
             ]
  , examples (NumeralValue 3e12)
             [ "tre billioner"
             , "3 billioner"
             ]
  , examples (NumeralValue 4e17)
             [ "fire hundre billiarder"
             , "400 billiarder"
             ]
  , examples (NumeralValue 2e18)
             [ "to trillioner"
             , "2 trillioner"
             ]
  , examples (NumeralValue 1.8e22)
             [ "atten trilliarder"
             , "18 trilliarder"
             ]
  , examples (NumeralValue 1.8e22)
             [ "atten trilliarder"
             , "18 trilliarder"
             ]
  , examples (NumeralValue 4.11e26)
             [ "fire hundre og elleve kvadrillioner"
             , "411 kvadrillioner"
             ]
  , examples (NumeralValue 9e27)
             [ "ni kvadrilliarder"
             , "9 kvadrilliarder"
             ]
  , examples (NumeralValue 21)
             [ "tjueen"
             , "tjueÃ©n"
             ]
  , examples (NumeralValue 22)
             [ "tjueto"
             ]
  , examples (NumeralValue 23)
             [ "tjuetre"
             ]
  , examples (NumeralValue 24)
             [ "tjuefire"
             ]
  , examples (NumeralValue 25)
             [ "tjuefem"
             ]
  , examples (NumeralValue 26)
             [ "tjueseks"
             ]
  , examples (NumeralValue 27)
             [ "tjuesju"
             , "tjuesyv"
             ]
  , examples (NumeralValue 28)
             [ "tjueÃ¥tte"
             ]
  , examples (NumeralValue 29)
             [ "tjueni"
             ]
  , examples (NumeralValue 31)
             [ "trettien"
             , "trettiÃ©n"
             ]
  , examples (NumeralValue 32)
             [ "trettito"
             ]
  , examples (NumeralValue 33)
             [ "trettitre"
             ]
  , examples (NumeralValue 34)
             [ "trettifire"
             ]
  , examples (NumeralValue 35)
             [ "trettifem"
             ]
  , examples (NumeralValue 36)
             [ "trettiseks"
             ]
  , examples (NumeralValue 37)
             [ "trettisju"
             , "trettisyv"
             ]
  , examples (NumeralValue 38)
             [ "trettiÃ¥tte"
             ]
  , examples (NumeralValue 39)
             [ "trettini"
             ]
  , examples (NumeralValue 41)
             [ "fÃ¸rtien"
             , "fÃ¸rtiÃ©n"
             ]
  , examples (NumeralValue 42)
             [ "fÃ¸rtito"
             ]
  , examples (NumeralValue 43)
             [ "fÃ¸rtitre"
             ]
  , examples (NumeralValue 44)
             [ "fÃ¸rtifire"
             ]
  , examples (NumeralValue 45)
             [ "fÃ¸rtifem"
             ]
  , examples (NumeralValue 46)
             [ "fÃ¸rtiseks"
             ]
  , examples (NumeralValue 47)
             [ "fÃ¸rtisju"
             , "fÃ¸rtisyv"
             ]
  , examples (NumeralValue 48)
             [ "fÃ¸rtiÃ¥tte"
             ]
  , examples (NumeralValue 49)
             [ "fÃ¸rtini"
             ]
  , examples (NumeralValue 51)
             [ "femtien"
             , "femtiÃ©n"
             ]
  , examples (NumeralValue 52)
             [ "femtito"
             ]
  , examples (NumeralValue 53)
             [ "femtitre"
             ]
  , examples (NumeralValue 54)
             [ "femtifire"
             ]
  , examples (NumeralValue 55)
             [ "femtifem"
             ]
  , examples (NumeralValue 56)
             [ "femtiseks"
             ]
  , examples (NumeralValue 57)
             [ "femtisju"
             , "femtisyv"
             ]
  , examples (NumeralValue 58)
             [ "femtiÃ¥tte"
             ]
  , examples (NumeralValue 59)
             [ "femtini"
             ]
  , examples (NumeralValue 61)
             [ "sekstien"
             , "sekstiÃ©n"
             ]
  , examples (NumeralValue 62)
             [ "sekstito"
             ]
  , examples (NumeralValue 63)
             [ "sekstitre"
             ]
  , examples (NumeralValue 64)
             [ "sekstifire"
             ]
  , examples (NumeralValue 65)
             [ "sekstifem"
             ]
  , examples (NumeralValue 66)
             [ "sekstiseks"
             ]
  , examples (NumeralValue 67)
             [ "sekstisju"
             , "sekstisyv"
             ]
  , examples (NumeralValue 68)
             [ "sekstiÃ¥tte"
             ]
  , examples (NumeralValue 69)
             [ "sekstini"
             ]
  , examples (NumeralValue 71)
             [ "syttien"
             , "sÃ¸ttien"
             , "sÃ¸ttiÃ©n"
             , "sÃ¸ttiÃ©n"
             ]
  , examples (NumeralValue 72)
             [ "syttito"
             , "sÃ¸ttito"
             ]
  , examples (NumeralValue 73)
             [ "syttitre"
             , "sÃ¸ttitre"
             ]
  , examples (NumeralValue 74)
             [ "syttifire"
             , "sÃ¸ttifire"
             ]
  , examples (NumeralValue 75)
             [ "syttifem"
             , "sÃ¸ttifem"
             ]
  , examples (NumeralValue 76)
             [ "syttiseks"
             , "sÃ¸ttiseks"
             ]
  , examples (NumeralValue 77)
             [ "syttisju"
             , "syttisju"
             , "sÃ¸ttisyv"
             , "sÃ¸ttisyv"
             ]
  , examples (NumeralValue 78)
             [ "sÃ¸ttiÃ¥tte"
             , "sÃ¸ttiÃ¥tte"
             ]
  , examples (NumeralValue 79)
             [ "sÃ¸ttini"
             , "sÃ¸ttini"
             ]
  , examples (NumeralValue 81)
             [ "Ã¥ttien"
             , "Ã¥ttiÃ©n"
             ]
  , examples (NumeralValue 82)
             [ "Ã¥ttito"
             ]
  , examples (NumeralValue 83)
             [ "Ã¥ttitre"
             ]
  , examples (NumeralValue 84)
             [ "Ã¥ttifire"
             ]
  , examples (NumeralValue 85)
             [ "Ã¥ttifem"
             ]
  , examples (NumeralValue 86)
             [ "Ã¥ttiseks"
             ]
  , examples (NumeralValue 87)
             [ "Ã¥ttisju"
             , "Ã¥ttisyv"
             ]
  , examples (NumeralValue 88)
             [ "Ã¥ttiÃ¥tte"
             ]
  , examples (NumeralValue 89)
             [ "Ã¥ttini"
             ]
  , examples (NumeralValue 91)
             [ "nittien"
             , "nittiÃ©n"
             ]
  , examples (NumeralValue 92)
             [ "nittito"
             ]
  , examples (NumeralValue 93)
             [ "nittitre"
             ]
  , examples (NumeralValue 94)
             [ "nittifire"
             ]
  , examples (NumeralValue 95)
             [ "nittifem"
             ]
  , examples (NumeralValue 96)
             [ "nittiseks"
             ]
  , examples (NumeralValue 97)
             [ "nittisju"
             , "nittisyv"
             ]
  , examples (NumeralValue 98)
             [ "nittiÃ¥tte"
             ]
  , examples (NumeralValue 99)
             [ "nittini"
             ]
  ]


-- 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.NB.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

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

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

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

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
  }

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

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

rulePowersOfTen :: Rule
rulePowersOfTen = Rule
  { name = "powers of tens"
  , pattern =
    [ regex "(hundre(de)?|tusen?|million(er)?|milliard(?:er)?|billion(?:er)?|billiard(?:er)?|trillion(?:er)?|trilliard(?:er)?|kvadrillion(?:er)?|kvadrilliard(?:er)?)"
    ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) -> case Text.toLower match of
        "hundre"    -> double 1e2 >>= withGrain 2 >>= withMultipliable
        "hundrede"  -> double 1e2 >>= withGrain 2 >>= withMultipliable
        "tuse"      -> double 1e3 >>= withGrain 3 >>= withMultipliable
        "tusen"     -> double 1e3 >>= withGrain 3 >>= withMultipliable
        "million"   -> double 1e6 >>= withGrain 6 >>= withMultipliable
        "millioner" -> double 1e6 >>= withGrain 6 >>= withMultipliable
        "milliard" -> double 1e9 >>= withGrain 9 >>= withMultipliable
        "milliarder" -> double 1e9 >>= withGrain 9 >>= withMultipliable
        "billion" -> double 1e12 >>= withGrain 12 >>= withMultipliable
        "billioner" -> double 1e12 >>= withGrain 12 >>= withMultipliable
        "billiard" -> double 1e15 >>= withGrain 15 >>= withMultipliable
        "billiarder" -> double 1e15 >>= withGrain 15 >>= withMultipliable
        "trillion" -> double 1e18 >>= withGrain 18 >>= withMultipliable
        "trillioner" -> double 1e18 >>= withGrain 18 >>= withMultipliable
        "trilliard" -> double 1e21 >>= withGrain 21 >>= withMultipliable
        "trilliarder" -> double 1e21 >>= withGrain 21 >>= withMultipliable
        "kvadrillion" -> double 1e24 >>= withGrain 24 >>= withMultipliable
        "kvadrillioner" -> double 1e24 >>= withGrain 24 >>= withMultipliable
        "kvadrilliard" -> double 1e27 >>= withGrain 27 >>= withMultipliable
        "kvadrilliarder" -> double 1e27 >>= withGrain 27 >>= withMultipliable
        _           -> Nothing
      _ -> Nothing
  }

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

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

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

ruleInteger :: Rule
ruleInteger = Rule
  { name = "integer (0..19)"
  , pattern =
    [ regex "(intet|ingen|null|en|ett|Ã©n|to|tretten|tre|fire|femten|fem|seksten|seks|syv|sju|Ã¥tte|nitten|ni|ti|elleve|tolv|fjorten|sytten|sÃ¸tten|atten)"
    ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) zeroToNineteenMap >>= integer
      _ -> Nothing
  }

twentyToHundredMap :: HashMap Text Integer
twentyToHundredMap = HashMap.fromList
  [ ("tjueen", 21)
  , ("tjueÃ©n", 21)
  , ("tjueto", 22)
  , ("tjuetre", 23)
  , ("tjuefire", 24)
  , ("tjuefem", 25)
  , ("tjueseks", 26)
  , ("tjuesju", 27)
  , ("tjuesyv", 27)
  , ("tjueÃ¥tte", 28)
  , ("tjueni", 29)
  , ("trettien", 31)
  , ("trettiÃ©n", 31)
  , ("trettito", 32)
  , ("trettitre", 33)
  , ("trettifire", 34)
  , ("trettifem", 35)
  , ("trettiseks", 36)
  , ("trettisju", 37)
  , ("trettisyv", 37)
  , ("trettiÃ¥tte", 38)
  , ("trettini", 39)
  , ("fÃ¸rtien", 41)
  , ("fÃ¸rtiÃ©n", 41)
  , ("fÃ¸rtito", 42)
  , ("fÃ¸rtitre", 43)
  , ("fÃ¸rtifire", 44)
  , ("fÃ¸rtifem", 45)
  , ("fÃ¸rtiseks", 46)
  , ("fÃ¸rtisju", 47)
  , ("fÃ¸rtisyv", 47)
  , ("fÃ¸rtiÃ¥tte", 48)
  , ("fÃ¸rtini", 49)
  , ("femtien", 51)
  , ("femtiÃ©n", 51)
  , ("femtito", 52)
  , ("femtitre", 53)
  , ("femtifire", 54)
  , ("femtifem", 55)
  , ("femtiseks", 56)
  , ("femtisju", 57)
  , ("femtisyv", 57)
  , ("femtiÃ¥tte", 58)
  , ("femtini", 59)
  , ("sekstien", 61)
  , ("sekstiÃ©n", 61)
  , ("sekstito", 62)
  , ("sekstitre", 63)
  , ("sekstifire", 64)
  , ("sekstifem", 65)
  , ("sekstiseks", 66)
  , ("sekstisju", 67)
  , ("sekstisyv", 67)
  , ("sekstiÃ¥tte", 68)
  , ("sekstini", 69)
  , ("syttien", 71)
  , ("syttiÃ©n", 71)
  , ("syttito", 72)
  , ("syttitre", 73)
  , ("syttifire", 74)
  , ("syttifem", 75)
  , ("syttiseks", 76)
  , ("syttisju", 77)
  , ("syttisyv", 77)
  , ("syttiÃ¥tte", 78)
  , ("syttini", 79)
  , ("sÃ¸ttien", 71)
  , ("sÃ¸ttiÃ©n", 71)
  , ("sÃ¸ttito", 72)
  , ("sÃ¸ttitre", 73)
  , ("sÃ¸ttifire", 74)
  , ("sÃ¸ttifem", 75)
  , ("sÃ¸ttiseks", 76)
  , ("sÃ¸ttisju", 77)
  , ("sÃ¸ttisyv", 77)
  , ("sÃ¸ttiÃ¥tte", 78)
  , ("sÃ¸ttini", 79)
  , ("Ã¥ttien", 81)
  , ("Ã¥ttiÃ©n", 81)
  , ("Ã¥ttito", 82)
  , ("Ã¥ttitre", 83)
  , ("Ã¥ttifire", 84)
  , ("Ã¥ttifem", 85)
  , ("Ã¥ttiseks", 86)
  , ("Ã¥ttisju", 87)
  , ("Ã¥ttisyv", 87)
  , ("Ã¥ttiÃ¥tte", 88)
  , ("Ã¥ttini", 89)
  , ("nittien", 91)
  , ("nittiÃ©n", 91)
  , ("nittito", 92)
  , ("nittitre", 93)
  , ("nittifire", 94)
  , ("nittifem", 95)
  , ("nittiseks", 96)
  , ("nittisju", 97)
  , ("nittisyv", 97)
  , ("nittiÃ¥tte", 98)
  , ("nittini", 99)
  ]

ruleInteger4 :: Rule
ruleInteger4 = Rule
  { name = "integer (21..99)"
  , pattern =
    [ regex "(tjueen|tjueÃ©n|tjueto|tjuetre|tjuefire|tjuefem|tjueseks|tjuesju|tjuesyv|tjueÃ¥tte|tjueni\
            \|trettien|trettiÃ©n|trettito|trettitre|trettifire|trettifem|trettiseks|trettisju|trettisyv|trettiÃ¥tte|trettini\
            \|fÃ¸rtien|fÃ¸rtiÃ©n|fÃ¸rtito|fÃ¸rtitre|fÃ¸rtifire|fÃ¸rtifem|fÃ¸rtiseks|fÃ¸rtisju|fÃ¸rtisyv|fÃ¸rtiÃ¥tte|fÃ¸rtini\
            \|femtien|femtiÃ©n|femtito|femtitre|femtifire|femtifem|femtiseks|femtisju|femtisyv|femtiÃ¥tte|femtini\
            \|sekstien|sekstiÃ©n|sekstito|sekstitre|sekstifire|sekstifem|sekstiseks|sekstisju|sekstisyv|sekstiÃ¥tte|sekstini\
            \|syttien|syttiÃ©n|syttito|syttitre|syttifire|syttifem|syttiseks|syttisju|syttisyv|syttiÃ¥tte|syttini\
            \|sÃ¸ttien|sÃ¸ttiÃ©n|sÃ¸ttito|sÃ¸ttitre|sÃ¸ttifire|sÃ¸ttifem|sÃ¸ttiseks|sÃ¸ttisju|sÃ¸ttisyv|sÃ¸ttiÃ¥tte|sÃ¸ttini\
            \|Ã¥ttien|Ã¥ttiÃ©n|Ã¥ttito|Ã¥ttitre|Ã¥ttifire|Ã¥ttifem|Ã¥ttiseks|Ã¥ttisju|Ã¥ttisyv|Ã¥ttiÃ¥tte|Ã¥ttini\
            \|nittien|nittiÃ©n|nittito|nittitre|nittifire|nittifem|nittiseks|nittisju|nittisyv|nittiÃ¥tte|nittini\
            \)"
    ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) twentyToHundredMap >>= integer
      _ -> Nothing
  }

tensMap :: HashMap Text Integer
tensMap = HashMap.fromList
  [ ( "tyve" , 20 )
  , ( "tjue" , 20 )
  , ( "tredve" , 30 )
  , ( "tretti" , 30 )
  , ( "fÃ¸rti" , 40 )
  , ( "femti" , 50 )
  , ( "seksti" , 60 )
  , ( "sytti" , 70 )
  , ( "sÃ¸tti" , 70 )
  , ( "Ã¥tti" , 80 )
  , ( "nitti" , 90 )
  ]

ruleInteger2 :: Rule
ruleInteger2 = Rule
  { name = "integer (20..90)"
  , pattern =
    [ regex "(tyve|tjue|tredve|tretti|fÃ¸rti|femti|seksti|sytti|sÃ¸tti|Ã¥tti|nitti)"
    ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) tensMap >>= integer
      _ -> Nothing
  }

ruleNumeralDotNumeral :: Rule
ruleNumeralDotNumeral = Rule
  { name = "number dot number"
  , pattern =
    [ dimension Numeral
    , regex "komma"
    , Predicate $ not . hasGrain
    ]
  , prod = \case
      (Token Numeral nd1:_:Token Numeral nd2:_) ->
        double $ TNumeral.value nd1 + decimalsToDouble (TNumeral.value nd2)
      _ -> 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 $ Text.replace "." Text.empty $ match)
        >>= double
      _ -> Nothing
  }

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


-- 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.NL.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 NL Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (NumeralValue 0)
             [ "0"
             , "nul"
             , "geen"
             , "niks"
             ]
  , examples (NumeralValue 1)
             [ "1"
             , "een"
             , "Ã©Ã©n"
             ]
  , examples (NumeralValue 2)
             [ "2"
             , "twee"
             ]
  , examples (NumeralValue 33)
             [ "33"
             , "3 en 30"
             , "drieendertig"
             , "drieÃ«ndertig"
             , "drie en dertig"
             , "0033"
             ]
  , examples (NumeralValue 12)
             [ "twaalf"
             , "dozijn"
             ]
  , examples (NumeralValue 14)
             [ "14"
             , "veertien"
             ]
  , examples (NumeralValue 16)
             [ "16"
             , "zestien"
             ]
  , examples (NumeralValue 17)
             [ "17"
             , "zeventien"
             ]
  , examples (NumeralValue 18)
             [ "18"
             , "achttien"
             ]
  , examples (NumeralValue 1.1)
             [ "1,1"
             , "1,10"
             , "01,10"
             ]
  , examples (NumeralValue 0.77)
             [ "0,77"
             , ",77"
             ]
  , examples (NumeralValue 100000)
             [ "100.000"
             , "100.000,0"
             , "100000"
             , "100K"
             , "100k"
             , "honderd duizend"
             ]
  , examples (NumeralValue 300)
             [ "3 honderd"
             , "drie honderd"
             ]
  , examples (NumeralValue 5000)
             [ "5 duizend"
             , "vijf duizend"
             , "5.000"
             , "5.000,00"
             ]
  , examples (NumeralValue 144)
             [ "gros"
             ]
  , examples (NumeralValue 122)
             [ "honderd tweeÃ«ntwintig"
             , "honderd tweeentwintig"
             , "honderd twee en twintig"
             ]
  , examples (NumeralValue 20000)
             [ "twintig duizend"
             , "20.000"
             , "20000"
             , "20.000,00"
             ]
  , examples (NumeralValue 0.2)
             [ "1/5"
             , "2/10"
             , "3/15"
             , "20/100"
             ]
  ]


-- 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.NL.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 "-|min|minus|negatief"
    , Predicate isPositive
    ]
  , prod = \tokens -> case tokens of
      (_:Token Numeral nd:_) -> double (TNumeral.value nd * (-1))
      _ -> Nothing
  }

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

ruleTen :: Rule
ruleTen = Rule
  { name = "ten"
  , pattern =
    [ regex "tien"
    ]
  , 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:_)):
       _) -> 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
  }

-- TODO: Single-word composition (#110)
ruleInteger3 :: Rule
ruleInteger3 = Rule
  { name = "integer ([2-9][1-9])"
  , pattern =
    [ regex "(een|twee|drie|vier|vijf|zes|zeven|acht|negen)(?:e|Ã«)n(twintig|dertig|veertig|vijftig|zestig|zeventig|tachtig|negentig)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (m1:m2:_)):_) -> do
        v1 <- HashMap.lookup (Text.toLower m1) zeroNineteenMap
        v2 <- HashMap.lookup (Text.toLower m2) dozenMap
        integer $ 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
  }

ruleNumeralsEn :: Rule
ruleNumeralsEn = Rule
  { name = "numbers en"
  , pattern =
    [ Predicate $ numberBetween 1 10
    , regex "-?en-?"
    , 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
  }

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

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

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

ruleGross :: Rule
ruleGross = Rule
  { name = "gros"
  , pattern =
    [ regex "gros"
    ]
  , prod = \_ -> integer 144 >>= withGrain 1
  }

zeroNineteenMap :: HashMap Text Integer
zeroNineteenMap = HashMap.fromList
  [ ("niks", 0)
  , ("nul", 0)
  , ("geen", 0)
  , ("Ã©Ã©n", 1)
  , ("een", 1)
  , ("twee", 2)
  , ("drie", 3)
  , ("vier", 4)
  , ("vijf", 5)
  , ("zes", 6)
  , ("zeven", 7)
  , ("acht", 8)
  , ("negen", 9)
  , ("tien", 10)
  , ("elf", 11)
  , ("twaalf", 12)
  , ("dertien", 13)
  , ("veertien", 14)
  , ("vijftien", 15)
  , ("zestien", 16)
  , ("zeventien", 17)
  , ("achttien", 18)
  , ("negentien", 19)
  ]

ruleInteger :: Rule
ruleInteger = Rule
  { name = "integer (0..19)"
  , pattern =
    [ regex "(geen|nul|niks|een|Ã©Ã©n|twee|drie|vier|vijftien|vijf|zestien|zes|zeventien|zeven|achttien|acht|negentien|negen|tien|elf|twaalf|dertien|veertien)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) zeroNineteenMap >>= integer
      _ -> Nothing
  }

dozenMap :: HashMap Text Integer
dozenMap = HashMap.fromList
  [ ("twintig", 20)
  , ("dertig", 30)
  , ("veertig", 40)
  , ("vijftig", 50)
  , ("zestig", 60)
  , ("zeventig", 70)
  , ("tachtig", 80)
  , ("negentig", 90)
  ]

ruleInteger2 :: Rule
ruleInteger2 = Rule
  { name = "integer (20..90)"
  , pattern =
    [ regex "(twintig|dertig|veertig|vijftig|zestig|zeventig|tachtig|negentig)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) dozenMap >>= integer
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleCouple
  , ruleDecimalNumeral
  , ruleDecimalWithThousandsSeparator
  , ruleDozen
  , ruleGross
  , ruleFew
  , ruleInteger
  , ruleInteger2
  , ruleInteger3
  , ruleIntersect
  , ruleMultiply
  , ruleNumeralsEn
  , ruleNumeralsPrefixWithNegativeOrMinus
  , ruleNumeralsSuffixesKMG
  , rulePowersOfTen
  , 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.PL.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 PL Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (NumeralValue 0)
             [ "0"
             , "nic"
             , "zero"
             ]
  , examples (NumeralValue 1)
             [ "1"
             , "jeden"
             , "pojedynczy"
             ]
  , examples (NumeralValue 2)
             [ "2"
             , "dwa"
             , "para"
             ]
  , examples (NumeralValue 33)
             [ "33"
             , "trzydzieÅ›ci trzy"
             , "0033"
             ]
  , examples (NumeralValue 14)
             [ "14"
             , "czternaÅ›cie"
             ]
  , examples (NumeralValue 16)
             [ "16"
             , "szesnaÅ›cie"
             ]
  , examples (NumeralValue 17)
             [ "17"
             , "siedemnaÅ›cie"
             ]
  , examples (NumeralValue 18)
             [ "18"
             , "osiemnaÅ›cie"
             ]
  , 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"
             ]
  , examples (NumeralValue (-1200000))
             [ "- 1,200,000"
             , "-1200000"
             , "minus 1,200,000"
             , "-1.2M"
             , "-1200K"
             , "-.0012G"
             ]
  , examples (NumeralValue 5000)
             [ "5 tysiÄ™cy"
             , "piÄ™Ä‡ tysiÄ™cy"
             ]
  , examples (NumeralValue 79)
             [ "siedemdziesiÄ…t dziewiÄ™Ä‡"
             , "siedemdziesiÄ™ciu dziewiÄ™ciu"
             ]
  , examples (NumeralValue 84)
             [ "osiemdziesiÄ…t cztery"
             , "osiemdziesiat cztery"
             , "osiemdziesiÄ™ciu czterem"
             , "osiemdziesieciu czterem"
             ]
  , examples (NumeralValue 92)
             [ "dziewiÄ™Ä‡dziesiÄ…t dwa"
             , "dziewiecdziesiat dwa"
             , "dziewiÄ™Ä‡dziesiÄ™ciu dwÃ³m"
             , "dziewiecdziesieciu dwom"
             ]
  , examples (NumeralValue 122)
             [ "sto dwadzieÅ›cia dwa"
             ]
  , examples (NumeralValue 200000)
             [ "dwieÅ›cie tysiÄ™cy"
             ]
  , examples (NumeralValue 21011)
             [ "dwadzieÅ›cia jeden tysiÄ™cy i jedenaÅ›cie"
             , "dwadzieÅ›cia jeden tysiÄ™cy jedenaÅ›cie"
             ]
  , examples (NumeralValue 721012)
             [ "siedemset dwadzieÅ›cia jeden tysiÄ™cy dwanaÅ›cie"
             , "siedemset dwadzieÅ›cia jeden tysiÄ™cy i dwanaÅ›cie"
             ]
  , examples (NumeralValue 65000000)
             [ "szeÅ›Ä‡dziesiÄ…t piÄ™Ä‡ milionÃ³w"
             ]
  , examples (NumeralValue 31256721)
             [ "trzydzieÅ›ci jeden milionÃ³w dwieÅ›cie piÄ™Ä‡dziesiÄ…t szeÅ›Ä‡ tysiÄ™cy siedemset dwadzieÅ›cia jeden"
             ]
  , examples (NumeralValue 15)
             [ "piÄ™tnasta"
             ]
  ]