{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE TypeApplications #-}
module Data.Locator.Common
( Locator(..)
, represent
, value
, toLocatorUnique
, multiply
, fromLocator
, concatToInteger
, digest
) where
import Prelude hiding (toInteger)
import Crypto.Hash as Crypto
import qualified Data.ByteArray as B
import qualified Data.ByteString.Char8 as S
import Data.List (mapAccumL)
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Word
class (Ord α, Enum α, Bounded α) => Locator α where
locatorToDigit :: α -> Char
digitToLocator :: Char -> α
represent :: Locator α => α -> Int -> Char
represent (_ :: α) n =
locatorToDigit $ (toEnum n :: α)
value :: Locator α => α -> Char -> Int
value (_ :: α) c =
fromEnum $ (digitToLocator c :: α)
toLocatorUnique :: Locator α => Int -> Int -> α -> String
toLocatorUnique limit n (_ :: α) =
let
n' = abs n
ls = convert n' (replicate limit (minBound @α))
(_,us) = mapAccumL uniq Set.empty ls
in
map locatorToDigit (take limit us)
where
convert :: Locator α => Int -> [α] -> [α]
convert 0 xs = xs
convert i xs =
let
(d,r) = divMod i 16
x = toEnum r
in
convert d (x:xs)
uniq :: Locator α => Set α -> α -> (Set α, α)
uniq s x =
if Set.member x s
then uniq s (subsequent x)
else (Set.insert x s, x)
subsequent :: Locator α => α -> α
subsequent x =
if x == maxBound
then minBound
else succ x
multiply :: Locator α => α -> Int -> Char -> Int
multiply (locator :: a) acc c =
let
base = fromEnum (maxBound @a) + 1
in
(acc * base) + (value locator c)
fromLocator :: Locator α => α -> String -> Int
fromLocator locator ss =
foldl (multiply locator) 0 ss
concatToInteger :: [Word8] -> Int
concatToInteger bytes =
foldl fn 0 bytes
where
fn acc b = (acc * 256) + (fromIntegral b)
digest :: String -> Int
digest ws =
i
where
i = concatToInteger h
h = B.unpack h'
h' = Crypto.hash x' :: Crypto.Digest Crypto.SHA1
x' = S.pack ws