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


{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NoRebindableSyntax #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}

module Duckling.Numeral.Types where

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

import Duckling.Resolve

data NumeralData = NumeralData
  { NumeralData -> Double
value        :: Double
  , NumeralData -> Maybe Int
grain        :: Maybe Int
  , NumeralData -> Bool
multipliable :: Bool
  -- Hack until other use cases pop up,
  -- at which point we'll craft a generic solution.
  -- This allows to explicitly flag Numerals that don't work well with Time.
  -- See `ruleTODLatent`. Prevents things like "at single", "dozens o'clock".
  , NumeralData -> Bool
okForAnyTime :: Bool
  }
  deriving (NumeralData -> NumeralData -> Bool
(NumeralData -> NumeralData -> Bool)
-> (NumeralData -> NumeralData -> Bool) -> Eq NumeralData
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: NumeralData -> NumeralData -> Bool
$c/= :: NumeralData -> NumeralData -> Bool
== :: NumeralData -> NumeralData -> Bool
$c== :: NumeralData -> NumeralData -> Bool
Eq, (forall x. NumeralData -> Rep NumeralData x)
-> (forall x. Rep NumeralData x -> NumeralData)
-> Generic NumeralData
forall x. Rep NumeralData x -> NumeralData
forall x. NumeralData -> Rep NumeralData x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep NumeralData x -> NumeralData
$cfrom :: forall x. NumeralData -> Rep NumeralData x
Generic, Int -> NumeralData -> Int
NumeralData -> Int
(Int -> NumeralData -> Int)
-> (NumeralData -> Int) -> Hashable NumeralData
forall a. (Int -> a -> Int) -> (a -> Int) -> Hashable a
hash :: NumeralData -> Int
$chash :: NumeralData -> Int
hashWithSalt :: Int -> NumeralData -> Int
$chashWithSalt :: Int -> NumeralData -> Int
Hashable, Eq NumeralData
Eq NumeralData
-> (NumeralData -> NumeralData -> Ordering)
-> (NumeralData -> NumeralData -> Bool)
-> (NumeralData -> NumeralData -> Bool)
-> (NumeralData -> NumeralData -> Bool)
-> (NumeralData -> NumeralData -> Bool)
-> (NumeralData -> NumeralData -> NumeralData)
-> (NumeralData -> NumeralData -> NumeralData)
-> Ord NumeralData
NumeralData -> NumeralData -> Bool
NumeralData -> NumeralData -> Ordering
NumeralData -> NumeralData -> NumeralData
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: NumeralData -> NumeralData -> NumeralData
$cmin :: NumeralData -> NumeralData -> NumeralData
max :: NumeralData -> NumeralData -> NumeralData
$cmax :: NumeralData -> NumeralData -> NumeralData
>= :: NumeralData -> NumeralData -> Bool
$c>= :: NumeralData -> NumeralData -> Bool
> :: NumeralData -> NumeralData -> Bool
$c> :: NumeralData -> NumeralData -> Bool
<= :: NumeralData -> NumeralData -> Bool
$c<= :: NumeralData -> NumeralData -> Bool
< :: NumeralData -> NumeralData -> Bool
$c< :: NumeralData -> NumeralData -> Bool
compare :: NumeralData -> NumeralData -> Ordering
$ccompare :: NumeralData -> NumeralData -> Ordering
$cp1Ord :: Eq NumeralData
Ord, Int -> NumeralData -> ShowS
[NumeralData] -> ShowS
NumeralData -> String
(Int -> NumeralData -> ShowS)
-> (NumeralData -> String)
-> ([NumeralData] -> ShowS)
-> Show NumeralData
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [NumeralData] -> ShowS
$cshowList :: [NumeralData] -> ShowS
show :: NumeralData -> String
$cshow :: NumeralData -> String
showsPrec :: Int -> NumeralData -> ShowS
$cshowsPrec :: Int -> NumeralData -> ShowS
Show, NumeralData -> ()
(NumeralData -> ()) -> NFData NumeralData
forall a. (a -> ()) -> NFData a
rnf :: NumeralData -> ()
$crnf :: NumeralData -> ()
NFData)

instance Resolve NumeralData where
  type ResolvedValue NumeralData = NumeralValue
  resolve :: Context
-> Options
-> NumeralData
-> Maybe (ResolvedValue NumeralData, Bool)
resolve Context
_ Options
_ NumeralData {Double
value :: Double
value :: NumeralData -> Double
value} = (NumeralValue, Bool) -> Maybe (NumeralValue, Bool)
forall a. a -> Maybe a
Just (NumeralValue :: Double -> NumeralValue
NumeralValue {vValue :: Double
vValue = Double
value}, Bool
False)

newtype NumeralValue = NumeralValue { NumeralValue -> Double
vValue :: Double }
  deriving (NumeralValue -> NumeralValue -> Bool
(NumeralValue -> NumeralValue -> Bool)
-> (NumeralValue -> NumeralValue -> Bool) -> Eq NumeralValue
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: NumeralValue -> NumeralValue -> Bool
$c/= :: NumeralValue -> NumeralValue -> Bool
== :: NumeralValue -> NumeralValue -> Bool
$c== :: NumeralValue -> NumeralValue -> Bool
Eq, Int -> NumeralValue -> ShowS
[NumeralValue] -> ShowS
NumeralValue -> String
(Int -> NumeralValue -> ShowS)
-> (NumeralValue -> String)
-> ([NumeralValue] -> ShowS)
-> Show NumeralValue
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [NumeralValue] -> ShowS
$cshowList :: [NumeralValue] -> ShowS
show :: NumeralValue -> String
$cshow :: NumeralValue -> String
showsPrec :: Int -> NumeralValue -> ShowS
$cshowsPrec :: Int -> NumeralValue -> ShowS
Show)

instance ToJSON NumeralValue where
  toJSON :: NumeralValue -> Value
toJSON (NumeralValue Double
value) = [Pair] -> Value
object
    [ Text
"type" Text -> Text -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= (Text
"value" :: Text)
    , Text
"value" Text -> Double -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= Double
value
    ]

getIntValue :: Double -> Maybe Int
getIntValue :: Double -> Maybe Int
getIntValue Double
x = if Double
rest Double -> Double -> Bool
forall a. Eq a => a -> a -> Bool
== Double
0 then Int -> Maybe Int
forall a. a -> Maybe a
Just Int
int else Maybe Int
forall a. Maybe a
Nothing
  where
    (Int
int, Double
rest) = Double -> (Int, Double)
forall a b. (RealFrac a, Integral b) => a -> (b, a)
properFraction Double
x :: (Int, Double)

isInteger :: Double -> Bool
isInteger :: Double -> Bool
isInteger = Maybe Int -> Bool
forall a. Maybe a -> Bool
isJust (Maybe Int -> Bool) -> (Double -> Maybe Int) -> Double -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Double -> Maybe Int
getIntValue

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