{-# LANGUAGE
  AllowAmbiguousTypes,
  DataKinds,
  DerivingStrategies,
  DerivingVia,
  GeneralizedNewtypeDeriving,
  ScopedTypeVariables,
  TypeApplications,
  TypeFamilies #-}

-- | Indices and offsets.
module DiffLoc.Index
  ( -- * One-dimensional indices
    -- ** Unbounded indices
    Plain(..)

    -- ** Indices bounded by an origin
  , IndexFrom()
  , indexFromM
  , indexFromM0
  , indexFromM1
  , fromIndex
  , fromIndex0
  , fromIndex1
  , zeroIndex
  , oneIndex

    -- ** Offsets
  , Offset()
  , offsetM
  , fromOffset
  ) where

import Data.Monoid (Sum(..))
import Data.Proxy (Proxy(..))
import GHC.TypeNats (KnownNat, Nat, natVal)
import Text.Show.Combinators (showCon, (@|))
import DiffLoc.Shift

-- | One-dimensional indices.
newtype Plain a = Plain a
  deriving (Plain a -> Plain a -> Bool
forall a. Eq a => Plain a -> Plain a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Plain a -> Plain a -> Bool
$c/= :: forall a. Eq a => Plain a -> Plain a -> Bool
== :: Plain a -> Plain a -> Bool
$c== :: forall a. Eq a => Plain a -> Plain a -> Bool
Eq, Plain a -> Plain a -> Bool
Plain a -> Plain a -> Ordering
Plain a -> Plain a -> Plain a
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
forall {a}. Ord a => Eq (Plain a)
forall a. Ord a => Plain a -> Plain a -> Bool
forall a. Ord a => Plain a -> Plain a -> Ordering
forall a. Ord a => Plain a -> Plain a -> Plain a
min :: Plain a -> Plain a -> Plain a
$cmin :: forall a. Ord a => Plain a -> Plain a -> Plain a
max :: Plain a -> Plain a -> Plain a
$cmax :: forall a. Ord a => Plain a -> Plain a -> Plain a
>= :: Plain a -> Plain a -> Bool
$c>= :: forall a. Ord a => Plain a -> Plain a -> Bool
> :: Plain a -> Plain a -> Bool
$c> :: forall a. Ord a => Plain a -> Plain a -> Bool
<= :: Plain a -> Plain a -> Bool
$c<= :: forall a. Ord a => Plain a -> Plain a -> Bool
< :: Plain a -> Plain a -> Bool
$c< :: forall a. Ord a => Plain a -> Plain a -> Bool
compare :: Plain a -> Plain a -> Ordering
$ccompare :: forall a. Ord a => Plain a -> Plain a -> Ordering
Ord, Int -> Plain a -> ShowS
forall a. Show a => Int -> Plain a -> ShowS
forall a. Show a => [Plain a] -> ShowS
forall a. Show a => Plain a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Plain a] -> ShowS
$cshowList :: forall a. Show a => [Plain a] -> ShowS
show :: Plain a -> String
$cshow :: forall a. Show a => Plain a -> String
showsPrec :: Int -> Plain a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> Plain a -> ShowS
Show)

instance (Num a, Ord a) => Amor (Plain a) where
  type Trans (Plain a) = Offset a
  Plain a
y .+ :: Plain a -> Trans (Plain a) -> Plain a
.+ Offset a
x = forall a. a -> Plain a
Plain (a
x forall a. Num a => a -> a -> a
Prelude.+ a
y)
  Plain a
x .-.? :: Plain a -> Plain a -> Maybe (Trans (Plain a))
.-.? Plain a
y | a
y forall a. Ord a => a -> a -> Bool
<= a
x = forall a. a -> Maybe a
Just (forall a. a -> Offset a
Offset (a
x forall a. Num a => a -> a -> a
- a
y))
                       | Bool
otherwise = forall a. Maybe a
Nothing

--

