dtmc-0.2.0.0: src/Dtmc/Analysis/FiniteTime/Internal.hs
{- |
Module : Dtmc.Analysis.FiniteTime.Internal
Description : Normalised timed observations (unsafe underbelly).
The private normal form behind the event and conditional probability queries
in "Dtmc.Analysis.FiniteTime". 'normalise' is the intended way to build a
t'NormalisedObservations': it establishes the invariant that a 'Consistent'
list holds exactly one @(time, state)@ entry per distinct time, in ascending
time order. Building 'Consistent' directly can break that invariant and give
the scoring in "Dtmc.Analysis.FiniteTime" a wrong answer.
-}
module Dtmc.Analysis.FiniteTime.Internal (
NormalisedObservations (..),
normalise,
) where
import Data.List (
sortBy,
)
import Data.Ord (
comparing,
)
import Numeric.Natural (
Natural,
)
{- | A conjunction of timed state observations after sorting, de-duplication,
and consistency checking.
-}
data NormalisedObservations state
= -- | Two observations demand different states at one time.
Impossible
| -- | Distinct times in ascending order, each with one required state.
Consistent [(Natural, state)]
{- | Normalise @(time, state)@ pairs by ascending time, collapse exact
duplicates, and detect contradictions. Pairs requiring different states at
the same time yield 'Impossible'; otherwise the result is 'Consistent' with
one entry per distinct time in ascending order, so consecutive entries always
have strictly increasing times.
Complexity: @O(m log(m + 1))@ time, @O(m)@ temporary space, and @O(m)@
worst-case result space for @m@ supplied pairs.
-}
normalise :: (Eq state) => [(Natural, state)] -> NormalisedObservations state
normalise pairs =
foldr insert (Consistent []) (sortBy (comparing fst) pairs)
where
insert _ Impossible = Impossible
insert step (Consistent []) = Consistent [step]
insert (t, i) (Consistent ((t', i') : rest))
| t == t' && i == i' = Consistent ((t', i') : rest)
| t == t' = Impossible
| otherwise = Consistent ((t, i) : (t', i') : rest)