content: {-# LANGUAGE BangPatterns #-}
  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE FlexibleInstances #-}
  {-# LANGUAGE TypeFamilies #-}
  {-# OPTIONS_GHC -fno-warn-orphans #-}
  
  -- |
  -- Module      : Data.Massiv.Core.Index.Tuple
  -- Copyright   : (c) Alexey Kuleshevich 2018-2022
  -- License     : BSD3
  -- Maintainer  : Alexey Kuleshevich <alexey@kuleshevi.ch>
  -- Stability   : experimental
  -- Portability : non-portable
  module Data.Massiv.Core.Index.Tuple (
    -- * Tuple based indices
  
    -- ** 1-dimensional
    Ix1T,
  
    -- ** 2-dimensional
    Ix2T,
    toIx2,
    fromIx2,
  
    -- ** 3-dimensional
    Ix3T,
    toIx3,
    fromIx3,
  
    -- ** 4-dimensional
    Ix4T,
    toIx4,
    fromIx4,
  
    -- ** 5-dimensional
    Ix5T,
    toIx5,
    fromIx5,
  ) where
  
  import Control.Monad.Catch (MonadThrow (..))
  import Data.Massiv.Core.Index.Internal (
    Index (..),
    IndexException (..),
    Lower,
    Sz (..),
   )
  import Data.Massiv.Core.Index.Ix
  
  -- | Another 1-dimensional index type synonym for `Int`, same as `Ix1` and is here just for
  -- consistency.
  type Ix1T = Int
  
  -- | 2-dimensional index as tuple of `Int`s.
  type Ix2T = (Int, Int)
  
  -- | 3-dimensional index as 3-tuple of `Int`s.
  type Ix3T = (Int, Int, Int)
  
  -- | 4-dimensional index as 4-tuple of `Int`s.
  type Ix4T = (Int, Int, Int, Int)
  
  -- | 5-dimensional index as 5-tuple of `Int`s.
  type Ix5T = (Int, Int, Int, Int, Int)
  
  type instance Lower Ix2T = Ix1T
  
  type instance Lower Ix3T = Ix2T
  
  type instance Lower Ix4T = Ix3T
  
  type instance Lower Ix5T = Ix4T
  
  -- | Convert an `Int` tuple to `Ix2`
  --
  -- ==== __Example__
  --
  -- >>> toIx2 (2, 3)
  -- 2 :. 3
  --
  -- @since 0.1.0
  toIx2 :: Ix2T -> Ix2
  toIx2 (i, j) = i :. j
  {-# INLINE toIx2 #-}
  
  -- | Convert an `Ix2` to `Int` tuple
  --
  -- ==== __Example__
  --
  -- >>> fromIx2 (2 :. 3)
  -- (2,3)
  --
  -- @since 0.1.0
  fromIx2 :: Ix2 -> Ix2T
  fromIx2 (i :. j) = (i, j)
  {-# INLINE fromIx2 #-}
  
  -- | Convert a `Int` 3-tuple to `Ix3`
  --
  -- ==== __Example__
  --
  -- >>> toIx3 (1, 2, 3)
  -- 1 :> 2 :. 3
  --
  -- @since 0.1.0
  toIx3 :: Ix3T -> Ix3
  toIx3 (i, j, k) = i :> j :. k
  {-# INLINE toIx3 #-}
  
  -- | Convert an `Ix3` to `Int` 3-tuple
  --
  -- ==== __Example__
  --
  -- >>> fromIx3 (1 :>  2 :. 3)
  -- (1,2,3)
  --
  -- @since 0.1.0
  fromIx3 :: Ix3 -> Ix3T
  fromIx3 (i :> j :. k) = (i, j, k)
  {-# INLINE fromIx3 #-}
  
  -- | Convert a `Int` 4-tuple to `Ix4`
  --
  -- ==== __Example__
  --
  -- >>> toIx4 (1, 2, 3, 4)
  -- 1 :> 2 :> 3 :. 4
  --
  -- @since 0.1.0
  toIx4 :: Ix4T -> Ix4
  toIx4 (i, j, k, l) = i :> j :> k :. l
  {-# INLINE toIx4 #-}
  
  -- | Convert an `Ix4` to `Int` 4-tuple
  --
  -- ==== __Example__
  --
  -- >>> fromIx4 (1 :> 2 :> 3 :. 4)
  -- (1,2,3,4)
  --
  -- @since 0.1.0
  fromIx4 :: Ix4 -> Ix4T
  fromIx4 (i :> j :> k :. l) = (i, j, k, l)
  {-# INLINE fromIx4 #-}
  
  -- | Convert a `Int` 5-tuple to `Ix5`
  --
  -- ==== __Example__
  --
  -- >>> toIx5 (1, 2, 3, 4, 5)
  -- 1 :> 2 :> 3 :> 4 :. 5
  --
  -- @since 0.1.0
  toIx5 :: Ix5T -> Ix5
  toIx5 (i, j, k, l, m) = i :> j :> k :> l :. m
  {-# INLINE toIx5 #-}
  
  -- | Convert an `Ix5` to `Int` 5-tuple
  --
  -- ==== __Example__
  --
  -- >>> fromIx5 (1 :> 2 :> 3 :> 4 :. 5)
  -- (1,2,3,4,5)
  --
  -- @since 0.1.0
  fromIx5 :: Ix5 -> Ix5T
  fromIx5 (i :> j :> k :> l :. m) = (i, j, k, l, m)
  {-# INLINE fromIx5 #-}
  
  -- |
  -- @since 0.1.0
  instance Index Ix2T where
    type Dimensions Ix2T = 2
    dimensions _ = 2
    {-# INLINE [1] dimensions #-}
    totalElem (SafeSz (k2, k1)) = k2 * k1
    {-# INLINE [1] totalElem #-}
    toLinearIndex (SafeSz (_, k1)) (i2, i1) = k1 * i2 + i1
    {-# INLINE [1] toLinearIndex #-}
    fromLinearIndex (SafeSz (_, k1)) !i = i `quotRem` k1
    {-# INLINE [1] fromLinearIndex #-}
    consDim = (,)
    {-# INLINE [1] consDim #-}
    unconsDim = id
    {-# INLINE [1] unconsDim #-}
    snocDim = (,)
    {-# INLINE [1] snocDim #-}
    unsnocDim = id
    {-# INLINE [1] unsnocDim #-}
    getDimM (i2, _) 2 = pure i2
    getDimM (_, i1) 1 = pure i1
    getDimM ix d = throwM $ IndexDimensionException ix d
    {-# INLINE [1] getDimM #-}
    setDimM (_, i1) 2 i2 = pure (i2, i1)
    setDimM (i2, _) 1 i1 = pure (i2, i1)
    setDimM ix d _ = throwM $ IndexDimensionException ix d
    {-# INLINE [1] setDimM #-}
    modifyDimM (i2, i1) 2 f = pure (i2, (f i2, i1))
    modifyDimM (i2, i1) 1 f = pure (i1, (i2, f i1))
    modifyDimM ix d _ = throwM $ IndexDimensionException ix d
    {-# INLINE [1] modifyDimM #-}
    pullOutDimM (i2, i1) 2 = pure (i2, i1)
    pullOutDimM (i2, i1) 1 = pure (i1, i2)
    pullOutDimM ix d = throwM $ IndexDimensionException ix d
    {-# INLINE [1] pullOutDimM #-}
    insertDimM i1 2 i2 = pure (i2, i1)
    insertDimM i2 1 i1 = pure (i2, i1)
    insertDimM ix d _ = throwM $ IndexDimensionException ix d
    {-# INLINE [1] insertDimM #-}
    pureIndex i = (i, i)
    {-# INLINE [1] pureIndex #-}
    liftIndex2 f (i2, i1) (i2', i1') = (f i2 i2', f i1 i1')
    {-# INLINE [1] liftIndex2 #-}
  
  -- |
  -- @since 0.1.0
  instance Index Ix3T where
    type Dimensions Ix3T = 3
    dimensions _ = 3
    {-# INLINE [1] dimensions #-}
    totalElem (SafeSz (k3, k2, k1)) = k3 * k2 * k1
    {-# INLINE [1] totalElem #-}
    consDim i3 (i2, i1) = (i3, i2, i1)
    {-# INLINE [1] consDim #-}
    unconsDim (i3, i2, i1) = (i3, (i2, i1))
    {-# INLINE [1] unconsDim #-}
    snocDim (i3, i2) i1 = (i3, i2, i1)
    {-# INLINE [1] snocDim #-}
    unsnocDim (i3, i2, i1) = ((i3, i2), i1)
    {-# INLINE [1] unsnocDim #-}
    getDimM (i3, _, _) 3 = pure i3
    getDimM (_, i2, _) 2 = pure i2
    getDimM (_, _, i1) 1 = pure i1
    getDimM ix d = throwM $ IndexDimensionException ix d
    {-# INLINE [1] getDimM #-}
    setDimM (_, i2, i1) 3 i3 = pure (i3, i2, i1)
    setDimM (i3, _, i1) 2 i2 = pure (i3, i2, i1)
    setDimM (i3, i2, _) 1 i1 = pure (i3, i2, i1)
    setDimM ix d _ = throwM $ IndexDimensionException ix d
    {-# INLINE [1] setDimM #-}
    modifyDimM (i3, i2, i1) 3 f = pure (i3, (f i3, i2, i1))
    modifyDimM (i3, i2, i1) 2 f = pure (i2, (i3, f i2, i1))
    modifyDimM (i3, i2, i1) 1 f = pure (i1, (i3, i2, f i1))
    modifyDimM ix d _ = throwM $ IndexDimensionException ix d
    {-# INLINE [1] modifyDimM #-}
    pullOutDimM (i3, i2, i1) 3 = pure (i3, (i2, i1))
    pullOutDimM (i3, i2, i1) 2 = pure (i2, (i3, i1))
    pullOutDimM (i3, i2, i1) 1 = pure (i1, (i3, i2))
    pullOutDimM ix d = throwM $ IndexDimensionException ix d
    {-# INLINE [1] pullOutDimM #-}
    insertDimM (i2, i1) 3 i3 = pure (i3, i2, i1)
    insertDimM (i3, i1) 2 i2 = pure (i3, i2, i1)
    insertDimM (i3, i2) 1 i1 = pure (i3, i2, i1)
    insertDimM ix d _ = throwM $ IndexDimensionException ix d
    pureIndex i = (i, i, i)
    {-# INLINE [1] pureIndex #-}
    liftIndex2 f (i3, i2, i1) (i3', i2', i1') = (f i3 i3', f i2 i2', f i1 i1')
    {-# INLINE [1] liftIndex2 #-}
  
  instance Index Ix4T where
    type Dimensions Ix4T = 4
    dimensions _ = 4
    {-# INLINE [1] dimensions #-}
    totalElem (SafeSz (k4, k3, k2, k1)) = k4 * k3 * k2 * k1
    {-# INLINE [1] totalElem #-}
    consDim i4 (i3, i2, i1) = (i4, i3, i2, i1)
    {-# INLINE [1] consDim #-}
    unconsDim (i4, i3, i2, i1) = (i4, (i3, i2, i1))
    {-# INLINE [1] unconsDim #-}
    snocDim (i4, i3, i2) i1 = (i4, i3, i2, i1)
    {-# INLINE [1] snocDim #-}
    unsnocDim (i4, i3, i2, i1) = ((i4, i3, i2), i1)
    {-# INLINE [1] unsnocDim #-}
    getDimM (i4, _, _, _) 4 = pure i4
    getDimM (_, i3, _, _) 3 = pure i3
    getDimM (_, _, i2, _) 2 = pure i2
    getDimM (_, _, _, i1) 1 = pure i1
    getDimM ix d = throwM $ IndexDimensionException ix d
    {-# INLINE [1] getDimM #-}
    setDimM (_, i3, i2, i1) 4 i4 = pure (i4, i3, i2, i1)
    setDimM (i4, _, i2, i1) 3 i3 = pure (i4, i3, i2, i1)
    setDimM (i4, i3, _, i1) 2 i2 = pure (i4, i3, i2, i1)
    setDimM (i4, i3, i2, _) 1 i1 = pure (i4, i3, i2, i1)
    setDimM ix d _ = throwM $ IndexDimensionException ix d
    {-# INLINE [1] setDimM #-}
    modifyDimM (i4, i3, i2, i1) 4 f = pure (i4, (f i4, i3, i2, i1))
    modifyDimM (i4, i3, i2, i1) 3 f = pure (i3, (i4, f i3, i2, i1))
    modifyDimM (i4, i3, i2, i1) 2 f = pure (i2, (i4, i3, f i2, i1))
    modifyDimM (i4, i3, i2, i1) 1 f = pure (i1, (i4, i3, i2, f i1))
    modifyDimM ix d _ = throwM $ IndexDimensionException ix d
    {-# INLINE [1] modifyDimM #-}
    pullOutDimM (i4, i3, i2, i1) 4 = pure (i4, (i3, i2, i1))
    pullOutDimM (i4, i3, i2, i1) 3 = pure (i3, (i4, i2, i1))
    pullOutDimM (i4, i3, i2, i1) 2 = pure (i2, (i4, i3, i1))
    pullOutDimM (i4, i3, i2, i1) 1 = pure (i1, (i4, i3, i2))
    pullOutDimM ix d = throwM $ IndexDimensionException ix d
    {-# INLINE [1] pullOutDimM #-}
    insertDimM (i3, i2, i1) 4 i4 = pure (i4, i3, i2, i1)
    insertDimM (i4, i2, i1) 3 i3 = pure (i4, i3, i2, i1)
    insertDimM (i4, i3, i1) 2 i2 = pure (i4, i3, i2, i1)
    insertDimM (i4, i3, i2) 1 i1 = pure (i4, i3, i2, i1)
    insertDimM ix d _ = throwM $ IndexDimensionException ix d
    {-# INLINE [1] insertDimM #-}
    pureIndex i = (i, i, i, i)
    {-# INLINE [1] pureIndex #-}
    liftIndex2 f (i4, i3, i2, i1) (i4', i3', i2', i1') = (f i4 i4', f i3 i3', f i2 i2', f i1 i1')
    {-# INLINE [1] liftIndex2 #-}
  
  instance Index Ix5T where
    type Dimensions Ix5T = 5
    dimensions _ = 5
    {-# INLINE [1] dimensions #-}
    totalElem (SafeSz (n5, n4, n3, n2, n1)) = n5 * n4 * n3 * n2 * n1
    {-# INLINE [1] totalElem #-}
    consDim i5 (i4, i3, i2, i1) = (i5, i4, i3, i2, i1)
    {-# INLINE [1] consDim #-}
    unconsDim (i5, i4, i3, i2, i1) = (i5, (i4, i3, i2, i1))
    {-# INLINE [1] unconsDim #-}
    snocDim (i5, i4, i3, i2) i1 = (i5, i4, i3, i2, i1)
    {-# INLINE [1] snocDim #-}
    unsnocDim (i5, i4, i3, i2, i1) = ((i5, i4, i3, i2), i1)
    {-# INLINE [1] unsnocDim #-}
    getDimM (i5, _, _, _, _) 5 = pure i5
    getDimM (_, i4, _, _, _) 4 = pure i4
    getDimM (_, _, i3, _, _) 3 = pure i3
    getDimM (_, _, _, i2, _) 2 = pure i2
    getDimM (_, _, _, _, i1) 1 = pure i1
    getDimM ix d = throwM $ IndexDimensionException ix d
    {-# INLINE [1] getDimM #-}
    setDimM (_, i4, i3, i2, i1) 5 i5 = pure (i5, i4, i3, i2, i1)
    setDimM (i5, _, i3, i2, i1) 4 i4 = pure (i5, i4, i3, i2, i1)
    setDimM (i5, i4, _, i2, i1) 3 i3 = pure (i5, i4, i3, i2, i1)
    setDimM (i5, i4, i3, _, i1) 2 i2 = pure (i5, i4, i3, i2, i1)
    setDimM (i5, i4, i3, i2, _) 1 i1 = pure (i5, i4, i3, i2, i1)
    setDimM ix d _ = throwM $ IndexDimensionException ix d
    {-# INLINE [1] setDimM #-}
    modifyDimM (i5, i4, i3, i2, i1) 5 f = pure (i5, (f i5, i4, i3, i2, i1))
    modifyDimM (i5, i4, i3, i2, i1) 4 f = pure (i4, (i5, f i4, i3, i2, i1))
    modifyDimM (i5, i4, i3, i2, i1) 3 f = pure (i3, (i5, i4, f i3, i2, i1))
    modifyDimM (i5, i4, i3, i2, i1) 2 f = pure (i2, (i5, i4, i3, f i2, i1))
    modifyDimM (i5, i4, i3, i2, i1) 1 f = pure (i1, (i5, i4, i3, i2, f i1))
    modifyDimM ix d _ = throwM $ IndexDimensionException ix d
    {-# INLINE [1] modifyDimM #-}
    pullOutDimM (i5, i4, i3, i2, i1) 5 = pure (i5, (i4, i3, i2, i1))
    pullOutDimM (i5, i4, i3, i2, i1) 4 = pure (i4, (i5, i3, i2, i1))
    pullOutDimM (i5, i4, i3, i2, i1) 3 = pure (i3, (i5, i4, i2, i1))
    pullOutDimM (i5, i4, i3, i2, i1) 2 = pure (i2, (i5, i4, i3, i1))
    pullOutDimM (i5, i4, i3, i2, i1) 1 = pure (i1, (i5, i4, i3, i2))
    pullOutDimM ix d = throwM $ IndexDimensionException ix d
    {-# INLINE [1] pullOutDimM #-}
    insertDimM (i4, i3, i2, i1) 5 i5 = pure (i5, i4, i3, i2, i1)
    insertDimM (i5, i3, i2, i1) 4 i4 = pure (i5, i4, i3, i2, i1)
    insertDimM (i5, i4, i2, i1) 3 i3 = pure (i5, i4, i3, i2, i1)
    insertDimM (i5, i4, i3, i1) 2 i2 = pure (i5, i4, i3, i2, i1)
    insertDimM (i5, i4, i3, i2) 1 i1 = pure (i5, i4, i3, i2, i1)
    insertDimM ix d _ = throwM $ IndexDimensionException ix d
    {-# INLINE [1] insertDimM #-}
    pureIndex i = (i, i, i, i, i)
    {-# INLINE [1] pureIndex #-}
    liftIndex2 f (i5, i4, i3, i2, i1) (i5', i4', i3', i2', i1') =
      (f i5 i5', f i4 i4', f i3 i3', f i2 i2', f i1 i1')
    {-# INLINE [1] liftIndex2 #-}
  