moonlight-triangulation-0.1.0.0: src-build/Moonlight/Triangulation/Internal/Join/SiteSet.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | Exact transient site sections derived from the authoritative coordinate
-- planes. This is the sole owner of coordinate-set classification for joins,
-- set algebra, and constrained union. Payloads travel as annotations; they do
-- not participate in site identity.
module Moonlight.Triangulation.Internal.Join.SiteSet
( SiteSet
, siteSetFromTriangulation
, siteSupportFromTriangulation
, siteSetSize
, siteSetRelation
, siteRelationFromTriangulations
, siteSetUnionWith
, siteSetIntersectionWith
, siteSetDifference
, siteSetSymmetricDifferenceFromTriangulations
, siteSetAssocs
, siteSetPoints
) where
import Control.Monad.ST (ST, runST)
import qualified Data.Map.Strict as Map
import qualified Data.Map.Merge.Strict as MapMerge
import Data.Functor.Const (Const (..))
import Data.Maybe (isJust)
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Unboxed.Mutable as MUV
import Moonlight.Triangulation.Dcel (numVertices, vertexData, vertexPoint)
import Moonlight.Triangulation.Handles.HandleDefs (VertexId (..))
import Moonlight.Triangulation.Handles.Iterators.FixedIterators
( foldVertices'
)
import Moonlight.Triangulation.Internal.Paged
( pagedUnsafeIndex
, toVector
)
import Moonlight.Triangulation.Internal.PointIndex
( MutablePointIndex
, lookupMutablePoint
, lookupPointIndex
, newMutablePointIndex
, seedMutablePointIndex
)
import Moonlight.Triangulation.Internal.Representation (Triangulation (..))
import Moonlight.Triangulation.Internal.Types
( BuildError
, Point (..)
, SiteRelation (..)
)
newtype SiteSet annotation = SiteSet (Map.Map (Point) annotation)
-- Strict sufficient statistics for the one-pass ordered-map descent used by
-- 'siteSetRelation'. Keeping the census strict prevents a relation query from
-- replacing an intermediate map allocation with a chain of monoidal thunks.
data SiteRelationCensus = SiteRelationCensus !Int !Int !Int
instance Semigroup SiteRelationCensus where
SiteRelationCensus leftA rightA overlapA <> SiteRelationCensus leftB rightB overlapB =
SiteRelationCensus
(leftA + leftB)
(rightA + rightB)
(overlapA + overlapB)
instance Monoid SiteRelationCensus where
mempty = SiteRelationCensus 0 0 0
siteSetFromTriangulation
:: Triangulation mode annotation directed undirected face
-> SiteSet annotation
siteSetFromTriangulation triangulation =
SiteSet
( Map.fromList
[ ( Point
(triPointX triangulation `pagedUnsafeIndex` vertex)
(triPointY triangulation `pagedUnsafeIndex` vertex)
, vertexData triangulation (VertexId (fromIntegral vertex))
)
| vertex <- [0 .. numVertices triangulation - 1]
]
)
-- | Coordinate support without touching the boxed annotation plane. Order and
-- set identity are geometric observations; callers that discard annotations
-- should not pay a boxed-page read per vertex merely to manufacture ignored
-- map values.
siteSupportFromTriangulation
:: Triangulation mode vertex directed undirected face
-> SiteSet ()
siteSupportFromTriangulation triangulation =
SiteSet
( Map.fromList
[ ( Point
(triPointX triangulation `pagedUnsafeIndex` vertex)
(triPointY triangulation `pagedUnsafeIndex` vertex)
, ()
)
| vertex <- [0 .. numVertices triangulation - 1]
]
)
{-# INLINE siteSupportFromTriangulation #-}
siteSetSize :: SiteSet annotation -> Int
siteSetSize (SiteSet sites) = Map.size sites
{-# INLINE siteSetSize #-}
siteSetRelation
:: SiteSet leftAnnotation
-> SiteSet rightAnnotation
-> SiteRelation
siteSetRelation (SiteSet left) (SiteSet right) =
siteRelationFromCardinalities
(leftOnly + overlap)
(rightOnly + overlap)
overlap
where
SiteRelationCensus leftOnly rightOnly overlap =
getConst
( MapMerge.mergeA
(MapMerge.traverseMissing (\_ _ -> Const (SiteRelationCensus 1 0 0)))
(MapMerge.traverseMissing (\_ _ -> Const (SiteRelationCensus 0 1 0)))
(MapMerge.zipWithAMatched (\_ _ _ -> Const (SiteRelationCensus 0 0 1)))
left
right
)
{-# INLINE siteSetRelation #-}
siteRelationFromTriangulations
:: Triangulation leftMode leftAnnotation leftDirected leftUndirected leftFace
-> Triangulation rightMode rightAnnotation rightDirected rightUndirected rightFace
-> SiteRelation
siteRelationFromTriangulations left right =
siteRelationFromCardinalities leftCount rightCount overlap
where
!leftCount = numVertices left
!rightCount = numVertices right
!overlap
| leftCount <= rightCount = exactOverlapCount right left
| otherwise = exactOverlapCount left right
{-# INLINE siteRelationFromTriangulations #-}
siteRelationFromCardinalities :: Int -> Int -> Int -> SiteRelation
siteRelationFromCardinalities leftCount rightCount overlap
| leftCount == rightCount && overlap == leftCount = EqualSites
| overlap == leftCount = LeftProperSubset
| overlap == rightCount = RightProperSubset
| overlap == 0 = DisjointSites
| otherwise = PartialOverlap overlap
{-# INLINE siteRelationFromCardinalities #-}
exactOverlapCount
:: Triangulation sourceMode sourceAnnotation sourceDirected sourceUndirected sourceFace
-> Triangulation indexedMode indexedAnnotation indexedDirected indexedUndirected indexedFace
-> Int
exactOverlapCount source indexed =
either
(const (persistentExactOverlapCount source indexed))
id
(transientExactOverlapCount source indexed)
{-# INLINE exactOverlapCount #-}
transientExactOverlapCount
:: Triangulation sourceMode sourceAnnotation sourceDirected sourceUndirected sourceFace
-> Triangulation indexedMode indexedAnnotation indexedDirected indexedUndirected indexedFace
-> Either BuildError Int
transientExactOverlapCount source indexed = runST $ do
pointIndex <- newMutablePointIndex (numVertices indexed)
seeded <-
seedMutablePointIndex
pointIndex
(numVertices indexed)
(readCoordinateX indexed)
(readCoordinateY indexed)
case seeded of
Left failure -> pure (Left failure)
Right () ->
fmap Right
( U.ifoldM'
(\count vertex x -> do
let y = triPointY source `pagedUnsafeIndex` vertex
match <-
lookupMutablePoint
pointIndex
(readCoordinateX indexed)
(readCoordinateY indexed)
x
y
pure (if isJust match then count + 1 else count)
)
0
(toVector (triPointX source))
)
{-# INLINE transientExactOverlapCount #-}
persistentExactOverlapCount
:: Triangulation sourceMode sourceAnnotation sourceDirected sourceUndirected sourceFace
-> Triangulation indexedMode indexedAnnotation indexedDirected indexedUndirected indexedFace
-> Int
persistentExactOverlapCount source indexed =
foldVertices'
source
(\count vertex ->
if pointOccursIn indexed (vertexPoint source vertex)
then count + 1
else count
)
0
{-# INLINE persistentExactOverlapCount #-}
siteSetUnionWith
:: (annotation -> annotation -> annotation)
-> SiteSet annotation
-> SiteSet annotation
-> SiteSet annotation
siteSetUnionWith combine (SiteSet left) (SiteSet right) =
SiteSet (Map.unionWith combine left right)
{-# INLINE siteSetUnionWith #-}
siteSetIntersectionWith
:: (leftAnnotation -> rightAnnotation -> annotation)
-> SiteSet leftAnnotation
-> SiteSet rightAnnotation
-> SiteSet annotation
siteSetIntersectionWith combine (SiteSet left) (SiteSet right) =
SiteSet (Map.intersectionWith combine left right)
{-# INLINE siteSetIntersectionWith #-}
siteSetDifference
:: SiteSet annotation
-> SiteSet other
-> SiteSet annotation
siteSetDifference (SiteSet left) (SiteSet right) = SiteSet (Map.difference left right)
{-# INLINE siteSetDifference #-}
siteSetSymmetricDifferenceFromTriangulations
:: Triangulation leftMode annotation leftDirected leftUndirected leftFace
-> Triangulation rightMode annotation rightDirected rightUndirected rightFace
-> Either BuildError (SiteSet annotation)
siteSetSymmetricDifferenceFromTriangulations left right
| numVertices left >= numVertices right = indexedSymmetricDifference left right
| otherwise = indexedSymmetricDifference right left
{-# INLINE siteSetSymmetricDifferenceFromTriangulations #-}
indexedSymmetricDifference
:: forall sourceMode annotation sourceDirected sourceUndirected sourceFace
indexedMode indexedDirected indexedUndirected indexedFace
. Triangulation sourceMode annotation sourceDirected sourceUndirected sourceFace
-> Triangulation indexedMode annotation indexedDirected indexedUndirected indexedFace
-> Either BuildError (SiteSet annotation)
indexedSymmetricDifference source indexed =
fmap (SiteSet . Map.fromList) (runST collectExclusiveAssociations)
where
collectExclusiveAssociations
:: forall state. ST state (Either BuildError [(Point, annotation)])
collectExclusiveAssociations = do
pointIndex <- newMutablePointIndex (numVertices indexed)
seeded <-
seedMutablePointIndex
pointIndex
(numVertices indexed)
(readCoordinateX indexed)
(readCoordinateY indexed)
case seeded of
Left failure -> pure (Left failure)
Right () -> do
matchedIndexedVertices <- MUV.replicate (numVertices indexed) False
sourceExclusive <-
U.ifoldM'
(collectSourceExclusive pointIndex matchedIndexedVertices)
[]
(toVector (triPointX source))
indexedExclusive <-
U.ifoldM'
(collectIndexedExclusive matchedIndexedVertices)
[]
(toVector (triPointX indexed))
pure (Right (sourceExclusive <> indexedExclusive))
where
collectSourceExclusive
:: MutablePointIndex state
-> MUV.MVector state Bool
-> [(Point, annotation)]
-> Int
-> Double
-> ST state [(Point, annotation)]
collectSourceExclusive pointIndex matchedIndexedVertices associations rawVertex x = do
let vertex = VertexId (fromIntegral rawVertex)
y = triPointY source `pagedUnsafeIndex` rawVertex
point = Point x y
match <-
lookupMutablePoint
pointIndex
(readCoordinateX indexed)
(readCoordinateY indexed)
x
y
case match of
Nothing -> pure ((point, vertexData source vertex) : associations)
Just indexedVertex -> do
MUV.unsafeWrite matchedIndexedVertices indexedVertex True
pure associations
collectIndexedExclusive
:: MUV.MVector state Bool
-> [(Point, annotation)]
-> Int
-> Double
-> ST state [(Point, annotation)]
collectIndexedExclusive matchedIndexedVertices associations rawVertex x = do
let vertex = VertexId (fromIntegral rawVertex)
point = Point x (triPointY indexed `pagedUnsafeIndex` rawVertex)
matched <- MUV.unsafeRead matchedIndexedVertices rawVertex
pure
( if matched
then associations
else (point, vertexData indexed vertex) : associations
)
{-# INLINE indexedSymmetricDifference #-}
readCoordinateX
:: Triangulation mode annotation directed undirected face
-> Int
-> ST state Double
readCoordinateX triangulation vertex =
pure (triPointX triangulation `pagedUnsafeIndex` vertex)
{-# INLINE readCoordinateX #-}
readCoordinateY
:: Triangulation mode annotation directed undirected face
-> Int
-> ST state Double
readCoordinateY triangulation vertex =
pure (triPointY triangulation `pagedUnsafeIndex` vertex)
{-# INLINE readCoordinateY #-}
pointOccursIn
:: Triangulation mode annotation directed undirected face
-> Point
-> Bool
pointOccursIn triangulation = isJust . lookupPointIn triangulation
{-# INLINE pointOccursIn #-}
lookupPointIn
:: Triangulation mode annotation directed undirected face
-> Point
-> Maybe Int
lookupPointIn triangulation =
lookupPointIndex
(triPointX triangulation)
(triPointY triangulation)
(triPointIndex triangulation)
{-# INLINE lookupPointIn #-}
siteSetAssocs :: SiteSet annotation -> [(Point, annotation)]
siteSetAssocs (SiteSet sites) = Map.toAscList sites
{-# INLINE siteSetAssocs #-}
siteSetPoints :: SiteSet annotation -> V.Vector (Point)
siteSetPoints (SiteSet sites) = V.fromList (Map.keys sites)
{-# INLINE siteSetPoints #-}