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


import Duckling.Dimensions.Types
import Duckling.Duration.Helpers
import Duckling.Regex.Types
import Control.Monad (join)
import qualified Data.Text as Text
import Prelude
import Duckling.Numeral.Helpers (parseInteger)
import Duckling.Duration.Types (DurationData(..))
import qualified Duckling.Duration.Types as TDuration
import Data.String
import Duckling.Numeral.Types (NumeralData(..))
import qualified Duckling.TimeGrain.Types as TG
import qualified Duckling.Numeral.Types as TNumeral
import Duckling.Types

ruleQuarterOfAnHour :: Rule
ruleQuarterOfAnHour :: Rule
ruleQuarterOfAnHour = Rule :: Text -> Pattern -> Production -> Rule
Rule
  { name :: Text
name = Text
"quarter of an hour"
  , pattern :: Pattern
pattern =
    [ String -> PatternItem
regex String
"(einer? )?Viertelstunde"
    ]
  , prod :: Production
prod = \[Token]
_ -> Token -> Maybe Token
forall a. a -> Maybe a
Just (Token -> Maybe Token) -> Token -> Maybe Token
forall a b. (a -> b) -> a -> b
$ Dimension DurationData -> DurationData -> Token
forall a.
(Resolve a, Eq a, Hashable a, Show a, NFData a) =>
Dimension a -> a -> Token
Token Dimension DurationData
Duration (DurationData -> Token) -> DurationData -> Token
forall a b. (a -> b) -> a -> b
$ Grain -> Int -> DurationData
duration Grain
TG.Minute Int
15
  }

ruleHalfAnHour :: Rule
ruleHalfAnHour :: Rule
ruleHalfAnHour = Rule :: Text -> Pattern -> Production -> Rule
Rule
  { name :: Text
name = Text
"half an hour"
  , pattern :: Pattern
pattern =
    [ String -> PatternItem
regex String
"(1/2\\s?|(eine )?halbe |(einer )?halben )stunde"
    ]
  , prod :: Production
prod = \[Token]
_ -> Token -> Maybe Token
forall a. a -> Maybe a
Just (Token -> Maybe Token) -> Token -> Maybe Token
forall a b. (a -> b) -> a -> b
$ Dimension DurationData -> DurationData -> Token
forall a.
(Resolve a, Eq a, Hashable a, Show a, NFData a) =>
Dimension a -> a -> Token
Token Dimension DurationData
Duration (DurationData -> Token) -> DurationData -> Token
forall a b. (a -> b) -> a -> b
$ Grain -> Int -> DurationData
duration Grain
TG.Minute Int
30
  }

ruleThreeQuartersOfAnHour :: Rule
ruleThreeQuartersOfAnHour :: Rule
ruleThreeQuartersOfAnHour = Rule :: Text -> Pattern -> Production -> Rule
Rule
  { name :: Text
name = Text
"three-quarters of an hour"
  , pattern :: Pattern
pattern =
    [ String -> PatternItem
regex String
"3/4\\s?stunde|(einer? )?dreiviertel stunde|drei viertelstunden"
    ]
  , prod :: Production
prod = \[Token]
_ -> Token -> Maybe Token
forall a. a -> Maybe a
Just (Token -> Maybe Token) -> Token -> Maybe Token
forall a b. (a -> b) -> a -> b
$ Dimension DurationData -> DurationData -> Token
forall a.
(Resolve a, Eq a, Hashable a, Show a, NFData a) =>
Dimension a -> a -> Token
Token Dimension DurationData
Duration (DurationData -> Token) -> DurationData -> Token
forall a b. (a -> b) -> a -> b
$ Grain -> Int -> DurationData
duration Grain
TG.Minute Int
45
  }

