--------------------------------------------------------------------------- -- | -- 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)