packages feed

moonlight-linalg-0.1.0.0: test/geometry/GeometryStorageSpec.hs

module GeometryStorageSpec
  ( tests,
  )
where

import Data.Vector.Unboxed qualified as U
import Foreign.Marshal.Alloc (alloca)
import Foreign.Storable (Storable (..))
import Moonlight.LinAlg.Geometry
  ( Vec2 (..),
    Vec3 (..),
  )
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (Assertion, assertEqual, testCase)

tests :: TestTree
tests =
  testGroup
    "Geometry storage"
    [ testCase "Vec2 unboxed vectors round-trip through fromList/toList" testVec2UnboxRoundTrip,
      testCase "Vec3 unboxed vectors round-trip through fromList/toList" testVec3UnboxRoundTrip,
      testCase "Vec2 storable layout round-trips through peek/poke" testVec2StorableRoundTrip,
      testCase "Vec3 storable layout round-trips through peek/poke" testVec3StorableRoundTrip,
      testCase "Vec2 unboxed indexing agrees with list indexing on generated batches" testVec2UnboxIndexing,
      testCase "Vec3 unboxed indexing agrees with list indexing on generated batches" testVec3UnboxIndexing
    ]

testVec2UnboxRoundTrip :: Assertion
testVec2UnboxRoundTrip =
  assertEqual
    "Vec2 U.fromList/U.toList identity"
    vec2Batch
    (U.toList (U.fromList vec2Batch :: U.Vector Vec2))

testVec3UnboxRoundTrip :: Assertion
testVec3UnboxRoundTrip =
  assertEqual
    "Vec3 U.fromList/U.toList identity"
    vec3Batch
    (U.toList (U.fromList vec3Batch :: U.Vector Vec3))

testVec2StorableRoundTrip :: Assertion
testVec2StorableRoundTrip =
  assertStorableRoundTrip "Vec2 peek/poke identity" (Vec2 3.25 (-8.5))

testVec3StorableRoundTrip :: Assertion
testVec3StorableRoundTrip =
  assertStorableRoundTrip "Vec3 peek/poke identity" (Vec3 3.25 (-8.5) 13.75)

testVec2UnboxIndexing :: Assertion
testVec2UnboxIndexing =
  assertEqual
    "Vec2 indexed unboxed vector agrees with indexed source list"
    (zip [0 :: Int ..] vec2Batch)
    (U.toList (U.indexed (U.fromList vec2Batch :: U.Vector Vec2)))

testVec3UnboxIndexing :: Assertion
testVec3UnboxIndexing =
  assertEqual
    "Vec3 indexed unboxed vector agrees with indexed source list"
    (zip [0 :: Int ..] vec3Batch)
    (U.toList (U.indexed (U.fromList vec3Batch :: U.Vector Vec3)))

assertStorableRoundTrip :: (Eq value, Show value, Storable value) => String -> value -> Assertion
assertStorableRoundTrip label value =
  alloca $ \pointerValue -> do
    poke pointerValue value
    actualValue <- peek pointerValue
    assertEqual label value actualValue

vec2Batch :: [Vec2]
vec2Batch =
  (\indexValue -> Vec2 (coordinateValue 17 indexValue) (coordinateValue 29 indexValue))
    <$> [0 .. 127]

vec3Batch :: [Vec3]
vec3Batch =
  ( \indexValue ->
      Vec3
        (coordinateValue 17 indexValue)
        (coordinateValue 29 indexValue)
        (coordinateValue 43 indexValue)
  )
    <$> [0 .. 127]

coordinateValue :: Int -> Int -> Double
coordinateValue saltValue indexValue =
  (fromIntegral ((indexValue * 1103515245 + saltValue * 12345) `mod` 65521) / 257.0) - 127.0