ruleFortnight :: Rule
ruleFortnight :: Rule
ruleFortnight = Rule :: Text -> Pattern -> Production -> Rule
Rule
  { name :: Text
name = Text
"fortnight"
  , pattern :: Pattern
pattern =
    [ String -> PatternItem
regex String
"zwei Wochen"
    ]
  , prod :: Production
prod = \[Token]
_ -> Token -> Maybe Token
forall a. a -> Maybe a
Just (Token -> Maybe Token) -> Token -> Maybe Token
forall a b. (a -> b) -> a -> b
$ Dimension DurationData -> DurationData -> Token
forall a.
(Resolve a, Eq a, Hashable a, Show a, NFData a) =>
Dimension a -> a -> Token
Token Dimension DurationData
Duration (DurationData -> Token) -> DurationData -> Token
forall a b. (a -> b) -> a -> b
$ Grain -> Int -> DurationData
duration Grain
TG.Day Int
14
  }

rulePrecision :: Rule
rulePrecision :: Rule
rulePrecision = Rule :: Text -> Pattern -> Production -> Rule
Rule
  { name :: Text
name = Text
"about|exactly <duration>"
  , pattern :: Pattern
pattern =
    [ String -> PatternItem
regex String
"ungef(ä|a)hr|zirka|genau|exakt"
    , Dimension DurationData -> PatternItem
forall a. Typeable a => Dimension a -> PatternItem
dimension Dimension DurationData
Duration
    ]
  , prod :: Production
prod = \case
      (Token
_:Token
token:[Token]
_) -> Token -> Maybe Token
forall a. a -> Maybe a
Just Token
token
      [Token]
_ -> Maybe Token
forall a. Maybe a
Nothing
  }

ruleCommaNumeralHours :: Rule
ruleCommaNumeralHours :: Rule
ruleCommaNumeralHours = Rule :: Text -> Pattern -> Production -> Rule
Rule
  { name :: Text
name = Text
"number,number hours"
  , pattern :: Pattern
pattern =
    [ String -> PatternItem
regex String
"(\\d+),(\\d+)"
    , Predicate -> PatternItem
Predicate (Predicate -> PatternItem) -> Predicate -> PatternItem
forall a b. (a -> b) -> a -> b
$ Grain -> Predicate
isGrain Grain
TG.Hour
    ]
  , prod :: Production
prod = \case
      (Token Dimension a
RegexMatch (GroupMatch (h:m:_)):[Token]
_) -> do
        Integer
hh <- Text -> Maybe Integer
parseInteger Text
h
        Integer
mnum <- Text -> Maybe Integer
parseInteger Text
m
        let mden :: Integer
mden = Integer
10 Integer -> Int -> Integer
forall a b. (Num a, Integral b) => a -> b -> a
^ Text -> Int
Text.length Text
m
        Token -> Maybe Token
forall a. a -> Maybe a
Just (Token -> Maybe Token) -> Token -> Maybe Token
forall a b. (a -> b) -> a -> b
$ Dimension DurationData -> DurationData -> Token
forall a.
(Resolve a, Eq a, Hashable a, Show a, NFData a) =>
Dimension a -> a -> Token
Token Dimension DurationData
Duration (DurationData -> Token) -> DurationData -> Token
forall a b. (a -> b) -> a -> b
$ Integer -> Integer -> Integer -> DurationData
minutesFromHourMixedFraction Integer
hh Integer
mnum Integer
mden
      [Token]
_ -> Maybe Token
forall a. Maybe a
Nothing
  }

