{-# LANGUAGE Safe #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
module Cryptol.TypeCheck.Solver.InfNat where
import Data.Bits
import Cryptol.Utils.Panic
import GHC.Generics (Generic)
import Control.DeepSeq
data Nat' = Nat Integer | Inf
deriving (Show, Eq, Ord, Generic, NFData)
fromNat :: Nat' -> Maybe Integer
fromNat n' =
case n' of
Nat i -> Just i
_ -> Nothing
nAdd :: Nat' -> Nat' -> Nat'
nAdd Inf _ = Inf
nAdd _ Inf = Inf
nAdd (Nat x) (Nat y) = Nat (x + y)
nMul :: Nat' -> Nat' -> Nat'
nMul (Nat 0) _ = Nat 0
nMul _ (Nat 0) = Nat 0
nMul Inf _ = Inf
nMul _ Inf = Inf
nMul (Nat x) (Nat y) = Nat (x * y)
nExp :: Nat' -> Nat' -> Nat'
nExp _ (Nat 0) = Nat 1
nExp Inf _ = Inf
nExp (Nat 0) Inf = Nat 0
nExp (Nat 1) Inf = Nat 1
nExp (Nat _) Inf = Inf
nExp (Nat x) (Nat y) = Nat (x ^ y)
nMin :: Nat' -> Nat' -> Nat'
nMin Inf x = x
nMin x Inf = x
nMin (Nat x) (Nat y) = Nat (min x y)
nMax :: Nat' -> Nat' -> Nat'
nMax Inf _ = Inf
nMax _ Inf = Inf
nMax (Nat x) (Nat y) = Nat (max x y)
nSub :: Nat' -> Nat' -> Maybe Nat'
nSub Inf (Nat _) = Just Inf
nSub (Nat x) (Nat y)
| x >= y = Just (Nat (x - y))
nSub _ _ = Nothing
nDiv :: Nat' -> Nat' -> Maybe Nat'
nDiv _ (Nat 0) = Nothing
nDiv Inf _ = Nothing
nDiv (Nat x) (Nat y) = Just (Nat (div x y))
nDiv (Nat _) Inf = Just (Nat 0)
nMod :: Nat' -> Nat' -> Maybe Nat'
nMod _ (Nat 0) = Nothing
nMod Inf _ = Nothing
nMod (Nat x) (Nat y) = Just (Nat (mod x y))
nMod (Nat x) Inf = Just (Nat x)
nCeilDiv :: Nat' -> Nat' -> Maybe Nat'
nCeilDiv _ (Nat 0) = Nothing
nCeilDiv Inf _ = Nothing
nCeilDiv (Nat _) Inf = Nothing
nCeilDiv (Nat x) (Nat y) = Just (Nat (- div (- x) y))
nCeilMod :: Nat' -> Nat' -> Maybe Nat'
nCeilMod _ (Nat 0) = Nothing
nCeilMod Inf _ = Nothing
nCeilMod (Nat _) Inf = Nothing
nCeilMod (Nat x) (Nat y) = Just (Nat (mod (- x) y))
nLg2 :: Nat' -> Nat'
nLg2 Inf = Inf
nLg2 (Nat 0) = Nat 0
nLg2 (Nat n) = case genLog n 2 of
Just (x,exact) | exact -> Nat x
| otherwise -> Nat (x + 1)
Nothing -> panic "Cryptol.TypeCheck.Solver.InfNat.nLg2"
[ "genLog returned Nothing" ]
nWidth :: Nat' -> Nat'
nWidth Inf = Inf
nWidth (Nat n) = Nat (widthInteger n)
nLenFromThenTo :: Nat' -> Nat' -> Nat' -> Maybe Nat'
nLenFromThenTo (Nat x) (Nat y) (Nat z)
| step /= 0 = let len = div dist step + 1
in Just $ Nat $ if x > y
then (if z > x then 0 else len)
else (if z < x then 0 else len)
where
step = abs (x - y)
dist = abs (x - z)
nLenFromThenTo _ _ _ = Nothing
genLog :: Integer -> Integer -> Maybe (Integer, Bool)
genLog x 0 = if x == 1 then Just (0, True) else Nothing
genLog _ 1 = Nothing
genLog 0 _ = Nothing
genLog x base = Just (exactLoop 0 x)
where
exactLoop s i
| i == 1 = (s,True)
| i < base = (s,False)
| otherwise =
let s1 = s + 1
in s1 `seq` case divMod i base of
(j,r)
| r == 0 -> exactLoop s1 j
| otherwise -> (underLoop s1 j, False)
underLoop s i
| i < base = s
| otherwise = let s1 = s + 1 in s1 `seq` underLoop s1 (div i base)
widthInteger :: Integer -> Integer
widthInteger x = go' 0 (if x < 0 then complement x else x)
where
go s 0 = s
go s n = let s' = s + 1 in s' `seq` go s' (n `shiftR` 1)
go' s n
| n < bit 32 = go s n
| otherwise = let s' = s + 32 in s' `seq` go' s' (n `shiftR` 32)
rootExact :: Integer -> Integer -> Maybe Integer
rootExact x y = do (z,True) <- genRoot x y
return z
genRoot :: Integer -> Integer -> Maybe (Integer, Bool)
genRoot _ 0 = Nothing
genRoot x0 1 = Just (x0, True)
genRoot x0 root = Just (search 0 (x0+1))
where
search from to = let x = from + div (to - from) 2
a = x ^ root
in case compare a x0 of
EQ -> (x, True)
LT | x /= from -> search x to
| otherwise -> (from, False)
GT | x /= to -> search from x
| otherwise -> (from, False)