{- CAO Compiler
Copyright (C) 2014 Cryptography and Information Security Group, HASLab - INESC TEC and Universidade do Minho
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . -}
{- |
Module : $Header$
Description : Sizes of the representation of CAO types.
Copyright : (C) 2014 Cryptography and Information Security Group, HASLab - INESC TEC and Universidade do Minho
License : GPL
Maintainer : Paulo Silva
Stability : experimental
Portability : non-portable
This module provides the mechanism to verify the sizes of the representation
for CAO types. The specification file can give static bounds for the type of
the library, allowing the verification of literals before translation.
-}
module Language.CAO.Platform.Literals where
import Data.Bits
import Data.List (genericSplitAt, genericReplicate)
import Language.CAO.Common.Literal
import Language.CAO.Common.Monad
import Language.CAO.Common.Polynomial
import Language.CAO.Common.Var
import Language.CAO.Index
import Language.CAO.Index.Eval
import Language.CAO.Type
{- |
Size of a data type
-}
data Size
= Generic -- ^ Not defined (don't care or unbounded)
| Simple Integer -- ^ Size of types other than matrices
| MSize Integer Integer -- ^ Size of a matrix (row, column)
deriving (Eq, Show)
--------------------------------------------------------------------------------
-- Integer usage
-- Int : 5
-- Bits : 5
-- Mod : 4
data LitCheck = LitCheck {
dynamic :: Bool -- Bits, Mod (Int is always dynamic)
, splitC :: Bool
, vi1 :: Integer
, vi2 :: Integer
, vi3 :: Integer
, vi4 :: Integer
, vi5 :: Integer
{-
dynamic :: Bool, -- Bits, Mod (Int is always dynamic)
min :: Integer, -- Int (value)
max :: Integer, -- Int (value), Bits (size), Mod (module)
shiftA :: Int, -- Int, Mod
mask :: Integer, -- Int, Mod
nrChunks :: Int, -- Int, Mod, Bits
nrPadd :: Int, -- Bits
remBits :: Int, -- Bits
chunkSize :: Integer -- Bits -}
} deriving Show
emptyLitCheck :: LitCheck
emptyLitCheck = LitCheck False False 0 0 0 0 0
setDynamic :: LitCheck -> LitCheck
setDynamic l = l { dynamic = True }
dynamicCheck :: LitCheck -> Bool
dynamicCheck = dynamic
setSplit :: LitCheck -> LitCheck
setSplit l = l { splitC = True }
splitLit :: LitCheck -> Bool
splitLit = splitC
-- Integers
lowerLimSet :: Integer -> LitCheck -> LitCheck
lowerLimSet n l = l { vi1 = n }
lowerLim :: LitCheck -> Integer
lowerLim = vi1
upperLimSet :: Integer -> LitCheck -> LitCheck
upperLimSet n l = l { vi2 = n }
upperLim :: LitCheck -> Integer
upperLim = vi2
shiftASet :: Integer -> LitCheck -> LitCheck
shiftASet n l = l { vi3 = n }
shiftA :: LitCheck -> Integer
shiftA = vi3
-- Integers and Mods
maskSet :: Integer -> LitCheck -> LitCheck
maskSet n l = l { vi4 = n }
mask :: LitCheck -> Integer
mask = vi4
nrChunksSet :: Integer -> LitCheck -> LitCheck
nrChunksSet n l = l { vi5 = n }
nrChunks :: LitCheck -> Integer
nrChunks = vi5
-- Mods
maxModSet :: Integer -> LitCheck -> LitCheck
maxModSet n l = l { vi2 = n }
maxMod :: LitCheck -> Integer
maxMod = vi2
-- Bit strings
maxSizeSet :: Integer -> LitCheck -> LitCheck
maxSizeSet n l = l { vi1 = n }
maxSize :: LitCheck -> Integer
maxSize = vi1
chunkBitSizeSet :: Integer -> LitCheck -> LitCheck
chunkBitSizeSet n l = l { vi2 = n}
chunkBitSize :: LitCheck -> Integer
chunkBitSize = vi2
nrTypChunkSet :: Integer -> LitCheck -> LitCheck
nrTypChunkSet n l = l { vi3 = n }
nrTypChunk :: LitCheck -> Integer
nrTypChunk = vi3
-- Always disjoint with nrTypChunk
nrChunkSet :: Integer -> LitCheck -> LitCheck
nrChunkSet n l = l { vi3 = n }
nrChunk :: LitCheck -> Integer
nrChunk = vi3
remBitsSet :: Integer -> LitCheck -> LitCheck
remBitsSet n l = l { vi4 = n }
remBits :: LitCheck -> Integer
remBits = vi4
nrPaddChunkSet :: Integer -> LitCheck -> LitCheck
nrPaddChunkSet n l = l { vi5 = n }
nrPaddChunk :: LitCheck -> Integer
nrPaddChunk = vi5
--------------------------------------------------------------------------------
type WordSize = Int
type NumberOfWords = Int
type WordsPerChunk = Int
onlyPositiveLim :: (Num a, Bits a) => WordSize -> NumberOfWords -> a
-- shift 2 (wordSize * numberOfWords - 1) == 2 ^ (wordSize * numberOfWords)
onlyPositiveLim wordSize numberOfWords = shift 2 (wordSize * numberOfWords - 1)
-- twosComplementLim nrWr = shift 2 (wordSize * nrWr - 2) -- TODO: expand specification
splitNum :: WordSize -> NumberOfWords -> WordsPerChunk -> LitCheck -> LitCheck
splitNum wordSize numberOfWords wordsPerChunk = let
sa = wordSize * wordsPerChunk
in shiftASet (toInteger sa)
. maskSet (shift 2 (sa - 1) - 1)
. nrChunksSet (toInteger $ numberOfWords `div` wordsPerChunk)
. setSplit
--------------------------------------------------------------------------------
checkInt
:: Maybe WordSize -> Maybe (NumberOfWords, Maybe WordsPerChunk)
-> Maybe LitCheck
-- Nothing case: Assume that the size is irrelevant or unlimited
checkInt Nothing = const Nothing
checkInt (Just wordSize) = fmap (flip worker emptyLitCheck)
where
worker :: (NumberOfWords, Maybe WordsPerChunk) -> LitCheck -> LitCheck
worker (numberOfWords, wordsPerChunk) = let
lim = onlyPositiveLim wordSize numberOfWords
other = maybe id (splitNum wordSize numberOfWords) wordsPerChunk
in other . upperLimSet (lim - 1) . lowerLimSet 0 . setDynamic
-- XXX: Is there a difference between signed and unsigned bits?
checkBits
:: Maybe WordSize -> Size -> Maybe (NumberOfWords, Maybe WordsPerChunk)
-> Maybe LitCheck
-- Nothing case: Assume that the size is irrelevant or unlimited
checkBits Nothing _ = const Nothing
checkBits (Just wordSize) strLength = maybe Nothing worker
where
worker (numberOfWords, mbWrChunk) = checkStaticSize
aux
(Just . maybe id auxChunkGeneric mbWrChunk . maxSizeSet lim)
lim strLength
where
aux litcheck strLen = fmap
(\x -> auxChunkSimple x strLen litcheck) mbWrChunk
auxChunkGeneric wordsPerChunk =
nrChunksSet (numberOfChunks wordsPerChunk) . auxChunk wordsPerChunk
auxChunkSimple wordsPerChunk strLen = let
len = chunkLength wordsPerChunk
(nrCompleteChunks, nrPaddChunks, nrPaddBits) =
bsParam (numberOfChunks wordsPerChunk) len strLen
in nrTypChunkSet nrCompleteChunks
. remBitsSet nrPaddBits
. nrPaddChunkSet nrPaddChunks
. auxChunk wordsPerChunk
auxChunk wordsPerChunk =
chunkBitSizeSet (chunkLength wordsPerChunk) . setSplit
chunkLength = toInteger . (wordSize *)
numberOfChunks = toInteger . div numberOfWords
lim = toInteger $ wordSize * numberOfWords
bsParam :: Integer -> Integer -> Integer -> (Integer, Integer, Integer)
bsParam numberOfChunks chunkLength strLen = let
(nrCompleteChunks, nrRemBits) = divMod strLen chunkLength
nrPaddBits = if nrRemBits == 0 then 0 else chunkLength - nrRemBits
nrPaddChunks = numberOfChunks
- nrCompleteChunks
- (if nrPaddBits == 0 then 0 else 1)
in (nrCompleteChunks, nrPaddChunks, nrPaddBits)
checkMod
:: Maybe WordSize -> Size -> Maybe (NumberOfWords, Maybe WordsPerChunk)
-> Maybe LitCheck
checkMod Nothing _ = const Nothing
-- Nothing case: Assume that the size is irrelevant or unlimited
checkMod (Just wordSize) strLength = maybe Nothing worker
where
worker :: (NumberOfWords, Maybe WordsPerChunk) -> Maybe LitCheck
worker (numberOfWords, mbWrChunk) = let
auxChunk = splitNum wordSize numberOfWords
lim = onlyPositiveLim wordSize numberOfWords
in checkStaticSize
(\litcheck -> const $ fmap (flip auxChunk litcheck) mbWrChunk)
(Just . maybe id auxChunk mbWrChunk . maxModSet lim)
lim strLength
{-
When the size of the type is defined statically and we have the maximum limit,
we can determine if the representation is enough to hold all the values of the
type. Otherwise, the compilation fails.
If the size is not defined, then dinamic verification is set. In this context,
dynamic does not mean during execution, but during translation, i.e., all
literal values are verified for the bounds. In the future, this can also be
used in an abstract analysis of all values.
-}
checkStaticSize
:: (LitCheck -> Integer -> Maybe LitCheck)
-> (LitCheck -> Maybe LitCheck)
-> Integer -> Size
-> Maybe LitCheck
checkStaticSize fsimple fgeneric lim strLength =
case strLength of
-- The size can be determined statically
Simple strLen -> if strLen <= lim
then fsimple emptyLitCheck strLen
else error $ "The platform type `" -- ++ nameInPlat ts ++ "' does not have \
-- \ enough precision to hold mods with module " ++ show strLength
-- No static information about the bit strings.
-- Dynamic testing required
Generic -> fgeneric $ setDynamic emptyLitCheck
_ -> error "Not expected matrix size"
--------------------------------------------------------------------------------
checkILit
:: CaoMonad m
=> LitCheck -> Integer
-> m [Integer]
checkILit lspec num =
if lowerLim lspec <= num && num <= upperLim lspec
then if splitLit lspec
then return $ chopNumLit (nrChunks lspec) (mask lspec) (fromInteger $ shiftA lspec) num
else return [num]
else error $ "Literal outside of the range of representation: " ++ show num
chopNumLit :: Integer -> Integer -> Int -> Integer -> [Integer]
chopNumLit 0 _ _ _ = []
chopNumLit n msk shft num = let
cn = num .&. msk
num' = num `shiftR` shft
in chopNumLit (n-1) msk shft num' ++ [cn] -- Endianness
checkBSLit :: CaoMonad m => LitCheck -> Type Var -> [Bool] -> m [[Bool]]
checkBSLit lspec (Bits sign ilen) bits =
case evalExpr ilen of
IInt len -> if dynamicCheck lspec
then if len <= maxSize lspec
then if splitLit lspec
then let chunkLength = chunkBitSize lspec
(nrCompleteChunks, nrPaddBits, nrPaddChunks) = bsParam (nrChunks lspec) chunkLength len
in return $ chopBSLit sign chunkLength nrCompleteChunks nrPaddChunks nrPaddBits bits
else return [bits]
else error $ "Literal outside of the range of representation: " -- ++ bits
-- If it is not dynamic AND the specification exists, it MUST be choked
else return $ chopBSLit sign (chunkBitSize lspec) (nrTypChunk lspec) (nrPaddChunk lspec) (nrPaddChunk lspec) bits
_ -> error $ "Not expected index"
checkBSLit _ _ _ = error $ "Not expected type"
chopBSLit :: Sign -> Integer -> Integer -> Integer -> Integer -> [Bool] -> [[Bool]]
chopBSLit sign chunkLength nrCompleteChunks nrPaddChunks nrPaddBits bits =
worker [] bits nrCompleteChunks
where
worker acc rest 0 = padd acc rest
worker acc bstr nc = let
(pref, rest) = genericSplitAt chunkLength bstr
in worker (pref : acc) rest (nc - 1)
padd acc bstr = let
ebit = case sign of
S -> last
U -> const False
in if nrPaddBits == 0
then genericReplicate nrPaddChunks (blankChunk (ebit $ head acc)) ++ acc
else let
ebit' = ebit bstr
in genericReplicate nrPaddChunks (blankChunk ebit') ++
(bstr ++ genericReplicate nrPaddBits ebit') :
acc
blankChunk = genericReplicate chunkLength
checkPLit
:: CaoMonad m
=> LitCheck -> Type Var -> Pol Var
-> m [Pol Var]
checkPLit lspec (Mod Nothing Nothing (Pol [Mon (CoefI idx) EZero]))
(Pol [Mon (CoefI cexp) EZero]) =
case (evalExpr idx, evalExpr cexp) of
(IInt i, IInt c) -> if dynamicCheck lspec
then if i <= maxMod lspec
then return (aux c)
else error $ "Literal outside of the range of representation"
else return (aux c)
_ -> error "checkPLit: Not literal!"
where
aux = map (\ c' -> Pol [Mon (CoefI (IInt c')) EZero]) .
chopNumLit (nrChunks lspec) (mask lspec) (fromInteger $ shiftA lspec)
checkPLit _ _ _ = error $ "Not supported literal of polynomial"