-- 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.Duration.EN.Corpus
  ( corpus
  , negativeCorpus
  ) where

import Prelude
import Data.String

import Duckling.Duration.Types
import Duckling.Testing.Types
import Duckling.TimeGrain.Types (Grain(..))

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

negativeCorpus :: NegativeCorpus
negativeCorpus = (testContext, testOptions, examples)
  where
    examples =
      [ "for months"
      , "in days"
      , "secretary"
      , "minutes"
      , "I second that"
      ]

allExamples :: [Example]
allExamples = concat
  [ examples (DurationData 1 Second)
             [ "one sec"
             , "1 second"
             , "1\""
             ]
  , examples (DurationData 2 Minute)
             [ "2 mins"
             , "two minutes"
             , "2'"
             , "2 more minutes"
             , "two additional minutes"
             , "2 extra minutes"
             , "2 less minutes"
             , "2 fewer minutes"
             , "2m"
             , "2 m"
             ]
  , examples (DurationData 30 Day)
             [ "30 days"
             ]
  , examples (DurationData 7 Week)
             [ "seven weeks"
             ]
  , examples (DurationData 1 Month)
             [ "1 month"
             , "a month"
             ]
  , examples (DurationData 3 Quarter)
             [ "3 quarters"
             ]
  , examples (DurationData 2 Year)
             [ "2 years"
             ]
  , examples (DurationData 30 Minute)
             [ "half an hour"
             , "half hour"
             , "1/2 hour"
             , "1/2h"
             , "1/2 h"
             ]
  , examples (DurationData 12 Hour)
             [ "half a day"
             , "half day"
             , "1/2 day"
             ]
  , examples (DurationData 90 Minute)
             [ "an hour and a half"
             , "one hour and half"
             , "1 hour thirty"
             , "1 hour and thirty"
             , "1.5 hours"
             , "1.5 hrs"
             , "one and two quarter hour"
             , "one and two quarters hour"
             , "one and two quarter of hour"
             , "one and two quarters of hour"
             ]
  , examples (DurationData 75 Minute)
             [ "1 hour fifteen"
             , "1 hour and fifteen"
             , "one and quarter hour"
             , "one and a quarter hour"
             , "one and one quarter hour"
             , "one and quarter of hour"
             , "one and a quarter of hour"
             , "one and one quarter of hour"
             ]
  , examples (DurationData 130 Minute)
             [ "2 hours ten"
             , "2 hour and 10"
             ]
  , examples (DurationData 3615 Second)
             [ "1 hour fifteen seconds"
             , "1 hour and fifteen seconds"
             ]
  , examples (DurationData 45 Day)
             [ "a month and a half"
             , "one month and half"
             ]
  , examples (DurationData 27 Month)
             [ "2 years and 3 months"
             , "2 years, 3 months"
             , "2 years 3 months"
             ]
  , examples (DurationData 31719604 Second)
             [ "1 year, 2 days, 3 hours and 4 seconds"
             , "1 year 2 days 3 hours and 4 seconds"
               -- Oxford comma not supported:
--           , "1 year, 2 days, 3 hours, and 4 seconds"
             ]
  , examples (DurationData 330 Second)
             [ "5 and a half minutes"
             , "five and half min"
             , "5 and an half minute"
             ]
  , examples (DurationData 105 Minute)
              [ "one and three quarter hour"
              , "one and three quarters hour"
              , "one and three quarter of hour"
              , "one and three quarters of hour"
              , "one and three quarter of hours"
              , "one and three quarters of hours"
              ]
  , examples (DurationData 135 Minute)
             [ "two and quarter hour"
             , "two and a quarter of hour"
             , "two and quarter of hours"
             , "two and a quarter of hours"
             ]
  , examples (DurationData 105 Minute)
              [ "an hour and 45 minutes"
              , "one hour and 45 minutes"
              ]
  , examples (DurationData 90 Second)
              [ "a minute and 30 seconds"
              , "one minute and 30 seconds"
              ]
  , examples (DurationData 3630 Second)
              [ "an hour and 30 seconds"]
  , examples (DurationData 930 Second)
              [ "15.5 minutes"
              , "15.5 minute"
              , "15.5 mins"
              , "15.5 min"
              ]
  ]


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

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

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

import Prelude
import qualified Data.Text as Text

import Duckling.Dimensions.Types
import Duckling.Duration.Helpers
import Duckling.Duration.Types (DurationData(..))
import Duckling.Numeral.Helpers (numberBetween, parseInteger)
import Duckling.Numeral.Types (NumeralData(..))
import Duckling.Regex.Types
import Duckling.Types
import qualified Duckling.Duration.Types as TDuration
import qualified Duckling.Numeral.Types as TNumeral
import qualified Duckling.TimeGrain.Types as TG

ruleDurationQuarterOfAnHour :: Rule
ruleDurationQuarterOfAnHour = Rule
  { name = "quarter of an hour"
  , pattern =
    [ regex "(1/4\\s?h(our)?|(a\\s)?quarter of an hour)"
    ]
  , prod = \_ -> Just $ Token Duration $ duration TG.Minute 15
  }

ruleDurationHalfAnHourAbbrev :: Rule
ruleDurationHalfAnHourAbbrev = Rule
  { name = "half an hour (abbrev)."
  , pattern =
    [ regex "1/2\\s?h"
    ]
  , prod = \_ -> Just $ Token Duration $ duration TG.Minute 30
  }

ruleDurationThreeQuartersOfAnHour :: Rule
ruleDurationThreeQuartersOfAnHour = Rule
  { name = "three-quarters of an hour"
  , pattern =
    [ regex "(3/4\\s?h(our)?|three(\\s|-)quarters of an hour)"
    ]
  , prod = \_ -> Just $ Token Duration $ duration TG.Minute 45
  }

ruleDurationFortnight :: Rule
ruleDurationFortnight = Rule
  { name = "fortnight"
  , pattern =
    [ regex "(a|one)? fortnight"
    ]
  , prod = \_ -> Just $ Token Duration $ duration TG.Day 14
  }

ruleNumeralQuotes :: Rule
ruleNumeralQuotes = Rule
  { name = "<integer> + '\""
  , pattern =
    [ Predicate isNatural
    , regex "(['\"])"
    ]
  , prod = \case
      (Token Numeral NumeralData{TNumeral.value = v}:
       Token RegexMatch (GroupMatch (x:_)):
       _) -> case x of
         "'"  -> Just $ Token Duration $ duration TG.Minute $ floor v
         "\"" -> Just $ Token Duration $ duration TG.Second $ floor v
         _    -> Nothing
      _ -> Nothing
  }

ruleDurationNumeralMore :: Rule
ruleDurationNumeralMore = Rule
  { name = "<integer> more <unit-of-duration>"
  , pattern =
    [ Predicate isNatural
    , regex "more|additional|extra|less|fewer"
    , dimension TimeGrain
    ]
  , prod = \case
      (Token Numeral nd:_:Token TimeGrain grain:_) ->
        Just $ Token Duration $ duration grain $ floor $ TNumeral.value nd
      _ -> Nothing
  }

ruleDurationDotNumeralHours :: Rule
ruleDurationDotNumeralHours = Rule
  { name = "number.number hours"
  , pattern =
    [ regex "(\\d+)\\.(\\d+)"
    , Predicate $ isGrain TG.Hour
    ]
  , prod = \case
      (Token RegexMatch (GroupMatch (h:m:_)):_) -> do
        hh <- parseInteger h
        mnum <- parseInteger m
        let mden = 10 ^ Text.length m
        Just $ Token Duration $ minutesFromHourMixedFraction hh mnum mden
      _ -> Nothing
  }

ruleDurationAndHalfHour :: Rule
ruleDurationAndHalfHour = Rule
  { name = "<integer> and an half hour"
  , pattern =
    [ Predicate isNatural
    , regex "and (an? )?half hours?"
    ]
  , prod = \case
      (Token Numeral NumeralData{TNumeral.value = v}:_) ->
        Just $ Token Duration $ duration TG.Minute $ 30 + 60 * floor v
      _ -> Nothing
  }

ruleDurationAndHalfMinute :: Rule
ruleDurationAndHalfMinute = Rule
  { name = "<integer> and a half minutes"
  , pattern =
    [ Predicate isNatural
    , regex "and (an? )?half min(ute)?s?"
    ]
  , prod = \case
      (Token Numeral NumeralData{TNumeral.value = v}:_) ->
        Just $ Token Duration $ duration TG.Second $ 30 + 60 * floor v
      _ -> Nothing
  }

ruleDurationA :: Rule
ruleDurationA = Rule
  { name = "a <unit-of-duration>"
  , pattern =
    [ regex "an?"
    , dimension TimeGrain
    ]
  , prod = \case
      (_:Token TimeGrain grain:_) -> Just $ Token Duration $ duration grain 1
      _ -> Nothing
  }

ruleDurationHalfATimeGrain :: Rule
ruleDurationHalfATimeGrain = Rule
  { name = "half a <time-grain>"
  , pattern =
    [ regex "(1/2|half)( an?)?"
    , dimension TimeGrain
    ]
  , prod = \case
      (_:Token TimeGrain grain:_) -> Token Duration <$> nPlusOneHalf grain 0
      _ -> Nothing
  }

ruleDurationOneGrainAndHalf :: Rule
ruleDurationOneGrainAndHalf = Rule
  { name = "a <unit-of-duration> and a half"
  , pattern =
    [ regex "an?|one"
    , dimension TimeGrain
    , regex "and (a )?half"
    ]
  , prod = \case
      (_:Token TimeGrain grain:_) -> Token Duration <$> nPlusOneHalf grain 1
      _ -> Nothing
  }

ruleDurationHoursAndMinutes :: Rule
ruleDurationHoursAndMinutes = Rule
  { name = "<integer> hour and <integer>"
  , pattern =
    [ Predicate isNatural
    , regex "hours?( and)?"
    , Predicate $ and . sequence [isNatural, numberBetween 1 60]
    ]
  , prod = \case
      (Token Numeral h:
       _:
       Token Numeral m:
       _) -> Just $ Token Duration $ duration TG.Minute $
         floor (TNumeral.value m) + 60 * floor (TNumeral.value h)
      _ -> Nothing
  }

ruleDurationPrecision :: Rule
ruleDurationPrecision = Rule
  { name = "about|exactly <duration>"
  , pattern =
    [ regex "(about|around|approximately|exactly)"
    , dimension Duration
    ]
    , prod = \case
        (_:token:_) -> Just token
        _ -> Nothing
  }

-- | NOTE: Oxford comma is not supported.
ruleCompositeDurationCommasAnd :: Rule
ruleCompositeDurationCommasAnd = Rule
  { name = "composite <duration> (with ,/and)"
  , pattern =
    [ Predicate isNatural
    , dimension TimeGrain
    , regex ",|and"
    , dimension Duration
    ]
  , prod = \case
      (Token Numeral NumeralData{TNumeral.value = v}:
       Token TimeGrain g:
       _:
       Token Duration dd@DurationData{TDuration.grain = dg}:
       _) | g > dg -> Just $ Token Duration $ duration g (floor v) <> dd
      _ -> Nothing
  }