-- | One-dimensional indices with an origin (an initial index).
-- Indices must be greater than the origin, hence the constructor is hidden.
--
-- Use 'indexFromM' to construct indices, with @TypeApplications@ to make the
-- indexing scheme explicit, and 'fromIndex' to destruct them.
--
-- @
-- (origin :: IndexFrom n a) <= i    -- for all i
-- @
newtype IndexFrom (n :: Nat) a = IndexFrom a
  deriving (IndexFrom n a -> IndexFrom n a -> Bool
forall (n :: Nat) a. Eq a => IndexFrom n a -> IndexFrom n a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: IndexFrom n a -> IndexFrom n a -> Bool
$c/= :: forall (n :: Nat) a. Eq a => IndexFrom n a -> IndexFrom n a -> Bool
== :: IndexFrom n a -> IndexFrom n a -> Bool
$c== :: forall (n :: Nat) a. Eq a => IndexFrom n a -> IndexFrom n a -> Bool
Eq, IndexFrom n a -> IndexFrom n a -> Bool
IndexFrom n a -> IndexFrom n a -> Ordering
IndexFrom n a -> IndexFrom n a -> IndexFrom n a
forall {n :: Nat} {a}. Ord a => Eq (IndexFrom n a)
forall (n :: Nat) a.
Ord a =>
IndexFrom n a -> IndexFrom n a -> Bool
forall (n :: Nat) a.
Ord a =>
IndexFrom n a -> IndexFrom n a -> Ordering
forall (n :: Nat) a.
Ord a =>
IndexFrom n a -> IndexFrom n a -> IndexFrom n a
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: IndexFrom n a -> IndexFrom n a -> IndexFrom n a
$cmin :: forall (n :: Nat) a.
Ord a =>
IndexFrom n a -> IndexFrom n a -> IndexFrom n a
max :: IndexFrom n a -> IndexFrom n a -> IndexFrom n a
$cmax :: forall (n :: Nat) a.
Ord a =>
IndexFrom n a -> IndexFrom n a -> IndexFrom n a
>= :: IndexFrom n a -> IndexFrom n a -> Bool
$c>= :: forall (n :: Nat) a.
Ord a =>
IndexFrom n a -> IndexFrom n a -> Bool
> :: IndexFrom n a -> IndexFrom n a -> Bool
$c> :: forall (n :: Nat) a.
Ord a =>
IndexFrom n a -> IndexFrom n a -> Bool
<= :: IndexFrom n a -> IndexFrom n a -> Bool
$c<= :: forall (n :: Nat) a.
Ord a =>
IndexFrom n a -> IndexFrom n a -> Bool
< :: IndexFrom n a -> IndexFrom n a -> Bool
$c< :: forall (n :: Nat) a.
Ord a =>
IndexFrom n a -> IndexFrom n a -> Bool
compare :: IndexFrom n a -> IndexFrom n a -> Ordering
$ccompare :: forall (n :: Nat) a.
Ord a =>
IndexFrom n a -> IndexFrom n a -> Ordering
Ord)
  deriving IndexFrom n a -> Trans (IndexFrom n a) -> IndexFrom n a
IndexFrom n a -> IndexFrom n a -> Maybe (Trans (IndexFrom n a))
forall {n :: Nat} {a}.
(Num a, Ord a) =>
Ord (Trans (IndexFrom n a))
forall {n :: Nat} {a}. (Num a, Ord a) => Ord (IndexFrom n a)
forall {n :: Nat} {a}.
(Num a, Ord a) =>
Monoid (Trans (IndexFrom n a))
forall (n :: Nat) a.
(Num a, Ord a) =>
IndexFrom n a -> Trans (IndexFrom n a) -> IndexFrom n a
forall (n :: Nat) a.
(Num a, Ord a) =>
IndexFrom n a -> IndexFrom n a -> Maybe (Trans (IndexFrom n a))
forall p.
Ord p
-> Ord (Trans p)
-> Monoid (Trans p)
-> (p -> Trans p -> p)
-> (p -> p -> Maybe (Trans p))
-> Amor p
.-.? :: IndexFrom n a -> IndexFrom n a -> Maybe (Trans (IndexFrom n a))
$c.-.? :: forall (n :: Nat) a.
(Num a, Ord a) =>
IndexFrom n a -> IndexFrom n a -> Maybe (Trans (IndexFrom n a))
.+ :: IndexFrom n a -> Trans (IndexFrom n a) -> IndexFrom n a
$c.+ :: forall (n :: Nat) a.
(Num a, Ord a) =>
IndexFrom n a -> Trans (IndexFrom n a) -> IndexFrom n a
Amor via (Plain a)

instance Show a => Show (IndexFrom n a) where
  showsPrec :: Int -> IndexFrom n a -> ShowS
showsPrec = forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a b. (a -> b) -> a -> b
$ \(IndexFrom a
i) -> String -> Int -> ShowS
showCon String
"indexFrom" forall a. Show a => (Int -> ShowS) -> a -> Int -> ShowS
@| a
i

