packages feed

grisette-0.8.0.0: src/Grisette/Internal/Core/Data/Class/SafeFromFP.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}

-- |
-- Module      :   Grisette.Internal.Core.Data.Class.SafeFromFP
-- 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.Core.Data.Class.SafeFromFP (SafeFromFP (..)) where

import Control.Monad.Error.Class (MonadError (throwError))
import GHC.TypeLits (KnownNat, type (<=))
import Grisette.Internal.Core.Control.Monad.Class.Union (MonadUnion)
import Grisette.Internal.Core.Data.Class.IEEEFP
  ( IEEEFPConvertible (fromFPOr),
    IEEEFPRoundingMode (rna, rne, rtn, rtp, rtz),
    fpIsNaN,
    fpIsNegativeInfinite,
    fpIsPositiveInfinite,
  )
import Grisette.Internal.Core.Data.Class.ITEOp (ITEOp (symIte))
import Grisette.Internal.Core.Data.Class.SimpleMergeable (mrgIf)
import Grisette.Internal.Core.Data.Class.Solvable (Solvable (con))
import Grisette.Internal.Core.Data.Class.SymEq (SymEq ((.==)))
import Grisette.Internal.Core.Data.Class.SymIEEEFP
  ( SymIEEEFPTraits
      ( symFpIsNaN,
        symFpIsNegativeInfinite,
        symFpIsPositiveInfinite
      ),
  )
import Grisette.Internal.Core.Data.Class.SymOrd (SymOrd ((.<), (.>)))
import Grisette.Internal.Core.Data.Class.TryMerge
  ( TryMerge,
    mrgSingle,
    tryMerge,
  )
import Grisette.Internal.SymPrim.AlgReal (AlgReal)
import Grisette.Internal.SymPrim.BV (IntN, WordN)
import Grisette.Internal.SymPrim.FP
  ( ConvertibleBound (convertibleLowerBound, convertibleUpperBound),
    FP,
    FPRoundingMode,
    NotRepresentableFPError
      ( FPOverflowError,
        FPUnderflowError,
        NaNError
      ),
    ValidFP,
  )
import Grisette.Internal.SymPrim.SymAlgReal (SymAlgReal)
import Grisette.Internal.SymPrim.SymBV (SymIntN, SymWordN)
import Grisette.Internal.SymPrim.SymFP (SymFP, SymFPRoundingMode)
import Grisette.Internal.SymPrim.SymInteger (SymInteger)

-- | Safe conversion from floating point numbers that throws exceptions when
-- the result isn't representable by the type.
class
  (MonadError e m, TryMerge m, IEEEFPConvertible a fp fprd) =>
  SafeFromFP e a fp fprd m
  where
  safeFromFP :: fprd -> fp -> m a

instance
  (MonadError NotRepresentableFPError m, TryMerge m, ValidFP eb sb) =>
  SafeFromFP NotRepresentableFPError AlgReal (FP eb sb) FPRoundingMode m
  where
  safeFromFP mode a
    | fpIsPositiveInfinite a = tryMerge $ throwError FPOverflowError
    | fpIsNegativeInfinite a = tryMerge $ throwError FPUnderflowError
    | fpIsNaN a = tryMerge $ throwError NaNError
    | otherwise = mrgSingle $ fromFPOr undefined mode a

instance
  (MonadError NotRepresentableFPError m, MonadUnion m, ValidFP eb sb) =>
  SafeFromFP
    NotRepresentableFPError
    SymAlgReal
    (SymFP eb sb)
    SymFPRoundingMode
    m
  where
  safeFromFP mode a =
    mrgIf (symFpIsPositiveInfinite a) (tryMerge $ throwError FPOverflowError)
      $ mrgIf
        (symFpIsNegativeInfinite a)
        (tryMerge $ throwError FPUnderflowError)
      $ mrgIf (symFpIsNaN a) (tryMerge $ throwError NaNError)
      $ mrgSingle
      $ fromFPOr 0 mode a

instance
  (MonadError NotRepresentableFPError m, TryMerge m, ValidFP eb sb) =>
  SafeFromFP NotRepresentableFPError Integer (FP eb sb) FPRoundingMode m
  where
  safeFromFP mode a
    | fpIsPositiveInfinite a = tryMerge $ throwError FPOverflowError
    | fpIsNegativeInfinite a = tryMerge $ throwError FPUnderflowError
    | fpIsNaN a = tryMerge $ throwError NaNError
    | otherwise = mrgSingle $ fromFPOr 0 mode a

instance
  (MonadError NotRepresentableFPError m, MonadUnion m, ValidFP eb sb) =>
  SafeFromFP
    NotRepresentableFPError
    SymInteger
    (SymFP eb sb)
    SymFPRoundingMode
    m
  where
  safeFromFP mode a =
    mrgIf (symFpIsPositiveInfinite a) (tryMerge $ throwError FPOverflowError)
      $ mrgIf
        (symFpIsNegativeInfinite a)
        (tryMerge $ throwError FPUnderflowError)
      $ mrgIf
        (symFpIsNaN a)
        (tryMerge $ throwError NaNError)
      $ mrgSingle
      $ fromFPOr 0 mode a

