-- | Biological sequence data is oftentimes indexed either @0-@ or
-- @1-@based. The @Index@ type developed provides static guarantees that
-- there is no confusion what index is in use.
--
-- This module does not export the ctor @Index@. If you want to (unsafely)
-- use it, import @Biobase.Types.Index.Type@ directly. Use @fromInt0@ to
-- make clear that you count from 0 and transform to an @Index t@. I.e.
-- @fromInt0 0 :: Index 1@ yields the lowest 1-base index.
--
-- Note that internally, every lowest index starts at @0 :: Int@.

module Biobase.Types.Index
  ( module Biobase.Types.Index
  , getIndex
  , index
  , maybeIndex
  , Index
  ) where

import Data.Coerce
import Data.Proxy
import GHC.TypeLits
import Text.Printf

import Biobase.Types.Index.Type -- hiding (getIndex)
import qualified Biobase.Types.Index.Type as IT



-- | Uses 'index' to guarantee that the 'Index' is ok.

checkIndex :: forall t . KnownNat t => Index t -> Index t
checkIndex :: Index t -> Index t
checkIndex i :: Index t
i@(Index Int
z)
  | Int
z Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0    = Index t
i
  | Bool
otherwise = [Char] -> Index t
forall a. HasCallStack => [Char] -> a
error ([Char] -> Index t) -> [Char] -> Index t
forall a b. (a -> b) -> a -> b
$ [Char] -> Int -> Int -> [Char]
forall r. PrintfType r => [Char] -> r
printf [Char]
"%d < Index %d\n" (Int
zInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
n) Int
n
  where Int
n :: Int = Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Integer -> Int) -> Integer -> Int
forall a b. (a -> b) -> a -> b
$ Proxy t -> Integer
forall (n :: Nat) (proxy :: Nat -> *).
KnownNat n =>
proxy n -> Integer
natVal (Proxy t
forall k (t :: k). Proxy t
Proxy :: Proxy t)
{-# Inline checkIndex #-}

-- | Re-Index an index of type @Index n@ as @Index m@. This is always safe,
-- as @0 :: Index 0@ gives @1 :: Index 1@ for example. I.e. valid indices
-- become valid indices.

reIndex  Index n  Index m
{-# Inline reIndex #-}
reIndex :: Index n -> Index m
reIndex = Index n -> Index m
coerce
--reIndex :: forall n m . (KnownNat n, KnownNat m) => Index n -> Index m
--reIndex (Index i) = Index $ i - n + m
--  where n = fromIntegral $ natVal (Proxy :: Proxy n)
--        m = fromIntegral $ natVal (Proxy :: Proxy m)

-- | Helper function that allows @addition@ of an 'Index' and an 'Int',
-- with the 'Int' on the right.

(+.) :: forall t . KnownNat t => Index t -> Int -> Index t
+. :: Index t -> Int -> Index t
(+.) Index t
i Int
n = Index t -> Index t
forall (t :: Nat). KnownNat t => Index t -> Index t
checkIndex (Index t -> Index t) -> Index t -> Index t
forall a b. (a -> b) -> a -> b
$ Index t -> Int -> Index t
forall (t :: Nat). KnownNat t => Index t -> Int -> Index t
unsafePlus Index t
i Int
n
{-# Inline (+.) #-}

-- | Helper function that allows @subtraction@ of an 'Index' and an 'Int',
-- with the 'Int' on the right.

(-.) :: forall t . KnownNat t => Index t -> Int -> Index t
-. :: Index t -> Int -> Index t
(-.) Index t
i Int
n = Index t -> Index t
forall (t :: Nat). KnownNat t => Index t -> Index t
checkIndex (Index t -> Index t) -> Index t -> Index t
forall a b. (a -> b) -> a -> b
$ Index t -> Int -> Index t
forall (t :: Nat). KnownNat t => Index t -> Int -> Index t
unsafePlus Index t
i (Int -> Int
forall a. Num a => a -> a
negate Int
n)
{-# Inline (-.) #-}

-- | Unsafe plus.

unsafePlus :: forall t . KnownNat t => Index t -> Int -> Index t
unsafePlus :: Index t -> Int -> Index t
unsafePlus Index t
i Int
n = Int -> Index t
forall (t :: Nat). Int -> Index t
Index (Int -> Index t) -> Int -> Index t
forall a b. (a -> b) -> a -> b
$ Index t -> Int
forall (t :: Nat). Index t -> Int
IT.getIndex Index t
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
n
{-# Inline unsafePlus #-}

-- | Delta between two 'Index' points.

delta :: forall t . KnownNat t => Index t -> Index t -> Int
delta :: Index t -> Index t -> Int
delta (Index Int
i) (Index Int
j) = Int -> Int
forall a. Num a => a -> a
abs (Int -> Int) -> Int -> Int
forall a b. (a -> b) -> a -> b
$ Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
j
{-# Inline delta #-}

toInt  forall t . KnownNat t  Index t  Int
{-# Inline toInt #-}
toInt :: Index t -> Int
toInt Index t
i = Index t -> Int
forall (t :: Nat). Index t -> Int
IT.getIndex Index t
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ (Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Integer -> Int) -> Integer -> Int
forall a b. (a -> b) -> a -> b
$ Proxy t -> Integer
forall (n :: Nat) (proxy :: Nat -> *).
KnownNat n =>
proxy n -> Integer
natVal (Proxy t
forall k (t :: k). Proxy t
Proxy  Proxy t))

-- | Return the index as an @Int@-style index that is zero-based.

toInt0 :: forall t . KnownNat t => Index t -> Int
toInt0 :: Index t -> Int
toInt0 = Index t -> Int
forall (t :: Nat). Index t -> Int
IT.getIndex
{-# Inline toInt0 #-}

-- | Return the index as an @Int@-style index that is one-based.

toInt1  forall t . KnownNat t  Index t  Int
{-# Inline toInt1 #-}
toInt1 :: Index t -> Int
toInt1 = (Int -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1) (Int -> Int) -> (Index t -> Int) -> Index t -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Index t -> Int
forall (t :: Nat). KnownNat t => Index t -> Int
toInt0

fromInt1  forall t . KnownNat t  Int  Index t
{-# Inline fromInt1 #-}
fromInt1 :: Int -> Index t
fromInt1 = Int -> Index t
forall (t :: Nat). KnownNat t => Int -> Index t
fromInt0 (Int -> Index t) -> (Int -> Int) -> Int -> Index t
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int -> Int -> Int
forall a. Num a => a -> a -> a
subtract Int
1)

-- | As an index from an @Int@-style zero-based one.
--
-- TODO We might want to check that the argument is @[0..]@.

fromInt0 :: forall t . KnownNat t => Int -> Index t
fromInt0 :: Int -> Index t
fromInt0 Int
i
  | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0    = Int -> Index t
forall (t :: Nat). Int -> Index t
Index Int
i
  | Bool
otherwise = [Char] -> Index t
forall a. HasCallStack => [Char] -> a
error [Char]
"fromInt0 needs an Int >= 0"
  where t :: Integer
t = Integer -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Integer -> Integer) -> Integer -> Integer
forall a b. (a -> b) -> a -> b
$ Proxy t -> Integer
forall (n :: Nat) (proxy :: Nat -> *).
KnownNat n =>
proxy n -> Integer
natVal (Proxy t
forall k (t :: k). Proxy t
Proxy :: Proxy t)
{-# Inline fromInt0 #-}

-- | Zero-based indices.

type I0 = Index 0

-- | One-based indices.

type I1 = Index 1