instance (Num a, Ord a, KnownNat n) => Origin (IndexFrom n a) where
  origin :: IndexFrom n a
origin = forall (n :: Nat) a. a -> IndexFrom n a
IndexFrom (forall (n :: Nat) a. (KnownNat n, Num a) => a
knownNum @n)

-- | Reify a 'KnownNat'.
--
-- @
-- knownNum @42 = 42
-- @
knownNum :: forall n a. (KnownNat n, Num a) => a
knownNum :: forall (n :: Nat) a. (KnownNat n, Num a) => a
knownNum = forall a b. (Integral a, Num b) => a -> b
fromIntegral (forall (n :: Nat) (proxy :: Nat -> *). KnownNat n => proxy n -> Nat
natVal @n forall {k} (t :: k). Proxy t
Proxy)

-- | Constructor for 'IndexFrom'.
--
-- See also 'DiffLoc.Unsafe.indexFrom' in "DiffLoc.Unsafe", a variant of 'indexFromM' that
-- throws errors instead of using @Maybe@.
indexFromM :: forall n a. (KnownNat n, Num a, Ord a) => a -> Maybe (IndexFrom n a)
indexFromM :: forall (n :: Nat) a.
(KnownNat n, Num a, Ord a) =>
a -> Maybe (IndexFrom n a)
indexFromM a
i | forall (n :: Nat) a. (KnownNat n, Num a) => a
knownNum @n forall a. Ord a => a -> a -> Bool
<= a
i = forall a. a -> Maybe a
Just (forall (n :: Nat) a. a -> IndexFrom n a
IndexFrom a
i)
             | Bool
otherwise = forall a. Maybe a
Nothing

-- | 'indexFromM' specialized to 0-indexing.
indexFromM0 :: forall a. (Num a, Ord a) => a -> Maybe (IndexFrom 0 a)
indexFromM0 :: forall a. (Num a, Ord a) => a -> Maybe (IndexFrom 0 a)
indexFromM0 = forall (n :: Nat) a.
(KnownNat n, Num a, Ord a) =>
a -> Maybe (IndexFrom n a)
indexFromM

-- | 'indexFromM' specialized to 1-indexing.
indexFromM1 :: forall a. (Num a, Ord a) => a -> Maybe (IndexFrom 1 a)
indexFromM1 :: forall a. (Num a, Ord a) => a -> Maybe (IndexFrom 1 a)
indexFromM1 = forall (n :: Nat) a.
(KnownNat n, Num a, Ord a) =>
a -> Maybe (IndexFrom n a)
indexFromM

-- | Destructor for 'IndexFrom'.
fromIndex :: forall n a. IndexFrom n a -> a
fromIndex :: forall (n :: Nat) a. IndexFrom n a -> a
fromIndex (IndexFrom a
i) = a
i

-- | 'fromIndex' specialized to 0-indexing.
fromIndex0 :: IndexFrom 0 a -> a
fromIndex0 :: forall a. IndexFrom 0 a -> a
fromIndex0 = forall (n :: Nat) a. IndexFrom n a -> a
fromIndex

-- | 'fromIndex' specialized to 1-indexing.
fromIndex1 :: IndexFrom 1 a -> a
fromIndex1 :: forall a. IndexFrom 1 a -> a
fromIndex1 = forall (n :: Nat) a. IndexFrom n a -> a
fromIndex

-- | Convert from zero-indexing to one-indexing.
oneIndex :: Num a => IndexFrom 0 a -> IndexFrom 1 a
oneIndex :: forall a. Num a => IndexFrom 0 a -> IndexFrom 1 a
oneIndex (IndexFrom a
i) = forall (n :: Nat) a. a -> IndexFrom n a
IndexFrom (a
i forall a. Num a => a -> a -> a
Prelude.+ a
1)

-- | Convert from one-indexing to zero-indexing.
zeroIndex :: Num a => IndexFrom 1 a -> IndexFrom 0 a
zeroIndex :: forall a. Num a => IndexFrom 1 a -> IndexFrom 0 a
zeroIndex (IndexFrom a
i) = forall (n :: Nat) a. a -> IndexFrom n a
IndexFrom (a
i forall a. Num a => a -> a -> a
- a
1)