ruleCompositeDuration :: Rule
ruleCompositeDuration = Rule
  { name = "composite <duration>"
  , pattern =
    [ Predicate isNatural
    , dimension TimeGrain
    , dimension Duration
    ]
  , prod = \case
      (Token Numeral NumeralData{TNumeral.value = v}:
       Token TimeGrain g:
       Token Duration dd@DurationData{TDuration.grain = dg}:
       _) | g > dg -> Just $ Token Duration $ duration g (floor v) <> dd
      _ -> Nothing
  }

ruleCompositeDurationAnd :: Rule
ruleCompositeDurationAnd = Rule
  { name = "composite <duration> and <duration>"
  , pattern =
    [ dimension Duration
    , regex ",|and"
    , dimension Duration
    ]
  , prod = \case
      (Token Duration DurationData{TDuration.value = v, TDuration.grain = g}:
       _:
       Token Duration dd@DurationData{TDuration.grain = dg}:
       _) | g > dg -> Just $ Token Duration $ duration g v <> dd
      _ -> Nothing
  }

ruleDurationDotNumeralMinutes :: Rule
ruleDurationDotNumeralMinutes = Rule
  { name = "number.number minutes"
  , pattern =
    [ regex "(\\d+)\\.(\\d+)"
    , Predicate $ isGrain TG.Minute
    ]
  , prod = \case
      (Token RegexMatch (GroupMatch (m:s:_)):_) -> do
        mm <- parseInteger m
        ss <- parseInteger s
        let sden = 10 ^ Text.length s
        Just $ Token Duration $ secondsFromHourMixedFraction mm ss sden
      _ -> Nothing
  }

ruleDurationNumeralAndQuarterHour :: Rule
ruleDurationNumeralAndQuarterHour = Rule
  { name = "<Integer> and <Integer> quarter of hour"
  , pattern =
    [ Predicate isNatural
    , regex "and (a |an |one |two |three )?quarters?( of)?( an)?"
    , Predicate $ isGrain TG.Hour
    ]
  , prod = \case
      (Token Numeral NumeralData{TNumeral.value = h}:
       Token RegexMatch (GroupMatch (match:_)):
       _) -> do
         q <- case Text.strip $ Text.toLower match of "a"     -> Just 1
                                                      "an"    -> Just 1
                                                      "one"   -> Just 1
                                                      "two"   -> Just 2
                                                      "three" -> Just 3
                                                      _       -> Just 1
         Just $ Token Duration $ duration TG.Minute $ 15 * q + 60 * floor h
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleCompositeDurationCommasAnd
  , ruleDurationQuarterOfAnHour
  , ruleDurationHalfAnHourAbbrev
  , ruleDurationThreeQuartersOfAnHour
  , ruleDurationFortnight
  , ruleDurationNumeralMore
  , ruleDurationDotNumeralHours
  , ruleDurationAndHalfHour
  , ruleDurationAndHalfMinute
  , ruleDurationA
  , ruleDurationHalfATimeGrain
  , ruleDurationOneGrainAndHalf
  , ruleDurationHoursAndMinutes
  , ruleDurationPrecision
  , ruleNumeralQuotes
  , ruleCompositeDuration
  , ruleCompositeDurationAnd
  , ruleCompositeDurationCommasAnd
  , ruleDurationDotNumeralMinutes
  , ruleDurationNumeralAndQuarterHour
  ]


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

import Prelude
import Data.String

import Duckling.Duration.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types
import Duckling.TimeGrain.Types (Grain(..))

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

allExamples :: [Example]
allExamples = concat
  [ examples (DurationData 30 Minute)
             [ "media hora"
             , "media horas"
             , "30 minutos"
             , "treinta minutos"
             ]
  , examples (DurationData 15 Minute)
             [  "cuarto de hora"
             ,  "cuartos de hora"
             ,  "quince minutos"
             ,  "15 minutos"
             ]
  , examples (DurationData 45 Minute)
             [  "tres cuartos de hora"
             ,  "tres cuartos de horas"
             ,  "tres cuarto de hora"
             ,  "tres cuarto de horas"
             ,  "cuarenta y cinco minutos"
             ,  "45 minutos"
             ]
  , examples (DurationData 92 Minute)
             [
               "una hora y treinta y dos minutos"
             , "una hora treinta y dos minutos"
             ]
  , examples (DurationData 155 Minute)
             [
               "dos hora y treinta y cinco minutos"
             , "dos hora  treinta y cinco minutos"
             ]
  ]


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

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

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

import Prelude
import Data.Semigroup ((<>))
import Duckling.Dimensions.Types
import Duckling.Duration.Helpers
import Duckling.Duration.Types (DurationData(..))
import Duckling.Numeral.Types (NumeralData(..))
import Duckling.Types
import qualified Duckling.Duration.Types as TDuration
import qualified Duckling.Numeral.Types as TNumeral
import qualified Duckling.TimeGrain.Types as TG

ruleDurationQuarterOfAnHour :: Rule
ruleDurationQuarterOfAnHour = Rule
  { name = "quarter of an hour"
  , pattern =
    [ regex "cuartos? de hora"
    ]
  , prod = \_ -> Just $ Token Duration $ duration TG.Minute 15
  }

ruleDurationHalfOfAnHour :: Rule
ruleDurationHalfOfAnHour = Rule
  { name = "half of an hour"
  , pattern =
    [ regex "media horas?"
    ]
  , prod = \_ -> Just $ Token Duration $ duration TG.Minute 30
  }

ruleDurationThreeQuartersOfAnHour :: Rule
ruleDurationThreeQuartersOfAnHour = Rule
  { name = "three-quarters of an hour"
  , pattern =
    [ regex "tres cuartos? de horas?"
    ]
  , prod = \_ -> Just $ Token Duration $ duration TG.Minute 45
  }

ruleCompositeDurationCommasAnd :: Rule
ruleCompositeDurationCommasAnd = Rule
  { name = "composite <duration> (with and)"
  , pattern =
    [ Predicate isNatural
    , dimension TimeGrain
    , regex "y"
    , dimension Duration
    ]
  , prod = \case
      (Token Numeral NumeralData{TNumeral.value = v}:
       Token TimeGrain g:
       _:
       Token Duration dd@DurationData{TDuration.grain = dg}:
       _) | g > dg -> Just $ Token Duration $ duration g (floor v) <> dd
      _ -> Nothing
  }

ruleCompositeDuration :: Rule
ruleCompositeDuration = Rule
  {
    name = "composite <duration>"
  , pattern =
    [
      Predicate isNatural
    , dimension TimeGrain
    , dimension Duration
    ]
  , prod = \case
      (Token Numeral NumeralData{TNumeral.value = v}:
       Token TimeGrain g:
       Token Duration dd@DurationData{TDuration.grain = dg}:
       _) | g > dg -> Just $ Token Duration $ duration g (floor v) <> dd
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDurationHalfOfAnHour
  , ruleDurationQuarterOfAnHour
  , ruleDurationThreeQuartersOfAnHour
  , ruleCompositeDuration
  , ruleCompositeDurationCommasAnd
  ]


-- 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.Duration.FR.Corpus
  ( corpus
  , negativeCorpus
  ) where

import Prelude
import Data.String

import Duckling.Duration.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types
import Duckling.TimeGrain.Types (Grain(..))

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

negativeCorpus :: NegativeCorpus
negativeCorpus = (testContext {locale = makeLocale FR Nothing}, testOptions, examples)
  where
    examples =
      [ "les jours"
      , "en secondaire"
      , "minutes"
      , "pendant des mois"
      ]

allExamples :: [Example]
allExamples = concat
  [ examples (DurationData 1 Second)
             [ "une sec"
             , "1 seconde"
             , "1\""
             ]
  , examples (DurationData 2 Minute)
             [ "2 mins"
             , "deux minutes"
             , "2'"
             ]
  , examples (DurationData 30 Day)
             [ "30 jours"
             ]
  , examples (DurationData 7 Week)
             [ "sept semaines"
             ]
  , examples (DurationData 1 Month)
             [ "1 mois"
             , "un mois"
             ]
  , examples (DurationData 3 Quarter)
             [ "3 trimestres"
             ]
  , examples (DurationData 2 Year)
             [ "2 ans"
             ]
  ]


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

import Prelude
import Data.String

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

ruleNumeralQuotes :: Rule
ruleNumeralQuotes = Rule
  { name = "<integer> + '\""
  , pattern =
    [ Predicate isNatural
    , regex "(['\"])"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:
       Token RegexMatch (GroupMatch (x:_)):
       _) -> case x of
         "'"  -> Just . Token Duration . duration TG.Minute $ floor v
         "\"" -> Just . Token Duration . duration TG.Second $ floor v
         _    -> Nothing
      _ -> Nothing
  }

ruleUneUnitofduration :: Rule
ruleUneUnitofduration = Rule
  { name = "une <unit-of-duration>"
  , pattern =
    [ regex "une|la|le?"
    , dimension TimeGrain
    ]
  , prod = \tokens -> case tokens of
      (_:
       Token TimeGrain grain:
       _) -> Just . Token Duration $ duration grain 1
      _ -> Nothing
  }

