module Data.TypeList.MultiIndex.Internal where

-- | Transform the position indicated by an index array inside an
-- array of given dimensions into the position inside a linear array,
-- starting at 0. For example, in an array of 3 rows and 4 columns,
-- the position [2,2] becomes 5. The indices in the dimension array
-- must be >= 1, the index array must have the same size of the
-- dimension array, and each index in the index array must be >= 1 and
-- <= then the corresponding index in the dimension array.
linearize :: Num n =>
             [n] -- ^ Dimension array
          -> [n] -- ^ Index array
          -> n
linearize ds is = go ds is 0
    where go es js acc = case js of
                           [] -> acc
                           x:xs -> let t = tail es in
                                   go t xs (acc + (x - 1) * product t)

-- | Inverse of @'linearize'@: @('linearize' ds) . ('unlinearize' ds)
-- == 'id'@, @('unlinearize' ds) . ('linearize' ds) == 'id'@.
unlinearize :: Integral n =>
               [n] -- ^ Dimension array
            -> n -- ^ Linearized position (starting from 0)
            -> [n]
unlinearize ds i = go ds i []
    where go es j acc = case es of
                          [] -> acc
                          _:xs -> let (q,r) = quotRem j (product xs) in
                                  go xs r (acc ++ [q + 1])