cryptol-2.2.0: src/Cryptol/TypeCheck/Solver/InfNat.hs
-- |
-- Module : $Header$
-- Copyright : (c) 2013-2015 Galois, Inc.
-- License : BSD3
-- Maintainer : cryptol@galois.com
-- Stability : provisional
-- Portability : portable
--
-- This module defines natural numbers with an additional infinity
-- element, and various arithmetic operators on them.
{-# LANGUAGE Safe #-}
module Cryptol.TypeCheck.Solver.InfNat where
import Data.Bits
import Cryptol.Utils.Panic
-- | Natural numbers with an infinity element
data Nat' = Nat Integer | Inf
deriving (Show,Eq,Ord)
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)
{-| Some algerbaic properties of interest:
> 1 * x = x
> x * (y * z) = (x * y) * z
> 0 * x = 0
> x * y = y * x
> x * (a + b) = x * a + x * b
-}
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)
{-| Some algeibraic properties of interest:
> x ^ 0 = 1
> x ^ (n + 1) = x * (x ^ n)
> x ^ (m + n) = (x ^ m) * (x ^ n)
> x ^ (m * n) = (x ^ m) ^ n
-}
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 x y = Just z@ iff @z@ is the unique value
such that @Add y z = Just x@. -}
nSub :: Nat' -> Nat' -> Maybe Nat'
nSub Inf (Nat _) = Just Inf
nSub (Nat x) (Nat y)
| x >= y = Just (Nat (x - y))
nSub _ _ = Nothing
{- | Rounds down.
> y * q + r = x
> x / y = q with remainder r
> 0 <= r && r < y
We don't allow `Inf` in the first argument for two reasons:
1. It matches the behavior of `nMod`,
2. The well-formedness constraints can be expressed as a conjunction.
-}
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) -- inf * 0 + x = 0 + x
-- | Rounds up.
-- @lg2 x = y@, iff @y@ is the smallest number such that @x <= 2 ^ 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 n@ is number of bits needed to represent all numbers
-- from 0 to n, inclusive. @nWidth x = nLg2 (x + 1)@.
nWidth :: Nat' -> Nat'
nWidth Inf = Inf
nWidth (Nat n) = Nat (widthInteger n)
nLenFromThen :: Nat' -> Nat' -> Nat' -> Maybe Nat'
nLenFromThen a@(Nat x) b@(Nat y) (Nat w)
| y > x = nLenFromThenTo a b (Nat (2^w - 1))
| y < x = nLenFromThenTo a b (Nat 0)
nLenFromThen _ _ _ = Nothing
nLenFromThenTo :: Nat' -> Nat' -> Nat' -> Maybe Nat'
nLenFromThenTo (Nat x) (Nat y) (Nat z)
| step /= 0 = let len = div dist step + 1
in Just $ Nat $ max 0 (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
--------------------------------------------------------------------------------
-- | Compute the logarithm of a number in the given base, rounded down to the
-- closest integer. The boolean indicates if we the result is exact
-- (i.e., True means no rounding happened, False means we rounded down).
-- The logarithm base is the second argument.
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)
-- | Compute the number of bits required to represent the given integer.
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)