ruleCommaNumeralMinutes :: Rule
ruleCommaNumeralMinutes :: Rule
ruleCommaNumeralMinutes = Rule :: Text -> Pattern -> Production -> Rule
Rule
  { name :: Text
name = Text
"number,number minutes"
  , pattern :: Pattern
pattern =
    [ String -> PatternItem
regex String
"(\\d+),(\\d+)"
    , Predicate -> PatternItem
Predicate (Predicate -> PatternItem) -> Predicate -> PatternItem
forall a b. (a -> b) -> a -> b
$ Grain -> Predicate
isGrain Grain
TG.Minute
    ]
  , prod :: Production
prod = \case
      (Token Dimension a
RegexMatch (GroupMatch (m:s:_)):[Token]
_) -> do
        Integer
mm <- Text -> Maybe Integer
parseInteger Text
m
        Integer
ss <- Text -> Maybe Integer
parseInteger Text
s
        let sden :: Integer
sden = Integer
10 Integer -> Int -> Integer
forall a b. (Num a, Integral b) => a -> b -> a
^ Text -> Int
Text.length Text
s
        Token -> Maybe Token
forall a. a -> Maybe a
Just (Token -> Maybe Token) -> Token -> Maybe Token
forall a b. (a -> b) -> a -> b
$ Dimension DurationData -> DurationData -> Token
forall a.
(Resolve a, Eq a, Hashable a, Show a, NFData a) =>
Dimension a -> a -> Token
Token Dimension DurationData
Duration (DurationData -> Token) -> DurationData -> Token
forall a b. (a -> b) -> a -> b
$ Integer -> Integer -> Integer -> DurationData
secondsFromHourMixedFraction Integer
mm Integer
ss Integer
sden
      [Token]
_ -> Maybe Token
forall a. Maybe a
Nothing
  }

ruleAndHalfHour :: Rule
ruleAndHalfHour :: Rule
ruleAndHalfHour = Rule :: Text -> Pattern -> Production -> Rule
Rule
  { name :: Text
name = Text
"<integer> and a half hour"
  , pattern :: Pattern
pattern =
    [ Predicate -> PatternItem
Predicate Predicate
isNatural
    , String -> PatternItem
regex String
"(und )?(ein(en?)? )?halb(en?)?"
    , Predicate -> PatternItem
Predicate (Predicate -> PatternItem) -> Predicate -> PatternItem
forall a b. (a -> b) -> a -> b
$ Grain -> Predicate
isGrain Grain
TG.Hour
    ]
  , prod :: Production
prod = \case
      (Token Dimension a
Numeral NumeralData{TNumeral.value = v}:[Token]
_) ->
        Token -> Maybe Token
forall a. a -> Maybe a
Just (Token -> Maybe Token) -> Token -> Maybe Token
forall a b. (a -> b) -> a -> b
$ Dimension DurationData -> DurationData -> Token
forall a.
(Resolve a, Eq a, Hashable a, Show a, NFData a) =>
Dimension a -> a -> Token
Token Dimension DurationData
Duration (DurationData -> Token) -> DurationData -> Token
forall a b. (a -> b) -> a -> b
$ Grain -> Int -> DurationData
duration Grain
TG.Minute (Int -> DurationData) -> Int -> DurationData
forall a b. (a -> b) -> a -> b
$ Int
30 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
60 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Double -> Int
forall a b. (RealFrac a, Integral b) => a -> b
floor Double
v
      [Token]
_ -> Maybe Token
forall a. Maybe a
Nothing
  }

ruleAndHalfMinute :: Rule
ruleAndHalfMinute :: Rule
ruleAndHalfMinute = Rule :: Text -> Pattern -> Production -> Rule
Rule
  { name :: Text
name = Text
"<integer> and a half minutes"
  , pattern :: Pattern
pattern =
    [ Predicate -> PatternItem
Predicate Predicate
isNatural
    , String -> PatternItem
regex String
"(und )?(ein(en?)? ?)?halb(en?)?"
    , Predicate -> PatternItem
Predicate (Predicate -> PatternItem) -> Predicate -> PatternItem
forall a b. (a -> b) -> a -> b
$ Grain -> Predicate
isGrain Grain
TG.Minute
    ]
  , prod :: Production
prod = \case
      (Token Dimension a
Numeral NumeralData{TNumeral.value = v}:[Token]
_) ->
        Token -> Maybe Token
forall a. a -> Maybe a
Just (Token -> Maybe Token) -> Token -> Maybe Token
forall a b. (a -> b) -> a -> b
$ Dimension DurationData -> DurationData -> Token
forall a.
(Resolve a, Eq a, Hashable a, Show a, NFData a) =>
Dimension a -> a -> Token
Token Dimension DurationData
Duration (DurationData -> Token) -> DurationData -> Token
forall a b. (a -> b) -> a -> b
$ Grain -> Int -> DurationData
duration Grain
TG.Second (Int -> DurationData) -> Int -> DurationData
forall a b. (a -> b) -> a -> b
$ Int
30 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
60 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Double -> Int
forall a b. (RealFrac a, Integral b) => a -> b
floor Double
v
      [Token]
_ -> Maybe Token
forall a. Maybe a
Nothing
  }

