```module Data.SequentialIndex
(
SequentialIndex
, mantissa
, exponent
, zero
, one
, sequentialIndex
, between
, toByteString
, fromByteString
)
where

import           Data.Bits
import           Prelude         hiding (exponent)
import qualified Data.ByteString as B

-- must always be in normalised form!
data SequentialIndex
= SI !Integer !Int
deriving (Eq)

mantissa :: SequentialIndex -> Integer
mantissa (SI m _) = m

exponent :: SequentialIndex -> Int
exponent (SI _ e) = e

zero :: SequentialIndex
zero = SI 0 0

one :: SequentialIndex
one = SI 1 0

commonBase :: SequentialIndex -> SequentialIndex -> (Integer, Integer, Int)
commonBase (SI m1 e1) (SI m2 e2) = (m1', m2', e)
where e = max e1 e2
m1' = m1 `shift` (e - e1)
m2' = m2 `shift` (e - e2)

sequentialIndex :: Integer -> Int -> SequentialIndex
sequentialIndex 0 _ = zero
sequentialIndex mx ex
= case () of
_ | v < zero  -> error "Invalid SequentialIndex: below zero"
| v > one   -> error "Invalid SequentialIndex: beyond one"
| otherwise -> v
where v = until (\(SI m _) -> m `testBit` 0)
(\(SI m e) -> SI (m `shiftR` 1) (e - 1))
(SI mx ex)

instance Bounded SequentialIndex where
minBound = zero
maxBound = one

instance Ord SequentialIndex where
a `compare` b = a' `compare` b'
where (a', b', _) = commonBase a b

instance Show SequentialIndex where
show (SI m e) = case map sbit bits of
[]      -> "0.0"
[d1]    -> d1 : ".0"
(d1:ds) -> d1 : '.' : ds
where bits = map (testBit m) [e, e - 1 .. 0]

sbit False = '0'
sbit True  = '1'

between :: SequentialIndex -> SequentialIndex -> SequentialIndex
between a b = sequentialIndex (m1 + m2) (e + 1)
where (m1, m2, e) = commonBase a b

toByteString :: SequentialIndex -> B.ByteString
toByteString (SI m e) = B.unfoldr step m'
where e' = (e `div` 8) * 8 + 7
m' = m `shift` (e' - e)

step 0 = Nothing
step v = let (q, r) = v `divMod` 256
in Just (fromInteger r, q)

fromByteString :: B.ByteString -> SequentialIndex
fromByteString bs = sequentialIndex m (max 0 \$ e - 1)
where (m, e) = B.foldr step (0, 0) bs
step w (mx, ex) = (mx `shiftL` 8 + toInteger w, ex + 8)
```