packages feed

grisette-0.13.0.1: src/Grisette/Internal/SymPrim/SymInteger.hs

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveLift #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE TypeFamilies #-}

-- |
-- Module      :   Grisette.Internal.SymPrim.SymInteger
-- Copyright   :   (c) Sirui Lu 2024
-- License     :   BSD-3-Clause (see the LICENSE file)
--
-- Maintainer  :   siruilu@cs.washington.edu
-- Stability   :   Experimental
-- Portability :   GHC only
module Grisette.Internal.SymPrim.SymInteger
  ( SymInteger (SymInteger),
    SymIntegerKey,
  )
where

import Control.DeepSeq (NFData)
import qualified Data.Binary as Binary
import Data.Bytes.Serial (Serial (deserialize, serialize))
import Data.Hashable (Hashable (hashWithSalt))
import qualified Data.Serialize as Cereal
import Data.String (IsString (fromString))
import GHC.Generics (Generic)
import Grisette.Internal.Core.Data.Class.AsKey
  ( AsKey,
    KeyEq (keyEq),
    KeyHashable (keyHashWithSalt),
    shouldUseAsKeyHasSymbolicVersionError,
    shouldUseSymbolicVersionError,
  )
import Grisette.Internal.Core.Data.Class.Function (Apply (FunType, apply))
import Grisette.Internal.Core.Data.Class.Solvable
  ( Solvable (con, conView, ssym, sym),
  )
import Grisette.Internal.Internal.Decl.SymPrim.AllSyms
  ( AllSyms (allSymsS),
    SomeSym (SomeSym),
  )
import Grisette.Internal.SymPrim.Prim.Term
  ( ConRep (ConType),
    LinkedRep (underlyingTerm, wrapTerm),
    PEvalNumTerm
      ( pevalAbsNumTerm,
        pevalAddNumTerm,
        pevalMulNumTerm,
        pevalNegNumTerm,
        pevalSignumNumTerm
      ),
    SymRep (SymType),
    Term,
    conTerm,
    pevalDivIntegralTerm,
    pevalITETerm,
    pevalLeOrdTerm,
    pevalModIntegralTerm,
    pevalQuotIntegralTerm,
    pevalRemIntegralTerm,
    pevalSubNumTerm,
    pformatTerm,
    symTerm,
    typedConstantSymbol,
    pattern ConTerm,
  )
import Language.Haskell.TH.Syntax (Lift)

-- $setup
-- >>> import Grisette.Core
-- >>> import Grisette.SymPrim
-- >>> import Grisette.Backend
-- >>> import Data.Proxy

-- | Symbolic (unbounded, mathematical) integer type.
--
-- >>> "a" + 1 :: SymInteger
-- (+ 1 a)
--
-- More operations are available. Please refer to "Grisette.Core#g:symops" for
-- more information.
newtype SymInteger = SymInteger {underlyingIntegerTerm :: Term Integer}
  deriving (Lift, NFData, Generic)

-- | t'SymInteger' type with identity equality.
type SymIntegerKey = AsKey SymInteger

instance ConRep SymInteger where
  type ConType SymInteger = Integer

instance SymRep Integer where
  type SymType Integer = SymInteger

instance LinkedRep Integer SymInteger where
  underlyingTerm (SymInteger a) = a
  wrapTerm = SymInteger

instance Apply SymInteger where
  type FunType SymInteger = SymInteger
  apply = id

instance Num SymInteger where
  (SymInteger l) + (SymInteger r) = SymInteger $ pevalAddNumTerm l r
  (SymInteger l) - (SymInteger r) = SymInteger $ pevalSubNumTerm l r
  (SymInteger l) * (SymInteger r) = SymInteger $ pevalMulNumTerm l r
  negate (SymInteger v) = SymInteger $ pevalNegNumTerm v
  abs (SymInteger v) = SymInteger $ pevalAbsNumTerm v
  signum (SymInteger v) = SymInteger $ pevalSignumNumTerm v
  fromInteger = con