ruleArticle :: Rule
ruleArticle :: Rule
ruleArticle = Rule :: Text -> Pattern -> Production -> Rule
Rule
  { name :: Text
name = Text
"a <unit-of-duration>"
  , pattern :: Pattern
pattern =
    [ String -> PatternItem
regex String
"ein(en?)?"
    , Dimension Grain -> PatternItem
forall a. Typeable a => Dimension a -> PatternItem
dimension Dimension Grain
TimeGrain
    ]
  , prod :: Production
prod = \case
      (Token
_:Token Dimension a
TimeGrain a
grain:[Token]
_) -> Token -> Maybe Token
forall a. a -> Maybe a
Just (Token -> Maybe Token) -> Token -> Maybe Token
forall a b. (a -> b) -> a -> b
$ Dimension DurationData -> DurationData -> Token
forall a.
(Resolve a, Eq a, Hashable a, Show a, NFData a) =>
Dimension a -> a -> Token
Token Dimension DurationData
Duration (DurationData -> Token) -> DurationData -> Token
forall a b. (a -> b) -> a -> b
$ Grain -> Int -> DurationData
duration a
Grain
grain Int
1
      [Token]
_ -> Maybe Token
forall a. Maybe a
Nothing
  }

ruleHalfTimeGrain :: Rule
ruleHalfTimeGrain :: Rule
ruleHalfTimeGrain = Rule :: Text -> Pattern -> Production -> Rule
Rule
  { name :: Text
name = Text
"half a <time-grain>"
  , pattern :: Pattern
pattern =
    [ String -> PatternItem
regex String
"(ein(en)?)?(1/2|halbe?)"
    , Dimension Grain -> PatternItem
forall a. Typeable a => Dimension a -> PatternItem
dimension Dimension Grain
TimeGrain
    ]
  , prod :: Production
prod = \case
      (Token
_:Token Dimension a
TimeGrain a
grain:[Token]
_) -> Dimension DurationData -> DurationData -> Token
forall a.
(Resolve a, Eq a, Hashable a, Show a, NFData a) =>
Dimension a -> a -> Token
Token Dimension DurationData
Duration (DurationData -> Token) -> Maybe DurationData -> Maybe Token
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Grain -> Int -> Maybe DurationData
nPlusOneHalf a
Grain
grain Int
0
      [Token]
_ -> Maybe Token
forall a. Maybe a
Nothing
  }

