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

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

module Duckling.Dimensions.Types
  ( Seal(..)
  , Dimension(..)

  , fromName
  , toName
  ) where

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

import Duckling.Types

toName :: Dimension a -> Text
toName RegexMatch = "regex"
toName CreditCardNumber = "credit-card-number"
toName Distance = "distance"
toName Duration = "duration"
toName Email = "email"
toName AmountOfMoney = "amount-of-money"
toName Numeral = "number"
toName Ordinal = "ordinal"
toName PhoneNumber = "phone-number"
toName Quantity = "quantity"
toName Temperature = "temperature"
toName Time = "time"
toName TimeGrain = "time-grain"
toName Url = "url"
toName Volume = "volume"
toName (CustomDimension dim) = Text.pack (show dim)

fromName :: Text -> Maybe (Seal Dimension)
fromName name = HashMap.lookup name m
  where
    m = HashMap.fromList
      [ ("amount-of-money", Seal AmountOfMoney)
      , ("credit-card-number", Seal CreditCardNumber)
      , ("distance", Seal Distance)
      , ("duration", Seal Duration)
      , ("email", Seal Email)
      , ("number", Seal Numeral)
      , ("ordinal", Seal Ordinal)
      , ("phone-number", Seal PhoneNumber)
      , ("quantity", Seal Quantity)
      , ("temperature", Seal Temperature)
      , ("time", Seal Time)
      , ("time-grain", Seal TimeGrain)
      , ("url", Seal Url)
      , ("volume", Seal Volume)
      ]


-- 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.Dimensions.UK
  ( allDimensions
  ) where

import Duckling.Dimensions.Types

allDimensions :: [Seal Dimension]
allDimensions =
  [ Seal Duration
  , Seal Numeral
  , Seal Ordinal
  , Seal Time
  ]


-- 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.Dimensions.VI
  ( allDimensions
  ) where

import Duckling.Dimensions.Types

allDimensions :: [Seal Dimension]
allDimensions =
  [ Seal Duration
  , Seal Numeral
  , Seal Ordinal
  , Seal Time
  ]


-- 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.Dimensions.ZH
  ( allDimensions
  ) where

import Duckling.Dimensions.Types

allDimensions :: [Seal Dimension]
allDimensions =
  [ Seal Distance
  , Seal Duration
  , Seal Numeral
  , Seal Ordinal
  , Seal Quantity
  , Seal Temperature
  , Seal Time
  ]


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

module Duckling.Distance.Helpers
  ( distance
  , distanceSum
  , isDistanceOfUnit
  , isSimpleDistance
  , unitOnly
  , withInterval
  , withMax
  , withMin
  , withUnit
  , withValue
  ) where

import Prelude
import Data.Semigroup ((<>))

import Duckling.Dimensions.Types
import Duckling.Distance.Types (DistanceData(..))
import Duckling.Types
import qualified Duckling.Distance.Types as TDistance
import qualified Duckling.DistanceUnits.Types as DGTypes

-- -----------------------------------------------------------------
-- Patterns

isSimpleDistance :: Predicate
isSimpleDistance (Token Distance DistanceData {TDistance.value = Just _
                                              , TDistance.unit = Just _}) = True
isSimpleDistance _ = False

isDistanceOfUnit :: TDistance.Unit -> Predicate
isDistanceOfUnit unit (Token Distance DistanceData {TDistance.unit = Just u}) =
    unit == u
isDistanceOfUnit _ _ = False

-- -----------------------------------------------------------------
-- Production

distance :: Double -> DistanceData
distance x = DistanceData {TDistance.value = Just x
                          , TDistance.unit = Nothing
                          , TDistance.minValue = Nothing
                          , TDistance.maxValue = Nothing}

distanceSum ::
     Double
  -> TDistance.Unit
  -> Double
  -> TDistance.Unit
  -> Maybe DistanceData
distanceSum v1 u1 v2 u2 = unwrapContext $ cd1 <> cd2
  where
    wrapContext v u = DGTypes.ContextualDistance v $ DGTypes.toSystemUnit u
    cd1 = wrapContext v1 u1
    cd2 = wrapContext v2 u2

    unwrapContext DGTypes.Nonrelatable = Nothing
    unwrapContext (DGTypes.ContextualDistance v u) =
        Just $ withUnit (DGTypes.toRawUnit u) $ distance v

unitOnly :: TDistance.Unit -> DistanceData
unitOnly u = DistanceData {TDistance.unit = Just u
                          , TDistance.value = Nothing
                          , TDistance.minValue = Nothing
                          , TDistance.maxValue = Nothing}

withUnit :: TDistance.Unit -> DistanceData -> DistanceData
withUnit u dd = dd {TDistance.unit = Just u}

withValue :: Double -> DistanceData -> DistanceData
withValue value dd = dd {TDistance.value = Just value}

withInterval :: (Double, Double) -> DistanceData -> DistanceData
withInterval (from, to) dd = dd {TDistance.minValue = Just from
                                , TDistance.maxValue = Just to}

withMin :: Double -> DistanceData -> DistanceData
withMin from dd = dd {TDistance.minValue = Just from}

withMax :: Double -> DistanceData -> DistanceData
withMax to dd = dd {TDistance.maxValue = Just to}


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

import Data.String
import Prelude

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

ruleNumeralAsDistance :: Rule
ruleNumeralAsDistance = Rule
  { name = "number as distance"
  , pattern =
    [ dimension Numeral
    ]
  , prod = \tokens -> case tokens of
      (Token Numeral NumeralData {TNumeral.value = v}:_) ->
        Just . Token Distance $ distance v
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleNumeralAsDistance
  ]


-- 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.Distance.Types where

import Control.DeepSeq
import Data.Aeson
import Data.Hashable
import Data.Text (Text)
import GHC.Generics
import Prelude
import qualified Data.HashMap.Strict as H
import qualified Data.Text as Text

import Duckling.Resolve (Resolve(..))

data Unit
  = Centimetre
  | Foot
  | Inch
  | Kilometre
  | M -- ambiguous between Mile and Metre
  | Metre
  | Mile
  | Millimetre
  | Yard
  deriving (Eq, Generic, Hashable, Ord, Show, NFData)

instance ToJSON Unit where
  toJSON = String . Text.toLower . Text.pack . show

data DistanceData = DistanceData
  { unit     :: Maybe Unit
  , value    :: Maybe Double
  , minValue :: Maybe Double
  , maxValue :: Maybe Double
  }
  deriving (Eq, Generic, Hashable, Ord, Show, NFData)

instance Resolve DistanceData where
  type ResolvedValue DistanceData = DistanceValue
  resolve _ _ DistanceData {unit = Just unit, value = Just val} =
    Just (simple unit val, False)
  resolve _ _ DistanceData {unit = Just unit, value = Nothing
                         , minValue = Just from, maxValue = Just to} =
    Just (between unit (from, to), False)
  resolve _ _ DistanceData {unit = Just unit, value = Nothing
                         , minValue = Just from, maxValue = Nothing} =
    Just (above unit from, False)
  resolve _ _ DistanceData {unit = Just unit, value = Nothing
                         , minValue = Nothing, maxValue = Just to} =
    Just (under unit to, False)
  resolve _ _ _ = Nothing

data IntervalDirection = Above | Under
  deriving (Eq, Generic, Hashable, Ord, Show, NFData)

data SingleValue = SingleValue
  { vUnit :: Unit
  , vValue :: Double
  }
  deriving (Eq, Ord, Show)

instance ToJSON SingleValue where
  toJSON (SingleValue unit value) = object
    [ "value" .= value
    , "unit"  .= unit
    ]

data DistanceValue
  = SimpleValue SingleValue
  | IntervalValue (SingleValue, SingleValue)
  | OpenIntervalValue (SingleValue, IntervalDirection)
  deriving (Eq, Ord, Show)

instance ToJSON DistanceValue where
  toJSON (SimpleValue value) = case toJSON value of
    Object o -> Object $ H.insert "type" (toJSON ("value" :: Text)) o
    _ -> Object H.empty
  toJSON (IntervalValue (from, to)) = object
    [ "type" .= ("interval" :: Text)
    , "from" .= toJSON from
    , "to" .= toJSON to
    ]
  toJSON (OpenIntervalValue (from, Above)) = object
    [ "type" .= ("interval" :: Text)
    , "from" .= toJSON from
    ]
  toJSON (OpenIntervalValue (to, Under)) = object
    [ "type" .= ("interval" :: Text)
    , "to" .= toJSON to
    ]

-- -----------------------------------------------------------------
-- Value helpers

simple :: Unit -> Double -> DistanceValue
simple u v = SimpleValue $ single u v

between :: Unit -> (Double, Double) -> DistanceValue
between u (from, to) = IntervalValue (single u from, single u to)

above :: Unit -> Double -> DistanceValue
above = openInterval Above

under :: Unit -> Double -> DistanceValue
under = openInterval Under

openInterval :: IntervalDirection -> Unit -> Double -> DistanceValue
openInterval direction u v = OpenIntervalValue (single u v, direction)

