crackNum 2.4 → 3.0
raw patch · 13 files changed
+576/−1007 lines, 13 filesdep +directorydep +filepathdep +libBFdep −FloatingHexdep −arraydep ~base
Dependencies added: directory, filepath, libBF, process, sbv, tasty, tasty-golden
Dependencies removed: FloatingHex, array
Dependency ranges changed: base
Files
- CHANGES.md +11/−1
- COPYRIGHT +2/−2
- Data/Numbers/CrackNum.hs +0/−273
- Data/Numbers/CrackNum/Data.hs +0/−66
- Data/Numbers/CrackNum/Main.hs +0/−288
- Data/Numbers/CrackNum/Utils.hs +0/−176
- INSTALL +0/−3
- LICENSE +2/−2
- README.md +86/−86
- crackNum.cabal +22/−30
- crackNum.vim +0/−80
- src/CrackNum/Main.hs +382/−0
- src/CrackNum/TestSuite.hs +71/−0
CHANGES.md view
@@ -1,9 +1,19 @@ * Hackage: <http://hackage.haskell.org/package/crackNum> * GitHub: <http://github.com/LeventErkok/crackNum/> -* Latest Hackage released version: 2.3, 2020-09-05+* Latest Hackage released version: 3.0, 2021-03-29 +### Version 3.0, 2021-03-29++ * A complete rewrite, much simplified, and supporting+ arbitrary precision floats. Some of the old features+ and the library are dropped; so if you rely on the library+ nature of CrackNum, do not upgrade. For other users who+ merely use crackNum as an executable, the new version is+ strongly recommended.+ ### Version 2.4, 2020-09-05+ * Changes required to compile cleanly with GHC 8.10.2 ### Version 2.3, 2018-11-17
COPYRIGHT view
@@ -1,5 +1,5 @@-Copyright (c) 2015-2016, Levent Erkok (erkokl@gmail.com)+Copyright (c) 2015-2021, Levent Erkok (erkokl@gmail.com) All rights reserved. -The crackIEEE754 library is distributed with the BSD3 license. See the LICENSE file+The crackNum executable is distributed with the BSD3 license. See the LICENSE file for details.
− Data/Numbers/CrackNum.hs
@@ -1,273 +0,0 @@------------------------------------------------------------------------------- |--- Module : Data.Numbers.CrackNum--- Copyright : (c) Levent Erkok--- License : BSD3--- Maintainer : erkokl@gmail.com--- Stability : experimental------ A library for formatting/analyzing FP and Integer values--------------------------------------------------------------------------------{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE NamedFieldPuns #-}--{-# OPTIONS_GHC -fno-warn-orphans #-}--module Data.Numbers.CrackNum- ( -- * Internal representation of a Floating-point numbers- FP(..), Precision(..), IPrecision(..), Kind(..)- -- * Creating FP values- , floatToFP, doubleToFP, stringToFP, integerToFP- -- * Displaying FP and Int/Word values- , displayFP, displayWord- -- * Converting between floats and bit-representations- , floatToWord, wordToFloat, doubleToWord, wordToDouble- )- where--import Data.Bits (testBit, setBit, Bits)-import Data.Char (toLower)-import Data.Int (Int8, Int16, Int32, Int64)-import Data.List (intercalate)-import Data.Maybe (isJust, fromJust, fromMaybe, catMaybes)--import Numeric-import Data.Numbers.CrackNum.Data-import Data.Numbers.CrackNum.Utils--import qualified Data.Numbers.FloatingHex as FH--import Data.Word (Word32, Word64)-import Data.Array.ST (newArray, readArray, MArray, STUArray)-import Data.Array.Unsafe (castSTUArray)-import GHC.ST (runST, ST)---- | Crack a Haskell Integer value as the given precision floating value. The Integer should--- be the value corresponding to the bit-pattern as the float is laid out in memory according--- to the IEEE rules.-integerToFP :: Precision -> Integer -> FP-integerToFP HP = crack HP 15 15 [14, 13 .. 10] [9, 8 .. 0]-integerToFP SP = crack SP 127 31 [30, 29 .. 23] [22, 21 .. 0]-integerToFP DP = crack DP 1023 63 [62, 61 .. 52] [51, 50 .. 0]---- | Use Haskell Float to represent SP-spVal :: Bool -> Int -> [Bool] -> Float-spVal dn expVal fracBits = ((2::Float) ** fromIntegral expVal) * add1 frac- where frac = sum $ zipWith (\b i -> if b then (2::Float)**(-(fromIntegral (i::Int))) else 0) fracBits [1..]- add1 | dn = id- | True = (1+)---- | Use Haskell Double to represent DP-dpVal :: Bool -> Int -> [Bool] -> Double-dpVal dn expVal fracBits = ((2::Double) ** fromIntegral expVal) * add1 frac- where frac = sum $ zipWith (\b i -> if b then (2::Double)**(-(fromIntegral (i::Int))) else 0) fracBits [1..]- add1 | dn = id- | True = (1+)---- | Assemble a FP from the given bits and pieces.-crack :: Precision -> Int -> Int -> [Int] -> [Int] -> Integer -> FP-crack vPrec vBias signPos expPos fracPos val- = FP { intVal = val- , prec = vPrec- , sign = vSign- , stExpt = vStoredExp- , expt = vStoredExp - curBias- , bias = curBias- , fracBits = vFracBits- , bitLayOut = layOut [[vSign], vExpBits, vFracBits]- , kind = vKind- }- where bit i = val `testBit` i- vSign = bit signPos- vExpBits = map bit expPos- vStoredExp = bv vExpBits- vFracBits = map bit fracPos- isZero = all0 vExpBits && all0 vFracBits- isDenormal = all0 vExpBits && any1 vFracBits- isInfinity = all1 vExpBits && all0 vFracBits- isNAN = all1 vExpBits && any1 vFracBits- vKind | isZero = Zero vSign- | isInfinity = Infty vSign- | isNAN = if head vFracBits then QNaN else SNaN- | isDenormal = Denormal- | True = Normal- curBias = case vKind of- Denormal -> vBias - 1- _ -> vBias---- | Display a Floating-point number in a nicely formatted way. (This function is also available--- through the 'Show' instance for 'FP', but is provided here for symmetry with 'displayWord'.)-displayFP :: FP -> String-displayFP FP{intVal, prec, sign, stExpt, bias, expt, fracBits, bitLayOut, kind} = intercalate "\n" ls- where ls = [ " " ++ inds1- , " " ++ inds2- , " " ++ inds3- , " Binary: " ++ bitLayOut- , " Hex: " ++ hexDisp allBits- , " Precision: " ++ show prec- , " Sign: " ++ if sign then "Negative" else "Positive"- , " Exponent: " ++ show expt ++ " (Stored: " ++ show stExpt ++ ", Bias: " ++ show bias ++ ")"- , " Hex-float: " ++ hexVal- , " Value: " ++ val- ]- ++ [ " Note: Representation for NaN's is not unique." | isNaNKind kind]-- (inds1, inds2, inds3) = case prec of- HP -> (hpInds1, hpInds2, hpInds3)- SP -> (spInds1, spInds2, spInds3)- DP -> (dpInds1, dpInds2, dpInds3)- allBits = case prec of- HP -> [intVal `testBit` i | i <- startsAt 15]- SP -> [intVal `testBit` i | i <- startsAt 31]- DP -> [intVal `testBit` i | i <- startsAt 63]- where startsAt n = [n, n-1 .. 0]-- dup x = (x, x)-- (val, hexVal) = case kind of- Zero False -> ("+0.0", "0x0p+0")- Zero True -> ("-0.0", "-0x0p+0")- Infty False -> dup "+Infinity"- Infty True -> dup "-Infinity"- SNaN -> dup "NaN (Signaling)"- QNaN -> dup "NaN (Quietized)"- Denormal -> nval True " (DENORMAL)"- Normal -> nval False " (NORMAL)"-- nval dn tag = (s ++ vd ++ tag, s ++ vh)- where s = if sign then "-" else "+"- vd = case prec of- HP -> showGFloat Nothing (spVal dn expt fracBits) ""- SP -> showGFloat Nothing (spVal dn expt fracBits) ""- DP -> showGFloat Nothing (dpVal dn expt fracBits) ""- vh = case prec of- HP -> FH.showHFloat (spVal dn expt fracBits) ""- SP -> FH.showHFloat (spVal dn expt fracBits) ""- DP -> FH.showHFloat (dpVal dn expt fracBits) ""---- | Show instance for FP-instance Show FP where- show = displayFP---- | Display a Integer (signed/unsigned) number in a nicely formatted way-displayWord :: IPrecision -> Integer -> String-displayWord iprec intVal = intercalate "\n" ls- where (sg, sz) = sgSz iprec- ls = [ " " ++ fromJust inds1 | isJust inds1]- ++ [ " " ++ inds2- , " Binary: " ++ binDisp allBits- , " Hex: " ++ hexDisp allBits- , " Type: " ++ show iprec- ]- ++ [ " Sign: " ++ if signBit then "Negative" else "Positive" | sg]- ++ [ " Value: " ++ val- ]- (inds1, inds2) = case sz of- 8 -> (Nothing, bInds2)- 16 -> (Just wInds1, wInds2)- 32 -> (Just dInds1, dInds2)- 64 -> (Just qInds1, qInds2)- _ -> error $ "displayWord: Unexpected size: " ++ show sz- allBits = [intVal `testBit` i | i <- [sz-1, sz-2 .. 0]]- signBit = head allBits- val | not sg = show intVal- | True = case iprec of- I8 -> show $ adjust (0::Int8)- I16 -> show $ adjust (0::Int16)- I32 -> show $ adjust (0::Int32)- I64 -> show $ adjust (0::Int64)- _ -> error $ "displayWord: Unexpected type: " ++ show iprec- adjust :: Bits a => a -> a- adjust v = foldr (flip setBit) v [i | (i, True) <- zip [0..] (reverse allBits)]---- | Convert the given string to a IEEE number with the required precision-stringToFP :: Precision -> String -> FP-stringToFP precision input- = case precision of- SP -> fromMaybe (error $ "*** stringToFP: Cannot read a valid SP number from: " ++ show input) mbF- DP -> fromMaybe (error $ "*** stringToFP: Cannot read a valid DP number from: " ++ show input) mbD- _ -> error $ "*** stringToFP: Unsupported precision: " ++ show precision- where i = map toLower (dropWhile (== '+') input)- specials :: [(String, (FP, FP))]- specials = [ (s, (floatToFP f, doubleToFP d))- | (s, (f, d)) <- [ ("infinity", ( infinityF, infinityD))- , ("-infinity", (-infinityF, - infinityD))- , ("0", ( 0, 0))- , ("-0", (-0, - 0))- , ("max", ( maxFiniteF, maxFiniteD))- , ("-max", (-maxFiniteF, - maxFiniteD))- , ("min", ( minNormalF, minNormalD))- , ("-min", (-minNormalF, - minNormalD))- , ("epsilon", ( epsilonF, epsilonD))] ]- ++ [ ("ulp", (integerToFP SP 1, integerToFP DP 1))- , ("nan", (integerToFP SP 0x7f800001, integerToFP DP 0x7ff0000000000001))- , ("snan", (integerToFP SP 0x7f800001, integerToFP DP 0x7ff0000000000001))- , ("qnan", (integerToFP SP 0x7fc00000, integerToFP DP 0x7ff8000000000000))- ]-- infinityF, maxFiniteF, minNormalF, epsilonF :: Float- infinityF = 1/0- maxFiniteF = 3.40282347e+38- minNormalF = 1.17549435e-38- epsilonF = 1.19209290e-07-- infinityD, maxFiniteD, minNormalD, epsilonD :: Double- infinityD = 1/0- maxFiniteD = 1.7976931348623157e+308- minNormalD = 2.2250738585072014e-308- epsilonD = 2.2204460492503131e-16-- mbF, mbD :: Maybe FP- (mbF, mbD) = case (i `lookup` specials, rd i :: Maybe Float, rd i :: Maybe Double) of- (Just (f, d), _ , _ ) -> (Just f, Just d)- (Nothing, Just f, Just d) -> (Just (floatToFP f), Just (doubleToFP d))- (Nothing, Just f, _ ) -> (Just (floatToFP f), Nothing)- (Nothing, _, Just d) -> (Nothing, Just (doubleToFP d))- _ -> (Nothing, Nothing)-- rd :: (Read a, FH.FloatingHexReader a) => String -> Maybe a- rd s = case [v | (v, "") <- reads s] ++ catMaybes [FH.readHFloat s] of- [v] -> Just v- _ -> Nothing---- | Turn a Haskell float to the internal detailed FP representation-floatToFP :: Float -> FP-floatToFP = integerToFP SP . toInteger . floatToWord---- | Turn a Haskell double to the internal detailed FP representation-doubleToFP :: Double -> FP-doubleToFP = integerToFP DP . toInteger . doubleToWord------------------------------------------------------------------------------ Reinterpreting float/double as word32/64 and back. Here, we use the--- definitions from the reinterpret-cast package:------ http://hackage.haskell.org/package/reinterpret-cast------ The reason we steal these definitions is to make sure we keep minimal--- dependencies and no FFI requirements anywhere.----------------------------------------------------------------------------- | Reinterpret-casts a `Float` to a `Word32`.-floatToWord :: Float -> Word32-floatToWord x = runST (cast x)-{-# INLINEABLE floatToWord #-}---- | Reinterpret-casts a `Word32` to a `Float`.-wordToFloat :: Word32 -> Float-wordToFloat x = runST (cast x)-{-# INLINEABLE wordToFloat #-}---- | Reinterpret-casts a `Double` to a `Word64`.-doubleToWord :: Double -> Word64-doubleToWord x = runST (cast x)-{-# INLINEABLE doubleToWord #-}---- | Reinterpret-casts a `Word64` to a `Double`.-wordToDouble :: Word64 -> Double-wordToDouble x = runST (cast x)-{-# INLINEABLE wordToDouble #-}--{-# INLINE cast #-}-cast :: (MArray (STUArray s) a (ST s), MArray (STUArray s) b (ST s)) => a -> ST s b-cast x = newArray (0 :: Int, 0) x >>= castSTUArray >>= flip readArray 0
− Data/Numbers/CrackNum/Data.hs
@@ -1,66 +0,0 @@------------------------------------------------------------------------------- |--- Module : Data.Numbers.CrackNum.Data--- Copyright : (c) Levent Erkok--- License : BSD3--- Maintainer : erkokl@gmail.com--- Stability : experimental------ Internal representation of FP values--------------------------------------------------------------------------------module Data.Numbers.CrackNum.Data where---- | Floating point precision-data Precision = HP -- ^ Half precision; 16 bits = 1 sign + 5 exponent + 10 mantissa- | SP -- ^ Single precision; 32 bits = 1 sign + 8 exponent + 23 mantissa- | DP -- ^ Double precision; 64 bits = 1 sign + 11 exponent + 52 mantissa- deriving (Eq, Show)---- | Integer/Word precision-data IPrecision = W8 -- ^ 8-bit unsigned (byte)- | I8 -- ^ 8-bit signed- | W16 -- ^ 16-bit unsigned (word)- | I16 -- ^ 16-bit signed- | W32 -- ^ 32-bit unsigned (double-word)- | I32 -- ^ 32-bit signed- | W64 -- ^ 64-bit unsigned (quad-word)- | I64 -- ^ 64-bit signed- deriving Eq---- | Kinds of floating point values-data Kind = Zero Bool -- ^ Zero: 0. If Bool is true, then this is -0; otherwise +0.- | Infty Bool -- ^ Infinity: oo. If Bool is true, then this is -oo, otherwie +oo.- | SNaN -- ^ The signaling-NaN.- | QNaN -- ^ The quiet-NaN.- | Denormal -- ^ Denormalized number, i.e., leading bit is not 1- | Normal -- ^ Normal value.---- | Determine if we have a NaN value-isNaNKind :: Kind -> Bool-isNaNKind SNaN = True-isNaNKind QNaN = True-isNaNKind _ = False---- | Show instance for integer-precisions-instance Show IPrecision where- show W8 = "Unsigned Byte"- show I8 = "Signed Byte"- show W16 = "Unsigned Word"- show I16 = "Signed Word"- show W32 = "Unsigned Double"- show I32 = "Signed Double"- show W64 = "Unsigned Quad"- show I64 = "Signed Quad"---- | Complete internal representation for a floating-point number-data FP = FP { intVal :: Integer -- ^ The value as represented as a full Integer. Storage purposes only.- , prec :: Precision -- ^ FP precision.- , sign :: Bool -- ^ Sign. If True then negative, otherwise positive.- , stExpt :: Int -- ^ The exponent as it is stored.- , bias :: Int -- ^ The implicit bias of the exponent.- , expt :: Int -- ^ The actual exponent.- , fracBits :: [Bool] -- ^ Bits in the fractional part- , bitLayOut :: String -- ^ Layout representation- , kind :: Kind -- ^ Floating-point kind (i.e., value)- }
− Data/Numbers/CrackNum/Main.hs
@@ -1,288 +0,0 @@------------------------------------------------------------------------------- |--- Module : Main--- Copyright : (c) Levent Erkok--- License : BSD3--- Maintainer : erkokl@gmail.com--- Stability : experimental------ Main entry point for the crackNum executable--------------------------------------------------------------------------------{-# LANGUAGE PatternGuards #-}--module Main(main) where--import Control.Monad (zipWithM_)-import Data.Char (isHexDigit, isDigit)-import Data.Maybe (fromMaybe, listToMaybe, isNothing)-import System.Console.GetOpt (ArgOrder(Permute), getOpt, ArgDescr(..), OptDescr(..), usageInfo)-import System.Environment (getArgs, getProgName)-import System.Exit (exitFailure)--import Data.Numbers.CrackNum-import Data.Numbers.CrackNum.Utils--import Data.Version (showVersion)-import Paths_crackNum (version)--copyRight :: String-copyRight = "(c) Levent Erkok. Released with a BSD3 license."---- | Options accepted by the executable-data Flag = FPType Precision -- ^ Crack as a Floating Point with given precision- | IType IPrecision -- ^ Crack as an Integer with the given number of bits- | ToIEEE String -- ^ Convert to IEEE SP/DP value- | Lanes String -- ^ Number of lanes present in the input, crackNum can guess but it can also be specified.- | Help -- ^ Help- | VIM -- ^ Are we being called from VIM?- | Version -- ^ Version- deriving Eq--options :: [OptDescr Flag]-options = [- Option "" ["hp"] (NoArg (FPType HP)) "16 bit half precision"- , Option "" ["sp"] (NoArg (FPType SP)) "32 bit single precision"- , Option "" ["dp"] (NoArg (FPType DP)) "64 bit double precision"- , Option "" ["sb"] (NoArg (IType I8)) " 8 bit signed byte"- , Option "" ["sw"] (NoArg (IType I16)) "16 bit signed word"- , Option "" ["sd"] (NoArg (IType I32)) "32 bit signed double"- , Option "" ["sq"] (NoArg (IType I64)) "64 bit signed quad"- , Option "" ["ub"] (NoArg (IType W8)) " 8 bit unsigned byte"- , Option "" ["uw"] (NoArg (IType W16)) "16 bit unsigned word"- , Option "" ["ud"] (NoArg (IType W32)) "32 bit unsigned double"- , Option "" ["uq"] (NoArg (IType W64)) "64 bit unsigned quad"- , Option "" ["toIEEE"] (ReqArg ToIEEE "n") "Convert from decimal to IEEE SP/DP formats."- , Option "l" ["lanes"] (ReqArg Lanes "n") "number of lanes"- , Option "" ["vim"] (NoArg VIM) "output in vim friendly format"- , Option "h?" ["help"] (NoArg Help) "print help, with examples"- , Option "v" ["version"] (NoArg Version) "print version info"- ]--helpStr :: String -> String-helpStr pn = usageInfo ("Usage: " ++ pn ++ " precision bit/hex-pattern") options--usage :: String -> IO ()-usage pn = do putStrLn $ helpStr pn- putStrLn "Examples:"- putStrLn ""- putStrLn $ " " ++ pn ++ " --hp fc00"- putStrLn $ " " ++ pn ++ " --sp fc00 abcd"- putStrLn $ " " ++ pn ++ " --dp fc00 abc1 2345 6789"- putStrLn $ " " ++ pn ++ " --sp 01111111110000000000000000000000"- putStrLn $ " " ++ pn ++ " -l2 --hp 01111111110000000000000000000000"- putStrLn $ " " ++ pn ++ " --sb 7f"- putStrLn $ " " ++ pn ++ " --sp --toIEEE=-2.3e6"- putStrLn $ " " ++ pn ++ " --dp --toIEEE=max"- putStrLn $ " " ++ pn ++ " --dp --toIEEE=ulp"- putStrLn ""- putStrLn "Notes:"- putStrLn " - You can use hexadecimal or binary as input."- putStrLn " - You can use _,- or space as a digit to improve readability."- putStrLn " - You can give input for multiple lanes, we will guess the #of lanes for you."- putStrLn " Or, you can specify number of lanes with the -l option."- putStrLn " - For \"toIEEE\" option (case doesn't matter):"- putStrLn " - You can enter a number in decimal notation (like 2.3)"- putStrLn " - You can enter a number in hexadecimal notation (like 0x1.abcp+3)"- putStrLn " - OR, enter one of the following:"- putStrLn " * infinity, -infinity: Positive/Negative infinities"- putStrLn " * nan, snan, qnan: Not-A-Number; signaling/quiet"- putStrLn " * 0, -0: Both kinds of zeros"- putStrLn " * max : The maximum finite positive value"- putStrLn " * -max: The minimum finite negative value"- putStrLn " * min : The minimum normal positive value"- putStrLn " * -min: The maximum normal negative value"- putStrLn " * epsilon: The smallest possible value x s.t. 1+x /= 1."- putStrLn " * ulp: The minimum subnormal value"- exitFailure---- instead of dealing with vimscript, munge our args here.. heh-vimpret :: [String] -> [String]-vimpret args = case break (== "--bv") args of- ([p], "--bv":rest) -> case mkArgs p of- Nothing -> ["--help"]- Just pr -> ("--" ++ pr) : rest- _ -> ["--help"]- where bad = (`elem` ["lanes", "vim", "help"])- validPrecs = filter (not . bad) $ concat [xs | Option _ xs _ _ <- options]- dvalidPrecs = map ('-':) validPrecs- ddvalidPrecs = map ('-':) dvalidPrecs- mkArgs p- | p `elem` validPrecs = Just p- | p `elem` dvalidPrecs = Just (drop 1 p)- | p `elem` ddvalidPrecs = Just (drop 2 p)- | True = Nothing--main :: IO ()-main = do origArgs <- getArgs- origPN <- getProgName-- let -- bugger.. make the args a bit more friendly- friendly :: String -> String- friendly ('-':ns) -- -2/-3 etc become lane stuff- | all isDigit ns- = "-l" ++ ns- friendly ('-':c:cs)- | c `notElem` "-l" = "--" ++ (c:cs)- friendly s = s-- cleanArgs = map friendly origArgs--- (argv, pn) | "--vim" `elem` cleanArgs = ("--vim" : vimpret (filter (/= "--vim") cleanArgs), "CrackNum")- | True = (cleanArgs, origPN)-- case getOpt Permute options argv of- (os, rs, []) -> if Version `elem` os- then putStrLn $ pn ++ " v" ++ showVersion version ++ ", " ++ copyRight- else process pn os rs- (_, _, errs) -> do mapM_ putStrLn errs- putStr $ helpStr pn- where getChosenPrec os = case [p | p@FPType{} <- os] ++ [p | p@IType{} <- os] of- [p] -> Just p- _ -> Nothing- process pn os rs- | Help `elem` os- = do putStrLn $ pn ++ " v" ++ showVersion version ++ ", " ++ copyRight- usage pn- | Just v <- listToMaybe [s | ToIEEE s <- os], null rs, Just (FPType p) <- mbPrec- = putStrLn $ displayFP $ stringToFP p v- | all isDigit lcs && lc > 0, Just p <- mbPrec- = lane pn (VIM `elem` os) lc p rs- | True- = putStr $ helpStr pn- where mbPrec = getChosenPrec os- lcs = fromMaybe (show (guessLaneCount mbPrec (cleanUp (concat rs)))) (listToMaybe (reverse [n | Lanes n <- os]))- lc = read lcs---- Try to guess the lane count if not given; if we can't we'll just return 1-guessLaneCount :: Maybe Flag -> String -> Int-guessLaneCount mbp s- | not (allHex || allBin) = 1- | isNothing mbp = 1- | Just (FPType p) <- mbp = guessFP ls p- | Just (IType p) <- mbp = guessIP ls p- | True = 1- where allHex = all isHexDigit s- allBin = all isBinDigit s- ls | allBin = length s- | True = 4 * length s---- | Guess lane count for floating-point-guessFP :: Int -> Precision -> Int-guessFP 0 _ = 1-guessFP l p- | r == 0 = q- | True = 1- where sz = fpSz p- (q, r) = l `quotRem` sz---- | Guess lane count for integer-guessIP :: Int -> IPrecision -> Int-guessIP 0 _ = 1-guessIP l p- | r == 0 = q- | True = 1- where (_, sz) = sgSz p- (q, r) = l `quotRem` sz---- | Do the lane..-lane :: String -> Bool -> Int -> Flag -> [String] -> IO ()-lane pn _ 1 f rs = dispatch pn f rs-lane pn vim n f rs- | ls `mod` n /= 0- = help $ "Input length " ++ show ls ++ " is not a multiple of lane count: " ++ show n- | True- = zipWithM_ cvt [n-1, n-2 .. 0] (cluster n s)- where s = cleanUp (concat rs)- ls = length s- help m = do putStrLn $ pn ++ ": " ++ m- usage pn- cvt i r = do putStrLn $ vimMarker vim ++ mkHeader (Just i) f- dispatch pn f [r]--vimMarker :: Bool -> String-vimMarker False = ""-vimMarker True = "VIM "---- | Display the ruler..-mkHeader :: Maybe Int -> Flag -> String-mkHeader mbl f = take (fit len) divider- where divider- | Just l <- mbl = "== Lane: " ++ show l ++ ' ' : repeat '='- | True = repeat '='- fit n = 30 `max` (n + 19)- len = case f of- FPType p -> fpLen p- IType p -> ipLen p- _ -> 80- get p xs = fromMaybe 78 (lookup p xs)- fpLen p = get p [ (HP, 8 + length hpInds3)- , (SP, length spInds3)- , (DP, length dpInds3)- ]- ipLen p = get p [ (W8, length bInds2), (I8, length bInds2)- , (W16, length wInds2), (I16, length wInds2)- , (W32, length dInds2), (I32, length dInds2)- , (W64, length qInds2), (I32, length qInds2)- ]--dispatch :: String -> Flag -> [String] -> IO ()-dispatch pn p@FPType{} rs = unpack pn p (unwords rs)-dispatch pn p@IType{} rs = unpack pn p (unwords rs)-dispatch pn _ _ = usage pn--unpack :: String -> Flag -> String -> IO ()-unpack pn precFlag orig =- case (precFlag, length s, allHex, allBin) of- (FPType HP, 4, True, _ ) -> putStrLn $ displayFP $ integerToFP HP hexVal- (FPType HP, 16, _ , True) -> putStrLn $ displayFP $ integerToFP HP binVal- (FPType SP, 8, True, _ ) -> putStrLn $ displayFP $ integerToFP SP hexVal- (FPType SP, 32, _ , True) -> putStrLn $ displayFP $ integerToFP SP binVal- (FPType DP, 16, True, _ ) -> putStrLn $ displayFP $ integerToFP DP hexVal- (FPType DP, 64, _ , True) -> putStrLn $ displayFP $ integerToFP DP binVal- (IType I8, 2, True, _ ) -> putStrLn $ displayWord I8 hexVal- (IType I8, 8, _ , True) -> putStrLn $ displayWord I8 binVal- (IType W8, 2, True, _ ) -> putStrLn $ displayWord W8 hexVal- (IType W8, 8, _ , True) -> putStrLn $ displayWord W8 binVal- (IType I16, 4, True, _ ) -> putStrLn $ displayWord I16 hexVal- (IType I16, 16, _ , True) -> putStrLn $ displayWord I16 binVal- (IType W16, 4, True, _ ) -> putStrLn $ displayWord W16 hexVal- (IType W16, 16, _ , True) -> putStrLn $ displayWord W16 binVal- (IType I32, 8, True, _ ) -> putStrLn $ displayWord I32 hexVal- (IType I32, 32, _ , True) -> putStrLn $ displayWord I32 binVal- (IType W32, 8, True, _ ) -> putStrLn $ displayWord W32 hexVal- (IType W32, 32, _ , True) -> putStrLn $ displayWord W32 binVal- (IType I64, 16, True, _ ) -> putStrLn $ displayWord I64 hexVal- (IType I64, 64, _ , True) -> putStrLn $ displayWord I64 binVal- (IType W64, 16, True, _ ) -> putStrLn $ displayWord W64 hexVal- (IType W64, 64, _ , True) -> putStrLn $ displayWord W64 binVal- _ -> if not (null orig)- then do case precFlag of- FPType HP -> putStrLn $ "ERROR: HP format requires 4 hex or 16 bin digits, received: " ++ what- FPType SP -> putStrLn $ "ERROR: SP format requires 8 hex or 32 bin digits, received: " ++ what- FPType DP -> putStrLn $ "ERROR: DP format requires 16 hex or 64 bin digits, received: " ++ what- IType I8 -> putStrLn $ "ERROR: Signed byte format requires 2 hex or 8 bin digits, received: " ++ what- IType I16 -> putStrLn $ "ERROR: Signed word format requires 4 hex or 16 bin digits, received: " ++ what- IType I32 -> putStrLn $ "ERROR: Signed double format requires 8 hex or 32 bin digits, received: " ++ what- IType I64 -> putStrLn $ "ERROR: Signed quad format requires 16 hex or 64 bin digits, received: " ++ what- IType W8 -> putStrLn $ "ERROR: Unsigned byte format requires 2 hex or 8 bin digits, received: " ++ what- IType W16 -> putStrLn $ "ERROR: Unsigned word format requires 4 hex or 16 bin digits, received: " ++ what- IType W32 -> putStrLn $ "ERROR: Unsigned double format requires 8 hex or 32 bin digits, received: " ++ what- IType W64 -> putStrLn $ "ERROR: Unsigned quad format requires 16 hex or 64 bin digits, received: " ++ what- _ -> putStrLn $ "ERROR: Illegal input received: " ++ what- putStrLn $ "\nUse '" ++ pn ++ " --help' for detailed help."- exitFailure- else usage pn- where s = cleanUp orig- ls = length s- allHex = all isHexDigit s- allBin = all isBinDigit s- hexVal = readB16 s- binVal = readB2 s- what | allHex && allBin = show ls ++ " bin/hex digit" ++ plural- | allHex = show ls ++ " hex digit" ++ plural- | allBin = show ls ++ " bin digit" ++ plural- | True = show ls ++ " bogus digit" ++ plural- where plural | ls == 1 = ""- | True = "s"
− Data/Numbers/CrackNum/Utils.hs
@@ -1,176 +0,0 @@------------------------------------------------------------------------------- |--- Module : Data.Numbers.CrackNum.Utils--- Copyright : (c) Levent Erkok--- License : BSD3--- Maintainer : erkokl@gmail.com--- Stability : experimental------ Various utils and sundry--------------------------------------------------------------------------------module Data.Numbers.CrackNum.Utils where--import Data.Char (toLower)-import Data.List (genericIndex)-import Numeric--import Data.Numbers.CrackNum.Data (Precision(..), IPrecision(..))---- | Returns True if all bits are False-all0 :: [Bool] -> Bool-all0 = all not---- | Returns True if all bits are True-all1 :: [Bool] -> Bool-all1 = and---- | Returns True if any bit is True-any1 :: [Bool] -> Bool-any1 = (True `elem`)---- | Lay out a sequence of separated bools as a nicely formatted binary number-layOut :: [[Bool]] -> String-layOut = unwords . map b2s---- | Binary to String conversion-b2s :: [Bool] -> String-b2s bs = concat [if b then "1" else "0" | b <- bs]---- | Test whether a digit is binary-isBinDigit :: Char -> Bool-isBinDigit = (`elem` "01")---- | Convert from binary char digit to value-binDigit :: Char -> Int-binDigit '0' = 0-binDigit '1' = 1-binDigit c = error $ "binDigit: recevied: " ++ show c---- | Read a number in base 16-readB16 :: String -> Integer-readB16 s = case readHex s of- [(v, "")] -> v- _ -> error $ "Invalid hex input: " ++ show s---- | Read a number in base 2-readB2 :: String -> Integer-readB2 s = case readInt 2 isBinDigit binDigit s of- [(v, "")] -> v- _ -> error $ "Invalid binary input: " ++ show s---- | Display a binary number in groups of 4-binDisp :: [Bool] -> String-binDisp = grpBy4 . b2s---- | Group in chunks of 44-grpBy4 :: String -> String-grpBy4 = grp False- where grp _ [] = []- grp sep xs = let (f, r) = splitAt 4 xs in (if sep then " " else "") ++ f ++ grp True r---- | Display a binary number in groups of 4, in hexadecimal format-hexDisp :: [Bool] -> String-hexDisp = grpBy4 . chunkHex- where chunkHex [] = []- chunkHex xs = let (f, r) = splitAt 4 xs in (letters `genericIndex` (bv f :: Int)) : chunkHex r- letters = ['0' .. '9'] ++ ['A' .. 'F']---- | Cluster a list into given size chunks-cluster :: Int -> [a] -> [[a]]-cluster n is = go is- where s = length is `div` n- go [] = []- go xs = let (f, r) = splitAt s xs in f : go r---- | Big-endian num converter-bv :: Num a => [Bool] -> a-bv = foldr (\b a -> 2 * a + b2i b) 0 . reverse- where b2i b = if b then 1 else 0---- | Drop unnecessary parts from input. This enables the user to be able to give data more easily-cleanUp :: String -> String-cleanUp = map toLower . filter (not . ignorable)- where ignorable = (`elem` " _-")--------------------------------------------------------------------------------------------------------- Rulers--------------------------------------------------------------------------------------------------------- | Half-precision ruler, line 1-hpInds1 :: String--- | Half-precision ruler, line 2-hpInds2 :: String--- | Half-precision ruler, line 3-hpInds3 :: String--hpInds1 = "1 0"-hpInds2 = "5 43210 9876543210"-hpInds3 = "S -E5-- ---F10----"---- | Single-precision ruler, line 1-spInds1 :: String--- | Single-precision ruler, line 2-spInds2 :: String--- | Single-precision ruler, line 3-spInds3 :: String--spInds1 = "3 2 1 0"-spInds2 = "1 09876543 21098765432109876543210"-spInds3 = "S ---E8--- ----------F23----------"---- | Double-precision ruler, line 1-dpInds1 :: String--- | Double-precision ruler, line 2-dpInds2 :: String--- | Double-precision ruler, line 3-dpInds3 :: String--dpInds1 = "6 5 4 3 2 1 0"-dpInds2 = "3 21098765432 1098765432109876543210987654321098765432109876543210"-dpInds3 = "S ----E11---- ------------------------F52-------------------------"---- | Byte-precision ruler, line 2 (note that no line 1 is needed!)-bInds2 :: String-bInds2 = "7654 3210"---- | Word-precision ruler, line 1-wInds1 :: String--- | Word-precision ruler, line 2-wInds2 :: String--wInds1 = "1 0"-wInds2 = "5432 1098 7654 3210"---- | Double-word-precision ruler, line 1-dInds1 :: String--- | Double-word-precision ruler, line 2-dInds2 :: String--dInds1 = "3 2 1 0"-dInds2 = "1098 7654 3210 9876 5432 1098 7654 3210"---- | Quad-word-precision ruler, line 1-qInds1 :: String--- | QuadDouble-word-precision ruler, line 2-qInds2 :: String--qInds1 = "6 5 4 3 2 1 0"-qInds2 = "3210 9876 5432 1098 7654 3210 9876 5432 1098 7654 3210 9876 5432 1098 7654 3210"---- | Convert Floating point precision to corresponding number of bits-fpSz :: Precision -> Int-fpSz HP = 16-fpSz SP = 32-fpSz DP = 64---- | Convert Integer precision to whether it's signed and how many bits-sgSz :: IPrecision -> (Bool, Int)-sgSz W8 = (False, 8)-sgSz I8 = (True, 8)-sgSz W16 = (False, 16)-sgSz I16 = (True, 16)-sgSz W32 = (False, 32)-sgSz I32 = (True, 32)-sgSz W64 = (False, 64)-sgSz I64 = (True, 64)
− INSTALL
@@ -1,3 +0,0 @@-The crackNum library can be installed simply by issuing cabal install like this:-- cabal install crackNum
LICENSE view
@@ -1,6 +1,6 @@-crackIEEE754: Cracking various Floating/Integer values+crackNum: Cracking various Floating/Integer values -Copyright (c) 2015-2016, Levent Erkok (erkokl@gmail.com)+Copyright (c) 2015-2021, Levent Erkok (erkokl@gmail.com) All rights reserved. Redistribution and use in source and binary forms, with or without
README.md view
@@ -1,99 +1,99 @@-## CrackNum: Decode/Encode IEE754 Numbers+## Decode/Encode Integers, Words, and IEE754 Floats [](http://hackage.haskell.org/package/crackNum) [](http://travis-ci.org/LeventErkok/crackNum) -CrackNum now comes with vim bindings, see http://github.com/LeventErkok/crackNum/blob/master/crackNum.vim--### Command line options:+```+Usage: crackNum value OR binary/hex-pattern+ -i N Signed integer of N-bits+ -w N Unsigned integer of N-bits+ -f fp Floating point format fp+ -r rm Rounding mode to use. If not given, Nearest-ties-to-Even.+ -h, -? --help print help, with examples+ -v --version print version info - crackNum v2.3, (c) Levent Erkok. Released with a BSD3 license.- Usage: crackNum precision bit/hex-pattern- --hp 16 bit half precision- --sp 32 bit single precision- --dp 64 bit double precision- --sb 8 bit signed byte- --sw 16 bit signed word- --sd 32 bit signed double- --sq 64 bit signed quad- --ub 8 bit unsigned byte- --uw 16 bit unsigned word- --ud 32 bit unsigned double- --uq 64 bit unsigned quad- --toIEEE=n Convert from decimal to IEEE SP/DP formats.- -l n --lanes=n number of lanes- --vim output in vim friendly format- -h, -? --help print help, with examples- -v --version print version info- - Examples:- - crackNum --hp fc00- crackNum --sp fc00 abcd- crackNum --dp fc00 abc1 2345 6789- crackNum --sp 01111111110000000000000000000000- crackNum -l2 --hp 01111111110000000000000000000000- crackNum --sb 7f- crackNum --sp --toIEEE=-2.3e6- crackNum --dp --toIEEE=max- crackNum --dp --toIEEE=ulp- - Notes:- - You can use hexadecimal or binary as input.- - You can use _,- or space as a digit to improve readability.- - You can give input for multiple lanes, we will guess the #of lanes for you.- Or, you can specify number of lanes with the -l option.- - For "toIEEE" option (case doesn't matter):- - You can enter a number in decimal notation (like 2.3)- - You can enter a number in hexadecimal notation (like 0x1.abcp+3)- - OR, enter one of the following:- * infinity, -infinity: Positive/Negative infinities- * nan, snan, qnan: Not-A-Number; signaling/quiet- * 0, -0: Both kinds of zeros- * max : The maximum finite positive value- * -max: The minimum finite negative value- * min : The minimum normal positive value- * -min: The maximum normal negative value- * epsilon: The smallest possible value x s.t. 1+x /= 1.- * ulp: The minimum subnormal value+Examples:+ Encoding:+ crackNum -i4 -- -2 -- encode as 4-bit signed integer+ crackNum -w4 2 -- encode as 4-bit unsigned integer+ crackNum -f3+4 2.5 -- encode as float with 3 bits exponent, 4 bits significand+ crackNum -f3+4 2.5 -rRTZ -- encode as above, but use RTZ rounding mode.+ crackNum -fbp 2.5 -- encode as a brain-precision float+ crackNum -fdp 2.5 -- encode as a double-precision float -### Example: Decoding single-precision numbers on two lanes+ Decoding:+ crackNum -i4 0b0110 -- decode as 4-bit signed integer, from binary+ crackNum -w4 0xE -- decode as 4-bit unsigned integer, from hex+ crackNum -f3+4 0b0111001 -- decode as float with 3 bits exponent, 4 bits significand+ crackNum -fbp 0x000F -- decode as a brain-precision float+ crackNum -fdp 0x8000000000000000 -- decode as a double-precision float - $ crackNum --sp fc00 abc1 7F80 0001- == Lane: 1 ==========================================- 3 2 1 0- 1 09876543 21098765432109876543210- S ---E8--- ----------F23----------- Binary: 1 11111000 00000001010101111000001- Hex: FC00 ABC1- Precision: SP- Sign: Negative- Exponent: 121 (Stored: 248, Bias: 127)- Hex-float: -0x1.015782p121- Value: -2.6723903e36 (NORMAL)- == Lane: 0 ==========================================- 3 2 1 0- 1 09876543 21098765432109876543210- S ---E8--- ----------F23----------- Binary: 0 11111111 00000000000000000000001- Hex: 7F80 0001- Precision: SP- Sign: Positive- Exponent: 128 (Stored: 255, Bias: 127)- Hex-float: NaN (Signaling)- Value: NaN (Signaling)- Note: Representation for NaN's is not unique.+ Notes:+ - For encoding:+ - Use -- to separate your argument if it's a negative number.+ - For floats: You can pass in NaN, Inf, -0, -Inf etc as the argument, along with a decimal float.+ - For decoding:+ - Use hexadecimal (0x) or binary (0b) as input. Input must have one of these prefixes.+ - You can use _,- or space as a digit to improve readability for the pattern to be decoded+``` -### Example: Encoding a float as a IEEE754 single-precision bit-pattern+VIM users: You can use the https://github.com/LeventErkok/crackNum/blob/master/crackNum.vim file to+use CrackNum directly from VIM. Simply locate your cursor on the text to crack, and use the+command `:CrackNum options`. - $ crackNum --sp --toIEEE=-2.3e6+### Example: Encode a decimal numer as a single-precision IEEE754 number+```+$ crackNum -fsp -- -2.3e6+Satisfiable. Model:+ ENCODED = -2300000.0 :: Float 3 2 1 0 1 09876543 21098765432109876543210- S ---E8--- ----------F23----------- Binary: 1 10010100 00011000110000110000000- Hex: CA0C 6180- Precision: SP+ S ---E8--- ----------S23----------+ Binary layout: 1 10010100 00011000110000110000000+ Hex layout: CA0C 6180+ Precision: Single Sign: Negative Exponent: 21 (Stored: 148, Bias: 127)- Hex-float: -0x1.18c3p21- Value: -2300000.0 (NORMAL)+ Classification: FP_NORMAL+ Binary: -0b1.0001100011000011p+21+ Octal: -0o1.061414p+21+ Decimal: -2300000.0+ Hex: -0x2.3186p+20+ Rounding mode: RNE: Round nearest ties to even.+```++### Example: Decode a double-precision IEEE754 number from memory-layout+```+$ crackNum -fdp 0xfc00 abc1 7F80 0001+Satisfiable. Model:+ DECODED = -2.0307920360962302e289 :: Double+ 6 5 4 3 2 1 0+ 3 21098765432 1098765432109876543210987654321098765432109876543210+ S ----E11---- ------------------------S52-------------------------+ Binary layout: 1 11111000000 0000101010111100000101111111100000000000000000000001+ Hex layout: FC00 ABC1 7F80 0001+ Precision: Double+ Sign: Negative+ Exponent: 961 (Stored: 1984, Bias: 1023)+ Classification: FP_NORMAL+ Binary: -0b1.0000101010111100000101111111100000000000000000000001p+961+ Octal: -0o2.05274057740000001p+960+ Decimal: -2.0307920360962302e289+ Hex: -0x2.15782FF000002p+960+```++### Example: Encode an integer as a 7-bit signed word+```+$ crackNum -i7 12+Satisfiable. Model:+ ENCODED = 12 :: IntN 7+ 654 3210+ Binary layout: 000 1100+ Hex layout: 0C+ Type: Signed 7-bit 2's complement integer+ Sign: Positive+ Binary: 0b1100+ Octal: 0o14+ Decimal: 12+ Hex: 0xc+```
crackNum.cabal view
@@ -1,40 +1,32 @@-Name: crackNum-Version: 2.4-Synopsis: Crack various integer, floating-point data formats-Description: Crack HP, SP and DP floats and 8, 16, 32, 64 bit words and integers.+Cabal-version : 2.2+Name : crackNum+Version : 3.0+Synopsis : Crack various integer and floating-point data formats+Description : Crack IEEE-754 float formats and arbitrary sized words and integers, showing the layout. . For details, please see: <http://github.com/LeventErkok/crackNum/>-License: BSD3-License-file: LICENSE-Author: Levent Erkok-Homepage: http://github.com/LeventErkok/CrackNum-Maintainer: erkokl@gmail.com-Copyright: Levent Erkok-Category: Tools-Build-type: Simple-Cabal-version: 1.14-Extra-Source-Files: INSTALL, README.md, COPYRIGHT, CHANGES.md, crackNum.vim+License : BSD-3-Clause+License-file : LICENSE+Author : Levent Erkok+Homepage : http://github.com/LeventErkok/CrackNum+Maintainer : erkokl@gmail.com+Copyright : Levent Erkok+Category : Tools+Build-type : Simple+Extra-Source-Files : README.md, COPYRIGHT, CHANGES.md -Tested-With : GHC==8.10.2+Tested-With : GHC==9.0.1 source-repository head type: git location: git://github.com/LeventErkok/crackNum.git Executable crackNum- main-is : Data/Numbers/CrackNum/Main.hs- ghc-options : -Wall+ main-is : CrackNum/Main.hs default-language: Haskell2010- build-depends: base >= 4.11 && < 5, array, FloatingHex- other-modules: Data.Numbers.CrackNum- , Data.Numbers.CrackNum.Utils- , Data.Numbers.CrackNum.Data- , Paths_crackNum--Library- ghc-options : -Wall- default-language: Haskell2010- Build-Depends : base >= 4 && < 5, array >= 0.4.0.1, FloatingHex >= 0.4- Exposed-modules : Data.Numbers.CrackNum- other-modules : Data.Numbers.CrackNum.Utils- , Data.Numbers.CrackNum.Data+ hs-source-dirs : src+ ghc-options : -Wall -Wunused-packages+ build-depends : base >= 4.11 && < 5, libBF, sbv >= 8.14+ , tasty, tasty-golden, filepath, directory, process+ other-modules : Paths_crackNum, CrackNum.TestSuite+ autogen-modules : Paths_crackNum
− crackNum.vim
@@ -1,80 +0,0 @@-""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""-" VI interface to crackNum-"-" Copyright : (c) Levent Erkok-" License : BSD3-" Maintainer : erkokl@gmail.com-"-" INSTALLATION: Put this file in a convenient location (typically your .vim directory),-" and put "so crackNum.vim" in your .vimrc file. (With the appropriate path.)-"-" Once you restart vim, locate your cursor over a stream of binary/hex digits, and-" enter the command :CrackNum to see further options.-"-" See https://github.com/LeventErkok/CrackNum for details.--let g:crackNumPrecisions = ["hp","sp","dp","sb","sw","sd","sq","ub","uw","ud","uq"]-function! CrackNumComplete(A, L, P)- if empty(a:A)- return g:crackNumPrecisions- else- let out = filter(copy(g:crackNumPrecisions), 'v:val =~ "^' . a:A . '.*"')- if empty(out)- return g:crackNumPrecisions- else- return out- endif-endfunction-function! CrackNum(...)- redraw- let curWord = expand("<cword>")- if empty(curWord)- echoerr "Place the cursor on a bin/hex number to crack!"- return- endif- if empty(a:000)- echo "Cracking \"" . curWord . "\".. Use TAB to see precisions supported."- call inputsave()- let prec = input("Precision> ", "", "customlist,CrackNumComplete")- call inputrestore()- let args = [prec] + copy(a:000)- else- echo "Cracking \"" . curWord . "\".."- let prec = a:1- let args = copy(a:000)- endif-- if index(g:crackNumPrecisions, prec) < 0- echoerr "Unknown precision: \"" . prec . "\"" . ". Must be one of: " . join(g:crackNumPrecisions, ' ')- return- endif-- let l:grepargs = join(['--vim'] + copy(args) + ['--bv', curWord], ' ')- let grepprg_bak=&grepprg- let grepformat_bak=&grepformat- try- let &grepprg="crackNum"- let &grepformat="VIM %m"- silent execute "grep" . " " . l:grepargs- finally- let &grepprg=grepprg_bak- let &grepformat=grepformat_bak- endtry-- botright copen-- redraw!-endfunction-command! -nargs=* -complete=customlist,CrackNumComplete CrackNum call CrackNum(<f-args>)-map @nhp :silent call CrackNum('hp')<CR>-map @nsp :silent call CrackNum('sp')<CR>-map @ndp :silent call CrackNum('dp')<CR>-map @nsb :silent call CrackNum('sb')<CR>-map @nsw :silent call CrackNum('sw')<CR>-map @nsd :silent call CrackNum('sd')<CR>-map @nsq :silent call CrackNum('sq')<CR>-map @nub :silent call CrackNum('ub')<CR>-map @nuw :silent call CrackNum('uw')<CR>-map @nud :silent call CrackNum('ud')<CR>-map @nuq :silent call CrackNum('uq')<CR>-" end crackNum interface
+ src/CrackNum/Main.hs view
@@ -0,0 +1,382 @@+---------------------------------------------------------------------------+-- |+-- Module : Main+-- Copyright : (c) Levent Erkok+-- License : BSD3+-- Maintainer : erkokl@gmail.com+-- Stability : experimental+--+-- Main entry point for the crackNum executable+-----------------------------------------------------------------------------++{-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-}++{-# OPTIONS_GHC -Wall -Werror #-}++module Main(main) where++import System.Environment (getArgs, getProgName, withArgs)+import Data.Char (isDigit, isSpace, toLower)+import Data.List (isPrefixOf, isSuffixOf, unfoldr)++import System.Console.GetOpt (ArgOrder(Permute), getOpt, ArgDescr(..), OptDescr(..), usageInfo)+import System.Exit (exitFailure)+import Text.Read (readMaybe)++import System.IO (hPutStr, stderr)++import LibBF+import Numeric++import Data.SBV hiding (crack)+import Data.SBV.Float hiding (FP)+import Data.SBV.Dynamic hiding (satWith)+import Data.SBV.Internals hiding (free)++import Data.Version (showVersion)+import Paths_crackNum (version)++import CrackNum.TestSuite++-- | Copyright info+copyRight :: String+copyRight = "(c) Levent Erkok. Released with a BSD3 license."++-- | Various precisions we support+data FP = SP -- Single precision+ | DP -- Double precision+ | FP Int Int -- Arbitrary precision with given exponent and significand sizes+ deriving (Show, Eq)++-- | How many bits does this float occupy+fpSize :: FP -> Int+fpSize SP = 32+fpSize DP = 64+fpSize (FP i j) = i+j++-- | Rounding modes we support+data RM = RNE -- ^ Round nearest ties to even+ | RNA -- ^ Round nearest ties to away+ | RTP -- ^ Round towards positive infinity+ | RTN -- ^ Round towards negative infinity+ | RTZ -- ^ Round towards zero+ deriving (Eq, Enum, Bounded)++-- | Show instance for RM, for descriptive purposes+instance Show RM where+ show RNE = "RNE: Round nearest ties to even."+ show RNA = "RNA: Round nearest ties to away."+ show RTP = "RTP: Round towards positive infinity."+ show RTN = "RTN: Round towards negative infinity."+ show RTZ = "RTZ: Round towards zero."++-- Covert to LibBF rounding mode+toLibBFRM :: RM -> RoundMode+toLibBFRM RNE = NearEven+toLibBFRM RNA = NearAway+toLibBFRM RTP = ToPosInf+toLibBFRM RTN = ToNegInf+toLibBFRM RTZ = ToZero++-- | Options accepted by the executable+data Flag = Signed Int -- ^ Crack as a signed word with the given number of bits+ | Unsigned Int -- ^ Crack as an unsigned word with the given number of bits+ | Floating FP -- ^ Crack as the corresponding floating-point type+ | RMode RM -- ^ Rounding mode to use+ | BadFlag [String] -- ^ Bad input+ | Version -- ^ Version+ | Help -- ^ Show help+ deriving (Show, Eq)++-- | Is this a rounding flag?+isRMode :: Flag -> Bool+isRMode RMode{} = True+isRMode _ = False++-- | Given an integer flag value, turn it into a flag+getSize :: String -> (Int -> Flag) -> String -> Flag+getSize flg f n = case readMaybe n of+ Just i | i > 0 -> f i+ | True -> BadFlag ["Option " ++ show flg ++ " requires an integer >= 1. Received: " ++ show n]+ Nothing -> BadFlag ["Option " ++ show flg ++ " requires an integer argument. Received: " ++ show n]++#include "MachDeps.h"++#define FP_MIN_EB 2+#define FP_MIN_SB 2+#if WORD_SIZE_IN_BITS == 64+#define FP_MAX_EB 61+#define FP_MAX_SB 4611686018427387902+#else+#define FP_MAX_EB 29+#define FP_MAX_SB 1073741822+#endif++-- | Given a float flag value, turn it into a flag+getFP :: String -> Flag+getFP "hp" = Floating $ FP 5 11+getFP "bp" = Floating $ FP 8 8+getFP "sp" = Floating SP+getFP "dp" = Floating DP+getFP "qp" = Floating $ FP 15 113+getFP ab = case span isDigit ab of+ (eb@(_:_), '+':r) -> case span isDigit r of+ (sp@(_:_), "") -> mkEBSB (read eb) (read sp)+ _ -> bad+ _ -> bad+ where bad = BadFlag [ "Option " ++ show "-f" ++ " requires one of:"+ , ""+ , " hp: Half float ( 5 + 11)"+ , " bp: Brain float ( 8 + 8)"+ , " sp: Single precision ( 8 + 24)"+ , " dp: Single precision (11 + 53)"+ , " qp: Quad precision (15 + 113)"+ , " a+b: Arbitrary precision ( a + b)"+ , ""+ , "where first number is the number of bits in the exponent"+ , "and the second number is the number of bits in the significand, including the implicit bit."+ ]+ mkEBSB :: Int -> Int -> Flag+ mkEBSB eb sb+ | eb >= FP_MIN_EB && eb <= FP_MAX_EB+ && sb >= FP_MIN_SB && sb <= FP_MAX_SB+ = Floating $ FP eb sb+ | True+ = BadFlag [ "Invalid floating-point precision."+ , ""+ , " Exponent size must be between " ++ show (FP_MIN_EB :: Int) ++ " to " ++ show (FP_MAX_EB :: Int)+ , " Significant size must be between " ++ show (FP_MIN_SB :: Int) ++ " to " ++ show (FP_MAX_SB :: Int)+ , ""+ , "Received: " ++ show eb ++ " " ++ show sb+ ]++getRM :: String -> Flag+getRM "rne" = RMode RNE+getRM "rna" = RMode RNA+getRM "rtp" = RMode RTP+getRM "rtn" = RMode RTN+getRM "rtz" = RMode RTZ+getRM m = BadFlag $ [ "Invalid rounding mode."+ , ""+ , " Must be one of:"+ ]+ ++ [ " " ++ show r | r <- [minBound .. maxBound::RM]]+ ++ [ ""+ , "Received: " ++ m+ ]++-- | Options we accept+pgmOptions :: [OptDescr Flag]+pgmOptions = [+ Option "i" [] (ReqArg (getSize "-i" Signed) "N" ) "Signed integer of N-bits"+ , Option "w" [] (ReqArg (getSize "-w" Unsigned) "N" ) "Unsigned integer of N-bits"+ , Option "f" [] (ReqArg getFP "fp") "Floating point format fp"+ , Option "r" [] (ReqArg (getRM . map toLower) "rm") "Rounding mode to use. If not given, Nearest-ties-to-Even."+ , Option "h?" ["help"] (NoArg Help) "print help, with examples"+ , Option "v" ["version"] (NoArg Version) "print version info"+ ]++-- | Help info+helpStr :: String -> String+helpStr pn = usageInfo ("Usage: " ++ pn ++ " value OR binary/hex-pattern") pgmOptions++-- | Print usage info and examples.+usage :: String -> IO ()+usage pn = putStr $ unlines [ helpStr pn+ , "Examples:"+ , " Encoding:"+ , " " ++ pn ++ " -i4 -- -2 -- encode as 4-bit signed integer"+ , " " ++ pn ++ " -w4 2 -- encode as 4-bit unsigned integer"+ , " " ++ pn ++ " -f3+4 2.5 -- encode as float with 3 bits exponent, 4 bits significand"+ , " " ++ pn ++ " -f3+4 2.5 -rRTZ -- encode as above, but use RTZ rounding mode."+ , " " ++ pn ++ " -fbp 2.5 -- encode as a brain-precision float"+ , " " ++ pn ++ " -fdp 2.5 -- encode as a double-precision float"+ , ""+ , " Decoding:"+ , " " ++ pn ++ " -i4 0b0110 -- decode as 4-bit signed integer, from binary"+ , " " ++ pn ++ " -w4 0xE -- decode as 4-bit unsigned integer, from hex"+ , " " ++ pn ++ " -f3+4 0b0111001 -- decode as float with 3 bits exponent, 4 bits significand"+ , " " ++ pn ++ " -fbp 0x000F -- decode as a brain-precision float"+ , " " ++ pn ++ " -fdp 0x8000000000000000 -- decode as a double-precision float"+ , ""+ , " Notes:"+ , " - For encoding:"+ , " - Use -- to separate your argument if it's a negative number."+ , " - For floats: You can pass in NaN, Inf, -0, -Inf etc as the argument, along with a decimal float."+ , " - For decoding:"+ , " - Use hexadecimal (0x) or binary (0b) as input. Input must have one of these prefixes."+ , " - You can use _,- or space as a digit to improve readability for the pattern to be decoded"+ ]++-- | Terminate early+die :: [String] -> IO a+die xs = do hPutStr stderr $ unlines $ "ERROR:" : map (" " ++) xs+ exitFailure++-- | main entry point to crackNum+crack :: String -> [String] -> IO ()+crack pn argv = case getOpt Permute pgmOptions argv of+ (_, _, errs@(_:_)) -> die $ errs ++ lines (helpStr pn)+ (os, rs, [])+ | Version `elem` os -> putStrLn $ pn ++ " v" ++ showVersion version ++ ", " ++ copyRight+ | Help `elem` os -> usage pn+ | True -> do let rm = case reverse [r | RMode r <- os] of+ (r:_) -> r+ _ -> RNE++ arg = dropWhile isSpace $ unwords rs++ case ([b | BadFlag b <- os], filter (not . isRMode) os) of+ (e:_, _) -> die e+ (_, [Signed n]) -> process (SInt n) rm arg+ (_, [Unsigned n]) -> process (SWord n) rm arg+ (_, [Floating s]) -> process (SFloat s) rm arg+ _ -> usage pn++-- | Kinds of numbers we understand+data NKind = SInt Int -- ^ Signed integer of n bits+ | SWord Int -- ^ Unsigned integer of n bits+ | SFloat FP -- ^ Floating point with precision++-- | main entry point to crackNum+main :: IO ()+main = do argv <- getArgs+ pn <- getProgName++ let rt = "--runTests"++ if rt `elem` argv+ then withArgs (filter (`notElem` [rt, "--"]) argv) runTests+ else crack pn argv++-- | Perform the encoding/decoding+process :: NKind -> RM -> String -> IO ()+process num rm inp = case num of+ SInt n -> print =<< (if decode then di else ei) True n+ SWord n -> print =<< (if decode then di else ei) False n+ SFloat s -> (if decode then df else ef) s+ where decode = any (`isPrefixOf` inp) ["0x", "0b"]++ bitString n = do let isSkippable c = c `elem` "_-" || isSpace c++ (isHex, stream) <- case map toLower (filter (not . isSkippable) inp) of+ '0':'x':rest -> pure (True, rest)+ '0':'b':rest -> pure (False, rest)+ _ -> die [ "Input string must start with 0b or 0x for decoding."+ , "Received prefix: " ++ show (take 2 inp)+ ]++ let cvtBin '1' = pure [True]+ cvtBin '0' = pure [False]+ cvtBin c = die ["Input has a non-binary digit: " ++ show c]++ cvtHex c = case readHex [c] of+ [(v, "")] -> pure $ pad+ $ map (== (1::Int))+ $ reverse+ $ unfoldr (\x -> if x == 0 then Nothing else Just (x `rem` 2, x `div` 2)) v+ _ -> die ["Input has a non-hexadecimal digit: " ++ show c]+ where pad p = replicate (4 - length p) False ++ p++ cvt i | isHex = concat <$> mapM cvtHex i+ | True = concat <$> mapM cvtBin i++ encoded <- cvt stream++ let bits 1 = "one bit"+ bits b = show b ++ " bits"++ case length encoded `compare` n of+ EQ -> pure encoded+ LT -> die ["Input needs to be " ++ show n ++ " bits wide, it's too short by " ++ bits (n - length encoded)]+ GT -> die ["Input needs to be " ++ show n ++ " bits wide, it's too long by " ++ bits (length encoded - n)]++ di :: Bool -> Int -> IO SatResult+ di sgn n = do bs <- bitString n+ satWith z3{crackNum=True} $ p bs+ where p :: [Bool] -> Goal+ p bs = do x <- (if sgn then sIntN else sWordN) n "DECODED"+ mapM_ constrain $ zipWith (.==) (map SBV (svBlastBE x)) (map literal bs)++ ei :: Bool -> Int -> IO SatResult+ ei sgn n = case reads inp of+ [(v :: Integer, "")] -> satWith z3{crackNum=True} $ p v+ _ -> die ["Expected an integer value to decode, received: " ++ show inp]+ where p :: Integer -> Predicate+ p iv = do let k = KBounded sgn n+ v = SBV $ SVal k $ Left $ mkConstCV k iv+ x <- (if sgn then sIntN else sWordN) n "ENCODED"+ pure $ SBV x .== v++ df :: FP -> IO ()+ df fp = do bs <- map literal <$> bitString (fpSize fp)+ case fp of+ SP -> print =<< satWith z3{crackNum=True} (dFloat bs)+ DP -> print =<< satWith z3{crackNum=True} (dDouble bs)+ FP i j -> print =<< satWith z3{crackNum=True} (dFP i j bs)++ dFloat :: [SBool] -> Goal+ dFloat bs = do x <- sFloat "DECODED"+ let (s, e, m) = blastSFloat x+ mapM_ constrain $ zipWith (.==) (s : e ++ m) bs++ dDouble :: [SBool] -> Goal+ dDouble bs = do x <- sDouble "DECODED"+ let (s, e, m) = blastSDouble x+ mapM_ constrain $ zipWith (.==) (s : e ++ m) bs++ dFP :: Int -> Int -> [SBool] -> Goal+ dFP i j bs = do sx <- svNewVar (KFP i j) "DECODED"+ let bits = svBlastBE sx+ mapM_ constrain $ zipWith (.==) (map SBV bits) bs++ convert :: Int -> Int -> (BigFloat, Maybe String)+ convert i j = case s of+ Ok -> (v, Nothing)+ _ -> (v, Just (trim (show s)))+ where bfOpts = allowSubnormal <> rnd (toLibBFRM rm) <> expBits (fromIntegral i) <> precBits (fromIntegral j)+ (v, s) = bfFromString 10 bfOpts inp+ trim xs | "[" `isPrefixOf` xs && "]" `isSuffixOf` xs = init (tail xs)+ | True = xs++ note :: Maybe String -> IO ()+ note mbs = do putStrLn $ " Rounding mode: " ++ show rm+ case mbs of+ Nothing -> pure ()+ Just s -> putStrLn $ " Note: Conversion from " ++ show inp ++ " was not faithful. Status: " ++ s ++ "."++ ef :: FP -> IO ()+ ef SP = case reads (fixup inp) of+ [(v :: Float, "")] -> do print =<< satWith z3{crackNum=True} (p v)+ note $ snd $ convert 8 24+ _ -> ef (FP 8 24)+ where p :: Float -> Predicate+ p f = do x <- sFloat "ENCODED"+ pure $ x .=== literal f++ ef DP = case reads (fixup inp) of+ [(v :: Double, "")] -> do print =<< satWith z3{crackNum=True} (p v)+ note $ snd $ convert 11 53+ _ -> ef (FP 11 53)+ where p :: Double -> Predicate+ p d = do x <- sDouble"ENCODED"+ pure $ x .=== literal d++ ef (FP i j) = do let (v, mbS) = convert i j+ print =<< satWith z3{crackNum=True} (p v)+ note mbS+ where p :: BigFloat -> Predicate+ p bf = do let k = KFP i j+ sx <- svNewVar k "ENCODED"+ pure $ SBV $ sx `svStrongEqual` SVal k (Left (CV k (CFP (fpFromBigFloat i j bf))))++-- | Convert certain strings to more understandable format by read+fixup :: String -> String+fixup inp+ | linp `elem` ["inf", "infinity"] = "Infinity"+ | linp `elem` ["-inf", "-infinity"] = "-Infinity"+ | linp == "nan" = "NaN"+ | linp == "-nan" = "-NaN"+ | True = inp+ where linp = map toLower inp
+ src/CrackNum/TestSuite.hs view
@@ -0,0 +1,71 @@+---------------------------------------------------------------------------+-- |+-- Module : TestSuite+-- Copyright : (c) Levent Erkok+-- License : BSD3+-- Maintainer : erkokl@gmail.com+-- Stability : experimental+--+-- Test-suite for crackNum+-----------------------------------------------------------------------------++{-# LANGUAGE ScopedTypeVariables #-}++{-# OPTIONS_GHC -Wall -Werror #-}++module CrackNum.TestSuite(runTests) where++import Control.Exception as C++import Test.Tasty+import Test.Tasty.Golden (goldenVsFileDiff)+import System.FilePath++import System.Directory (removeFile)+import System.Process (readProcessWithExitCode)++import Data.List (intercalate)++gold :: TestName -> [String] -> TestTree+gold n args = goldenVsFileDiff n diff gf gfTmp (rm gfTmp >> run)+ where gf = "Golds" </> n <.> "gold"+ gfTmp = gf ++ "_temp"++ rm f = removeFile f `C.catch` (\(_ :: C.SomeException) -> return ())++ as = unwords args+ run = do (ec, so, se) <- readProcessWithExitCode "crackNum" args ""+ writeFile gfTmp $ intercalate "\n" $ [ "Arguments: " ++ as+ , "Exit code: " ++ show ec+ , so+ ]+ ++ concat [["STDERR:", se] | not (null se)]++ diff ref new = ["diff", "-w", "-u", ref, new]++-- | run the test suite+runTests :: IO ()+runTests = defaultMain tests++tests :: TestTree+tests = testGroup "CrackNum" [+ testGroup "Encode" [+ gold "encode0" ["-i4", "--", "-2"]+ , gold "encode1" ["-w4", "2"]+ , gold "encode2" ["-f3+4", "2.5"]+ , gold "encode3" ["-f3+4", "2.5", "-rRTZ"]+ , gold "encode4" ["-fbp", "2.5"]+ , gold "encode5" ["-fdp", "2.5"]+ ]+ , testGroup "Decode" [+ gold "decode0" ["-i4", "0b0110"]+ , gold "decode1" ["-w4", "0xE"]+ , gold "decode2" ["-f3+4", "0b0111001"]+ , gold "decode3" ["-fbp", "0x000F"]+ , gold "decode4" ["-fdp", "0x8000000000000000"]+ ]+ , testGroup "Bad" [+ gold "badInvocation0" ["-f3+4", "0b01"]+ , gold "badInvocation1" ["-f3+4", "0xFFFF"]+ ]+ ]