ruleCompositeDurationCommasAnd :: Rule
ruleCompositeDurationCommasAnd :: Rule
ruleCompositeDurationCommasAnd = Rule :: Text -> Pattern -> Production -> Rule
Rule
  { name :: Text
name = Text
"composite <duration> (with ,/and)"
  , pattern :: Pattern
pattern =
    [ Predicate -> PatternItem
Predicate Predicate
isNatural
    , Dimension Grain -> PatternItem
forall a. Typeable a => Dimension a -> PatternItem
dimension Dimension Grain
TimeGrain
    , String -> PatternItem
regex String
",|und"
    , Dimension DurationData -> PatternItem
forall a. Typeable a => Dimension a -> PatternItem
dimension Dimension DurationData
Duration
    ]
  , prod :: Production
prod = \case
      (Token Dimension a
Numeral NumeralData{TNumeral.value = v}:
       Token Dimension a
TimeGrain a
g:
       Token
_:
       Token Dimension a
Duration dd :: a
dd@DurationData{TDuration.grain = dg}:
       [Token]
_) | a
g a -> a -> Bool
forall a. Ord a => a -> a -> Bool
> a
Grain
dg -> Token -> Maybe Token
forall a. a -> Maybe a
Just (Token -> Maybe Token) -> Token -> Maybe Token
forall a b. (a -> b) -> a -> b
$ Dimension DurationData -> DurationData -> Token
forall a.
(Resolve a, Eq a, Hashable a, Show a, NFData a) =>
Dimension a -> a -> Token
Token Dimension DurationData
Duration (DurationData -> Token) -> DurationData -> Token
forall a b. (a -> b) -> a -> b
$ Grain -> Int -> DurationData
duration a
Grain
g (Double -> Int
forall a b. (RealFrac a, Integral b) => a -> b
floor Double
v) DurationData -> DurationData -> DurationData
forall a. Semigroup a => a -> a -> a
<> a
DurationData
dd
      [Token]
_ -> Maybe Token
forall a. Maybe a
Nothing
  }

ruleCompositeDuration :: Rule
ruleCompositeDuration :: Rule
ruleCompositeDuration = Rule :: Text -> Pattern -> Production -> Rule
Rule
  { name :: Text
name = Text
"composite <duration>"
  , pattern :: Pattern
pattern =
    [ Predicate -> PatternItem
Predicate Predicate
isNatural
    , Dimension Grain -> PatternItem
forall a. Typeable a => Dimension a -> PatternItem
dimension Dimension Grain
TimeGrain
    , Dimension DurationData -> PatternItem
forall a. Typeable a => Dimension a -> PatternItem
dimension Dimension DurationData
Duration
    ]
  , prod :: Production
prod = \case
      (Token Dimension a
Numeral NumeralData{TNumeral.value = v}:
       Token Dimension a
TimeGrain a
g:
       Token Dimension a
Duration dd :: a
dd@DurationData{TDuration.grain = dg}:
       [Token]
_) | a
g a -> a -> Bool
forall a. Ord a => a -> a -> Bool
> a
Grain
dg -> Token -> Maybe Token
forall a. a -> Maybe a
Just (Token -> Maybe Token) -> Token -> Maybe Token
forall a b. (a -> b) -> a -> b
$ Dimension DurationData -> DurationData -> Token
forall a.
(Resolve a, Eq a, Hashable a, Show a, NFData a) =>
Dimension a -> a -> Token
Token Dimension DurationData
Duration (DurationData -> Token) -> DurationData -> Token
forall a b. (a -> b) -> a -> b
$ Grain -> Int -> DurationData
duration a
Grain
g (Double -> Int
forall a b. (RealFrac a, Integral b) => a -> b
floor Double
v) DurationData -> DurationData -> DurationData
forall a. Semigroup a => a -> a -> a
<> a
DurationData
dd
      [Token]
_ -> Maybe Token
forall a. Maybe a
Nothing
  }

