packages feed

moonlight-planar-1.1.0.0: test/native/Moonlight/Planar/OverlaySpec.hs

-- | Labelled arrangement construction, normalization, and source provenance.
module Moonlight.Planar.OverlaySpec (tests) where

import Control.Monad ( when )
import Moonlight.Planar.CellSet ( exactCellSetEdgeCount, exactCellSetFaceCount,
  exactCellSetVertexCount )
import Moonlight.Planar.Exact ( exactPointCoordinates, exactRationalDenominator )
import Moonlight.Planar.Overlay ( BoundaryRef(boundaryRefSource),
  OverlayEdgeOrigin(overlayEdgeSources), OverlayReceipt(overlayExactCrossings, overlayInputSegments,
  overlayAtomicEdges, overlayOverlapIntervals), OverlaySourceId(OverlaySourceId),
  OverlayVertex(overlayExactPoint), overlayArrangementEdges, overlayArrangementVertices,
  overlayCells, overlayClosedIntersection, overlayConfusion, overlayLayers, overlayPlanarLayer,
  overlayReceipt, overlaySelectedRegion )
import Moonlight.Planar.OverlayAssertions ( assertOverlayIntegrity, unboundedLoopCount )
import Moonlight.Planar.OverlayFixtures ( singletonLayer )
import Moonlight.Planar.Region ( planarLayerRegions, polygonOuterLoop, exactLoopPoints, planarLayer,
  planarRegion, planarRegionComponents, RegionPublicationError(RegionUnboundedSelection) )
import Moonlight.Planar.Valuation ( exactAreaValue )
import Support ( assertEqual, integerPoint, rectangleComponent, requireRight, rectangleRegion,
  polygonRegion )
import qualified Data.Map.Strict as Map
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Set as Set

tests :: IO ()
tests =
  sequence_
    [ testOverlappingSquares
    , testSeamAndOverlapNormalization
    , testNestedAndNonDyadicOverlay
    , testCancelledSeamCrossesLabelTransition
    ]

testOverlappingSquares :: IO ()
testOverlappingSquares = do
  left <- rectangleRegion 0 0 2 2 >>= singletonLayer "left-outside" "left"
  right <- rectangleRegion 1 (-1) 3 1 >>= singletonLayer "right-outside" "right"
  result <- requireRight "overlapping square overlay" (overlayLayers left right)
  assertOverlayIntegrity "overlapping square" result
  let receipt = overlayReceipt result
  assertEqual "overlay source segment count" 8 (overlayInputSegments receipt)
  assertEqual "overlay has two proper crossings" 2 (overlayExactCrossings receipt)
  when (overlayAtomicEdges receipt < 8) $
    fail ("overlay lost atomic edges: " <> show receipt)
  intersection <-
    requireRight
      "closed square intersection"
      (overlayClosedIntersection (== "left") (== "right") result)
  when
    ( exactCellSetVertexCount intersection < 4
        || exactCellSetEdgeCount intersection < 4
        || exactCellSetFaceCount intersection < 1
    )
    (fail "closed intersection omitted incidence closure")
  published <-
    requireRight
      "selected square intersection publication"
      (overlaySelectedRegion (== ("left", "right")) result)
  assertEqual
    "selected square intersection component count"
    1
    (length (planarRegionComponents published))
  assertEqual
    "unbounded selected publication refusal"
    (Left RegionUnboundedSelection)
    (overlaySelectedRegion (const True) result)
  merged <-
    requireRight
      "selected adjacent overlay cells merge"
      (overlaySelectedRegion (\(leftLabel, _) -> leftLabel == "left") result)
  assertEqual
    "selected adjacent overlay cells publish one component"
    [4]
    (map (length . exactLoopPoints . polygonOuterLoop) (planarRegionComponents merged))

