moonlight-triangulation-1.0.0.0: src-build/Moonlight/Triangulation/Internal/Join.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | The join of two triangulations: a Delaunay representative of the union of
-- their sites.
module Moonlight.Triangulation.Internal.Join
( joinNormalForm
, joinBalanced
, executeTournamentPlan
, TournamentPlan (..)
, planTournament
) where
import Data.Foldable (traverse_)
import Data.List.NonEmpty (NonEmpty (..))
import Moonlight.Triangulation.BulkLoad (empty)
import Moonlight.Triangulation.Internal.Join.Plan
( PairPlan (..)
, TournamentPlan (..)
, planPair
, planTournament
)
import Moonlight.Triangulation.Internal.Join.Seam (executeSeam)
import Moonlight.Triangulation.Internal.Join.Rebuild (rebuildCanonicalSiteSet)
import Moonlight.Triangulation.Internal.Join.SiteSet
( SiteSet
, siteSetAssocs
, siteSetSize
)
import Moonlight.Triangulation.JoinSemilattice
( JoinSemilattice (joinAnnotations)
)
import Moonlight.Triangulation.Session
( insertVertexAtCombining
, withLocalSession
)
import Moonlight.Triangulation.Types
-- | A Delaunay representative of the union of two site sets.
--
-- Skewed operands descend through the existing local copy-on-write session, so
-- the larger operand's vertex handles and untouched pages survive. Comparable
-- operands may merge along a separating seam or rebuild from their combined
-- site set. Every schedule returns valid topology;
-- 'Moonlight.Triangulation.Dcel.canonicalize' is the
-- separate physical observation when construction-independent numbering is
-- required.
--
-- Empty and structurally identical operands return an existing value verbatim.
-- Algebraic agreement between all schedules is stated by equal canonical
-- observations; structural equality continues to describe exact resident
-- representation.
joinNormalForm
:: JoinSemilattice annotation
=> Triangulation 'Unconstrained annotation () () ()
-> Triangulation 'Unconstrained annotation () () ()
-> Either BuildError (Triangulation 'Unconstrained annotation () () ())
joinNormalForm left right = executePairPlan (planPair left right) left right
executePairPlan
:: JoinSemilattice annotation
=> PairPlan annotation
-> Triangulation 'Unconstrained annotation () () ()
-> Triangulation 'Unconstrained annotation () () ()
-> Either BuildError (Triangulation 'Unconstrained annotation () () ())
executePairPlan pairPlan left right =
case pairPlan of
ReturnLeftOperand -> Right left
ReturnRightOperand -> Right right
InsertLeftIntoRight sites -> insertSites sites right
InsertRightIntoLeft sites -> insertSites sites left
MergeSeparated seamPlan -> executeSeam seamPlan left right
RebuildCanonicalUnion sites -> rebuildCanonicalSiteSet sites
insertSites
:: JoinSemilattice annotation
=> SiteSet annotation
-> Triangulation 'Unconstrained annotation () () ()
-> Either BuildError (Triangulation 'Unconstrained annotation () () ())
insertSites sites base = do
((), inserted, _) <-
withLocalSession
base
(siteSetSize sites)
( traverse_
(\(point, annotation) ->
() <$ insertVertexAtCombining joinAnnotations point annotation
)
(siteSetAssocs sites)
)
pure inserted
-- | Combine by a balanced tournament rather than by a fold.
--
-- Associativity and commutativity make every bracketing canonically equivalent,
-- so this is a cost choice and not a semantic one. A left fold republishes an
-- accumulator that grows by one shard per step and so rebuilds @Θ(nk)@ sites
-- over @k@ shards; halving the list rebuilds @Θ(n log k)@.
--
-- The specialization below is load-bearing rather than decorative. Without it
-- this function is the only one on the path that stays polymorphic, and every
-- join in the tournament pays for a dictionary while a caller's own fold at a
-- known element type does not. That alone cost a factor of two and hid the
-- advantage this function exists for.
--
-- The tournament retains the binary operation's shortcut and annotation-gluing
-- semantics rather than inventing a second n-ary implementation.
joinBalanced
:: JoinSemilattice annotation
=> [Triangulation 'Unconstrained annotation () () ()]
-> Either BuildError (Triangulation 'Unconstrained annotation () () ())
joinBalanced [] = Right (empty unitElementDefaults)
joinBalanced (first : rest) =
executeTournamentPlan (planTournament (first :| rest))
{-# SPECIALIZE joinBalanced
:: [Triangulation 'Unconstrained () () () ()]
-> Either BuildError (Triangulation 'Unconstrained () () () ()) #-}
executeTournamentPlan
:: JoinSemilattice annotation
=> TournamentPlan (Triangulation 'Unconstrained annotation () () ())
-> Either BuildError (Triangulation 'Unconstrained annotation () () ())
executeTournamentPlan tournament =
case tournament of
TournamentLeaf mesh -> Right mesh
TournamentNode left right -> do
leftMesh <- executeTournamentPlan left
rightMesh <- executeTournamentPlan right
joinNormalForm leftMesh rightMesh