ruleCompositeDurationAnd :: Rule
ruleCompositeDurationAnd :: Rule
ruleCompositeDurationAnd = Rule :: Text -> Pattern -> Production -> Rule
Rule
  { name :: Text
name = Text
"composite <duration> and <duration>"
  , pattern :: Pattern
pattern =
    [ Dimension DurationData -> PatternItem
forall a. Typeable a => Dimension a -> PatternItem
dimension Dimension DurationData
Duration
    , String -> PatternItem
regex String
",|und"
    , Dimension DurationData -> PatternItem
forall a. Typeable a => Dimension a -> PatternItem
dimension Dimension DurationData
Duration
    ]
  , prod :: Production
prod = \case
      (Token Dimension a
Duration DurationData{TDuration.value = v, TDuration.grain = g}:
       Token
_:
       Token Dimension a
Duration dd :: a
dd@DurationData{TDuration.grain = dg}:
       [Token]
_) | Grain
g Grain -> Grain -> Bool
forall a. Ord a => a -> a -> Bool
> Grain
dg -> Token -> Maybe Token
forall a. a -> Maybe a
Just (Token -> Maybe Token) -> Token -> Maybe Token
forall a b. (a -> b) -> a -> b
$ Dimension DurationData -> DurationData -> Token
forall a.
(Resolve a, Eq a, Hashable a, Show a, NFData a) =>
Dimension a -> a -> Token
Token Dimension DurationData
Duration (DurationData -> Token) -> DurationData -> Token
forall a b. (a -> b) -> a -> b
$ Grain -> Int -> DurationData
duration Grain
g Int
v DurationData -> DurationData -> DurationData
forall a. Semigroup a => a -> a -> a
<> a
DurationData
dd
      [Token]
_ -> Maybe Token
forall a. Maybe a
Nothing
  }

ruleHoursAndMinutes :: Rule
ruleHoursAndMinutes :: Rule
ruleHoursAndMinutes = Rule :: Text -> Pattern -> Production -> Rule
Rule
  { name :: Text
name = Text
"<integer> hour and <integer>"
  , pattern :: Pattern
pattern =
    [ Predicate -> PatternItem
Predicate Predicate
isNatural
    , String -> PatternItem
regex String
"(ein(en?) )?stunden?( und)?"
    , Predicate -> PatternItem
Predicate Predicate
isNatural
    , Predicate -> PatternItem
Predicate (Predicate -> PatternItem) -> Predicate -> PatternItem
forall a b. (a -> b) -> a -> b
$ Grain -> Predicate
isGrain Grain
TG.Minute
    ]
  , prod :: Production
prod = \case
      (Token Dimension a
Numeral a
h:
       Token
_:
       Token Dimension a
Numeral a
m:
       [Token]
_) -> Token -> Maybe Token
forall a. a -> Maybe a
Just (Token -> Maybe Token) -> Token -> Maybe Token
forall a b. (a -> b) -> a -> b
$ Dimension DurationData -> DurationData -> Token
forall a.
(Resolve a, Eq a, Hashable a, Show a, NFData a) =>
Dimension a -> a -> Token
Token Dimension DurationData
Duration (DurationData -> Token) -> DurationData -> Token
forall a b. (a -> b) -> a -> b
$ Grain -> Int -> DurationData
duration Grain
TG.Minute (Int -> DurationData) -> Int -> DurationData
forall a b. (a -> b) -> a -> b
$
                Double -> Int
forall a b. (RealFrac a, Integral b) => a -> b
floor (NumeralData -> Double
TNumeral.value a
NumeralData
m) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
60 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Double -> Int
forall a b. (RealFrac a, Integral b) => a -> b
floor (NumeralData -> Double
TNumeral.value a
NumeralData
h)
      [Token]
_ -> Maybe Token
forall a. Maybe a
Nothing
  }

rules :: [Rule]
rules :: [Rule]
rules =
  [ Rule
ruleQuarterOfAnHour
  , Rule
ruleHalfAnHour
  , Rule
ruleThreeQuartersOfAnHour
  , Rule
rulePrecision
  , Rule
ruleCommaNumeralHours
  , Rule
ruleCommaNumeralMinutes
  , Rule
ruleFortnight
  , Rule
ruleAndHalfHour
  , Rule
ruleAndHalfMinute
  , Rule
ruleArticle
  , Rule
ruleHalfTimeGrain
  , Rule
ruleCompositeDurationCommasAnd
  , Rule
ruleCompositeDuration
  , Rule
ruleCompositeDurationAnd
  , Rule
ruleHoursAndMinutes
  , Rule
ruleAndHalfMinute
  ]