testSeamAndOverlapNormalization :: IO ()
testSeamAndOverlapNormalization = do
  first <- rectangleComponent 0 0 1 1
  second <- rectangleComponent 1 0 2 1
  joinedRegion <- requireRight "same-label joined region" (planarRegion [first, second])
  joinedLayer <-
    requireRight
      "same-label joined layer"
      (planarLayer "outside" (Map.singleton "inside" joinedRegion))
  emptyLayer <- requireRight "empty normalization layer" (planarLayer "void" Map.empty)
  seamResult <- requireRight "same-label seam overlay" (overlayLayers joinedLayer emptyLayer)
  assertOverlayIntegrity "same-label seam" seamResult
  assertEqual "joined rectangle has one unbounded boundary cycle" (Just 1) (unboundedLoopCount seamResult)
  assertEqual
    "same-label shared boundary is removed before topology"
    6
    (overlayAtomicEdges (overlayReceipt seamResult))
  seamPublication <- requireRight "publish same-label seam" (overlayPlanarLayer seamResult)
  assertEqual
    "the implicit outside pair is never duplicated as a bounded layer key"
    Nothing
    ( Map.lookup
        ("outside", "void")
        (planarLayerRegions seamPublication)
    )
  assertEqual
    "same-label seam dissolves to the rectangle boundary"
    [4]
    ( map
        (length . exactLoopPoints . polygonOuterLoop)
        ( maybe
            []
            planarRegionComponents
            (Map.lookup ("inside", "void") (planarLayerRegions seamPublication))
        )
    )

  duplicateLeft <- rectangleRegion 0 0 2 2 >>= singletonLayer "left-outside" "left"
  duplicateRight <- rectangleRegion 0 0 2 2 >>= singletonLayer "right-outside" "right"
  duplicateResult <-
    requireRight "cross-operand duplicate boundary overlay" (overlayLayers duplicateLeft duplicateRight)
  assertOverlayIntegrity "duplicate boundary" duplicateResult
  assertEqual
    "duplicate boundaries normalize to one atomic cycle"
    4
    (overlayAtomicEdges (overlayReceipt duplicateResult))
  assertEqual "duplicate atomic support retains both typed source origins" True
    (all
      ((== Set.fromList [OverlaySourceId 0, OverlaySourceId 1])
        . Set.fromList . fmap boundaryRefSource . NonEmpty.toList . overlayEdgeSources . snd)
      (overlayArrangementEdges duplicateResult))

  partialLeft <- rectangleRegion 0 0 3 2 >>= singletonLayer "left-outside" "left"
  partialRight <- rectangleRegion 1 0 4 1 >>= singletonLayer "right-outside" "right"
  partialResult <-
    requireRight "partial collinear overlap" (overlayLayers partialLeft partialRight)
  assertOverlayIntegrity "partial collinear overlap" partialResult
  if overlayOverlapIntervals (overlayReceipt partialResult) > 0
    then pure ()
    else fail "partial collinear overlap emitted no overlap event"

testNestedAndNonDyadicOverlay :: IO ()
testNestedAndNonDyadicOverlay = do
  outer <- rectangleRegion 0 0 4 4 >>= singletonLayer "left-outside" "left"
  inner <- rectangleRegion 1 1 3 3 >>= singletonLayer "right-outside" "right"
  nested <- requireRight "nested overlay" (overlayLayers outer inner)
  assertOverlayIntegrity "nested" nested
  assertEqual "nested overlay unbounded boundary is outermost only" (Just 1) (unboundedLoopCount nested)
  assertEqual
    "nested overlay has unbounded, shell, and intersection cells"
    3
    (length (overlayCells nested))

  leftTriangle <-
    polygonRegion [(0, 0), (4, 0), (0, 4)] >>= singletonLayer "left-outside" "left"
  rightTriangle <-
    polygonRegion [(1, -1), (3, -1), (2, 2)] >>= singletonLayer "right-outside" "right"
  nonDyadic <-
    requireRight "non-dyadic proper-crossing overlay" (overlayLayers leftTriangle rightTriangle)
  assertOverlayIntegrity "non-dyadic proper crossing" nonDyadic
  let hasNonDyadicCoordinate =
        any
          (\(_, vertex) ->
             let (x, y) = exactPointCoordinates (overlayExactPoint vertex)
              in any
                   (not . isPowerOfTwo . exactRationalDenominator)
                   [x, y])
          (overlayArrangementVertices nonDyadic)
  if overlayExactCrossings (overlayReceipt nonDyadic) > 0 && hasNonDyadicCoordinate
    then pure ()
    else fail "proper-crossing overlay lost its non-dyadic exact witness"

-- A cancelled same-source seam crosses a retained label-change support.
-- The resulting event remains a vertex, and each subinterval must transport
-- only the source labels whose transition is present on that support.
testCancelledSeamCrossesLabelTransition :: IO ()
testCancelledSeamCrossesLabelTransition = do
  leftComponent <- rectangleComponent 0 0 2 4
  rightComponent <- rectangleComponent 2 0 4 4
  leftRegion <- requireRight "left label region" (planarRegion [leftComponent])
  rightRegion <- requireRight "right label region" (planarRegion [rightComponent])
  labelled <- requireRight "adjacent distinct labels"
    (planarLayer "void" (Map.fromList [("a", leftRegion), ("b", rightRegion)]))
  lower <- rectangleComponent 1 1 3 2
  upper <- rectangleComponent 1 2 3 3
  maskRegion <- requireRight "mask with cancellable internal seam" (planarRegion [lower, upper])
  mask <- requireRight "same-label mask source" (planarLayer "off" (Map.singleton "on" maskRegion))
  result <- requireRight "cancelled seam across retained transition" (overlayLayers labelled mask)
  assertOverlayIntegrity "cancelled seam across retained transition" result
  masses <- requireRight "interval-wise sparse source transitions" (overlayConfusion result)
  assertEqual "cancellation preserves both labels across all transition intervals"
    (Map.fromList [(("a", "off"), 6), (("a", "on"), 2), (("b", "off"), 6), (("b", "on"), 2)])
    (fmap exactAreaValue masses)
  assertEqual "cancelled seam crossing remains an exact provenance event" True
    (Set.member (integerPoint 2 2)
      (Set.fromList (fmap (overlayExactPoint . snd) (overlayArrangementVertices result))))

isPowerOfTwo :: Integer -> Bool
isPowerOfTwo value =
  value > 0 && value `elem` takeWhile (<= value) (iterate (* 2) 1)