packages feed

telescope-0.2.0: test/Test/ArraySpec.hs

{-# LANGUAGE OverloadedLists #-}

module Test.ArraySpec where

import Data.ByteString qualified as BS
import Data.Massiv.Array as M
import Data.Word (Word8)
import GHC.Int
import Skeletest
import System.ByteOrder
import Telescope.Data.Array
import Telescope.Data.Axes
import Telescope.Fits.DataArray


spec :: Spec
spec = do
  describe "indices" testIndices
  describe "decode" $ do
    -- describe "vector" testDecode
    describe "array" testDecodeArray
    describe "image" testDecodeImage

  describe "encode" $ do
    describe "array/image" testEncode


testIndices :: Spec
testIndices = do
  describe "axesIndex" $ do
    it "Ix1" $ do
      ix <- axesIndex $ toRowMajor $ Axes [10]
      ix `shouldBe` Ix1 10

    it "Ix2 in reverse order" $ do
      ix <- axesIndex $ toRowMajor $ Axes [3, 2]
      ix `shouldBe` 2 :. 3

    it "Ix3 in reverse order" $ do
      ix <- axesIndex $ toRowMajor $ Axes [3, 2, 1]
      ix `shouldBe` 1 :> 2 :. 3

  describe "indexAxes" $ do
    -- indexAxes produces row major
    it "Ix1" $ do
      indexAxes @Ix1 2 `shouldBe` Axes [2]

    it "Ix2 Row Major" $ do
      indexAxes (2 :. 3) `shouldBe` Axes [2, 3]

    it "Ix3 Row Major" $ do
      indexAxes (1 :> 2 :. 3) `shouldBe` Axes [1, 2, 3]

    it "Ix3 Column Major" $ do
      sizeAxes (Sz (1 :> 2 :. 3)) `shouldBe` Axes [3, 2, 1]


-- generate a series of Word8
genInput :: Word8 -> BS.ByteString
genInput start = BS.pack [start .. start + 100]


testDecodeArray :: Spec
testDecodeArray = do
  let input = genInput 0

  it "should parse int8 vector" $ do
    let v = decodeVector @Int8 Par BigEndian input
    computeAs P (M.take 6 v) `shouldBe` [0, 1, 2, 3, 4, 5]

  it "should decode Ix1 from vector" $ do
    let v = decodeVector @Int8 Par BigEndian input
    a <- fromVector @Ix1 (Axes [3]) $ M.take 3 v
    computeAs P a `shouldBe` [0, 1, 2]

  it "should decode Ix2" $ do
    a <- decodeArray @Ix2 @Int8 [2, 3] $ BS.take 6 input
    computeAs P a `shouldBe` [[0, 1, 2], [3, 4, 5]]

  it "should decode Ix3" $ do
    a <- decodeArray @Ix3 @Int8 [2, 2, 2] $ BS.take 8 input
    computeAs P a `shouldBe` [[[0, 1], [2, 3]], [[4, 5], [6, 7]]]


testDecodeImage :: Spec
testDecodeImage = do
  let input = genInput 0
  it "should decode image" $ do
    a <- decodeArray @Ix2 @Int8 [2, 3] $ BS.take 6 input
    computeAs P a `shouldBe` [[0, 1, 2], [3, 4, 5]]


testEncode :: Spec
testEncode = do
  let array = delay @Ix2 @P $ M.fromLists' Seq [[1, 2, 3], [4, 5, 6]] :: Array D Ix2 Int8

  it "should encode to 6 items" $ do
    BS.length (encodeArray array) `shouldBe` 6

  it "int8 should round-trip to original array" $ do
    let enc = encodeArray array :: BS.ByteString
    arr <- decodeArray @Ix2 @Int8 [2, 3] enc
    arr `shouldBe` array

  let arrayFloat = delay @Ix2 @P $ M.fromLists' Seq [[1, 2, 3], [4, 5, 6]] :: Array D Ix2 Float
  it "float should round-trip to original array" $ do
    let enc = encodeArray arrayFloat :: BS.ByteString
    arr <- decodeArray [2, 3] enc
    arr `shouldBe` arrayFloat

  let arrayInt = delay @Ix2 @P $ M.fromLists' Seq [[1, 2, 3], [4, 5, 6]] :: Array D Ix2 Int
  it "int should round-trip to original array" $ do
    let enc = encodeArray arrayInt :: BS.ByteString
    arr <- decodeArray [2, 3] enc
    arr `shouldBe` arrayInt

  it "should encode naxes in correct order" $ do
    size array `shouldBe` Sz (2 :. 3)
    sizeAxes (size array) `shouldBe` Axes [3, 2]