single :: Unit -> Double -> SingleValue
single u v = SingleValue {vUnit = u, vValue = 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 OverloadedStrings #-}

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

import Data.String
import Prelude

import Duckling.Distance.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types

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

allExamples :: [Example]
allExamples = concat
  [ examples (simple Kilometre 3)
             [ "3 kilÃ²metres"
             , "3 kilometres"
             , "3 kilÃ³metres"
             , "3 quilÃ²metres"
             , "3 km"
             , "3km"
             ]
  , examples (simple Kilometre 3.0)
             [ "3 km"
             ]
  , examples (simple Mile 8)
             [ "8 milles"
             ]
  , examples (simple Metre 9)
             [ "9m"
             , "9 metres"
             , "9 m"
             ]
  , examples (simple Metre 1)
             [ "1m"
             , "1 metre"
             ]
  , examples (simple Centimetre 2)
             [ "2cm"
             , "2 centÃmetres"
             , "2 cm"
             ]
  ]


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

import Data.String
import Prelude

import Duckling.Dimensions.Types
import Duckling.Distance.Helpers
import qualified Duckling.Distance.Types as TDistance
import Duckling.Types

ruleLatentDistKm :: Rule
ruleLatentDistKm = Rule
  { name = "<latent dist> km"
  , pattern =
    [ dimension Distance
    , regex "(k|qu)(il(o|Ã²|Ã³))?m?(etre)?s?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just $ Token Distance $ withUnit TDistance.Kilometre dd
      _ -> Nothing
  }

ruleDistMeters :: Rule
ruleDistMeters = Rule
  { name = "<dist> meters"
  , pattern =
    [ dimension Distance
    , regex "m(etres?)?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just $ Token Distance $ withUnit TDistance.Metre dd
      _ -> Nothing
  }

ruleDistCentimeters :: Rule
ruleDistCentimeters = Rule
  { name = "<dist> centimeters"
  , pattern =
    [ dimension Distance
    , regex "(cm|cent(Ã|i)m(etres?))"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just $ Token Distance $ withUnit TDistance.Centimetre dd
      _ -> Nothing
  }

ruleDistMiles :: Rule
ruleDistMiles = Rule
  { name = "<dist> miles"
  , pattern =
    [ dimension Distance
    , regex "milles?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just $ Token Distance $ withUnit TDistance.Mile dd
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDistCentimeters
  , ruleDistMeters
  , ruleDistMiles
  , ruleLatentDistKm
  ]


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

import Data.String
import Prelude

import Duckling.Distance.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types

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

allExamples :: [Example]
allExamples = concat
  [ examples (simple Kilometre 3)
             [ "3 kilometry"
             , "3 km"
             , "3km"
             ]
  , examples (simple Mile 8)
             [ "8 mil"
             , "osm mil"
             ]
  , examples (simple Metre 1)
             [ "1m"
             , "1 metr"
             ]
  , examples (simple Metre 2)
             [ "2m"
             , "2 metry"
             ]
  , examples (simple Metre 9)
             [ "9m"
             , "9 metrÅ¯"
             ]
  , examples (simple Centimetre 1)
             [ "1cm"
             , "1 centimetr"
             ]
  , examples (simple Centimetre 2)
             [ "2cm"
             , "2 centimetry"
             ]
  , examples (simple Centimetre 9)
             [ "9cm"
             , "9 centimetrÅ¯"
             ]
  ]


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

import Data.String
import Prelude

import Duckling.Dimensions.Types
import Duckling.Distance.Helpers
import Duckling.Types
import qualified Duckling.Distance.Types as TDistance

ruleLatentDistKm :: Rule
ruleLatentDistKm = Rule
  { name = "<latent dist> km"
  , pattern =
    [ dimension Distance
    , regex "km|kilometr(y|\x016f)?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Kilometre dd
      _ -> Nothing
  }

ruleDistMeters :: Rule
ruleDistMeters = Rule
  { name = "<dist> meters"
  , pattern =
    [ dimension Distance
    , regex "metr(y|\x016f)?|m"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Metre dd
      _ -> Nothing
  }

ruleDistCentimeters :: Rule
ruleDistCentimeters = Rule
  { name = "<dist> centimeters"
  , pattern =
    [ dimension Distance
    , regex "cm|centimetr(y|\x016f)?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Centimetre dd
      _ -> Nothing
  }

ruleDistMiles :: Rule
ruleDistMiles = Rule
  { name = "<dist> miles"
  , pattern =
    [ dimension Distance
    , regex "mil"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Mile dd
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDistCentimeters
  , ruleDistMeters
  , ruleDistMiles
  , ruleLatentDistKm
  ]


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

import Data.String
import Prelude

import Duckling.Distance.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types

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

allExamples :: [Example]
allExamples = concat
  [ examples (simple Kilometre 3)
             [ "3 kilometer"
             , "3 km"
             , "3km"
             , "3,0 km"
             ]
  , examples (simple Mile 8)
             [ "acht meilen"
             , "8 meilen"
             ]
  , examples (simple Metre 9)
             [ "9m"
             ]
  , examples (simple Centimetre 2)
             [ "2cm"
             , "2 zentimeter"
             ]
  , examples (simple Inch 5)
             [ "5''"
             , "fÃ¼nf zoll"
             , "5\""
             ]
  , examples (simple Metre 1.87)
             [ "1,87 meter"
             ]
  , examples (between Kilometre (3, 5))
             [ "zwischen 3 und 5 kilometern"
             , "von 3km bis 5km"
             , "um die 3-5 kilometer"
             , "etwa 3km-5km"
             , "3-5 kilometer"
             ]
  , examples (under Mile 3.5)
             [ "unter 3,5 meilen"
             , "weniger als 3,5meilen"
             --, "niedriger als dreikommafÃ¼nf meilen"
             ]
  , examples (above Inch 5)
             [ "mehr als fÃ¼nf zoll"
             , "mindestens 5''"
             , "Ã¼ber 5\""
             ]
  , examples (between Millimetre (5, 6))
             [ "zwischen 5 und sechs Millimetern"
             , "zwischen 5 und sechs millimeter"
             , "5-6 mm"
             ]
  ]


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

import Data.String
import Data.Text (Text)
import Prelude

import Duckling.Dimensions.Types
import Duckling.Distance.Helpers
import Duckling.Distance.Types (DistanceData(..))
import qualified Duckling.Distance.Types as TDistance
import Duckling.Numeral.Helpers
import Duckling.Numeral.Types (NumeralData(..))
import qualified Duckling.Numeral.Types as TNumeral
import Duckling.Types

distances :: [(Text, String, TDistance.Unit)]
distances =
  [ -- Imperial
    ("miles", "meilen?", TDistance.Mile)
  , ("inch", "(\"|''|zoll)", TDistance.Inch)
              -- Metric
  , ("km", "k(ilo)?m(etern?)?", TDistance.Kilometre)
  , ("meters", "m(etern?)?", TDistance.Metre)
  , ("centimeters", "(cm|[zc]entimetern?)", TDistance.Centimetre)
  , ("millimeters", "(mm|millimetern?)", TDistance.Millimetre)
  ]

rulePrecision :: Rule
rulePrecision = Rule
  { name = "about|exactly <dist>"
  , pattern =
    [ regex "genau|exakt|prÃ¤zise|ungefÃ¤hr|(in )?etwa|nahe?( an)?|um( die)?|fast|rund|gut"
    , dimension Distance
    ]
  , prod = \case
      (_:token:_) -> Just token
      _ -> Nothing
  }

ruleDistances :: [Rule]
ruleDistances = map go distances
  where
    go :: (Text, String, TDistance.Unit) -> Rule
    go (name, regexPattern, u) = Rule
      { name = name
      , pattern = [ dimension Distance, regex regexPattern ]
      , prod = \case
          (Token Distance dd:_) -> Just . Token Distance $ withUnit u dd
          _ -> Nothing
      }

ruleIntervalBetweenNumeral :: Rule
ruleIntervalBetweenNumeral = Rule
  { name = "between|from <numeral> to|and <dist>"
  , pattern =
    [ regex "zwischen|von"
    , Predicate isPositive
    , regex "bis|und"
    , Predicate isSimpleDistance
    ]
  , prod = \case
      (_:
       Token Numeral NumeralData{TNumeral.value = from}:
       _:
       Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u}:
       _) | from < to ->
        Just . Token Distance . withInterval (from, to) $ unitOnly u
      _ -> Nothing
  }

ruleIntervalBetween :: Rule
ruleIntervalBetween = Rule
  { name = "between|from <dist> to|and <dist>"
  , pattern =
    [ regex "zwischen|von"
    , Predicate isSimpleDistance
    , regex "und|bis"
    , Predicate isSimpleDistance
    ]
  , prod = \case
      (_:
       Token Distance DistanceData{TDistance.value = Just from, TDistance.unit = Just u1}:
       _:
       Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u2}:
       _) | from < to && u1 == u2 ->
        Just . Token Distance . withInterval (from, to) $ unitOnly u1
      _ -> Nothing
  }

ruleIntervalNumeralDash :: Rule
ruleIntervalNumeralDash = Rule
  { name = "<numeral> - <dist>"
  , pattern =
    [ Predicate isPositive
    , regex "-"
    , Predicate isSimpleDistance
    ]
  , prod = \case
      (Token Numeral NumeralData{TNumeral.value = from}:
       _:
       Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u}:
       _) | from < to ->
         Just . Token Distance . withInterval (from, to) $ unitOnly u
      _ -> Nothing
  }

ruleIntervalDash :: Rule
ruleIntervalDash = Rule
  { name = "<dist> - <dist>"
  , pattern =
    [ Predicate isSimpleDistance
    , regex "-"
    , Predicate isSimpleDistance
    ]
  , prod = \case
      (Token Distance DistanceData{TDistance.value = Just from, TDistance.unit = Just u1}:
       _:
       Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u2}:
       _) | from < to && u1 == u2 ->
        Just . Token Distance . withInterval (from, to) $ unitOnly u1
      _ -> Nothing
  }

ruleIntervalMax :: Rule
ruleIntervalMax = Rule
  { name = "under/less/lower/no more than <dist>"
  , pattern =
    [ regex "unter|hÃ¶chstens|maximal|(weniger|nicht mehr) als"
    , Predicate isSimpleDistance
    ]
  , prod = \case
      (_:
       Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u}:
       _) -> Just . Token Distance . withMax to $ unitOnly u
      _ -> Nothing
  }

ruleIntervalMin :: Rule
ruleIntervalMin = Rule
  { name = "over/above/at least/more than <dist>"
  , pattern =
    [ regex "Ã¼ber|(mehr|nicht weniger) als|mindestens|wenigstens|minimal"
    , Predicate isSimpleDistance
    ]
  , prod = \case
      (_:
       Token Distance DistanceData{TDistance.value = Just to, TDistance.unit = Just u}:
       _) -> Just . Token Distance . withMin to $ unitOnly u
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleIntervalBetweenNumeral
  , ruleIntervalBetween
  , ruleIntervalMax
  , ruleIntervalMin
  , ruleIntervalNumeralDash
  , ruleIntervalDash
  , rulePrecision
  ]
    ++ ruleDistances


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

import Data.String
import Prelude

import Duckling.Distance.Types
import Duckling.Testing.Types

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

allExamples :: [Example]
allExamples = concat
  [ examples (simple Kilometre 3)
             [ "3 kilometers"
             , "3 km"
             , "3km"
             , "3k"
             , "3.0 km"
             ]
  , examples (simple Mile 8)
             [ "8 miles"
             , "eight mile"
             , "8 mi"
             ]
  , examples (simple M 9)
             [ "9m"
             ]
  , examples (simple Centimetre 2)
             [ "2cm"
             , "2 centimeters"
             ]
  , examples (simple Inch 5)
             [ "5 in"
             , "5''"
             , "five inches"
             , "5\""
             ]
  , examples (simple Metre 1.87)
             [ "1.87 meters"
             ]
  -- Composite values:
  , examples (simple Inch 94)
             [ "7 feet and 10 inches"
             , "7 feet, 10 inches"
             , "7 feet 10 inches"
             ]
  , examples (simple Metre 2001)
             [ "2 km and 1 meter"
             , "2 kilometer, 1 metre"
             , "2 kilometer 1 metre"
             ]
  , examples (simple Inch 166)
             [ "2 yards 7 ft 10 inches"
             , "2 yds, 7 feet and 10 inches"
             , "2 yards, 7 feet, 10 in"
             ]
  , examples (simple Foot 13)
             [ "2 yards and 7 feet"
             , "2 yards, 7 feet"
             , "2 yd 7'"
             ]
  , examples (simple Centimetre 1000806)
             [ "10 kms 8 metres 6 cm"
             , "10 kms, 8 meters, 6 cm"
             , "10 kms, 8 meters and 6 centimeters"
--             , "10 kms, 8 meters, and 6 cm" -- Oxford comma not supported
             ]
  , examples (simple Metre 1.3048)
             [ "1 meter and 1 foot"
             ]
  , examples (simple Kilometre 2.609344)
             [ "1 kilometer and 1 mile"
             ]
  , examples (simple M 3)
             -- The original, ambiguous "m" unit is preserved
             [ "3m"
             ]
  , examples (simple Centimetre 305)
             -- The ambiguous "m" unit is inferred as "meteres"
             [ "3m and 5cm"
             ]
  , examples (simple Foot 5281)
             -- The ambiguous "m" unit is inferred as "miles"
             [ "1m and 1ft"
             ]
  -- Ranges:
  , examples (between Kilometre (3, 5))
             [ "between 3 and 5 kilometers"
             , "from 3km to 5km"
             , "around 3-5 kilometers"
             , "about 3km-5km"
             , "3-5 kilometers"
             ]
  , examples (under Mile 3.5)
             [ "under 3.5 miles"
             , "less than 3.5mi"
             , "lower than three point five miles"
             ]
  , examples (above Inch 5)
             [ "more than five inches"
             , "at least 5''"
             , "over 5\""
             , "above 5 in"
             ]
  , examples (between Millimetre (5, 6))
             [ "between 5 and six millimeters"
             , "between 5 and six millimetres"
             , "5-6 mm"
             ]
  ]


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

import Data.String
import Data.Text (Text)
import Prelude

import Duckling.Dimensions.Types
import Duckling.Distance.Helpers
import Duckling.Distance.Types (DistanceData(..))
import Duckling.Numeral.Helpers
import Duckling.Numeral.Types (NumeralData (..))
import Duckling.Types
import qualified Duckling.Distance.Types as TDist
import qualified Duckling.Numeral.Types as TNumeral

distances :: [(Text, String, TDist.Unit)]
distances = [ -- Imperial
              ("miles", "mi(le(s)?)?", TDist.Mile)
            , ("yard", "y(ar)?ds?", TDist.Yard)
            , ("feet", "('|f(oo|ee)?ts?)", TDist.Foot)
            , ("inch", "(\"|''|in(ch(es)?)?)", TDist.Inch)
              -- Metric
            , ("km", "k(ilo)?m?(et(er|re))?s?", TDist.Kilometre)
            , ("meters", "met(er|re)s?", TDist.Metre)
            , ("centimeters", "cm|centimet(er|re)s?", TDist.Centimetre)
            , ("millimeters", "mm|millimet(er|re)s?", TDist.Millimetre)
              -- Ambiguous
            , ("m (miles or meters)", "m", TDist.M)
            ]

rulePrecision :: Rule
rulePrecision = Rule
  { name = "about|exactly <dist>"
  , pattern =
    [ regex "exactly|precisely|about|approx(\\.|imately)?|close to| near( to)?|around|almost"
    , dimension Distance
    ]
  , prod = \case
      (_:token:_) -> Just token
      _ -> Nothing
  }

ruleDistances :: [Rule]
ruleDistances = map go distances
  where
    go :: (Text, String, TDist.Unit) -> Rule
    go (name, regexPattern, u) = Rule
      { name = name
      , pattern = [ dimension Distance, regex regexPattern ]
      , prod = \case
          (Token Distance dd:_) -> Just . Token Distance $ withUnit u dd
          _ -> Nothing
      }

ruleIntervalBetweenNumeral :: Rule
ruleIntervalBetweenNumeral = Rule
  { name = "between|from <numeral> to|and <dist>"
  , pattern =
    [ regex "between|from"
    , Predicate isPositive
    , regex "to|and"
    , Predicate isSimpleDistance
    ]
  , prod = \case
      (_:
       Token Numeral NumeralData{TNumeral.value = from}:
       _:
       Token Distance DistanceData{TDist.value=Just to, TDist.unit=Just u}:
       _) | from < to ->
        Just . Token Distance . withInterval (from, to) $ unitOnly u
      _ -> Nothing
  }

ruleIntervalBetween :: Rule
ruleIntervalBetween = Rule
  { name = "between|from <dist> to|and <dist>"
  , pattern =
    [ regex "between|from"
    , Predicate isSimpleDistance
    , regex "to|and"
    , Predicate isSimpleDistance
    ]
  , prod = \case
      (_:
       Token Distance DistanceData{TDist.value=Just from, TDist.unit=Just u1}:
       _:
       Token Distance DistanceData{TDist.value=Just to, TDist.unit=Just u2}:
       _) | from < to && u1 == u2 ->
        Just . Token Distance . withInterval (from, to) $ unitOnly u1
      _ -> Nothing
  }

ruleIntervalNumeralDash :: Rule
ruleIntervalNumeralDash = Rule
  { name = "<numeral> - <dist>"
  , pattern =
    [ Predicate isPositive
    , regex "-"
    , Predicate isSimpleDistance
    ]
  , prod = \case
      (Token Numeral NumeralData{TNumeral.value = from}:
       _:
       Token Distance DistanceData{TDist.value=Just to, TDist.unit=Just u}:
       _) | from < to ->
         Just . Token Distance . withInterval (from, to) $ unitOnly u
      _ -> Nothing
  }

ruleIntervalDash :: Rule
ruleIntervalDash = Rule
  { name = "<dist> - <dist>"
  , pattern =
    [ Predicate isSimpleDistance
    , regex "-"
    , Predicate isSimpleDistance
    ]
  , prod = \case
      (Token Distance DistanceData{TDist.value=Just from, TDist.unit=Just u1}:
       _:
       Token Distance DistanceData{TDist.value=Just to, TDist.unit=Just u2}:
       _) | from < to && u1 == u2 ->
        Just . Token Distance . withInterval (from, to) $ unitOnly u1
      _ -> Nothing
  }

ruleIntervalMax :: Rule
ruleIntervalMax = Rule
  { name = "under/less/lower/no more than <dist>"
  , pattern =
    [ regex "under|(less|lower|not? more) than"
    , Predicate isSimpleDistance
    ]
  , prod = \case
      (_:
       Token Distance DistanceData{TDist.value=Just to, TDist.unit=Just u}:
       _) -> Just . Token Distance . withMax to $ unitOnly u
      _ -> Nothing
  }

ruleIntervalMin :: Rule
ruleIntervalMin = Rule
  { name = "over/above/at least/more than <dist>"
  , pattern =
    [ regex "over|above|at least|more than"
    , Predicate isSimpleDistance
    ]
  , prod = \case
      (_:
       Token Distance DistanceData{TDist.value=Just to, TDist.unit=Just u}:
       _) -> Just . Token Distance . withMin to $ unitOnly u
      _ -> Nothing
  }

-- | NOTE: Oxford comma is not supported.
ruleCompositeDistanceCommasAnd :: Rule
ruleCompositeDistanceCommasAnd = Rule
  { name = "composite <distance> (with ,/and)"
  , pattern =
    [ Predicate isSimpleDistance
    , regex ",|and"
    , Predicate isSimpleDistance
    ]
  , prod = \case
      (Token Distance DistanceData{TDist.value=Just v1, TDist.unit=Just u1}:
       _:
       Token Distance DistanceData{TDist.value=Just v2, TDist.unit=Just u2}:
       _) | u1 /= u2 && v1 > 0 && v2 > 0 -> Token Distance <$>
                                              distanceSum v1 u1 v2 u2
      _ -> Nothing
  }

ruleCompositeDistance :: Rule
ruleCompositeDistance = Rule
  { name = "composite <distance>"
  , pattern =
    [ Predicate isSimpleDistance
    , Predicate isSimpleDistance
    ]
  , prod = \case
      (Token Distance DistanceData{TDist.value=Just v1, TDist.unit=Just u1}:
       Token Distance DistanceData{TDist.value=Just v2, TDist.unit=Just u2}:
       _) | u1 /= u2 && v1 > 0 && v2 > 0 -> Token Distance <$>
                                              distanceSum v1 u1 v2 u2
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleIntervalBetweenNumeral
  , ruleIntervalBetween
  , ruleIntervalMax
  , ruleIntervalMin
  , ruleIntervalNumeralDash
  , ruleIntervalDash
  , rulePrecision
  , ruleCompositeDistanceCommasAnd
  , ruleCompositeDistance
  ] ++ ruleDistances


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

import Data.String
import Prelude

import Duckling.Distance.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types

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

allExamples :: [Example]
allExamples = concat
  [ examples (simple Kilometre 3)
             [ "3 kilÃ³metros"
             , "3 kilometros"
             , "3 km"
             , "3km"
             , "3k"
             ]
  , examples (simple Kilometre 3.0)
             [ "3,0 km"
             ]
  , examples (simple Mile 8)
             [ "8 miles"
             ]
  , examples (simple Metre 9)
             [ "9m"
             , "9 metros"
             ]
  , examples (simple Centimetre 2)
             [ "2cm"
             , "2 centÃmetros"
             ]
  ]


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

import Data.String
import Prelude

import Duckling.Dimensions.Types
import Duckling.Distance.Helpers
import qualified Duckling.Distance.Types as TDistance
import Duckling.Types

ruleLatentDistKm :: Rule
ruleLatentDistKm = Rule
  { name = "<latent dist> km"
  , pattern =
    [ dimension Distance
    , regex "k(il(Ã³|o))?m?(etro)?s?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Kilometre dd
      _ -> Nothing
  }

ruleDistMeters :: Rule
ruleDistMeters = Rule
  { name = "<dist> meters"
  , pattern =
    [ dimension Distance
    , regex "m(etros?)?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Metre dd
      _ -> Nothing
  }

ruleDistCentimeters :: Rule
ruleDistCentimeters = Rule
  { name = "<dist> centimeters"
  , pattern =
    [ dimension Distance
    , regex "(cm|cent(Ã|i)m(etros?))"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Centimetre dd
      _ -> Nothing
  }

ruleDistMiles :: Rule
ruleDistMiles = Rule
  { name = "<dist> miles"
  , pattern =
    [ dimension Distance
    , regex "miles?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Mile dd
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDistCentimeters
  , ruleDistMeters
  , ruleDistMiles
  , ruleLatentDistKm
  ]


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

import Prelude
import Data.String

import Duckling.Distance.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 (simple Kilometre 3)
             [ "3 kilomÃ¨tres"
             , "3 kilometres"
             , "3 km"
             , "3km"
             , "3k"
             ]
  , examples (simple Kilometre 3.0)
             [ "3,0 km"
             ]
  , examples (simple Mile 8)
             [ "8 miles"
             ]
  , examples (simple Metre 9)
             [ "9 metres"
             , "9m"
             ]
  , examples (simple Centimetre 2)
             [ "2cm"
             , "2 centimetres"
             ]
  ]


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

import Data.String
import Prelude

import Duckling.Dimensions.Types
import Duckling.Distance.Helpers
import qualified Duckling.Distance.Types as TDistance
import Duckling.Types

ruleLatentDistKm :: Rule
ruleLatentDistKm = Rule
  { name = "<latent dist> km"
  , pattern =
    [ dimension Distance
    , regex "k(ilo)?m?((e|Ã©|Ã¨)tre)?s?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Kilometre dd
      _ -> Nothing
  }

ruleDistMeters :: Rule
ruleDistMeters = Rule
  { name = "<dist> meters"
  , pattern =
    [ dimension Distance
    , regex "m((e|Ã©|Ã¨)tres?)?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Metre dd
      _ -> Nothing
  }

ruleDistCentimeters :: Rule
ruleDistCentimeters = Rule
  { name = "<dist> centimeters"
  , pattern =
    [ dimension Distance
    , regex "cm|centim(e|Ã©|Ã¨)tres?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Centimetre dd
      _ -> Nothing
  }

ruleDistMiles :: Rule
ruleDistMiles = Rule
  { name = "<dist> miles"
  , pattern =
    [ dimension Distance
    , regex "miles?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Mile dd
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDistCentimeters
  , ruleDistMeters
  , ruleDistMiles
  , ruleLatentDistKm
  ]


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

import Prelude
import Data.String

import Duckling.Distance.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types

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

allExamples :: [Example]
allExamples = concat
  [ examples (simple Kilometre 3)
             [ "3 cilimÃ©adair"
             , "3 km"
             , "3km"
             , "3k"
             ]
  , examples (simple Kilometre 3.0)
             [ "3.0 km"
             ]
  , examples (simple Mile 8)
             [ "8 mhÃle"
             , "8 mÃle"
             ]
  , examples (simple M 9)
             [ "9m"
             ]
  , examples (simple Centimetre 2)
             [ "2cm"
             , "2 cheintimÃ©adar"
             ]
  ]


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

import Data.String
import Prelude

import Duckling.Dimensions.Types
import Duckling.Distance.Helpers
import qualified Duckling.Distance.Types as TDistance
import Duckling.Types

ruleDistMeters :: Rule
ruleDistMeters = Rule
  { name = "<dist> meters"
  , pattern =
    [ dimension Distance
    , regex "mh?(e|Ã©)adai?r"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Metre dd
      _ -> Nothing
  }

ruleDistCentimeters :: Rule
ruleDistCentimeters = Rule
  { name = "<dist> centimeters"
  , pattern =
    [ dimension Distance
    , regex "(c\\.?m\\.?|g?ch?eintimh?(e|Ã©)adai?r)"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Centimetre dd
      _ -> Nothing
  }

ruleDistMiles :: Rule
ruleDistMiles = Rule
  { name = "<dist> miles"
  , pattern =
    [ dimension Distance
    , regex "mh?(Ã|i)lt?e"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Mile dd
      _ -> Nothing
  }

ruleLatentDistKm :: Rule
ruleLatentDistKm = Rule
  { name = "<latent dist> km"
  , pattern =
    [ dimension Distance
    , regex "(k\\.?(m\\.?)?|g?ch?ilim(e|Ã©)adai?r)"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Kilometre dd
      _ -> Nothing
  }

ruleLatentDistTroigh :: Rule
ruleLatentDistTroigh = Rule
  { name = "<latent dist> troigh"
  , pattern =
    [ dimension Distance
    , regex "('|d?th?roi[tg]he?|tr\\.?)"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Foot dd
      _ -> Nothing
  }

ruleLatentDistOrlach :: Rule
ruleLatentDistOrlach = Rule
  { name = "<latent dist> orlach"
  , pattern =
    [ dimension Distance
    , regex "(''|([nth]-?)?orl(ach|aigh|a(Ã|i)|\\.))"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Inch dd
      _ -> Nothing
  }

ruleDistMAmbiguousMilesOrMeters :: Rule
ruleDistMAmbiguousMilesOrMeters = Rule
  { name = "<dist> m (ambiguous miles or meters)"
  , pattern =
    [ dimension Distance
    , regex "m"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.M dd
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDistCentimeters
  , ruleDistMAmbiguousMilesOrMeters
  , ruleDistMeters
  , ruleDistMiles
  , ruleLatentDistKm
  , ruleLatentDistOrlach
  , ruleLatentDistTroigh
  ]


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

import Prelude
import Data.String

import Duckling.Distance.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types

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

allExamples :: [Example]
allExamples = concat
  [ examples (simple Kilometre 3)
             [ "3 kilometra"
             , "3 km"
             , "3km"
             , "3k"
             ]
  , examples (simple Kilometre 3.0)
             [ "3,0 km"
             ]
  , examples (simple Mile 8)
             [ "8 milja"
             ]
  , examples (simple M 9)
             [ "9m"
             ]
  , examples (simple Centimetre 2)
             [ "2cm"
             , "2 centimetra"
             ]
  ]


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

import Data.String
import Prelude

import Duckling.Dimensions.Types
import Duckling.Distance.Helpers
import Duckling.Types
import qualified Duckling.Distance.Types as TDistance

ruleLatentDistKm :: Rule
ruleLatentDistKm = Rule
  { name = "<latent dist> km"
  , pattern =
    [ dimension Distance
    , regex "k(ilo)?m?(eta?r)?a?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Kilometre dd
      _ -> Nothing
  }

ruleDistMetar :: Rule
ruleDistMetar = Rule
  { name = "<dist> metar"
  , pattern =
    [ dimension Distance
    , regex "metara?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Metre dd
      _ -> Nothing
  }

ruleDistCentimetar :: Rule
ruleDistCentimetar = Rule
  { name = "<dist> centimetar"
  , pattern =
    [ dimension Distance
    , regex "cm|centimeta?ra?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Centimetre dd
      _ -> Nothing
  }

ruleDistMilja :: Rule
ruleDistMilja = Rule
  { name = "<dist> milja"
  , pattern =
    [ dimension Distance
    , regex "milja"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) -> Just . Token Distance $ withUnit TDistance.Mile dd
      _ -> Nothing
  }

ruleDistMAmbiguousMilesOrMeters :: Rule
ruleDistMAmbiguousMilesOrMeters = Rule
  { name = "<dist> m (ambiguous miles or meters)"
  , pattern =
    [ dimension Distance
    , regex "m"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) -> Just . Token Distance $ withUnit TDistance.M dd
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDistCentimetar
  , ruleDistMAmbiguousMilesOrMeters
  , ruleDistMetar
  , ruleDistMilja
  , ruleLatentDistKm
  ]


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

import Data.String
import Prelude

import Duckling.Distance.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types

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

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

allExamples :: [Example]
allExamples = concat
  [ examples (simple Kilometre 3)
             [ "3 kilometri"
             , "3 chilometri"
             , "3 km"
             , "3km"
             ]
  , examples (simple Kilometre 3.0)
             [ "3,0 km"
             ]
  , examples (simple Mile 8)
             [ "8 miglia"
             ]
  , examples (simple Metre 9)
             [ "9 metri"
             , "9m"
             ]
  , examples (simple Centimetre 2)
             [ "2cm"
             , "2 centimetri"
             ]
  ]


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

import Data.String
import Prelude

import Duckling.Dimensions.Types
import Duckling.Distance.Helpers
import Duckling.Types
import qualified Duckling.Distance.Types as TDistance

ruleLatentDistKm :: Rule
ruleLatentDistKm = Rule
  { name = "<latent dist> km"
  , pattern =
    [ dimension Distance
    , regex "km|(k|ch)(ilo)?m?(etr)o?i?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Kilometre dd
      _ -> Nothing
  }

ruleDistMeters :: Rule
ruleDistMeters = Rule
  { name = "<dist> meters"
  , pattern =
    [ dimension Distance
    , regex "metro?i?|m"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Metre dd
      _ -> Nothing
  }

ruleDistCentimeters :: Rule
ruleDistCentimeters = Rule
  { name = "<dist> centimeters"
  , pattern =
    [ dimension Distance
    , regex "cm|centimetro?i?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Centimetre dd
      _ -> Nothing
  }

ruleDistMiles :: Rule
ruleDistMiles = Rule
  { name = "<dist> miles"
  , pattern =
    [ dimension Distance
    , regex "miglio?a?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Mile dd
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDistCentimeters
  , ruleDistMeters
  , ruleDistMiles
  , ruleLatentDistKm
  ]


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

import Prelude
import Data.String

import Duckling.Distance.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types

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

allExamples :: [Example]
allExamples = concat
  [ examples (simple Kilometre 3)
             [ "3 kilometer"
             , "3 km"
             , "3km"
             , "3k"
             ]
  , examples (simple Kilometre 3.0)
             [ "3,0 km"
             , "3,0km"
             ]
  , examples (simple Mile 8)
             [ "8 mijl"
             ]
  , examples (simple Metre 9)
             [ "9m"
             , "9 m"
             , "9 meter"
             ]
  , examples (simple Centimetre 2)
             [ "2cm"
             , "2 cm"
             , "2 centimeter"
             ]
  ]


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

import Data.String
import Prelude

import Duckling.Dimensions.Types
import Duckling.Distance.Helpers
import qualified Duckling.Distance.Types as TDistance
import Duckling.Types

ruleLatentDistKm :: Rule
ruleLatentDistKm = Rule
  { name = "<latent dist> km"
  , pattern =
    [ dimension Distance
    , regex "k(ilo)?m?(eter)?s?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Kilometre dd
      _ -> Nothing
  }

ruleDistMeters :: Rule
ruleDistMeters = Rule
  { name = "<dist> meters"
  , pattern =
    [ dimension Distance
    , regex "m(eter)?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Metre dd
      _ -> Nothing
  }

ruleDistCentimeters :: Rule
ruleDistCentimeters = Rule
  { name = "<dist> centimeters"
  , pattern =
    [ dimension Distance
    , regex "(c(enti)?m(eter)?s?)"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Centimetre dd
      _ -> Nothing
  }

ruleDistMiles :: Rule
ruleDistMiles = Rule
  { name = "<dist> miles"
  , pattern =
    [ dimension Distance
    , regex "mijl?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Mile dd
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDistCentimeters
  , ruleDistMeters
  , ruleDistMiles
  , ruleLatentDistKm
  ]


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

import Prelude
import Data.String

import Duckling.Distance.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types

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

allExamples :: [Example]
allExamples = concat
  [ examples (simple Kilometre 3)
             [ "3 kilÃ³metros"
             , "3 kilometros"
             , "3 km"
             , "3km"
             , "3k"
             ]
  , examples (simple Kilometre 3.0)
             [ "3,0 km"
             ]
  , examples (simple Mile 8)
             [ "8 milhas"
             ]
  , examples (simple Metre 9)
             [ "9m"
             , "9 metros"
             ]
  , examples (simple Centimetre 2)
             [ "2cm"
             , "2 centÃmetros"
             ]
  ]


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

import Data.String
import Prelude

import Duckling.Dimensions.Types
import Duckling.Distance.Helpers
import qualified Duckling.Distance.Types as TDistance
import Duckling.Types

ruleLatentDistKm :: Rule
ruleLatentDistKm = Rule
  { name = "<latent dist> km"
  , pattern =
    [ dimension Distance
    , regex "k(il(Ã³|o))?m?(etro)?s?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Kilometre dd
      _ -> Nothing
  }

ruleDistMeters :: Rule
ruleDistMeters = Rule
  { name = "<dist> meters"
  , pattern =
    [ dimension Distance
    , regex "m(etros?)?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Metre dd
      _ -> Nothing
  }

ruleDistCentimeters :: Rule
ruleDistCentimeters = Rule
  { name = "<dist> centimeters"
  , pattern =
    [ dimension Distance
    , regex "(cm|cent(Ã|i)m(etros?))"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Centimetre dd
      _ -> Nothing
  }

ruleDistMiles :: Rule
ruleDistMiles = Rule
  { name = "<dist> miles"
  , pattern =
    [ dimension Distance
    , regex "milhas?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Mile dd
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDistCentimeters
  , ruleDistMeters
  , ruleDistMiles
  , ruleLatentDistKm
  ]


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

import Prelude
import Data.String

import Duckling.Distance.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types

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

allExamples :: [Example]
allExamples = concat
  [ examples (simple Kilometre 3)
             [ "3 kilometri"
             , "3 km"
             , "3km"
             , "3,0 km"
             ]
  , examples (simple Mile 8)
             [ "8 mile"
             ]
  , examples (simple Metre 9)
             [ "9m"
             , "9 m"
             ]
  , examples (simple Centimetre 2)
             [ "2cm"
             , "2 centimetri"
             ]
  , examples (simple Foot 10)
             [ "zece picioare"
             ]
  , examples (simple Foot 20)
             [ "20 de picioare"
             ]
  ]


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

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

import Duckling.Dimensions.Types
import Duckling.Distance.Helpers
import Duckling.Regex.Types
import Duckling.Types
import qualified Duckling.Distance.Types as TDistance

unitMap :: HashMap Text TDistance.Unit
unitMap = HashMap.fromList
  [ ( "cm", TDistance.Centimetre )
  , ( "centimetri", TDistance.Centimetre )
  , ( "centimetru", TDistance.Centimetre )
  , ( "picior", TDistance.Foot )
  , ( "picioare", TDistance.Foot )
  , ( "inch", TDistance.Inch )
  , ( "inchi", TDistance.Inch )
  , ( "inci", TDistance.Inch )
  , ( "km", TDistance.Kilometre )
  , ( "kilometri", TDistance.Kilometre )
  , ( "kilometru", TDistance.Kilometre )
  , ( "m", TDistance.Metre )
  , ( "metri", TDistance.Metre )
  , ( "metru", TDistance.Metre )
  , ( "mila", TDistance.Mile )
  , ( "milÄƒ", TDistance.Mile )
  , ( "mile", TDistance.Mile )
  , ( "y", TDistance.Yard )
  , ( "yar", TDistance.Yard )
  , ( "yard", TDistance.Yard )
  , ( "yarzi", TDistance.Yard )
  , ( "yd", TDistance.Yard )
  , ( "yzi", TDistance.Yard )
  ]

ruleLatentDistUnit :: Rule
ruleLatentDistUnit = Rule
  { name = "<latent dist> foot/inch/yard/meter/kilometer/centimeter"
  , pattern =
    [ dimension Distance
    , regex "(inc(hi?|i)|(centi|kilo)?metr[iu]|mil[eaÄƒ]|[ck]?m|picio(are|r)|y(ar)?(zi|d)?)"
    ]
  , prod = \case
      (Token Distance dd:
       Token RegexMatch (GroupMatch (match:_)):
       _) -> do
         x <- HashMap.lookup (Text.toLower match) unitMap
         Just . Token Distance $ withUnit x dd
      _ -> Nothing
  }

ruleLatentDistDeUnit :: Rule
ruleLatentDistDeUnit = Rule
  { name = "<latent dist> foot/inch/yard/meter/kilometer/centimeter"
  , pattern =
    [ dimension Distance
    , regex "de (inc(hi?|i)|(centi|kilo)?metr[iu]|mil[eaÄƒ]|[ck]?m|picio(are|r)|y(ar)?(zi|d)?)"
    ]
  , prod = \case
      (Token Distance dd:
       Token RegexMatch (GroupMatch (match:_)):
       _) -> do
         x <- HashMap.lookup (Text.toLower match) unitMap
         Just . Token Distance $ withUnit x dd
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleLatentDistUnit
  , ruleLatentDistDeUnit
  ]


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

import Data.String
import Prelude

import Duckling.Distance.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types

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

allExamples :: [Example]
allExamples = concat
  [ examples (simple Kilometre 3)
             [ "3 kilometer"
             , "3 km"
             , "3km"
             , "3k"
             ]
  , examples (simple Kilometre 30)
             [ "3 mil"
             , "tre mil"
             , "nÃ¥gra fÃ¥ mil"
             ]
  , examples (simple Kilometre 3.0)
             [ "3,0 km"
             ]
  , examples (simple Metre 9)
             [ "9 meter"
             , "9m"
             ]
  , examples (simple Centimetre 2)
             [ "2cm"
             , "2 centimeter"
             ]
  ]


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

import Data.String
import Prelude

import Duckling.Dimensions.Types
import Duckling.Distance.Helpers
import Duckling.Distance.Types (DistanceData(..))
import Duckling.Types
import qualified Duckling.Distance.Types as TDistance

ruleLatentDistKm :: Rule
ruleLatentDistKm = Rule
  { name = "<latent dist> km"
  , pattern =
    [ dimension Distance
    , regex "k(ilo)?m?(eter)?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Kilometre dd
      _ -> Nothing
  }

ruleDistMeters :: Rule
ruleDistMeters = Rule
  { name = "<dist> meter"
  , pattern =
    [ dimension Distance
    , regex "m(eter)?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Metre dd
      _ -> Nothing
  }

ruleDistCentimeters :: Rule
ruleDistCentimeters = Rule
  { name = "<dist> centimeters"
  , pattern =
    [ dimension Distance
    , regex "cm|centimeter"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Centimetre dd
      _ -> Nothing
  }

ruleDistMils :: Rule
ruleDistMils = Rule
  { name = "<dist> mils"
  , pattern =
    [ dimension Distance
    , regex "mils?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd@DistanceData {TDistance.value = Just x}:_) ->
        Just . Token Distance . withValue (10 * x)
          $ withUnit TDistance.Kilometre dd
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDistCentimeters
  , ruleDistMeters
  , ruleDistMils
  , ruleLatentDistKm
  ]


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

import Data.String
import Prelude

import Duckling.Distance.Types
import Duckling.Locale
import Duckling.Resolve
import Duckling.Testing.Types

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

allExamples :: [Example]
allExamples = concat
  [ examples (simple Kilometre 3)
             [ "3 Kilometre"
             , "3 kilometre"
             , "3 km"
             , "3km"
             ]
  , examples (simple Kilometre 3.0)
             [ "3,0 km"
             ]
  , examples (simple Mile 8)
             [ "8 Mil"
             , "8 mil"
             ]
  , examples (simple Metre 9)
             [ "9 Metre"
             , "9 metre"
             , "9 m"
             , "9m"
             ]
  , examples (simple Centimetre 2)
             [ "2 Santimetre"
             , "2 santim"
             , "2 cm"
             , "2cm"
             ]
  ]


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

import Data.String
import Prelude

import Duckling.Dimensions.Types
import Duckling.Distance.Helpers
import qualified Duckling.Distance.Types as TDistance
import Duckling.Types

ruleLatentDistKm :: Rule
ruleLatentDistKm = Rule
  { name = "<latent dist> km"
  , pattern =
    [ dimension Distance
    , regex "k(ilo)?m?(etre)?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Kilometre dd
      _ -> Nothing
  }

ruleDistMeters :: Rule
ruleDistMeters = Rule
  { name = "<dist> meters"
  , pattern =
    [ dimension Distance
    , regex "m(etre)?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Metre dd
      _ -> Nothing
  }

ruleDistCentimeters :: Rule
ruleDistCentimeters = Rule
  { name = "<dist> centimeters"
  , pattern =
    [ dimension Distance
    , regex "cm|santim(etre)?"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Centimetre dd
      _ -> Nothing
  }

ruleDistMiles :: Rule
ruleDistMiles = Rule
  { name = "<dist> miles"
  , pattern =
    [ dimension Distance
    , regex "mil"
    ]
  , prod = \tokens -> case tokens of
      (Token Distance dd:_) ->
        Just . Token Distance $ withUnit TDistance.Mile dd
      _ -> Nothing
  }

rules :: [Rule]
rules =
  [ ruleDistCentimeters
  , ruleDistMeters
  , ruleDistMiles
  , ruleLatentDistKm
  ]


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

-- | This module shadows names of some units previously defined in
-- 'Duckling.Distance.Types'.
-- Therefore, the units from that module must be used qualified by module.
module Duckling.DistanceUnits.Types
  ( ContextualDistance (..)
  , toSystemUnit
  , toRawUnit
  ) where

import Data.Semigroup
import Data.Tuple.Extra (both)
import Prelude

import qualified Duckling.Distance.Types as TDist

-- | Supports deferred resolution of ambiguous units.
-- Note that this sum type cannot be simply replaced by
-- "Maybe (ContextualDistance Double DeferrableUnit)"
-- (with "Nothing" representing "Nonrelatable").
-- See "NOTE A" below.
data ContextualDistance
  = Nonrelatable
    -- ^ If two different ambiguous units were to be composed, then
    -- it would not be possible to decide which to resolve to. As of today,
    -- this won't happen since for now "M" is the lone ambiguous unit.
  | ContextualDistance Double DeferrableUnit

-- | This represents Units that have not yet been established as unrelatable.
-- The "Definite" constructor is purposely listed in front of "Ambiguous",
-- so that it will be preferred when taking choosing the "min" between
-- an "Ambiguous" and "Definite" unit.
data DeferrableUnit
  = Definite SystemUnit
  | Ambiguous AmbiguousUnit
  deriving (Eq, Ord)

-- | These measurement-system-specific units exist to disambiguate
-- the \"M\" of the 'Unit' type and to maintain the measurement-system context
-- for resolving to the appropriate precision.
--
-- It's also just handy to have separate programmatically accessible lists
-- of units for each type of measurement system.
data SystemUnit
  = Metric MetricUnit
  | Imperial ImperialUnit
  deriving (Eq, Ord)

data ImperialUnit
  = Inch
  | Foot
  | Yard
  | Mile
  deriving (Eq, Ord)

data MetricUnit
  = Millimetre
  | Centimetre
  | Metre
  | Kilometre
  deriving (Eq, Ord)

-- | Currently there is only one actually ambiguous unit, but this design allows
-- for expansion.
data AmbiguousUnit
  = M -- ^ "Miles" or "Metres"
  deriving (Eq, Ord)

-- | Represents a value with an unambiguous unit
data UnitValuePair = UnitValuePair SystemUnit Double

sumScaledUnits :: SystemUnit -> (UnitValuePair, UnitValuePair) -> Double
sumScaledUnits preferredUnit = uncurry (+) . both (scaleUnits preferredUnit)

-- | Determine the definite meaning of an "Ambiguous" unit using the context of
-- another "Definite" unit
reconcileAmbiguousWithDefinite ::
     Double
  -> AmbiguousUnit
  -> Double
  -> SystemUnit
  -> ContextualDistance
reconcileAmbiguousWithDefinite av au dv du =
  ContextualDistance combinedValue $ Definite preferredUnit
  where
    resolvedAmbiguousUnit = resolveUnit au du
    preferredUnit = du `min` resolvedAmbiguousUnit
    combinedValue = sumScaledUnits preferredUnit
      (UnitValuePair resolvedAmbiguousUnit av, UnitValuePair du dv)

-- | When both Metric and Imperial units are given, resolve to Metric.
-- Otherwise, preserve the original measurement system and use the smaller unit.
-- For the purpose of this resolution, all Metric units are considered "smaller"
-- than Imperial units.
instance Semigroup ContextualDistance where

  _ <> Nonrelatable = Nonrelatable
  Nonrelatable <> _ = Nonrelatable

  (ContextualDistance v1 u@(Ambiguous u1))
    <> (ContextualDistance v2 (Ambiguous u2))
      | u1 == u2  = ContextualDistance (v1 + v2) u
      | otherwise = Nonrelatable
      -- NOTE A: Needing to return "Nonrelatable" in this edge case is why
      -- the two-member "ContextualDistance" sum type cannot be simply
      -- represented as (Maybe (ContextualDistance Double DeferrableUnit)).

  (ContextualDistance av (Ambiguous au))
    <> (ContextualDistance dv (Definite du)) =
      reconcileAmbiguousWithDefinite av au dv du

  (ContextualDistance dv (Definite du))
    <> (ContextualDistance av (Ambiguous au)) =
      reconcileAmbiguousWithDefinite av au dv du

  (ContextualDistance v1 (Definite u1))
    <> (ContextualDistance v2 (Definite u2)) =
      ContextualDistance v $ Definite u
      where
        v = sumScaledUnits u (UnitValuePair u1 v1, UnitValuePair u2 v2)
        u = u1 `min` u2

resolveUnit :: AmbiguousUnit -> SystemUnit -> SystemUnit
resolveUnit M (Metric _) = Metric Metre
resolveUnit M (Imperial _) = Imperial Mile

-- | Disambiguation of original Unit type
toSystemUnit :: TDist.Unit -> DeferrableUnit
toSystemUnit TDist.M          = Ambiguous M
toSystemUnit TDist.Millimetre = Definite $ Metric Millimetre
toSystemUnit TDist.Centimetre = Definite $ Metric Centimetre
toSystemUnit TDist.Metre      = Definite $ Metric Metre
toSystemUnit TDist.Kilometre  = Definite $ Metric Kilometre
toSystemUnit TDist.Inch       = Definite $ Imperial Inch
toSystemUnit TDist.Foot       = Definite $ Imperial Foot
toSystemUnit TDist.Yard       = Definite $ Imperial Yard
toSystemUnit TDist.Mile       = Definite $ Imperial Mile

-- | Reconversion to original Unit type
toRawUnit :: DeferrableUnit -> TDist.Unit
toRawUnit (Ambiguous M) = TDist.M
toRawUnit (Definite (Metric Millimetre)) = TDist.Millimetre
toRawUnit (Definite (Metric Centimetre)) = TDist.Centimetre
toRawUnit (Definite (Metric Metre)) = TDist.Metre
toRawUnit (Definite (Metric Kilometre)) = TDist.Kilometre
toRawUnit (Definite (Imperial Inch)) = TDist.Inch
toRawUnit (Definite (Imperial Foot)) = TDist.Foot
toRawUnit (Definite (Imperial Yard)) = TDist.Yard
toRawUnit (Definite (Imperial Mile)) = TDist.Mile

-- | Convert a distance to the given units.
-- This only works if the unit is unambiguous.
scaleUnits ::
     SystemUnit -- ^ target unit
  -> UnitValuePair -- ^ Original unit and value
  -> Double
scaleUnits targetUnit (UnitValuePair startingUnit v)
  | startingUnit == targetUnit = v
  | otherwise = inSIUnits v startingUnit / inSIUnits 1 targetUnit

-- | This is used when distances
-- must be normalized across measurement systems.
-- The Metric metre is the Standard International unit of distance.
inSIUnits :: Double -> SystemUnit -> Double
inSIUnits val (Metric u) = inMetres u val
inSIUnits val (Imperial u) = inInches u val * metersPerInch

-- | This conversion factor is exact.
metersPerInch :: Double
metersPerInch = 0.0254

inMetres :: Fractional a => MetricUnit -> a -> a
inMetres Millimetre n = n / 1000
inMetres Centimetre n = n / 100
inMetres Metre      n = n
inMetres Kilometre  n = n * 1000

inInches :: Num a => ImperialUnit -> a -> a
inInches Inch n = n
inInches Foot n = 12   * n
inInches Yard n = 3    * inInches Foot n
inInches Mile n = 5280 * inInches Foot n


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

{-# LANGUAGE GADTs #-}

module Duckling.Duration.Helpers
  ( duration
  , isGrain
  , isNatural
  , minutesFromHourMixedFraction
  , nPlusOneHalf
  , secondsFromHourMixedFraction
  , timesOneQuarter
  , timesThreeQuarter
  , inCoarsestGrain
  ) where

import Prelude

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

-- -----------------------------------------------------------------
-- Patterns

isGrain :: TG.Grain -> Predicate
isGrain value (Token TimeGrain grain) = grain == value
isGrain _ _ = False

-- -----------------------------------------------------------------
-- Production

duration :: TG.Grain -> Int -> DurationData
duration grain n = DurationData {TDuration.grain = grain, TDuration.value = n}

minutesFromHourMixedFraction :: Integer -> Integer -> Integer -> DurationData
minutesFromHourMixedFraction h n d =
  duration TG.Minute $ fromIntegral $ 60 * h + quot (n * 60) d

nPlusOneHalf :: TG.Grain -> Int -> Maybe DurationData
nPlusOneHalf grain = case grain of
  TG.Minute -> Just . duration TG.Second . (30+) . (60*)
  TG.Hour   -> Just . duration TG.Minute . (30+) . (60*)
  TG.Day    -> Just . duration TG.Hour   . (12+) . (24*)
  TG.Month  -> Just . duration TG.Day    . (15+) . (30*)
  TG.Year   -> Just . duration TG.Month  . (6+)  . (12*)
  _         -> const Nothing

secondsFromHourMixedFraction :: Integer -> Integer -> Integer -> DurationData
secondsFromHourMixedFraction m s d =
  duration TG.Second $ fromIntegral $ 60 * m + quot (s * 60) d

timesOneQuarter :: TG.Grain -> Int -> Maybe DurationData
timesOneQuarter grain = case grain of
  TG.Minute -> Just . duration TG.Second . (15+) . (60*)
  TG.Hour   -> Just . duration TG.Minute . (15+) . (60*)
  TG.Day    -> Just . duration TG.Hour   . (6+) . (24*)
  TG.Month  -> Just . duration TG.Day    . (7+) . (30*)
  TG.Year   -> Just . duration TG.Month  . (3+)  . (12*)
  _         -> const Nothing

timesThreeQuarter :: TG.Grain -> Int -> Maybe DurationData
timesThreeQuarter grain = case grain of
  TG.Minute -> Just . duration TG.Second . (45+) . (60*)
  TG.Hour   -> Just . duration TG.Minute . (45+) . (60*)
  TG.Day    -> Just . duration TG.Hour   . (18+) . (24*)
  TG.Month  -> Just . duration TG.Day    . (21+) . (30*)
  TG.Year   -> Just . duration TG.Month  . (9+)  . (12*)
  _         -> const Nothing

inCoarsestGrain :: TG.Grain -> Double -> DurationData
inCoarsestGrain g v =
  go g
  where
    seconds = fromIntegral (floor $ TG.inSeconds g v :: Int)
    go grain =
      -- recursion terminates because `seconds` is an Integer
      let inGrain = seconds / TG.inSeconds grain 1
      in case TNumeral.getIntValue inGrain of
        Just n -> duration grain n
        Nothing -> go $ TG.lower grain


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

import Data.String
import Prelude

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

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

rules :: [Rule]
rules =
  [ ruleIntegerUnitofduration
  ]


-- 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.Duration.Types where

import Control.DeepSeq
import Data.Aeson
import Data.Hashable
import Data.Semigroup
import Data.Text (Text)
import Data.Tuple.Extra (both)
import GHC.Generics
import TextShow (showt)
import Prelude

import Duckling.Resolve (Resolve(..))
import Duckling.TimeGrain.Types (Grain(..), inSeconds)

data DurationData = DurationData
  { value :: Int
  , grain :: Grain
  }
  deriving (Eq, Generic, Hashable, Show, Ord, NFData)

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

instance Semigroup DurationData where
  d1@(DurationData _ g1) <> d2@(DurationData _ g2) = DurationData (v1+v2) g
    where
    g = g1 `min` g2
    (DurationData v1 _, DurationData v2 _) = both (withGrain g) (d1,d2)

instance ToJSON DurationData where
  toJSON DurationData {value, grain} = object
    [ "type"       .= ("value" :: Text)
    , "value"      .= value
    , "unit"       .= grain
    , showt grain  .= value
    , "normalized" .= object
      [ "unit"  .= ("second" :: Text)
      , "value" .= inSeconds grain value
      ]
    ]

-- | Convert a duration to the given grain, rounded to the
-- nearest integer. For example, 1 month is converted to 4 weeks.
withGrain :: Grain -> DurationData -> DurationData
withGrain g d@(DurationData v1 g1)
  | g == g1 = d
  | otherwise = DurationData v g
      where
      v = round $ inSeconds g1 (fromIntegral v1 :: Double) / inSeconds g 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 OverloadedStrings #-}

module Duckling.Duration.AR.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 AR Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (DurationData 1 Second)
             [ "Ø«Ø§Ù†ÙŠØ©"
             , "Ù„ØØ¸Ø©"
             ]
  , examples (DurationData 2 Minute)
             [ "Ø¯Ù‚ÙŠÙ‚ØªØ§Ù†"
             , "Ø¯Ù‚ÙŠÙ‚ØªÙŠÙ†"
             ]
  , examples (DurationData 5 Hour)
             [ "Ø®Ù…Ø³Ø© Ø³Ø§Ø¹Ø§Øª"
             ]
  , examples (DurationData 30 Day)
             [ "30 ÙŠÙˆÙ…"
             ]
  , examples (DurationData 1 Week)
             [ "Ø§Ø³Ø¨ÙˆØ¹"
             ]
  , examples (DurationData 7 Week)
             [ "Ø³Ø¨Ø¹ Ø§Ø³Ø§Ø¨ÙŠØ¹"
             ]
  , examples (DurationData 1 Month)
             [ "Ø´Ù‡Ø±"
             ]
  , examples (DurationData 2 Month)
             [ "Ø´Ù‡Ø±ÙŠÙ†"
             ]
  , examples (DurationData 2 Year)
             [ "Ø³Ù†ØªÙŠÙ†"
             , "Ø³Ù†ØªØ§Ù†"
             , "Ø¹Ø§Ù…ÙŠÙ†"
             , "Ø¹Ø§Ù…Ø§Ù†"
             ]
  ]


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

allExamples :: [Example]
allExamples = concat
  [ examples (DurationData 30 Minute)
             [ "mitja hora"
             , "dos quarts"
             , "30 minuts"
             , "trenta minuts"
             ]
  , examples (DurationData 15 Minute)
             [  "quart d'hora"
             ,  "un quart d'hora"
             ,  "1 quart d'hora"
             ,  "1/4 d'hora"
             ,  "quinze minuts"
             ,  "15 minuts"
             ]
  , examples (DurationData 45 Minute)
             [  "tres quarts d'hora"
             ,  "3/4 d'hora"
             ,  "3 quarts d'hora"
             ,  "tres quarts"
             ,  "quaranta-cinc minuts"
             ,  "45 minuts"
             ]
  , examples (DurationData 92 Minute)
             [ "una hora i trenta-dos minuts"
             , "una hora trenta-dos minuts"
             ]
  , examples (DurationData 155 Minute)
             [ "dues hores i trenta-cinc minuts"
             , "dues hores trenta-cinc minuts"
             ]
  ]


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

import Prelude
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 "((1 |un )?quarts?|1/4) d'hora"
    ]
  , prod = \_ -> Just $ Token Duration $ duration TG.Minute 15
  }

ruleDurationHalfOfAnHour :: Rule
ruleDurationHalfOfAnHour = Rule
  { name = "half of an hour"
  , pattern =
    [ regex "(mitja hora|dos quarts)"
    ]
  , prod = \_ -> Just $ Token Duration $ duration TG.Minute 30
  }

ruleDurationThreeQuartersOfAnHour :: Rule
ruleDurationThreeQuartersOfAnHour = Rule
  { name = "three-quarters of an hour"
  , pattern =
    [ regex "((tres|3) quarts|3/4)( d'hor([a]|(es)))?"
    ]
  , prod = \_ -> Just $ Token Duration $ duration TG.Minute 45
  }

ruleCompositeDurationCommasAnd :: Rule
ruleCompositeDurationCommasAnd = Rule
  { name = "composite <duration> (with and)"
  , pattern =
    [ Predicate isNatural
    , dimension TimeGrain
    , regex "i"
    , 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 GADTs #-}
{-# LANGUAGE OverloadedStrings #-}

module Duckling.Duration.DA.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

ruleExactlyDuration :: Rule
ruleExactlyDuration = Rule
  { name = "exactly <duration>"
  , pattern =
    [ regex "pr(Ã¦)cis"
    , dimension Duration
    ]
  , prod = \tokens -> case tokens of
      -- TODO(jodent) +precision exact
      (_:token:_) -> Just token
      _ -> Nothing
  }

ruleIntegerAndAnHalfHours :: Rule
ruleIntegerAndAnHalfHours = Rule
  { name = "<integer> and an half hours"
  , pattern =
    [ Predicate isNatural
    , regex "og (en )?halv timer?"
    ]
  , 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|et?"
    , dimension TimeGrain
    ]
  , prod = \tokens -> case tokens of
      (_:Token TimeGrain grain:_) -> Just . Token Duration $ duration grain 1
      _ -> Nothing
  }

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

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

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

ruleNumeralnumberHours :: Rule
ruleNumeralnumberHours = Rule
  { name = "number.number hours"
  , pattern =
    [ regex "(\\d+)\\,(\\d+)"
    , regex "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
  }

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

rules :: [Rule]
rules =
  [ ruleAUnitofduration
  , ruleAboutDuration
  , ruleExactlyDuration
  , ruleFortnight
  , 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.DE.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 DE Nothing}, testOptions, allExamples)

allExamples :: [Example]
allExamples = concat
  [ examples (DurationData 1 Second)
             [ "ein sekunde"
             , "zirka eine sekunde"
             ]
  , examples (DurationData 30 Minute)
             [ "1/2 stunde"
             , "1/2stunde"
             , "eine halbe stunde"
             , "einer halben stunde"
             , "halbe stunde"
             , "halben stunde"
             , "ungefahr einer halben stunde"
             ]
  , examples (DurationData 15 Minute)
             [ "einer Viertelstunde"
             , "eine viertelstunde"
             , "ViErTelStUnDe"
             , "genau viertelstunde"
             ]
  , examples (DurationData 45 Minute)
             [ "3/4 stunde"
             , "3/4stunde"
             , "eine dreiviertel stunde"
             , "einer dreiviertel stunde"
             , "dreiviertel stunde"
             , "drei viertelstunden"
             ]
  , examples (DurationData 92 Minute)
             [ "92 minuten"
             , "zweiundneunzig minuten"
             , "eine Stunde und zweiunddreiÃŸig Minuten"
             , "ein stunde und zweiunddreissig minuten"
             ]
  , examples (DurationData 30 Day)
             [ "30 tage"
             , "dreiÃŸig tage"
             , "dreissig tage"
             ]
  , examples (DurationData 7 Week)
             [ "7 Wochen"
             , "sieben wochen"
             ]
  , examples (DurationData 90 Minute)
             [ "1,5 stunden"
             , "1,5 stunde"
             , "90 min"
             , "90min"
             ]
  , examples (DurationData 75 Minute)
             [ "1,25 stunden"
             ]
  , examples (DurationData 31719604 Second)
             [ "1 Jahr, 2 Tage, 3 Stunden und 4 Sekunden"
             , "1 Jahr 2 Tage 3 Stunden und 4 Sekunden"
             ]
  , examples (DurationData 330 Second)
             [ "5 und eine halbe Minuten"
             , "5,5 min"
             ]
  , examples (DurationData 330 Minute)
             [ "5 und eine halbe stunden"
             , "5,5 stunde"
             , "exakt 5,5 stunden"
             ]
  , examples (DurationData 930 Second)
              [ "15,5 minuten"
              , "15,5 minute"
              , "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 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 (numberBetween, 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
  { name = "quarter of an hour"
  , pattern =
    [ regex "(einer? )?Viertelstunde"
    ]
  , prod = \_ -> Just $ Token Duration $ duration TG.Minute 15
  }

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

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

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

rulePrecision :: Rule
rulePrecision = Rule
  { name = "about|exactly <duration>"
  , pattern =
    [ regex "ungef(Ã¤|a)hr|zirka|genau|exakt"
    , dimension Duration
    ]
  , prod = \case
      (_:token:_) -> Just token
      _ -> Nothing
  }

ruleCommaNumeralHours :: Rule
ruleCommaNumeralHours = 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
  }

ruleCommaNumeralMinutes :: Rule
ruleCommaNumeralMinutes = 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
  }

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

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

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

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

ruleCompositeDurationCommasAnd :: Rule
ruleCompositeDurationCommasAnd = Rule
  { name = "composite <duration> (with ,/and)"
  , pattern =
    [ Predicate isNatural
    , dimension TimeGrain
    , regex ",|und"
    , 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 ",|und"
    , 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
  }

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

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