{-# NOINLINE [1] enumDeltaSymInteger #-}
enumDeltaSymInteger :: SymInteger -> SymInteger -> [SymInteger]
enumDeltaSymInteger x d = x `seq` (x : enumDeltaSymInteger (d + x) d)

instance Enum SymInteger where
  succ x = x + 1
  pred x = x - 1
  toEnum = fromIntegral
  fromEnum = error "fromEnum: fromEnum isn't supported for SymInteger"
  enumFrom x = enumDeltaSymInteger x 1
  {-# INLINE enumFrom #-}
  enumFromThen x y = enumDeltaSymInteger x (y - x)
  {-# INLINE enumFromThen #-}
  enumFromTo = error "enumFromTo: enumFromTo isn't supported for SymInteger"
  enumFromThenTo =
    error "enumFromThenTo: enumFromThenTo isn't supported for SymInteger"

-- | Except for 'max' and 'min', the other functions will crash the program.
--
-- 'SymInteger' cannot be compared concretely.
--
-- If you want symbolic version of the comparison operators, use
-- t'Grisette.Core.SymOrd' instead.
instance Ord SymInteger where
  (<) = shouldUseSymbolicVersionError "SymInteger" "(<)" "(.<)"
  (<=) = shouldUseSymbolicVersionError "SymInteger" "(<=)" "(.<=)"
  (>=) = shouldUseSymbolicVersionError "SymInteger" "(>=)" "(.>=)"
  (>) = shouldUseSymbolicVersionError "SymInteger" "(>)" "(.>)"
  max (SymInteger l) (SymInteger r) =
    SymInteger $ pevalITETerm (pevalLeOrdTerm l r) r l
  min (SymInteger l) (SymInteger r) =
    SymInteger $ pevalITETerm (pevalLeOrdTerm l r) l r
  compare = shouldUseSymbolicVersionError "SymInteger" "compare" "symCompare"

instance Real SymInteger where
  toRational _ = error "toRational: toRational isn't supported for SymInteger"

-- | The functions are total and will not throw errors. The result is considered
-- undefined if the divisor is 0.
--
-- It is the responsibility of the caller to ensure that the divisor is not
-- zero with the symbolic constraints, or use the t'Grisette.Core.DivOr' or
-- t'Grisette.Core.SafeDiv' classes.
instance Integral SymInteger where
  toInteger = error "toInteger: toInteger isn't supported for SymInteger"
  div (SymInteger l) (SymInteger r) = SymInteger $ pevalDivIntegralTerm l r
  mod (SymInteger l) (SymInteger r) = SymInteger $ pevalModIntegralTerm l r
  quot (SymInteger l) (SymInteger r) = SymInteger $ pevalQuotIntegralTerm l r
  rem (SymInteger l) (SymInteger r) = SymInteger $ pevalRemIntegralTerm l r
  divMod (SymInteger l) (SymInteger r) =
    (SymInteger $ pevalDivIntegralTerm l r, SymInteger $ pevalModIntegralTerm l r)
  quotRem (SymInteger l) (SymInteger r) =
    (SymInteger $ pevalQuotIntegralTerm l r, SymInteger $ pevalRemIntegralTerm l r)

-- | This will crash the program.
--
-- 'SymInteger' cannot be compared concretely.
--
-- If you want to use the type as keys in hash maps based on term equality, say
-- memo table, you should use @'AsKey' 'SymInteger'@ instead.
--
-- If you want symbolic version of the equality operator, use
-- t'Grisette.Core.SymEq' instead.
instance Eq SymInteger where
  (==) = shouldUseAsKeyHasSymbolicVersionError "SymInteger" "(==)" "(.==)"

instance KeyEq SymInteger where
  keyEq (SymInteger l) (SymInteger r) = l == r

instance KeyHashable SymInteger where
  keyHashWithSalt s (SymInteger v) = s `hashWithSalt` v

instance Solvable Integer SymInteger where
  con = SymInteger . conTerm
  sym = SymInteger . symTerm . typedConstantSymbol
  conView (SymInteger (ConTerm t)) = Just t
  conView _ = Nothing

instance IsString SymInteger where
  fromString = ssym . fromString

instance Show SymInteger where
  show (SymInteger t) = pformatTerm t

instance AllSyms SymInteger where
  allSymsS v = (SomeSym v :)

instance Serial SymInteger where
  serialize = serialize . underlyingIntegerTerm
  deserialize = SymInteger <$> deserialize

instance Cereal.Serialize SymInteger where
  put = serialize
  get = deserialize

instance Binary.Binary SymInteger where
  put = serialize
  get = deserialize