ruleUnQuartDHeure :: Rule
ruleUnQuartDHeure = Rule
  { name = "un quart d'heure"
  , pattern =
    [ regex "(1/4\\s?h(eure)?|(un|1) quart d'heure)"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 15
  }

ruleUneDemiHeure :: Rule
ruleUneDemiHeure = Rule
  { name = "une demi heure"
  , pattern =
    [ regex "(1/2\\s?h(eure)?|(1|une) demi(e)?(\\s|-)heure)"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 30
  }

ruleTroisQuartsDHeure :: Rule
ruleTroisQuartsDHeure = Rule
  { name = "trois quarts d'heure"
  , pattern =
    [ regex "(3/4\\s?h(eure)?|(3|trois) quart(s)? d'heure)"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 45
  }

ruleDurationEnviron :: Rule
ruleDurationEnviron = Rule
  { name = "environ <duration>"
  , pattern =
    [ regex "environ"
    ]
    , prod = \tokens -> case tokens of
      -- TODO(jodent) +precision approximate
      (_:token:_) -> Just token
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleUneUnitofduration
  , ruleUnQuartDHeure
  , ruleUneDemiHeure
  , ruleTroisQuartsDHeure
  , ruleDurationEnviron
  , ruleNumeralQuotes
  ]


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

import Data.String
import Prelude

import Duckling.Duration.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types
import Duckling.TimeGrain.Types (Grain(..))

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

allExamples :: [Example]
allExamples = concat
  [ examples (DurationData 1 Second)
             [ "aon soicind amhain"
             , "aon soicind"
             , "1 tshoicindi"
             , "1 tsoicind"
             ]
  , examples (DurationData 30 Minute)
             [ "leathuair"
             , "30 noimead"
             ]
  , examples (DurationData 27 Minute)
             [ "7 noimead 20"
             ]
  , examples (DurationData 14 Day)
             [ "coicÃs"
             ]
  ]


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

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

module Duckling.Duration.GA.Rules
  ( rules ) where

import Prelude
import Data.String

import Duckling.Dimensions.Types
import Duckling.Duration.Helpers
import Duckling.Numeral.Helpers (numberWith)
import Duckling.Numeral.Types (NumeralData(..))
import qualified Duckling.Numeral.Types as TNumeral
import qualified Duckling.TimeGrain.Types as TG
import Duckling.Types

ruleCoics :: Rule
ruleCoics = Rule
  { name = "coicÃs"
  , pattern =
    [ regex "coic(Ã|i)s(Ã|i|e)?"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Day 14
  }

ruleLeathuair :: Rule
ruleLeathuair = Rule
  { name = "leathuair"
  , pattern =
    [ regex "leathuair(e|eanta)?"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 30
  }

ruleAonDurationAmhain :: Rule
ruleAonDurationAmhain = Rule
  { name = "aon X amhain"
  , pattern =
    [ numberWith TNumeral.value (== 1)
    , dimension TimeGrain
    , numberWith TNumeral.value (== 1)
    ]
  , prod = \tokens -> case tokens of
      (_:Token TimeGrain grain:_) -> Just . Token Duration $ duration grain 1
      _ -> Nothing
  }

ruleIntegerUnitofdurationInteger :: Rule
ruleIntegerUnitofdurationInteger = Rule
  { name = "<unit-integer> <unit-of-duration> <tens-integer>"
  , pattern =
    [ numberWith TNumeral.value (< 10)
    , dimension TimeGrain
    , numberWith TNumeral.value (`elem` [10, 20 .. 50])
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v1}:
       Token TimeGrain grain:
       Token Numeral NumeralData{TNumeral.value = v2}:
       _) -> Just . Token Duration . duration grain . floor $ v1 + v2
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleCoics
  , ruleIntegerUnitofdurationInteger
  , ruleLeathuair
  , ruleAonDurationAmhain
  ]


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

import Control.Monad (join)
import Data.Maybe
import Data.String
import Data.Text (Text)
import Prelude
import qualified Data.Text as Text

import Duckling.Dimensions.Types
import Duckling.Duration.Helpers
import Duckling.Duration.Types (DurationData (..))
import Duckling.Numeral.Helpers (parseInteger)
import Duckling.Numeral.Types (NumeralData(..))
import Duckling.Regex.Types
import Duckling.Types
import qualified Duckling.Duration.Types as TDuration
import qualified Duckling.Numeral.Types as TNumeral
import qualified Duckling.TimeGrain.Types as TG

ruleQuarterOfAnHour :: Rule
ruleQuarterOfAnHour = Rule
  { name = "quarter of an hour"
  , pattern =
    [ regex "(1/4/s ×©×¢×”|×¨×‘×¢ ×©×¢×”)"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 15
  }

ruleHalfAnHour :: Rule
ruleHalfAnHour = Rule
  { name = "half an hour"
  , pattern =
    [ regex "(1/2/s ×©×¢×”|×—×¦×™ ×©×¢×”)"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 30
  }

ruleThreequartersOfAnHour :: Rule
ruleThreequartersOfAnHour = Rule
  { name = "three-quarters of an hour"
  , pattern =
    [ regex "(3/4/s ×©×¢×”|×©×œ×•×©×ª ×¨×‘×¢×™ ×©×¢×”)"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 45
  }

ruleNumbernumberHours :: Rule
ruleNumbernumberHours = Rule
  { name = "number.number hours"
  , pattern =
    [ regex "(\\d+)\\.(\\d+)"
    , regex "×©×¢×”|×©×¢×•×ª"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (h:m:_)):_) -> do
        hh <- parseInteger h
        mnum <- parseInteger m
        let mden = 10 ^ Text.length m
        Just . Token Duration $ minutesFromHourMixedFraction hh mnum mden
      _ -> Nothing
  }

ruleIntegerAndAnHalfHours :: Rule
ruleIntegerAndAnHalfHours = Rule
  { name = "<integer> and an half hours"
  , pattern =
    [ Predicate isNatural
    , regex "×•×—×¦×™ (×©×¢×•×ª|×©×¢×”)"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:_) ->
        Just . Token Duration . duration TG.Minute $ 30 + 60 * floor v
      _ -> Nothing
  }

ruleAboutDuration :: Rule
ruleAboutDuration = Rule
  { name = "about <duration>"
  , pattern =
    [ regex "(×‘×¢×¨×š|×¡×‘×™×‘×•×ª|×‘×§×™×¨×•×‘)"
    , dimension Duration
    ]
  , prod = \tokens -> case tokens of
      (_:token:_) -> Just token
      _ -> Nothing
  }

ruleExactlyDuration :: Rule
ruleExactlyDuration = Rule
  { name = "exactly <duration>"
  , pattern =
    [ regex "×‘×“×™×•×§"
    , dimension Duration
    ]
  , prod = \tokens -> case tokens of
      (_:token:_) -> Just token
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleAboutDuration
  , ruleExactlyDuration
  , ruleHalfAnHour
  , ruleIntegerAndAnHalfHours
  , ruleNumbernumberHours
  , ruleQuarterOfAnHour
  , ruleThreequartersOfAnHour
  ]


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

import Data.String
import Prelude

import Duckling.Duration.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types
import Duckling.TimeGrain.Types (Grain(..))

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

allExamples :: [Example]
allExamples = concat
  [ examples (DurationData 1 Second)
             [ "egy mÃ¡sodperc"
             , "1 mÃ¡sodperc"
             ]
  , examples (DurationData 2 Minute)
             [ "2 perc"
             , "kettÅ‘ perc"
             ]
  , examples (DurationData 15 Minute)
             [ "negyed Ã³ra"
             , "negyedÃ³ra"
             , "negyed-Ã³ra"
             ]
  , examples (DurationData 30 Minute)
             [ "fÃ©l Ã³ra"
             , "fÃ©lÃ³ra"
             , "fÃ©l-Ã³ra"
             ]
  , examples (DurationData 45 Minute)
             [ "hÃ¡romnegyed Ã³ra"
             , "hÃ¡romnegyed-Ã³ra"
             , "hÃ¡romnegyedÃ³ra"
             , "3/4 Ã³ra"
             , "3/4Ã³ra"
             ]
  , examples (DurationData 150 Minute)
             [ "2.5 Ã³ra"
             , "kettÅ‘ Ã©s fÃ©l Ã³ra"
             , "kettÅ‘ Ã©s fÃ©l-Ã³ra"
             , "kettÅ‘ Ã©s fÃ©lÃ³ra"
             ]
  , examples (DurationData 30 Day)
             [ "30 nap"
             ]
  , examples (DurationData 7 Week)
             [ "hÃ©t hÃ©t"
             ]
  , examples (DurationData 1 Month)
             [ "egy hÃ³nap"
             ]
  , examples (DurationData 3 Quarter)
             [ "3 negyedÃ©v"
             ]
  , examples (DurationData 2 Year)
             [ "2 Ã©v"
             ]
  ]


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

import Control.Monad (join)
import Data.String
import Prelude
import qualified Data.Text as Text

import Duckling.Dimensions.Types
import Duckling.Duration.Helpers
import Duckling.Numeral.Helpers (parseInt, parseInteger)
import Duckling.Numeral.Types (NumeralData(..))
import Duckling.Regex.Types
import Duckling.Types
import qualified Duckling.Numeral.Types as TNumeral
import qualified Duckling.TimeGrain.Types as TG

ruleDurationQuarterOfAnHour :: Rule
ruleDurationQuarterOfAnHour = Rule
  { name = "quarter of an hour"
  , pattern =
    [ regex "(negyed[\\s-]?\x00F3ra)"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 15
  }

ruleDurationHalfAnHour :: Rule
ruleDurationHalfAnHour = Rule
  { name = "half an hour"
  , pattern =
    [ regex "(f\x00E9l[\\s-]?\x00F3ra)"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 30
  }

ruleDurationThreeQuartersOfAnHour :: Rule
ruleDurationThreeQuartersOfAnHour = Rule
  { name = "three-quarters of an hour"
  , pattern =
    [ regex "(3/4[\\s-]?Ã³ra|h\x00E1romnegyed[\\s-]?\x00F3ra)"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 45
  }

ruleDurationDotNumeralHours :: Rule
ruleDurationDotNumeralHours = Rule
  { name = "number.number hours"
  , pattern =
    [ regex "(\\d+)\\.(\\d+) Ã³ra"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (h:m:_)):_) -> do
        hh <- parseInteger h
        mnum <- parseInteger m
        let mden = 10 ^ Text.length m
        Just . Token Duration $ minutesFromHourMixedFraction hh mnum mden
      _ -> Nothing
  }

ruleDurationAndHalfHour :: Rule
ruleDurationAndHalfHour = Rule
  { name = "<integer> and an half hour"
  , pattern =
    [ Predicate isNatural
    , regex "\x00E9s f\x00E9l[\\s-]?\x00F3ra"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:_) ->
        Just . Token Duration . duration TG.Minute $ 30 + 60 * floor v
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDurationQuarterOfAnHour
  , ruleDurationHalfAnHour
  , ruleDurationThreeQuartersOfAnHour
  , ruleDurationDotNumeralHours
  , ruleDurationAndHalfHour
  ]


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

import Prelude
import Data.String

import Duckling.Dimensions.Types
import Duckling.Duration.Helpers
import qualified Duckling.TimeGrain.Types as TG
import Duckling.Types

ruleUneUnitofduration :: Rule
ruleUneUnitofduration = Rule
  { name = "une <unit-of-duration>"
  , pattern =
    [ regex "un[a']?"
    , dimension TimeGrain
    ]
  , prod = \tokens -> case tokens of
      (_:Token TimeGrain grain:_) -> Just . Token Duration $ duration grain 1
      _ -> Nothing
  }

ruleUnQuartoDora :: Rule
ruleUnQuartoDora = Rule
  { name = "un quarto d'ora"
  , pattern =
    [ regex "un quarto d['i] ?ora"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 15
  }

ruleMezzOra :: Rule
ruleMezzOra = Rule
  { name = "mezz'ora"
  , pattern =
    [ regex "mezz[a'] ?ora"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 30
  }

ruleTreQuartiDora :: Rule
ruleTreQuartiDora = Rule
  { name = "tre quarti d'ora"
  , pattern =
    [ regex "(3|tre) quarti d['i] ?ora"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 45
  }

rules :: [Rule]
rules =
  [ ruleUneUnitofduration
  , ruleUnQuartoDora
  , ruleMezzOra
  , ruleTreQuartiDora
  ]


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

import Prelude
import Data.String

import Duckling.Duration.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types
import Duckling.TimeGrain.Types (Grain(..))

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

allExamples :: [Example]
allExamples = concat
  [ examples (DurationData 1 Second)
             [ "1 ì´ˆ"
             ]
  , examples (DurationData 30 Minute)
             [ "ì‹œê°„ë°˜"
             , "ì‹œë°˜"
             , "ì„œë¥¸ë¶„"
             ]
  , examples (DurationData 66 Minute)
             [ "1.1 ì‹œê°„"
             ]
  ]


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

import Control.Monad (join)
import qualified Data.Text as Text
import Prelude
import Data.String

import Duckling.Dimensions.Types
import Duckling.Duration.Helpers
import Duckling.Numeral.Helpers (parseInteger)
import Duckling.Numeral.Types (NumeralData (..))
import qualified Duckling.Numeral.Types as TNumeral
import Duckling.Regex.Types
import qualified Duckling.TimeGrain.Types as TG
import Duckling.Types

ruleHalfAnHour :: Rule
ruleHalfAnHour = Rule
  { name = "half an hour"
  , pattern =
    [ Predicate $ isGrain TG.Hour
    , regex "ë°˜"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 30
  }

ruleADay :: Rule
ruleADay = Rule
  { name = "a day - í•˜ë£¨"
  , pattern =
    [ regex "í•˜ë£¨"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Day 1
  }

ruleNumeralnumberHours :: Rule
ruleNumeralnumberHours = Rule
  { name = "number.number hours"
  , pattern =
    [ regex "(\\d+)\\.(\\d+)"
    , regex "ì‹œê°„"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (h:m:_)):_) -> do
        hh <- parseInteger h
        mnum <- parseInteger m
        let mden = 10 ^ Text.length m
        Just . Token Duration $ minutesFromHourMixedFraction hh mnum mden
      _ -> Nothing
  }

ruleIntegerAndAnHalfHours :: Rule
ruleIntegerAndAnHalfHours = Rule
  { name = "<integer> and an half hours"
  , pattern =
    [ Predicate isNatural
    , regex "ì‹œê°„ë°˜"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:_) ->
        Just . Token Duration . duration TG.Minute $ 30 + 60 * floor v
      _ -> Nothing
  }

ruleAboutDuration :: Rule
ruleAboutDuration = Rule
  { name = "about <duration>"
  , pattern =
    [ regex "ëŒ€ì¶©|ì•½"
    , dimension Duration
    ]
  , prod = \tokens -> case tokens of
      (_:token:_) -> Just token
      _ -> Nothing
  }

ruleExactlyDuration :: Rule
ruleExactlyDuration = Rule
  { name = "exactly <duration>"
  , pattern =
    [ regex "ì •í™•ížˆ"
    , dimension Duration
    ]
  , prod = \tokens -> case tokens of
      (_:token:_) -> Just token
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleADay
  , ruleAboutDuration
  , ruleExactlyDuration
  , ruleHalfAnHour
  , ruleIntegerAndAnHalfHours
  , ruleNumeralnumberHours
  ]


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

import Prelude
import Data.String

import Duckling.Duration.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types
import Duckling.TimeGrain.Types (Grain(..))

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

allExamples :: [Example]
allExamples = concat
  [ examples (DurationData 1 Second)
             [ "1 sek"
             , "en sek"
             , "ett sekund"
             , "e sekunder"
             ]
  , examples (DurationData 30 Minute)
             [ "tredve min"
             , "30 minutt"
             , "30 minutter"
             , "1/2 time"
             , "en halv time"
             ]
  , examples (DurationData 2 Day)
             [ "et par dager"
             , "2 dag"
             , "to dag"
             ]
  ]


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

import Control.Monad (join)
import qualified Data.Text as Text
import Prelude
import Data.String

import Duckling.Dimensions.Types
import Duckling.Duration.Helpers
import Duckling.Numeral.Helpers (parseInteger)
import Duckling.Numeral.Types (NumeralData (..))
import qualified Duckling.Numeral.Types as TNumeral
import Duckling.Regex.Types
import qualified Duckling.TimeGrain.Types as TG
import Duckling.Types

ruleHalfAnHour :: Rule
ruleHalfAnHour = Rule
  { name = "half an hour"
  , pattern =
    [ regex "(1/2|en halv) time"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 30
  }

ruleIntegerMoreUnitofduration :: Rule
ruleIntegerMoreUnitofduration = Rule
  { name = "<integer> more <unit-of-duration>"
  , pattern =
    [ Predicate isNatural
    , dimension TimeGrain
    , regex "mere?"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:
       Token TimeGrain grain:
       _) -> Just . Token Duration . duration grain $ floor v
      _ -> Nothing
  }

ruleNumeralnumberHours :: Rule
ruleNumeralnumberHours = Rule
  { name = "number.number hours"
  , pattern =
    [ regex "(\\d+)\\,(\\d+) *timer?"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (h:m:_)):_) -> do
        hh <- parseInteger h
        mnum <- parseInteger m
        let mden = 10 ^ Text.length m
        Just . Token Duration $ minutesFromHourMixedFraction hh mnum mden
      _ -> Nothing
  }

ruleIntegerAndAnHalfHours :: Rule
ruleIntegerAndAnHalfHours = Rule
  { name = "<integer> and an half hours"
  , pattern =
    [ Predicate isNatural
    , regex "og (en )?halv time?"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:_) ->
        Just . Token Duration . duration TG.Minute $ 30 + 60 * floor v
      _ -> Nothing
  }

ruleAUnitofduration :: Rule
ruleAUnitofduration = Rule
  { name = "a <unit-of-duration>"
  , pattern =
    [ regex "en|ett|et?"
    , dimension TimeGrain
    ]
  , prod = \tokens -> case tokens of
      (_:Token TimeGrain grain:_) -> Just . Token Duration $ duration grain 1
      _ -> Nothing
  }

ruleAboutDuration :: Rule
ruleAboutDuration = Rule
  { name = "about <duration>"
  , pattern =
    [ regex "(omkring|cirka|ca.|ca)"
    , dimension Duration
    ]
  , prod = \tokens -> case tokens of
      (_:token:_) -> Just token
      _ -> Nothing
  }

ruleExactlyDuration :: Rule
ruleExactlyDuration = Rule
  { name = "exactly <duration>"
  , pattern =
    [ regex "(precis|akkurat)"
    , dimension Duration
    ]
  , prod = \tokens -> case tokens of
      (_:token:_) -> Just token
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleAUnitofduration
  , ruleAboutDuration
  , ruleExactlyDuration
  , ruleHalfAnHour
  , ruleIntegerAndAnHalfHours
  , ruleIntegerMoreUnitofduration
  , ruleNumeralnumberHours
  ]


-- 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.Duration.NL.Corpus
  ( corpus
  , negativeCorpus
  ) where

import Prelude
import Data.String

import Duckling.Duration.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types
import Duckling.TimeGrain.Types (Grain(..))

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

negativeCorpus :: NegativeCorpus
negativeCorpus = (testContext {locale = makeLocale NL Nothing}, testOptions, examples)
  where
    examples =
      [ "voor maanden"
      , "in enkele dagen"
      , "secretaris"
      , "last minute"
      , "12 uurtje"
      ]

allExamples :: [Example]
allExamples = concat
  [ examples (DurationData 1 Second)
             [ "een seconde"
             , "Ã©Ã©n seconde"
             , "1 secondes"
             , "1 s"
             , "1\""
             ]
  , examples (DurationData 14 Second)
             [ "veertien seconden"
             , "14 s"
             ]
  , examples (DurationData 2 Minute)
             [ "2 min"
             , "twee minuten"
             , "2 m"
             , "2'"
             ]
  , examples (DurationData 45 Minute)
             [ "3 kwartier"
             ]
  , examples (DurationData 90 Minute)
             [ "anderhalf uur"
             ]
  , examples (DurationData 75 Minute)
             [ "een uur en een kwartier"
             , "een uur en 15 minuten"
             , "een uur, 15 minuten"
             , "een uur 15 minuten"
             ]
  , examples (DurationData 165 Minute)
             [ "twee uur en drie kwartier"
             , "twee uur, 3 kwartier"
             , "twee uur, 45 minuten"
             , "twee uur 45 minuten"
             ]
  , examples (DurationData 1 Hour)
             [ "een uur"
             , "Ã©Ã©n uur"
             , "1 u"
             , "1 h"
             ]
  , examples (DurationData 30 Day)
             [ "30 dagen"
             , "4 weken en 2 dagen"
             ]
  , examples (DurationData 7 Day)
             [ "7 dagen"
             ]
  , examples (DurationData 7 Week)
             [ "zeven weken"
             , "7 w"
             ]
  , examples (DurationData 1 Month)
             [ "1 mnd"
             , "een maand"
             , "Ã©Ã©n maand"
             ]
  , examples (DurationData 3 Quarter)
             [ "drie kwartalen"
             ]
  , examples (DurationData 2 Year)
             [ "2 jaar"
             , "2 jaren"
             , "twee jaren"
             , "2 j"
             ]
  , examples (DurationData 150 Minute)
            [ "twee en een half uur"
            , "2,5 uur"
            ]
  ]


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

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

module Duckling.Duration.NL.Rules
  ( rules ) where

import Control.Monad (join)
import Prelude
import Data.String
import qualified Data.Text as Text

import Duckling.Dimensions.Types
import Duckling.Duration.Helpers
import Duckling.Duration.Types (DurationData(..))
import Duckling.Numeral.Helpers (parseInt, parseInteger)
import Duckling.Numeral.Types (NumeralData(..))
import Duckling.Regex.Types
import Duckling.Types
import qualified Duckling.Duration.Types as TDuration
import qualified Duckling.Numeral.Types as TNumeral
import qualified Duckling.TimeGrain.Types as TG

ruleDurationQuarterOfAnHour :: Rule
ruleDurationQuarterOfAnHour = Rule
  { name = "quarter of an hour"
  , pattern = [ regex "1/4\\s?(h|u(ur)?)|kwartier"]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 15
  }

ruleDurationKwartier :: Rule
ruleDurationKwartier = Rule
  { name = "<integer> kwartier"
  , pattern =
    [ Predicate isNatural
    , regex "kwartier"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:_) ->
        Just . Token Duration . duration TG.Minute $ 15 * floor v
      _ -> Nothing
  }

ruleDurationHalfAnHour :: Rule
ruleDurationHalfAnHour = Rule
  { name = "half an hour"
  , pattern = [regex "(1/2\\s?uur|half uur)"]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 30
  }

ruleDurationThreeQuartersOfAnHour :: Rule
ruleDurationThreeQuartersOfAnHour = Rule
  { name = "three-quarters of an hour"
  , pattern = [regex "3/4\\s?uur"]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 45
  }

ruleNumeralQuotes :: Rule
ruleNumeralQuotes = Rule
  { name = "<integer> + '\""
  , pattern =
    [ Predicate isNatural
    , regex "(['\"])"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:
       Token RegexMatch (GroupMatch (x:_)):
       _) -> case x of
         "'"  -> Just . Token Duration . duration TG.Minute $ floor v
         "\"" -> Just . Token Duration . duration TG.Second $ floor v
         _    -> Nothing
      _ -> Nothing
  }

ruleDurationDotNumeralHours :: Rule
ruleDurationDotNumeralHours = Rule
  { name = "number,number uur"
  , pattern = [regex "(\\d+)\\,(\\d+) *(uur|uren)"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (h:m:_)):_) -> do
        hh <- parseInteger h
        mnum <- parseInteger m
        let mden = 10 ^ Text.length m
        Just . Token Duration $ minutesFromHourMixedFraction hh mnum mden
      _ -> Nothing
  }

ruleDurationAndHalfHour :: Rule
ruleDurationAndHalfHour = Rule
  { name = "<integer> and an half hour"
  , pattern =
    [ Predicate isNatural
    , regex "en een half (uur|uren)"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:_) ->
        Just . Token Duration . duration TG.Minute $ 30 + 60 * floor v
      _ -> Nothing
  }

ruleDurationOneAndHalfHour :: Rule
ruleDurationOneAndHalfHour = Rule
  { name = "1 and an half hour"
  , pattern = [ regex "anderhalf uur"]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 90
  }

-- | NOTE: Oxford comma is not supported.
ruleCompositeDurationCommasAnd :: Rule
ruleCompositeDurationCommasAnd = Rule
  { name = "composite <duration> (with ,/and)"
  , pattern =
    [ Predicate isNatural
    , dimension TimeGrain
    , regex ",|en|plus"
    , dimension Duration
    ]
  , prod = \case
      (Token Numeral NumeralData{TNumeral.value = v}:
       Token TimeGrain g:
       _:
       Token Duration dd@DurationData{TDuration.grain = dg}:
       _) | g > dg -> Just . Token Duration $ duration g (floor v) <> dd
      _ -> Nothing
  }

ruleCompositeDuration :: Rule
ruleCompositeDuration = Rule
  { name = "composite <duration>"
  , pattern =
    [ Predicate isNatural
    , dimension TimeGrain
    , dimension Duration
    ]
  , prod = \case
      (Token Numeral NumeralData{TNumeral.value = v}:
       Token TimeGrain g:
       Token Duration dd@DurationData{TDuration.grain = dg}:
       _) | g > dg -> Just . Token Duration $ duration g (floor v) <> dd
      _ -> Nothing
  }

ruleDurationPrecision :: Rule
ruleDurationPrecision = Rule
  { name = "about|exactly <duration>"
  , pattern =
    [ regex "(ongeveer|precies|plusminus|exact)"
    , dimension Duration
    ]
    , prod = \tokens -> case tokens of
        (_:token:_) -> Just token
        _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDurationQuarterOfAnHour
  , ruleDurationKwartier
  , ruleCompositeDurationCommasAnd
  , ruleCompositeDuration
  , ruleDurationHalfAnHour
  , ruleDurationThreeQuartersOfAnHour
  , ruleDurationDotNumeralHours
  , ruleDurationAndHalfHour
  , ruleDurationOneAndHalfHour
  , ruleDurationPrecision
  , ruleNumeralQuotes
  ]


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

import Prelude
import Data.String

import Duckling.Duration.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types
import Duckling.TimeGrain.Types (Grain(..))

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

allExamples :: [Example]
allExamples = concat
  [ examples (DurationData 1 Second)
             [ "1s"
             , "1 sekund"
             , "jeden sekundzie"
             , "pojedynczy sekundach"
             , "mniej wiÄ™cej jednÄ… sekundÄ™"
             ]
  , examples (DurationData 30 Minute)
             [ "pol godziny"
             , "pÃ³Å‚ godziny"
             , "30m"
             , "30 minut"
             , "trzydzieÅ›ci minutami"
             ]
  , examples (DurationData 5 Day)
             [ "piÄ™ciu dniach"
             ]
  , examples (DurationData 100 Day)
             [ "sto dzieÅ„"
             , "setki dniÄ…"
             ]
  ]


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

import Control.Monad (join)
import qualified Data.Text as Text
import Prelude
import Data.String

import Duckling.Dimensions.Types
import Duckling.Duration.Helpers
import Duckling.Numeral.Helpers (parseInteger)
import Duckling.Numeral.Types (NumeralData (..))
import qualified Duckling.Numeral.Types as TNumeral
import Duckling.Regex.Types
import qualified Duckling.TimeGrain.Types as TG
import Duckling.Types

ruleHalfAnHour :: Rule
ruleHalfAnHour = Rule
  { name = "half an hour"
  , pattern =
    [ regex "p(o|Ã³)(l|Å‚) godziny"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 30
  }

ruleIntegerMoreUnitofduration :: Rule
ruleIntegerMoreUnitofduration = Rule
  { name = "<integer> more <unit-of-duration>"
  , pattern =
    [ Predicate isNatural
    , regex "more|less"
    , dimension TimeGrain
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:
       _:
       Token TimeGrain grain:
       _) -> Just . Token Duration . duration grain $ floor v
      _ -> Nothing
  }

ruleNumeralnumberHours :: Rule
ruleNumeralnumberHours = Rule
  { name = "number.number hours"
  , pattern =
    [ regex "(\\d+)\\.(\\d+)"
    , regex "godzin(y)?"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (h:m:_)):_) -> do
        hh <- parseInteger h
        mnum <- parseInteger m
        let mden = 10 ^ Text.length m
        Just . Token Duration $ minutesFromHourMixedFraction hh mnum mden
      _ -> Nothing
  }

ruleIntegerAndAnHalfHours :: Rule
ruleIntegerAndAnHalfHours = Rule
  { name = "<integer> and an half hours"
  , pattern =
    [ Predicate isNatural
    , regex "i (p(o|Ã³)(l|Å‚)) godziny"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:_) ->
        Just . Token Duration . duration TG.Minute $ 30 + 60 * floor v
      _ -> Nothing
  }

ruleUnitofdurationAsADuration :: Rule
ruleUnitofdurationAsADuration = Rule
  { name = "<unit-of-duration> as a duration"
  , pattern =
    [ dimension TimeGrain
    ]
  , prod = \tokens -> case tokens of
      (Token TimeGrain grain:_) ->
        Just . Token Duration $ duration grain 1
      _ -> Nothing
  }

ruleAboutDuration :: Rule
ruleAboutDuration = Rule
  { name = "about <duration>"
  , pattern =
    [ regex "(oko(l|Å‚)o|mniej wi(Ä™|e)cej|jakie(s|Å›))"
    , dimension Duration
    ]
  , prod = \tokens -> case tokens of
      (_:token:_) -> Just token
      _ -> Nothing
  }

ruleExactlyDuration :: Rule
ruleExactlyDuration = Rule
  { name = "exactly <duration>"
  , pattern =
    [ regex "r(o|Ã³)wno|dok(l|Å‚)adnie"
    , dimension Duration
    ]
  , prod = \tokens -> case tokens of
      (_:token:_) -> Just token
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleAboutDuration
  , ruleExactlyDuration
  , ruleHalfAnHour
  , ruleIntegerAndAnHalfHours
  , ruleIntegerMoreUnitofduration
  , ruleNumeralnumberHours
  , ruleUnitofdurationAsADuration
  ]


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

import Prelude
import Data.String

import Duckling.Duration.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types
import Duckling.TimeGrain.Types (Grain(..))

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

allExamples :: [Example]
allExamples = concat
  [ examples (DurationData 1 Second)
             [ "um segundo"
             , "uma seg"
             ]
  , examples (DurationData 2 Minute)
             [ "duas mins"
             , "dois minutos"
             ]
  , examples (DurationData 20 Day)
             [ "20 dias"
             , "vinte dÃas"
             ]
  ]


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

import Prelude
import Data.String

import Duckling.Duration.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types
import Duckling.TimeGrain.Types (Grain(..))

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

allExamples :: [Example]
allExamples = concat
  [ examples (DurationData 1 Second)
             [ "o sec"
             , "1 secunda"
             ]
  , examples (DurationData 30 Minute)
             [ "jumatate ora"
             , "1/2h"
             , "treizeci minute"
             ]
  , examples (DurationData 45 Minute)
             [ "trei sferturi de orÄƒ"
             , "45min"
             , "45 de minute"
             ]
  , examples (DurationData 12 Week)
             [ "doiÈ™pe saptamanile"
             , "doisprezece saptÄƒmÃ¢ni"
             ]
  , examples (DurationData 2 Month)
             [ "2 luni"
             ]
  , examples (DurationData 1 Quarter)
             [ "un trimestru"
             ]
  ]


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

import Data.String
import Prelude

import Duckling.Dimensions.Types
import Duckling.Duration.Helpers
import Duckling.Numeral.Helpers (numberWith)
import Duckling.Types
import qualified Duckling.Numeral.Types as TNumeral
import qualified Duckling.TimeGrain.Types as TG

ruleQuarterOfAnHour :: Rule
ruleQuarterOfAnHour = Rule
  { name = "quarter of an hour"
  , pattern =
    [ regex "(1/4\\s?(h|or(a|Äƒ))|sfert de or(a|Äƒ))"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 15
  }

ruleJumatateDeOra :: Rule
ruleJumatateDeOra = Rule
  { name = "jumatate de ora"
  , pattern =
    [ regex "(1/2\\s?(h|or(a|Äƒ))|jum(a|Äƒ)tate (de )?or(a|Äƒ))"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 30
  }

ruleTreiSferturiDeOra :: Rule
ruleTreiSferturiDeOra = Rule
  { name = "trei sferturi de ora"
  , pattern =
    [ regex "(3/4\\s?(h|or(a|Äƒ))|trei sferturi de or(a|Äƒ))"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 45
  }

ruleOUnitofduration :: Rule
ruleOUnitofduration = Rule
  { name = "o <unit-of-duration>"
  , pattern =
    [ regex "o|un"
    , dimension TimeGrain
    ]
  , prod = \case
      (_:Token TimeGrain grain:_) -> Just . Token Duration $ duration grain 1
      _ -> Nothing
  }

ruleExactInJurDeDuration :: Rule
ruleExactInJurDeDuration = Rule
  { name = "exact|in jur de <duration>"
  , pattern =
    [ regex "(exact|aproximativ|(i|Ã®)n jur de)"
    , dimension Duration
    ]
  , prod = \case
      (_:token:_) -> Just token
      _ -> Nothing
  }

ruleIntegerDeUnitofduration :: Rule
ruleIntegerDeUnitofduration = Rule
  { name = "<integer> <unit-of-duration>"
  , pattern =
    [ numberWith TNumeral.value (>= 20)
    , regex "de"
    , dimension TimeGrain
    ]
  , prod = \case
      (Token Numeral TNumeral.NumeralData{TNumeral.value = v}:
       _:
       Token TimeGrain grain:
       _) -> Just . Token Duration . duration grain $ floor v
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleExactInJurDeDuration
  , ruleJumatateDeOra
  , ruleOUnitofduration
  , ruleQuarterOfAnHour
  , ruleTreiSferturiDeOra
  , ruleIntegerDeUnitofduration
  ]


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

import Prelude
import Data.String

import Duckling.Duration.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types
import Duckling.TimeGrain.Types (Grain(..))

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

allExamples :: [Example]
allExamples = concat
  [ examples (DurationData 1 Second)
             [ "enkel sek"
             , "1 sekund fler"
             , "1 sekunder mer"
             , "en sekunderna"
             , "et sek"
             , "ett sek"
             ]
  , examples (DurationData 30 Minute)
             [ "1/2 timme"
             , "en halv timme"
             , "0,5 timmar"
             , "30 minuterna"
             , "trettio min"
             ]
  , examples (DurationData 5 Year)
             [ "5 Ã¥r"
             , "fem Ã¥r"
             , "omkring fem Ã¥r"
             , "ungefÃ¤r fem Ã¥r"
             , "runt fem Ã¥r"
             , "cirka fem Ã¥r"
             , "ca fem Ã¥r"
             , "ca. fem Ã¥r"
             , "c:a fem Ã¥r"
             ]
  ]


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

import Control.Monad (join)
import Data.String
import Prelude
import qualified Data.Text as Text

import Duckling.Dimensions.Types
import Duckling.Duration.Helpers
import Duckling.Numeral.Helpers (parseInteger)
import Duckling.Numeral.Types (NumeralData(..))
import Duckling.Regex.Types
import Duckling.Types
import qualified Duckling.Numeral.Types as TNumeral
import qualified Duckling.TimeGrain.Types as TG

ruleHalfAnHour :: Rule
ruleHalfAnHour = Rule
  { name = "half an hour"
  , pattern =
    [ regex "(1/2|en halv) timme"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 30
  }

ruleIntegerMoreUnitofduration :: Rule
ruleIntegerMoreUnitofduration = Rule
  { name = "<integer> more <unit-of-duration>"
  , pattern =
    [ Predicate isNatural
    , dimension TimeGrain
    , regex "fler|mer"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:
       Token TimeGrain grain:
       _) -> Just . Token Duration . duration grain $ floor v
      _ -> Nothing
  }

ruleNumeralnumberHours :: Rule
ruleNumeralnumberHours = Rule
  { name = "number.number hours"
  , pattern =
    [ regex "(\\d+)\\,(\\d+)"
    , regex "timm(e|ar)?"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (h:m:_)):_) -> do
        hh <- parseInteger h
        mnum <- parseInteger m
        let mden = 10 ^ Text.length m
        Just . Token Duration $ minutesFromHourMixedFraction hh mnum mden
      _ -> Nothing
  }

ruleIntegerAndAnHalfHours :: Rule
ruleIntegerAndAnHalfHours = Rule
  { name = "<integer> and an half hours"
  , pattern =
    [ Predicate isNatural
    , regex "och (en )?halv timme?"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:_) ->
        Just . Token Duration . duration TG.Minute $ 30 + 60 * floor v
      _ -> Nothing
  }

ruleAUnitofduration :: Rule
ruleAUnitofduration = Rule
  { name = "a <unit-of-duration>"
  , pattern =
    [ regex "en|ett?"
    , dimension TimeGrain
    ]
  , prod = \tokens -> case tokens of
      (_:Token TimeGrain grain:_) ->
        Just . Token Duration $ duration grain 1
      _ -> Nothing
  }

ruleAboutDuration :: Rule
ruleAboutDuration = Rule
  { name = "about <duration>"
  , pattern =
    [ regex "(omkring|cirka|ca\\.?|c:a|runt|ungefÃ¤r)"
    , dimension Duration
    ]
  , prod = \tokens -> case tokens of
      (_:token:_) -> Just token
      _ -> Nothing
  }

ruleExactlyDuration :: Rule
ruleExactlyDuration = Rule
  { name = "exactly <duration>"
  , pattern =
    [ regex "(precis|exakt)"
    , dimension Duration
    ]
  , prod = \tokens -> case tokens of
      (_:token:_) -> Just token
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleAUnitofduration
  , ruleAboutDuration
  , ruleExactlyDuration
  , ruleHalfAnHour
  , ruleIntegerAndAnHalfHours
  , ruleIntegerMoreUnitofduration
  , ruleNumeralnumberHours
  ]


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

import Data.String
import Prelude

import Duckling.Duration.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types
import Duckling.TimeGrain.Types (Grain (..))

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

allExamples :: [Example]
allExamples = concat
  [ examples (DurationData 1 Second)
             [ "bir sn"
             , "1 saniye"
             , "1\""
             ]
  , examples (DurationData 2 Minute)
             [ "2 dk"
             , "iki dakika"
             , "2'"
             ]
  , examples (DurationData 30 Day)
             [ "30 gÃ¼n"
             ]
  , examples (DurationData 7 Week)
             [ "yedi hafta"
             ]
  , examples (DurationData 1 Month)
             [ "1 ay"
             , "bir ay"
             ]
  , examples (DurationData 3 Quarter)
             [ "3 Ã§eyrek yÄ±l"
             ]
  , examples (DurationData 2 Year)
             [ "2 yÄ±l"
             ]
  ]


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

import Control.Monad (join)
import Data.String
import Prelude
import qualified Data.Text as Text

import Duckling.Dimensions.Types
import Duckling.Duration.Helpers
import Duckling.Numeral.Helpers (parseInteger)
import Duckling.Numeral.Types (NumeralData (..))
import Duckling.Regex.Types
import Duckling.Types
import qualified Duckling.Numeral.Types as TNumeral
import qualified Duckling.TimeGrain.Types as TG

ruleDurationQuarterOfAnHour :: Rule
ruleDurationQuarterOfAnHour = Rule
  { name = "quarter of an hour"
  , pattern = [ regex "(1/4\\s?sa(at)?|Ã§eyrek saat)" ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 15
  }

ruleDurationHalfAnHour :: Rule
ruleDurationHalfAnHour = Rule
  { name = "half an hour"
  , pattern = [regex "(1/2\\s?sa(at)?|yar\305m saat)"]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 30
  }

ruleDurationThreeQuartersOfAnHour :: Rule
ruleDurationThreeQuartersOfAnHour = Rule
  { name = "three-quarters of an hour"
  , pattern = [regex "(3/4\\s?sa(at)?|Ã¼Ã§ \231eyrek sa(at)?)"]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 45
  }

ruleDurationFortnight :: Rule
ruleDurationFortnight = Rule
  { name = "fortnight"
  , pattern = [regex "iki hafta"]
  , prod = \_ -> Just . Token Duration $ duration TG.Day 14
  }

ruleNumeralQuotes :: Rule
ruleNumeralQuotes = Rule
  { name = "<integer> + '\""
  , pattern =
    [ Predicate isNatural
    , regex "(['\"])"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:
       Token RegexMatch (GroupMatch (x:_)):
       _) -> case x of
         "'"  -> Just . Token Duration . duration TG.Minute $ floor v
         "\"" -> Just . Token Duration . duration TG.Second $ floor v
         _    -> Nothing
      _ -> Nothing
  }

ruleDurationDotNumeralHours :: Rule
ruleDurationDotNumeralHours = Rule
  { name = "number.number hours"
  , pattern = [regex "(\\d+)(\\.|,)(\\d+) *sa(at)?"]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (h:m:_)):_) -> do
        hh <- parseInteger h
        mnum <- parseInteger m
        let mden = 10 ^ Text.length m
        Just . Token Duration $ minutesFromHourMixedFraction hh mnum mden
      _ -> Nothing
  }

ruleDurationAndHalfHour :: Rule
ruleDurationAndHalfHour = Rule
  { name = "<integer> and an half hour"
  , pattern =
    [ Predicate isNatural
    , regex "buÃ§uk sa(at)?"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:_) ->
        Just . Token Duration . duration TG.Minute $ 30 + 60 * floor v
      _ -> Nothing
  }

ruleDurationPrecision :: Rule
ruleDurationPrecision = Rule
  { name = "<duration> about|exactly"
  , pattern =
    [ regex "(yaklaÅŸÄ±k|tam(\\solarak)?)"
    , dimension Duration
    ]
    , prod = \tokens -> case tokens of
        (_:token:_) -> Just token
        _           -> Nothing
  }

ruleDurationPrecision2 :: Rule
ruleDurationPrecision2 = Rule
  { name = "<duration> about|exactly"
  , pattern =
    [ dimension Duration
    , regex "gibi|civarÄ±nda"
    ]
  , prod = \case
      (token:_:_) -> Just token
      _           -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDurationQuarterOfAnHour
  , ruleDurationHalfAnHour
  , ruleDurationThreeQuartersOfAnHour
  , ruleDurationFortnight
  , ruleDurationDotNumeralHours
  , ruleDurationAndHalfHour
  , ruleDurationPrecision
  , ruleDurationPrecision2
  , ruleNumeralQuotes
  ]


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

import Control.Monad (join)
import Data.Semigroup ((<>))
import Data.String
import Prelude
import qualified Data.Text as Text

import Duckling.Dimensions.Types
import Duckling.Duration.Helpers
import Duckling.Duration.Types (DurationData(..))
import Duckling.Numeral.Helpers (parseInt, parseInteger)
import Duckling.Numeral.Types (NumeralData(..))
import Duckling.Regex.Types
import Duckling.Types
import qualified Duckling.Duration.Types as TDuration
import qualified Duckling.Numeral.Types as TNumeral
import qualified Duckling.TimeGrain.Types as TG

ruleHalfAnHour :: Rule
ruleHalfAnHour = Rule
  { name = "half an hour"
  , pattern =
    [ regex "(1/2\\s?|Ð¿Ñ–Ð²\\s?)Ð³Ð¾Ð´Ð¸Ð½Ð¸?"
    ]
  , prod = \_ -> Just . Token Duration $ duration TG.Minute 30
  }

ruleNumeralnumberHours :: Rule
ruleNumeralnumberHours = Rule
  { name = "number.number hours"
  , pattern =
    [ regex "(\\d+)\\.(\\d+) Ð³Ð¾Ð´Ð¸Ð½Ð¸?"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (h:m:_)):_) -> do
        hh <- parseInteger h
        mnum <- parseInteger m
        let mden = 10 ^ Text.length m
        Just . Token Duration $ minutesFromHourMixedFraction hh mnum mden
      _ -> Nothing
  }

ruleIntegerAndAnHalfHours :: Rule
ruleIntegerAndAnHalfHours = Rule
  { name = "<integer> and an half hours"
  , pattern =
    [ Predicate isNatural
    , regex "Ð· Ð¿Ð¾Ð»Ð¾Ð²Ð¸Ð½Ð¾ÑŽ Ð³Ð¾Ð´Ð¸Ð½Ð¸"
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:_) ->
        Just . Token Duration . duration TG.Minute $ 30 + 60 * floor v
      _ -> Nothing
  }

ruleAboutDuration :: Rule
ruleAboutDuration = Rule
  { name = "about <duration>"
  , pattern =
    [ regex "Ð±Ð»Ð¸Ð·ÑŒÐºÐ¾"
    , dimension Duration
    ]
  , prod = \tokens -> case tokens of
      (_:token:_) -> Just token
      _ -> Nothing
  }

ruleExactlyDuration :: Rule
ruleExactlyDuration = Rule
  { name = "exactly <duration>"
  , pattern =
    [ regex "Ñ€Ñ–Ð²Ð½Ð¾"
    , dimension Duration
    ]
  , prod = \tokens -> case tokens of
      (_:token:_) -> Just token
      _ -> Nothing
  }

ruleDurationHalfATimeGrain :: Rule
ruleDurationHalfATimeGrain = Rule
  { name = "half a <time-grain>"
  , pattern =
    [ regex "(1/2|Ð¿Ñ–Ð²)"
    , dimension TimeGrain
    ]
  , prod = \tokens -> case tokens of
      (_:Token TimeGrain grain:_) -> Token Duration <$> nPlusOneHalf grain 0
      _ -> Nothing
  }

ruleDurationOneGrainAndHalf :: Rule
ruleDurationOneGrainAndHalf = Rule
  { name = "one and a hald <unit-of-duration>"
  , pattern =
    [ regex "Ð¿Ñ–Ð²Ñ‚Ð¾Ñ€Ð¸"
    , dimension TimeGrain
    ]
  , prod = \tokens -> case tokens of
      (_:Token TimeGrain grain:_) -> Token Duration <$> nPlusOneHalf grain 1
      _ -> Nothing
  }

ruleCompositeDurationCommasAnd :: Rule
ruleCompositeDurationCommasAnd = Rule
  { name = "composite <duration> (with ,/and)"
  , pattern =
    [ Predicate isNatural
    , dimension TimeGrain
    , regex ",|Ñ–"
    , dimension Duration
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData{TNumeral.value = v}:
       Token TimeGrain g:
       _:
       Token Duration dd@DurationData{TDuration.grain = dg}:
       _) | g > dg -> Just . Token Duration $ duration g (floor v) <> dd
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleAboutDuration
  , ruleExactlyDuration
  , ruleHalfAnHour
  , ruleIntegerAndAnHalfHours
  , ruleNumeralnumberHours
  , ruleDurationHalfATimeGrain
  , ruleDurationOneGrainAndHalf
  , ruleCompositeDurationCommasAnd
  ]


-- 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.Email.Corpus
  ( corpus
  , negativeCorpus
  ) where

import Data.String
import Prelude

import Duckling.Email.Types
import Duckling.Testing.Types

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

negativeCorpus :: NegativeCorpus
negativeCorpus = (testContext, testOptions, examples)
  where
    examples =
      [ "hey@6"
      , "hey@you"
      ]

allExamples :: [Example]
allExamples = concat
  [ examples (EmailData "alice@exAmple.io")
             [ "alice@exAmple.io"
             ]
  , examples (EmailData "yo+yo@blah.org")
             [ "yo+yo@blah.org"
             ]
  , examples (EmailData "1234+abc@x.net")
             [ "1234+abc@x.net"
             ]
  , examples (EmailData "jean-jacques@stuff.co.uk")
             [ "jean-jacques@stuff.co.uk"
             ]
  ]


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

import Prelude
import Data.String

import Duckling.Dimensions.Types
import Duckling.Email.Types (EmailData (..))
import qualified Duckling.Email.Types as TEmail
import Duckling.Regex.Types
import Duckling.Types

ruleEmail :: Rule
ruleEmail = Rule
  { name = "email"
  , pattern =
    [ regex "([\\w\\._+-]+@[\\w_-]+(\\.[\\w_-]+)+)"
    ]
  , prod = \xs -> case xs of
      (Token RegexMatch (GroupMatch (x:_)):_) ->
        Just $ Token Email EmailData {TEmail.value = x}
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleEmail
  ]


-- 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 DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NoRebindableSyntax #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}

module Duckling.Email.Types where

import Control.DeepSeq
import Data.Aeson
import Data.Hashable
import Data.Text (Text)
import GHC.Generics
import Prelude

import Duckling.Resolve (Resolve(..))

newtype EmailData = EmailData { value :: Text }
  deriving (Eq, Generic, Hashable, Ord, Show, NFData)

instance Resolve EmailData where
  type ResolvedValue EmailData = EmailData
  resolve _ _ x = Just (x, False)

instance ToJSON EmailData where
  toJSON EmailData {value} = object
    [ "value" .= value
    , "type"  .= ("value" :: Text)
    ]


-- 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.Email.DE.Corpus
  ( corpus
  , negativeCorpus
  ) where

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

import Duckling.Email.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types

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

negativeCorpus :: NegativeCorpus
negativeCorpus = (context, testOptions, examples)
  where
    examples =
      [ "fitness at 6.40"
      , "class at 12.00"
      , "tonight at 9.15"
      , " dot 2@abci"
      , "x@ dot x"
      , "x@ x dot "
      , "abc@x dot "
      , Text.replicate 10000 "a at a"
      ]

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

allExamples :: [Example]
allExamples = concat
  [ examples (EmailData "alice@exAmple.io")
             [ "alice at exAmple.io"
             ]
  , examples (EmailData "yo+yo@blah.org")
             [ "yo+yo at blah.org"
             ]
  , examples (EmailData "1234+abc@x.net")
             [ "1234+abc at x.net"
             ]
  , examples (EmailData "jean-jacques@stuff.co.uk")
             [ "jean-jacques at stuff.co.uk"
             ]
  , examples (EmailData "asdf+ab.c@gmail.com")
             [ "asdf+ab punkt c at gmail punkt com"
             ]
  , examples (EmailData "asdf.k@fb.com")
             [ "asdf punkt k@fb punkt com"
             ]
  ]


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

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

import Duckling.Dimensions.Types
import Duckling.Email.Types (EmailData (..))
import Duckling.Regex.Types
import Duckling.Types
import qualified Duckling.Email.Types as TEmail

ruleEmailSpelledOut :: Rule
ruleEmailSpelledOut = Rule
  { name = "email spelled out"
  , pattern =
    [ regex "([\\w_+-]+(?:(?: punkt |\\.)[\\w_+-]+){0,10})(?: at |@)([a-zA-Z]+(?:(?:\\.| punkt )[\\w_-]+){1,10})"
    ]
  , prod = \xs -> case xs of
      (Token RegexMatch (GroupMatch (m1:m2:_)):_) ->
        Just $ Token Email EmailData
          { TEmail.value = Text.concat [replaceDots m1, "@", replaceDots m2] }
        where replaceDots = Text.replace " punkt " "."
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleEmailSpelledOut
  ]


-- 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.Email.EN.Corpus
  ( corpus
  , negativeCorpus
  ) where

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

import Duckling.Email.Types
import Duckling.Testing.Types

negativeCorpus :: NegativeCorpus
negativeCorpus = (testContext, testOptions, examples)
  where
    examples =
      [ "fitness at 6.40"
      , "class at 12.00"
      , "tonight at 9.15"
      , " dot 2@abci"
      , "x@ dot x"
      , "x@ x dot "
      , "abc@x dot "
      , Text.replicate 10000 "a at a"
      ]

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

allExamples :: [Example]
allExamples = concat
  [ examples (EmailData "alice@exAmple.io")
             [ "alice at exAmple.io"
             ]
  , examples (EmailData "yo+yo@blah.org")
             [ "yo+yo at blah.org"
             ]
  , examples (EmailData "1234+abc@x.net")
             [ "1234+abc at x.net"
             ]
  , examples (EmailData "jean-jacques@stuff.co.uk")
             [ "jean-jacques at stuff.co.uk"
             ]
  , examples (EmailData "asdf+ab.c@gmail.com")
             [ "asdf+ab dot c at gmail dot com"
             ]
  , examples (EmailData "asdf.k@fb.com")
             [ "asdf dot k@fb dot com"
             ]
  ]


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

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

import Duckling.Dimensions.Types
import Duckling.Email.Types (EmailData (..))
import Duckling.Regex.Types
import Duckling.Types
import qualified Duckling.Email.Types as TEmail

ruleEmailSpelledOut :: Rule
ruleEmailSpelledOut = Rule
  { name = "email spelled out"
  , pattern =
    [ regex "([\\w_+-]+(?:(?: dot |\\.)[\\w_+-]+){0,10})(?: at |@)([a-zA-Z]+(?:(?:\\.| dot )[\\w_-]+){1,10})"
    ]
  , prod = \xs -> case xs of
      (Token RegexMatch (GroupMatch (m1:m2:_)):_) ->
        Just $ Token Email EmailData
          { TEmail.value = Text.concat [replaceDots m1, "@", replaceDots m2] }
        where replaceDots = Text.replace " dot " "."
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleEmailSpelledOut
  ]


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

import Data.String
import Prelude

import Duckling.Email.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types

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

allExamples :: [Example]
allExamples = concat
  [ examples (EmailData "alice@exAmple.io")
             [ "alice arobase exAmple.io"
             ]
  , examples (EmailData "yo+yo@blah.org")
             [ "yo+yo arobase blah.org"
             ]
  , examples (EmailData "1234+abc@x.net")
             [ "1234+abc arobase x.net"
             ]
  , examples (EmailData "jean-jacques@stuff.co.uk")
             [ "jean-jacques arobase stuff.co.uk"
             ]
  ]


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

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

import Duckling.Dimensions.Types
import Duckling.Email.Types (EmailData (..))
import qualified Duckling.Email.Types as TEmail
import Duckling.Regex.Types
import Duckling.Types

ruleEmailSpelledOut :: Rule
ruleEmailSpelledOut = Rule
  { name = "email spelled out"
  , pattern =
    [ regex "([\\w\\._+-]+) arobase ([\\w_-]+(\\.[\\w_-]+)+)"
    ]
  , prod = \xs -> case xs of
      (Token RegexMatch (GroupMatch (m1:m2:_)):_) ->
        Just $ Token Email EmailData {TEmail.value = Text.concat [m1, "@", m2]}
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleEmailSpelledOut
  ]


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

import Data.String
import Prelude

import Duckling.Email.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types

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

allExamples :: [Example]
allExamples = concat
  [ examples (EmailData "alice@exAmple.io")
             [ "alice ingi exAmple.io"
             ]
  , examples (EmailData "yo+yo@blah.org")
             [ "yo+yo ingi blah.org"
             ]
  , examples (EmailData "1234+abc@x.net")
             [ "1234+abc ingi x.net"
             ]
  , examples (EmailData "jean-jacques@stuff.co.uk")
             [ "jean-jacques ingi stuff.co.uk"
             ]
  ]


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

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

import Duckling.Dimensions.Types
import Duckling.Email.Types (EmailData (..))
import qualified Duckling.Email.Types as TEmail
import Duckling.Regex.Types
import Duckling.Types

ruleEmailSpelledOut :: Rule
ruleEmailSpelledOut = Rule
  { name = "email spelled out"
  , pattern =
    [ regex "([\\w\\._+-]+) ingi ([\\w_-]+(\\.[\\w_-]+)+)"
    ]
  , prod = \xs -> case xs of
      (Token RegexMatch (GroupMatch (m1:m2:_)):_) ->
        Just $ Token Email EmailData {TEmail.value = Text.concat [m1, "@", m2]}
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleEmailSpelledOut
  ]


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

import Data.String
import Prelude

import Duckling.Email.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types

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

allExamples :: [Example]
allExamples = concat
  [ examples (EmailData "alice@exAmple.io")
             [ "alice chiocciola exAmple.io"
             ]
  , examples (EmailData "yo+yo@blah.org")
             [ "yo+yo chiocciola blah.org"
             ]
  , examples (EmailData "1234+abc@x.net")
             [ "1234+abc chiocciola x.net"
             ]
  , examples (EmailData "jean-jacques@stuff.co.uk")
             [ "jean-jacques chiocciola stuff.co.uk"
             ]
  ]


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

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

import Duckling.Dimensions.Types
import Duckling.Email.Types (EmailData (..))
import qualified Duckling.Email.Types as TEmail
import Duckling.Regex.Types
import Duckling.Types

ruleEmailSpelledOut :: Rule
ruleEmailSpelledOut = Rule
  { name = "email spelled out"
  , pattern =
    [ regex "([\\w\\._+-]+) chiocciola ([\\w_-]+(\\.[\\w_-]+)+)"
    ]
  , prod = \xs -> case xs of
      (Token RegexMatch (GroupMatch (m1:m2:_)):_) ->
        Just $ Token Email EmailData {TEmail.value = Text.concat [m1, "@", m2]}
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleEmailSpelledOut
  ]


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

module Duckling.Engine.Regex
  ( matchAll
  , matchOnce
  ) where

import Text.Regex.Base (matchAll, matchOnce)


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

import Prelude

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

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

ruleFractions :: Rule
ruleFractions = Rule
  { name = "fractional number"
  , pattern =
    [ regex "(\\d+)/(\\d+)"
    ]
  , prod = \tokens -> case tokens of
      (Token RegexMatch (GroupMatch (numerator:denominator:_)):_) -> do
        n <- parseDecimal False numerator
        d <- parseDecimal False denominator
        divide n d >>= notOkForAnyTime
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleIntegerNumeric
  , ruleFractions
  ]


-- 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 DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NoRebindableSyntax #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}

module Duckling.Numeral.Types where

import Control.DeepSeq
import Data.Aeson
import Data.Hashable
import Data.Maybe
import Data.Text (Text)
import GHC.Generics
import Prelude

import Duckling.Resolve

data NumeralData = NumeralData
  { value        :: Double
  , grain        :: Maybe Int
  , multipliable :: Bool
  -- Hack until other use cases pop up,
  -- at which point we'll craft a generic solution.
  -- This allows to explicitly flag Numerals that don't work well with Time.
  -- See `ruleTODLatent`. Prevents things like "at single", "dozens o'clock".
  , okForAnyTime :: Bool
  }
  deriving (Eq, Generic, Hashable, Ord, Show, NFData)

instance Resolve NumeralData where
  type ResolvedValue NumeralData = NumeralValue
  resolve _ _ NumeralData {value} = Just (NumeralValue {vValue = value}, False)

newtype NumeralValue = NumeralValue { vValue :: Double }
  deriving (Eq, Show)

instance ToJSON NumeralValue where
  toJSON (NumeralValue value) = object
    [ "type" .= ("value" :: Text)
    , "value" .= value
    ]

getIntValue :: Double -> Maybe Int
getIntValue x = if rest == 0 then Just int else Nothing
  where
    (int, rest) = properFraction x :: (Int, Double)

isInteger :: Double -> Bool
isInteger = isJust . getIntValue

isMultiple :: Int -> Double -> Bool
isMultiple base = isJust . getIntValue . (/ fromIntegral base)

isIntegerBetween :: Double -> Int -> Int -> Bool
isIntegerBetween x low high = case getIntValue x of
  Just int -> low <= int && int <= high
  Nothing -> False


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

allExamples :: [Example]
allExamples = concat
  [ examples (NumeralValue 0)
             [ "nul"
             , "geen"
             , "niks"
             ]
  , examples (NumeralValue 1)
             [ "een"
             ]
  , examples (NumeralValue 2)
             [ "twee"
             ]
  , examples (NumeralValue 3)
             [ "drie"
             ]
  , examples (NumeralValue 4)
             [ "vier"
             ]
  , examples (NumeralValue 5)
             [ "vyf"
             ]
  , examples (NumeralValue 6)
             [ "ses"
             ]
  , examples (NumeralValue 7)
             [ "sewe"
             ]
  , examples (NumeralValue 8)
             [ "agt"
             , "ag"
             ]
  , examples (NumeralValue 9)
             [ "nege"
             ]
  , examples (NumeralValue 10)
             [ "tien"
             ]
  , examples (NumeralValue 11)
             [ "elf"
             ]
  , examples (NumeralValue 12)
             [ "twaalf"
             , "dosyn"
             ]
  , examples (NumeralValue 14)
             [ "veertien"
             ]
  , examples (NumeralValue 15)
             [ "vyftien"
             ]
  , examples (NumeralValue 16)
             [ "sestien"
             ]
  , examples (NumeralValue 17)
             [ "sewentien"
             ]
  , examples (NumeralValue 19)
             [ "negentien"
             , "neentien"
             ]
  , examples (NumeralValue 20)
             [ "twintig"
             ]
  , examples (NumeralValue 22)
             [ "twee en twintig"
             ]
  , examples (NumeralValue 24)
             [ "vier en twintig"
             ]
  , examples (NumeralValue 26)
             [ "ses en twintig"
             ]
  , examples (NumeralValue 28)
             [ "agt en twintig"
             ]
  , examples (NumeralValue 33)
             [ "drie en dertig"
             ]
  , examples (NumeralValue 34)
             [ "vier en dertig"
             ]
  , examples (NumeralValue 50)
             [ "vyftig"
             ]
  , examples (NumeralValue 55)
             [ "vyf en vyftig"
             ]
  , examples (NumeralValue 1.1)
             [ "1,1"
             , "1,10"
             , "01,10"
             ]
  , examples (NumeralValue 0.77)
             [ "0,77"
             , ",77"
             ]
  , examples (NumeralValue 300)
             [ "3 honderd"
             , "drie honderd"
             ]
  , examples (NumeralValue 5000)
             [ "5 duisend"
             , "vyf duisend"
             ]
  , examples (NumeralValue 144)
             [ "een honderd vier en veertig"
             , "honderd vier en veertig"
             ]
  , examples (NumeralValue 122)
             [ "een honderd twee en twintig"
             , "honderd twee en twintig"
             ]
  , examples (NumeralValue 20000)
             [ "twintig duisend"
             ]
  , examples (NumeralValue 0.2)
             [ "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 LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoRebindableSyntax #-}

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

import Control.Applicative ((<|>))
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

zeroAndTenMap :: HashMap Text Integer
zeroAndTenMap = HashMap.fromList
  [ ( "nul" , 0  )
  , ( "geen", 0  )
  , ( "niks", 0  )
  , ( "zero", 0  )
  , ( "tien", 10 )
  ]

oneToNineMap :: HashMap Text Integer
oneToNineMap = HashMap.fromList
  [ ( "een" , 1 )
  , ( "twee", 2 )
  , ( "drie", 3 )
  , ( "vier", 4 )
  , ( "vyf" , 5 )
  , ( "ses" , 6 )
  , ( "sewe", 7 )
  , ( "agt" , 8 )
  , ( "ag"  , 8 )
  , ( "nege", 9 )
  ]

ruleNumeral :: Rule
ruleNumeral = Rule
  { name = "number (0..10)"
  , pattern =
    [ regex . Text.unpack $
        Text.concat [ "(", Text.intercalate "|"
          ((HashMap.keys zeroAndTenMap) ++ (HashMap.keys oneToNineMap)), ")" ]
    ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        let x = Text.toLower match in
        (HashMap.lookup x oneToNineMap >>= integer) <|>
        (HashMap.lookup x zeroAndTenMap >>= integer)
      _ -> Nothing
  }

elevenToNineteenMap :: HashMap Text Integer
elevenToNineteenMap = HashMap.fromList
  [ ( "elf"      , 11 )
  , ( "twaalf"   , 12 )
  , ( "dertien"  , 13 )
  , ( "veertien" , 14 )
  , ( "vyftien"  , 15 )
  , ( "sestien"  , 16 )
  , ( "sewentien", 17 )
  , ( "agtien"   , 18 )
  , ( "negentien", 19 )
  , ( "neentien" , 19 )
  ]

ruleElevenToNineteen :: Rule
ruleElevenToNineteen = Rule
  { name = "number (11..19)"
  , pattern =
    [ regex "(elf|twaalf|dertien|veertien|vyftien|sestien|sewentien|agtien|neg?entien)"
    ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) elevenToNineteenMap >>= integer
      _ -> Nothing
  }

tensMap :: HashMap Text Integer
tensMap = HashMap.fromList
  [ ( "twin" , 20 )
  , ( "der"  , 30 )
  , ( "veer" , 40 )
  , ( "vyf"  , 50 )
  , ( "ses"  , 60 )
  , ( "sewen", 70 )
  , ( "tag"  , 80 )
  , ( "negen", 90 )
  , ( "neen" , 90 )
  ]

ruleTens :: Rule
ruleTens = Rule
  { name = "integer (20,30..90)"
  , pattern =
    [ regex "(twin|der|veer|vyf|ses|sewen|tag|neg?en)tig"
    ]
  , prod = \case
      (Token RegexMatch (GroupMatch (match:_)):_) ->
        HashMap.lookup (Text.toLower match) tensMap >>= integer
      _ -> Nothing
  }

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

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

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

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

ruleSum :: Rule
ruleSum = Rule
  { name = "intersect 2 numbers"
  , pattern =
    [ Predicate $ and . sequence [hasGrain, isPositive]
    , 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
  }

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

rules :: [Rule]
rules =
  [ ruleNumeral
  , ruleDozen
  , ruleDecimals
  , rulePowersOfTen
  , ruleElevenToNineteen
  , ruleTens
  , ruleCompositeTens
  , ruleSum
  , ruleMultiply
  ]