-- | Type of nonnegative offsets.
newtype Offset a = Offset a
  deriving (Offset a -> Offset a -> Bool
forall a. Eq a => Offset a -> Offset a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Offset a -> Offset a -> Bool
$c/= :: forall a. Eq a => Offset a -> Offset a -> Bool
== :: Offset a -> Offset a -> Bool
$c== :: forall a. Eq a => Offset a -> Offset a -> Bool
Eq, Offset a -> Offset a -> Bool
Offset a -> Offset a -> Ordering
Offset a -> Offset a -> Offset a
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
forall {a}. Ord a => Eq (Offset a)
forall a. Ord a => Offset a -> Offset a -> Bool
forall a. Ord a => Offset a -> Offset a -> Ordering
forall a. Ord a => Offset a -> Offset a -> Offset a
min :: Offset a -> Offset a -> Offset a
$cmin :: forall a. Ord a => Offset a -> Offset a -> Offset a
max :: Offset a -> Offset a -> Offset a
$cmax :: forall a. Ord a => Offset a -> Offset a -> Offset a
>= :: Offset a -> Offset a -> Bool
$c>= :: forall a. Ord a => Offset a -> Offset a -> Bool
> :: Offset a -> Offset a -> Bool
$c> :: forall a. Ord a => Offset a -> Offset a -> Bool
<= :: Offset a -> Offset a -> Bool
$c<= :: forall a. Ord a => Offset a -> Offset a -> Bool
< :: Offset a -> Offset a -> Bool
$c< :: forall a. Ord a => Offset a -> Offset a -> Bool
compare :: Offset a -> Offset a -> Ordering
$ccompare :: forall a. Ord a => Offset a -> Offset a -> Ordering
Ord)
  deriving (NonEmpty (Offset a) -> Offset a
Offset a -> Offset a -> Offset a
forall b. Integral b => b -> Offset a -> Offset a
forall a. Num a => NonEmpty (Offset a) -> Offset a
forall a. Num a => Offset a -> Offset a -> Offset a
forall a b. (Num a, Integral b) => b -> Offset a -> Offset a
forall a.
(a -> a -> a)
-> (NonEmpty a -> a)
-> (forall b. Integral b => b -> a -> a)
-> Semigroup a
stimes :: forall b. Integral b => b -> Offset a -> Offset a
$cstimes :: forall a b. (Num a, Integral b) => b -> Offset a -> Offset a
sconcat :: NonEmpty (Offset a) -> Offset a
$csconcat :: forall a. Num a => NonEmpty (Offset a) -> Offset a
<> :: Offset a -> Offset a -> Offset a
$c<> :: forall a. Num a => Offset a -> Offset a -> Offset a
Semigroup, Offset a
[Offset a] -> Offset a
Offset a -> Offset a -> Offset a
forall a. Num a => Semigroup (Offset a)
forall a. Num a => Offset a
forall a. Num a => [Offset a] -> Offset a
forall a. Num a => Offset a -> Offset a -> Offset a
forall a.
Semigroup a -> a -> (a -> a -> a) -> ([a] -> a) -> Monoid a
mconcat :: [Offset a] -> Offset a
$cmconcat :: forall a. Num a => [Offset a] -> Offset a
mappend :: Offset a -> Offset a -> Offset a
$cmappend :: forall a. Num a => Offset a -> Offset a -> Offset a
mempty :: Offset a
$cmempty :: forall a. Num a => Offset a
Monoid) via (Sum a)

instance Show a => Show (Offset a) where
  showsPrec :: Int -> Offset a -> ShowS
showsPrec = forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a b. (a -> b) -> a -> b
$ \(Offset a
i) -> String -> Int -> ShowS
showCon String
"offset" forall a. Show a => (Int -> ShowS) -> a -> Int -> ShowS
@| a
i

-- | Construct a nonnegative 'Offset'.
--
-- See also 'DiffLoc.Unsafe.offset' in "DiffLoc.Unsafe", a variant of 'offsetM' that
-- throws errors instead of using @Maybe@.
offsetM :: (Num a, Ord a) => a -> Maybe (Offset a)
offsetM :: forall a. (Num a, Ord a) => a -> Maybe (Offset a)
offsetM a
i | a
0 forall a. Ord a => a -> a -> Bool
<= a
i = forall a. a -> Maybe a
Just (forall a. a -> Offset a
Offset a
i)
          | Bool
otherwise = forall a. Maybe a
Nothing

-- | Unwrap 'Offset'.
fromOffset :: Offset a -> a
fromOffset :: forall a. Offset a -> a
fromOffset (Offset a
i) = a
i