instance
  ( MonadError NotRepresentableFPError m,
    TryMerge m,
    ValidFP eb sb,
    KnownNat n,
    1 <= n
  ) =>
  SafeFromFP NotRepresentableFPError (WordN n) (FP eb sb) FPRoundingMode m
  where
  safeFromFP mode a = do
    p :: Integer <- safeFromFP mode a
    if p < (fromIntegral (minBound :: WordN n))
      then tryMerge $ throwError FPUnderflowError
      else
        if p > (fromIntegral (maxBound :: WordN n))
          then tryMerge $ throwError FPOverflowError
          else mrgSingle $ fromIntegral p

symConvertibleLowerBound ::
  forall conBV symBV n eb sb.
  ( ConvertibleBound conBV,
    ValidFP eb sb,
    KnownNat n,
    1 <= n,
    Solvable (conBV n) (symBV n)
  ) =>
  symBV n ->
  SymFPRoundingMode ->
  SymFP eb sb
symConvertibleLowerBound _ mode =
  symIte
    (mode .== rne)
    (con $ convertibleLowerBound (undefined :: conBV n) rne)
    $ symIte
      (mode .== rna)
      (con $ convertibleLowerBound (undefined :: conBV n) rna)
    $ symIte
      (mode .== rtp)
      (con $ convertibleLowerBound (undefined :: conBV n) rtp)
    $ symIte
      (mode .== rtn)
      (con $ convertibleLowerBound (undefined :: conBV n) rtn)
      (con $ convertibleLowerBound (undefined :: conBV n) rtz)

symConvertibleUpperBound ::
  forall conBV symBV n eb sb.
  ( ConvertibleBound conBV,
    ValidFP eb sb,
    KnownNat n,
    1 <= n,
    Solvable (conBV n) (symBV n)
  ) =>
  symBV n ->
  SymFPRoundingMode ->
  SymFP eb sb
symConvertibleUpperBound _ mode =
  symIte
    (mode .== rne)
    (con $ convertibleUpperBound (undefined :: conBV n) rne)
    $ symIte
      (mode .== rna)
      (con $ convertibleUpperBound (undefined :: conBV n) rna)
    $ symIte
      (mode .== rtp)
      (con $ convertibleUpperBound (undefined :: conBV n) rtp)
    $ symIte
      (mode .== rtn)
      (con $ convertibleUpperBound (undefined :: conBV n) rtn)
      (con $ convertibleUpperBound (undefined :: conBV n) rtz)

instance
  ( MonadError NotRepresentableFPError m,
    MonadUnion m,
    ValidFP eb sb,
    KnownNat n,
    1 <= n
  ) =>
  SafeFromFP NotRepresentableFPError (SymWordN n) (SymFP eb sb) SymFPRoundingMode m
  where
  safeFromFP mode a =
    mrgIf (symFpIsPositiveInfinite a) (tryMerge $ throwError FPOverflowError)
      $ mrgIf
        (symFpIsNegativeInfinite a)
        (tryMerge $ throwError FPUnderflowError)
      $ mrgIf (symFpIsNaN a) (tryMerge $ throwError NaNError)
      $ mrgIf
        (a .< symConvertibleLowerBound (undefined :: SymWordN n) mode)
        (tryMerge $ throwError FPUnderflowError)
      $ mrgIf
        (a .> symConvertibleUpperBound (undefined :: SymWordN n) mode)
        (tryMerge $ throwError FPOverflowError)
      $ mrgSingle
      $ fromFPOr 0 mode a

instance
  ( MonadError NotRepresentableFPError m,
    TryMerge m,
    ValidFP eb sb,
    KnownNat n,
    1 <= n
  ) =>
  SafeFromFP NotRepresentableFPError (IntN n) (FP eb sb) FPRoundingMode m
  where
  safeFromFP mode a = do
    p :: Integer <- safeFromFP mode a
    if p < (fromIntegral (minBound :: IntN n))
      then tryMerge $ throwError FPUnderflowError
      else
        if p > (fromIntegral (maxBound :: IntN n))
          then tryMerge $ throwError FPOverflowError
          else mrgSingle $ fromIntegral p

instance
  ( MonadError NotRepresentableFPError m,
    MonadUnion m,
    ValidFP eb sb,
    KnownNat n,
    1 <= n
  ) =>
  SafeFromFP NotRepresentableFPError (SymIntN n) (SymFP eb sb) SymFPRoundingMode m
  where
  safeFromFP mode a =
    mrgIf (symFpIsPositiveInfinite a) (tryMerge $ throwError FPOverflowError)
      $ mrgIf
        (symFpIsNegativeInfinite a)
        (tryMerge $ throwError FPUnderflowError)
      $ mrgIf (symFpIsNaN a) (tryMerge $ throwError NaNError)
      $ mrgIf
        (a .< symConvertibleLowerBound (undefined :: SymIntN n) mode)
        (tryMerge $ throwError FPUnderflowError)
      $ mrgIf
        (a .> symConvertibleUpperBound (undefined :: SymIntN n) mode)
        (tryMerge $ throwError FPOverflowError)
      $ mrgSingle
      $ fromFPOr 0 mode a