interval-algebra 2.1.3 → 2.2.0
raw patch · 18 files changed
+2419/−2837 lines, 18 filesdep −safedep −witchdep −witherabledep ~hspecPVP ok
version bump matches the API change (PVP)
Dependencies removed: safe, witch, witherable
Dependency ranges changed: hspec
API changes (from Hackage documentation)
- IntervalAlgebra.Arbitrary: instance Test.QuickCheck.Arbitrary.Arbitrary Data.Time.Calendar.Days.Day
- IntervalAlgebra.Arbitrary: instance Test.QuickCheck.Arbitrary.Arbitrary Data.Time.Clock.Internal.DiffTime.DiffTime
- IntervalAlgebra.Arbitrary: instance Test.QuickCheck.Arbitrary.Arbitrary Data.Time.Clock.Internal.NominalDiffTime.NominalDiffTime
- IntervalAlgebra.Arbitrary: instance Test.QuickCheck.Arbitrary.Arbitrary Data.Time.Clock.Internal.UTCTime.UTCTime
- IntervalAlgebra.Axioms: class (IntervalSizeable a b) => IntervalAxioms a b
- IntervalAlgebra.Axioms: instance IntervalAlgebra.Axioms.IntervalAxioms Data.Time.Calendar.Days.Day GHC.Num.Integer.Integer
- IntervalAlgebra.Axioms: instance IntervalAlgebra.Axioms.IntervalAxioms Data.Time.Clock.Internal.UTCTime.UTCTime Data.Time.Clock.Internal.NominalDiffTime.NominalDiffTime
- IntervalAlgebra.Axioms: instance IntervalAlgebra.Axioms.IntervalAxioms GHC.Types.Int GHC.Types.Int
- IntervalAlgebra.Core: (.+.) :: IntervalCombinable i a => i a -> i a -> Maybe (i a)
- IntervalAlgebra.Core: (<+>) :: (IntervalCombinable i a, Semigroup (f (i a)), Applicative f) => i a -> i a -> f (i a)
- IntervalAlgebra.Core: (><) :: IntervalCombinable i a => i a -> i a -> Maybe (i a)
- IntervalAlgebra.Core: add :: IntervalSizeable a b => b -> a -> a
- IntervalAlgebra.Core: class (Ord a, Intervallic i) => IntervalCombinable i a
- IntervalAlgebra.Core: class (Ord a, Num b, Ord b) => IntervalSizeable a b | a -> b
- IntervalAlgebra.Core: diff :: IntervalSizeable a b => a -> a -> b
- IntervalAlgebra.Core: instance (GHC.Classes.Ord a, Test.QuickCheck.Arbitrary.Arbitrary a) => Test.QuickCheck.Arbitrary.Arbitrary (IntervalAlgebra.Core.Interval a)
- IntervalAlgebra.Core: instance GHC.Classes.Ord a => IntervalAlgebra.Core.IntervalCombinable IntervalAlgebra.Core.Interval a
- IntervalAlgebra.Core: instance IntervalAlgebra.Core.IntervalSizeable Data.Time.Calendar.Days.Day GHC.Num.Integer.Integer
- IntervalAlgebra.Core: instance IntervalAlgebra.Core.IntervalSizeable Data.Time.Clock.Internal.UTCTime.UTCTime Data.Time.Clock.Internal.NominalDiffTime.NominalDiffTime
- IntervalAlgebra.Core: instance IntervalAlgebra.Core.IntervalSizeable GHC.Num.Integer.Integer GHC.Num.Integer.Integer
- IntervalAlgebra.Core: instance IntervalAlgebra.Core.IntervalSizeable GHC.Types.Int GHC.Types.Int
- IntervalAlgebra.IntervalDiagram: instance (GHC.Enum.Enum b, IntervalAlgebra.Core.IntervalSizeable a b) => Prettyprinter.Internal.Pretty (IntervalAlgebra.IntervalDiagram.IntervalText a)
- IntervalAlgebra.IntervalDiagram: instance IntervalAlgebra.Core.IntervalSizeable a b => Prettyprinter.Internal.Pretty (Data.Either.Either IntervalAlgebra.IntervalDiagram.IntervalDiagramParseError (IntervalAlgebra.IntervalDiagram.IntervalDiagram a))
- IntervalAlgebra.IntervalDiagram: instance IntervalAlgebra.Core.IntervalSizeable a b => Prettyprinter.Internal.Pretty (IntervalAlgebra.IntervalDiagram.IntervalDiagram a)
- IntervalAlgebra.IntervalUtilities: allGapsWithinLessThanDuration :: (IntervalSizeable a b, Intervallic i0, IntervalCombinable i1 a, Monoid (t (Interval a)), Monoid (t (Maybe (Interval a))), Applicative t, Witherable t) => b -> i0 a -> t (i1 a) -> Bool
- IntervalAlgebra.IntervalUtilities: anyGapsWithinAtLeastDuration :: (IntervalSizeable a b, Intervallic i0, IntervalCombinable i1 a, Monoid (t (Interval a)), Monoid (t (Maybe (Interval a))), Applicative t, Witherable t) => b -> i0 a -> t (i1 a) -> Bool
- IntervalAlgebra.IntervalUtilities: combineIntervalsFromSortedL :: forall a i. (Ord a, Intervallic i) => [i a] -> [Interval a]
- IntervalAlgebra.IntervalUtilities: combineIntervalsL :: (Intervallic i, Ord a) => [i a] -> [Interval a]
- IntervalAlgebra.IntervalUtilities: filterAfter :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterBefore :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterConcur :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterContains :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterDisjoint :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterDuring :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterEnclosedBy :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterEncloses :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterEquals :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterFinishedBy :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterFinishes :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterMeets :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterMetBy :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterNotDisjoint :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterOverlappedBy :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterOverlaps :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterStartedBy :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterStarts :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: filterWithin :: (Filterable f, Ord a, Intervallic i0, Intervallic i1) => i0 a -> f (i1 a) -> f (i1 a)
- IntervalAlgebra.IntervalUtilities: foldMeetingSafe :: (Eq b, Ord a, Show a) => [PairedInterval b a] -> [PairedInterval b a]
- IntervalAlgebra.IntervalUtilities: formMeetingSequence :: (Eq b, Show a, Monoid b, IntervalSizeable a c) => [PairedInterval b a] -> [PairedInterval b a]
- IntervalAlgebra.IntervalUtilities: gapsL :: (IntervalCombinable i a, Applicative f, Monoid (f (Maybe (Interval a))), Traversable f) => f (i a) -> [Interval a]
- IntervalAlgebra.IntervalUtilities: gapsWithin :: (Applicative f, Witherable f, Monoid (f (Interval a)), Monoid (f (Maybe (Interval a))), IntervalSizeable a b, Intervallic i0, IntervalCombinable i1 a) => i0 a -> f (i1 a) -> Maybe (f (Interval a))
- IntervalAlgebra.IntervalUtilities: instance GHC.Classes.Eq a => GHC.Classes.Eq (IntervalAlgebra.IntervalUtilities.Meeting a)
- IntervalAlgebra.IntervalUtilities: instance GHC.Show.Show a => GHC.Show.Show (IntervalAlgebra.IntervalUtilities.Meeting a)
- IntervalAlgebra.IntervalUtilities: makeGapsWithinPredicate :: (Monoid (t (Interval a)), Monoid (t (Maybe (Interval a))), Applicative t, Witherable t, IntervalSizeable a b, Intervallic i0, IntervalCombinable i1 a) => ((b -> Bool) -> t b -> Bool) -> (b -> b -> Bool) -> b -> i0 a -> t (i1 a) -> Bool
- IntervalAlgebra.IntervalUtilities: nothingIf :: (Monoid (f (i a)), Filterable f) => ((i a -> Bool) -> f (i a) -> Bool) -> (i a -> Bool) -> f (i a) -> Maybe (f (i a))
- IntervalAlgebra.IntervalUtilities: nothingIfAll :: (Monoid (f (i a)), Foldable f, Filterable f) => (i a -> Bool) -> f (i a) -> Maybe (f (i a))
- IntervalAlgebra.IntervalUtilities: nothingIfAny :: (Monoid (f (i a)), Foldable f, Filterable f) => (i a -> Bool) -> f (i a) -> Maybe (f (i a))
- IntervalAlgebra.IntervalUtilities: nothingIfNone :: (Monoid (f (i a)), Foldable f, Filterable f) => (i a -> Bool) -> f (i a) -> Maybe (f (i a))
- IntervalAlgebra.IntervalUtilities: relationsL :: (Foldable f, Ord a, Intervallic i) => f (i a) -> [IntervalRelation]
- IntervalAlgebra.PairedInterval: instance (GHC.Classes.Ord a, GHC.Classes.Eq b, GHC.Base.Monoid b) => IntervalAlgebra.Core.IntervalCombinable (IntervalAlgebra.PairedInterval.PairedInterval b) a
- IntervalAlgebra.PairedInterval: instance (Test.QuickCheck.Arbitrary.Arbitrary b, GHC.Classes.Ord a, Test.QuickCheck.Arbitrary.Arbitrary a) => Test.QuickCheck.Arbitrary.Arbitrary (IntervalAlgebra.PairedInterval.PairedInterval b a)
- IntervalAlgebra.RelationProperties: class (IntervalSizeable a b) => IntervalRelationProperties a b
- IntervalAlgebra.RelationProperties: instance IntervalAlgebra.RelationProperties.IntervalRelationProperties Data.Time.Calendar.Days.Day GHC.Num.Integer.Integer
- IntervalAlgebra.RelationProperties: instance IntervalAlgebra.RelationProperties.IntervalRelationProperties Data.Time.Clock.Internal.UTCTime.UTCTime Data.Time.Clock.Internal.NominalDiffTime.NominalDiffTime
- IntervalAlgebra.RelationProperties: instance IntervalAlgebra.RelationProperties.IntervalRelationProperties GHC.Types.Int GHC.Types.Int
+ IntervalAlgebra.Arbitrary: arbitrarySizedPositive :: Integral a => Gen a
+ IntervalAlgebra.Arbitrary: genDay :: Gen Day
+ IntervalAlgebra.Arbitrary: genDiffTime :: Gen DiffTime
+ IntervalAlgebra.Arbitrary: genNominalDiffTime :: Gen NominalDiffTime
+ IntervalAlgebra.Arbitrary: genUTCTime :: Gen UTCTime
+ IntervalAlgebra.Arbitrary: instance Test.QuickCheck.Arbitrary.Arbitrary (IntervalAlgebra.Core.Interval Data.Time.Calendar.Days.Day)
+ IntervalAlgebra.Arbitrary: instance Test.QuickCheck.Arbitrary.Arbitrary (IntervalAlgebra.Core.Interval Data.Time.Clock.Internal.UTCTime.UTCTime)
+ IntervalAlgebra.Arbitrary: instance Test.QuickCheck.Arbitrary.Arbitrary (IntervalAlgebra.Core.Interval GHC.Integer.Type.Integer)
+ IntervalAlgebra.Arbitrary: instance Test.QuickCheck.Arbitrary.Arbitrary (IntervalAlgebra.Core.Interval GHC.Types.Double)
+ IntervalAlgebra.Arbitrary: instance Test.QuickCheck.Arbitrary.Arbitrary (IntervalAlgebra.Core.Interval GHC.Types.Int)
+ IntervalAlgebra.Arbitrary: maxDiffTime :: Int
+ IntervalAlgebra.Arbitrary: sizedIntervalGen :: (SizedIv (Interval a), Ord (Moment (Interval a))) => Int -> Gen a -> Gen (Moment (Interval a)) -> Gen (Interval a)
+ IntervalAlgebra.Axioms: makePos :: (Ord b, Num b) => b -> b
+ IntervalAlgebra.Axioms: xor :: Bool -> Bool -> Bool
+ IntervalAlgebra.Core: -- | Type of <a>moment</a>.
+ IntervalAlgebra.Core: class Iv iv
+ IntervalAlgebra.Core: class PointedIv iv where {
+ IntervalAlgebra.Core: class (PointedIv iv) => SizedIv iv where {
+ IntervalAlgebra.Core: converseRelation :: IntervalRelation -> IntervalRelation
+ IntervalAlgebra.Core: instance GHC.Classes.Ord a => IntervalAlgebra.Core.Iv (IntervalAlgebra.Core.Interval a)
+ IntervalAlgebra.Core: instance IntervalAlgebra.Core.PointedIv (IntervalAlgebra.Core.Interval a)
+ IntervalAlgebra.Core: instance IntervalAlgebra.Core.SizedIv (IntervalAlgebra.Core.Interval Data.Time.Calendar.Days.Day)
+ IntervalAlgebra.Core: instance IntervalAlgebra.Core.SizedIv (IntervalAlgebra.Core.Interval Data.Time.Clock.Internal.UTCTime.UTCTime)
+ IntervalAlgebra.Core: instance IntervalAlgebra.Core.SizedIv (IntervalAlgebra.Core.Interval GHC.Integer.Type.Integer)
+ IntervalAlgebra.Core: instance IntervalAlgebra.Core.SizedIv (IntervalAlgebra.Core.Interval GHC.Types.Double)
+ IntervalAlgebra.Core: instance IntervalAlgebra.Core.SizedIv (IntervalAlgebra.Core.Interval GHC.Types.Int)
+ IntervalAlgebra.Core: ivAfter :: Iv iv => iv -> iv -> Bool
+ IntervalAlgebra.Core: ivBefore :: Iv iv => iv -> iv -> Bool
+ IntervalAlgebra.Core: ivBegin :: PointedIv iv => iv -> Point iv
+ IntervalAlgebra.Core: ivContains :: Iv iv => iv -> iv -> Bool
+ IntervalAlgebra.Core: ivDuring :: Iv iv => iv -> iv -> Bool
+ IntervalAlgebra.Core: ivEnd :: PointedIv iv => iv -> Point iv
+ IntervalAlgebra.Core: ivEquals :: Iv iv => iv -> iv -> Bool
+ IntervalAlgebra.Core: ivExpandl :: SizedIv iv => Moment iv -> iv -> iv
+ IntervalAlgebra.Core: ivExpandr :: SizedIv iv => Moment iv -> iv -> iv
+ IntervalAlgebra.Core: ivFinishedBy :: Iv iv => iv -> iv -> Bool
+ IntervalAlgebra.Core: ivFinishes :: Iv iv => iv -> iv -> Bool
+ IntervalAlgebra.Core: ivMeets :: Iv iv => iv -> iv -> Bool
+ IntervalAlgebra.Core: ivMetBy :: Iv iv => iv -> iv -> Bool
+ IntervalAlgebra.Core: ivOverlappedBy :: Iv iv => iv -> iv -> Bool
+ IntervalAlgebra.Core: ivOverlaps :: Iv iv => iv -> iv -> Bool
+ IntervalAlgebra.Core: ivRelate :: Iv iv => iv -> iv -> IntervalRelation
+ IntervalAlgebra.Core: ivStartedBy :: Iv iv => iv -> iv -> Bool
+ IntervalAlgebra.Core: ivStarts :: Iv iv => iv -> iv -> Bool
+ IntervalAlgebra.Core: type family Moment iv;
+ IntervalAlgebra.Core: }
+ IntervalAlgebra.IntervalDiagram: instance (GHC.Enum.Enum (IntervalAlgebra.Core.Moment (IntervalAlgebra.Core.Interval a)), IntervalAlgebra.Core.SizedIv (IntervalAlgebra.Core.Interval a)) => Prettyprinter.Internal.Pretty (IntervalAlgebra.IntervalDiagram.IntervalText a)
+ IntervalAlgebra.IntervalDiagram: instance IntervalAlgebra.Core.SizedIv (IntervalAlgebra.Core.Interval a) => Prettyprinter.Internal.Pretty (Data.Either.Either IntervalAlgebra.IntervalDiagram.IntervalDiagramParseError (IntervalAlgebra.IntervalDiagram.IntervalDiagram a))
+ IntervalAlgebra.IntervalDiagram: instance IntervalAlgebra.Core.SizedIv (IntervalAlgebra.Core.Interval a) => Prettyprinter.Internal.Pretty (IntervalAlgebra.IntervalDiagram.IntervalDiagram a)
+ IntervalAlgebra.IntervalUtilities: (.+.) :: (Iv (Interval a), Ord (Moment (Interval a)), SizedIv (Interval a), Intervallic i) => i a -> i a -> Maybe (Interval a)
+ IntervalAlgebra.IntervalUtilities: (><) :: (Iv (Interval a), Ord (Moment (Interval a)), SizedIv (Interval a), Intervallic i) => i a -> i a -> Maybe (Interval a)
+ IntervalAlgebra.RelationProperties: allIArelations :: (SizedIv (Interval a), Ord a) => [ComparativePredicateOf1 (Interval a)]
- IntervalAlgebra.Arbitrary: arbitraryWithRelation :: forall i a b. (IntervalSizeable a b, Intervallic i, Arbitrary (i a)) => i a -> Set IntervalRelation -> Gen (Maybe (i a))
+ IntervalAlgebra.Arbitrary: arbitraryWithRelation :: forall i a b. (SizedIv (Interval a), Ord a, Eq (Moment (Interval a)), Arbitrary (Interval a)) => Interval a -> Set IntervalRelation -> Gen (Maybe (Interval a))
- IntervalAlgebra.Axioms: m1set :: (IntervalAxioms a b, IntervalSizeable a b) => Interval a -> b -> b -> b -> M1set a
+ IntervalAlgebra.Axioms: m1set :: (SizedIv (Interval a), b ~ Moment (Interval a), Ord b, Num b) => Interval a -> b -> b -> b -> M1set a
- IntervalAlgebra.Axioms: m2set :: (IntervalAxioms a b, IntervalSizeable a b) => Interval a -> Interval a -> b -> b -> M2set a
+ IntervalAlgebra.Axioms: m2set :: SizedIv (Interval a) => Interval a -> Interval a -> Moment (Interval a) -> Moment (Interval a) -> M2set a
- IntervalAlgebra.Axioms: m5set :: (IntervalAxioms a b, IntervalSizeable a b) => Interval a -> b -> b -> M5set a
+ IntervalAlgebra.Axioms: m5set :: (SizedIv (Interval a), Eq a, Ord (Moment (Interval a)), Num (Moment (Interval a))) => Interval a -> Moment (Interval a) -> Moment (Interval a) -> M5set a
- IntervalAlgebra.Axioms: prop_IAaxiomM1 :: (IntervalAxioms a b, Ord a) => M1set a -> Property
+ IntervalAlgebra.Axioms: prop_IAaxiomM1 :: (Iv (Interval a), SizedIv (Interval a)) => M1set a -> Property
- IntervalAlgebra.Axioms: prop_IAaxiomM2 :: (IntervalAxioms a b, IntervalSizeable a b, Show a) => M2set a -> Property
+ IntervalAlgebra.Axioms: prop_IAaxiomM2 :: (SizedIv (Interval a), Show a, Ord a) => M2set a -> Property
- IntervalAlgebra.Axioms: prop_IAaxiomM3 :: (IntervalAxioms a b, IntervalSizeable a b) => b -> Interval a -> Property
+ IntervalAlgebra.Axioms: prop_IAaxiomM3 :: (Iv (Interval a), SizedIv (Interval a)) => Moment (Interval a) -> Interval a -> Property
- IntervalAlgebra.Axioms: prop_IAaxiomM4 :: (IntervalAxioms a b, IntervalSizeable a b) => b -> M2set a -> Property
+ IntervalAlgebra.Axioms: prop_IAaxiomM4 :: forall a. (Iv (Interval a), SizedIv (Interval a), Ord (Moment (Interval a))) => Moment (Interval a) -> M2set a -> Property
- IntervalAlgebra.Axioms: prop_IAaxiomM4_1 :: (IntervalAxioms a b, IntervalSizeable a b) => b -> M2set a -> Property
+ IntervalAlgebra.Axioms: prop_IAaxiomM4_1 :: (SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => Moment (Interval a) -> M2set a -> Property
- IntervalAlgebra.Axioms: prop_IAaxiomM5 :: (IntervalAxioms a b, IntervalSizeable a b) => M5set a -> Property
+ IntervalAlgebra.Axioms: prop_IAaxiomM5 :: forall a. (SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => M5set a -> Property
- IntervalAlgebra.Axioms: prop_IAaxiomML1 :: (IntervalAxioms a b, Ord a) => Interval a -> Property
+ IntervalAlgebra.Axioms: prop_IAaxiomML1 :: (Iv (Interval a), SizedIv (Interval a)) => Interval a -> Property
- IntervalAlgebra.Axioms: prop_IAaxiomML2 :: (IntervalAxioms a b, Ord a) => M2set a -> Property
+ IntervalAlgebra.Axioms: prop_IAaxiomML2 :: (Iv (Interval a), SizedIv (Interval a)) => M2set a -> Property
- IntervalAlgebra.Core: after :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: after :: (Iv (Interval a), Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: before :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: before :: (Iv (Interval a), Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: begin :: Intervallic i => i a -> a
+ IntervalAlgebra.Core: begin :: forall i a. (SizedIv (Interval a), Intervallic i) => i a -> a
- IntervalAlgebra.Core: beginerval :: forall a b. IntervalSizeable a b => b -> a -> Interval a
+ IntervalAlgebra.Core: beginerval :: forall a. SizedIv (Interval a) => Moment (Interval a) -> a -> Interval a
- IntervalAlgebra.Core: beginervalFromEnd :: (IntervalSizeable a b, Intervallic i) => b -> i a -> Interval a
+ IntervalAlgebra.Core: beginervalFromEnd :: (SizedIv (Interval a), Intervallic i) => Moment (Interval a) -> i a -> Interval a
- IntervalAlgebra.Core: beginervalMoment :: forall a b. IntervalSizeable a b => a -> Interval a
+ IntervalAlgebra.Core: beginervalMoment :: forall a. SizedIv (Interval a) => a -> Interval a
- IntervalAlgebra.Core: bi :: IntervalSizeable a b => b -> a -> Interval a
+ IntervalAlgebra.Core: bi :: forall a. SizedIv (Interval a) => Moment (Interval a) -> a -> Interval a
- IntervalAlgebra.Core: concur :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: concur :: (SizedIv (Interval a), Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: contains :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: contains :: (Iv (Interval a), Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: disjoint :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: disjoint :: (SizedIv (Interval a), Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: duration :: (IntervalSizeable a b, Intervallic i) => i a -> b
+ IntervalAlgebra.Core: duration :: (SizedIv iv, Point iv ~ Moment iv, Num (Point iv)) => iv -> Moment iv
- IntervalAlgebra.Core: during :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: during :: (Iv (Interval a), Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: ei :: IntervalSizeable a b => b -> a -> Interval a
+ IntervalAlgebra.Core: ei :: forall a. SizedIv (Interval a) => Moment (Interval a) -> a -> Interval a
- IntervalAlgebra.Core: enclosedBy :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: enclosedBy :: (SizedIv (Interval a), Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: encloses :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: encloses :: (SizedIv (Interval a), Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: end :: Intervallic i => i a -> a
+ IntervalAlgebra.Core: end :: forall i a. (SizedIv (Interval a), Intervallic i) => i a -> a
- IntervalAlgebra.Core: enderval :: forall a b. IntervalSizeable a b => b -> a -> Interval a
+ IntervalAlgebra.Core: enderval :: forall a. SizedIv (Interval a) => Moment (Interval a) -> a -> Interval a
- IntervalAlgebra.Core: endervalFromBegin :: (IntervalSizeable a b, Intervallic i) => b -> i a -> Interval a
+ IntervalAlgebra.Core: endervalFromBegin :: (SizedIv (Interval a), Intervallic i) => Moment (Interval a) -> i a -> Interval a
- IntervalAlgebra.Core: endervalMoment :: forall a b. IntervalSizeable a b => a -> Interval a
+ IntervalAlgebra.Core: endervalMoment :: forall a. SizedIv (Interval a) => a -> Interval a
- IntervalAlgebra.Core: equals :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: equals :: (Iv (Interval a), Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: expand :: forall i a b. (IntervalSizeable a b, Intervallic i) => b -> b -> i a -> i a
+ IntervalAlgebra.Core: expand :: (SizedIv (Interval a), Intervallic i) => Moment (Interval a) -> Moment (Interval a) -> i a -> i a
- IntervalAlgebra.Core: expandl :: (IntervalSizeable a b, Intervallic i) => b -> i a -> i a
+ IntervalAlgebra.Core: expandl :: (SizedIv (Interval a), Intervallic i) => Moment (Interval a) -> i a -> i a
- IntervalAlgebra.Core: expandr :: (IntervalSizeable a b, Intervallic i) => b -> i a -> i a
+ IntervalAlgebra.Core: expandr :: (SizedIv (Interval a), Intervallic i) => Moment (Interval a) -> i a -> i a
- IntervalAlgebra.Core: extenterval :: (Ord a, Intervallic i) => i a -> i a -> Interval a
+ IntervalAlgebra.Core: extenterval :: (SizedIv (Interval a), Ord a, Intervallic i) => i a -> i a -> Interval a
- IntervalAlgebra.Core: finishedBy :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: finishedBy :: (Iv (Interval a), Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: finishes :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: finishes :: (Iv (Interval a), Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: meets :: (Eq a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: meets :: (Iv (Interval a), Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: metBy :: (Eq a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: metBy :: (Iv (Interval a), Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: moment :: forall a. IntervalSizeable a b => b
+ IntervalAlgebra.Core: moment :: (SizedIv iv, Num (Moment iv)) => Moment iv
- IntervalAlgebra.Core: momentize :: forall i a b. (IntervalSizeable a b, Intervallic i) => i a -> i a
+ IntervalAlgebra.Core: momentize :: forall i a. (SizedIv (Interval a), Intervallic i) => i a -> i a
- IntervalAlgebra.Core: notDisjoint :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: notDisjoint :: (SizedIv (Interval a), Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: overlappedBy :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: overlappedBy :: (Iv (Interval a), Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: overlaps :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: overlaps :: (Iv (Interval a), Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: precededBy :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: precededBy :: (Iv (Interval a), Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: precedes :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: precedes :: (Iv (Interval a), Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: predicate :: (Ord a, Intervallic i0, Intervallic i1) => Set IntervalRelation -> ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: predicate :: (SizedIv (Interval a), Ord a, Intervallic i0, Intervallic i1) => Set IntervalRelation -> ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: relate :: (Ord a, Intervallic i0, Intervallic i1) => i0 a -> i1 a -> IntervalRelation
+ IntervalAlgebra.Core: relate :: (Iv (Interval a), Intervallic i0, Intervallic i1) => i0 a -> i1 a -> IntervalRelation
- IntervalAlgebra.Core: safeInterval :: IntervalSizeable a b => (a, a) -> Interval a
+ IntervalAlgebra.Core: safeInterval :: forall a. (SizedIv (Interval a), Ord (Moment (Interval a))) => (a, a) -> Interval a
- IntervalAlgebra.Core: shiftFromBegin :: (IntervalSizeable a b, Intervallic i1, Intervallic i0) => i0 a -> i1 a -> i1 b
+ IntervalAlgebra.Core: shiftFromBegin :: (Num a, SizedIv (Interval a), Intervallic i1, Intervallic i0) => i0 a -> i1 a -> i1 a
- IntervalAlgebra.Core: shiftFromEnd :: (IntervalSizeable a b, Intervallic i1, Intervallic i0) => i0 a -> i1 a -> i1 b
+ IntervalAlgebra.Core: shiftFromEnd :: (Num a, SizedIv (Interval a), Intervallic i1, Intervallic i0) => i0 a -> i1 a -> i1 a
- IntervalAlgebra.Core: si :: IntervalSizeable a b => (a, a) -> Interval a
+ IntervalAlgebra.Core: si :: (SizedIv (Interval a), Ord (Moment (Interval a))) => (a, a) -> Interval a
- IntervalAlgebra.Core: startedBy :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: startedBy :: (Iv (Interval a), Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: starts :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: starts :: (Iv (Interval a), Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.Core: within :: (Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
+ IntervalAlgebra.Core: within :: (SizedIv (Interval a), Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a)
- IntervalAlgebra.IntervalDiagram: parseIntervalDiagram :: (Ord a, IntervalSizeable a b, Enum b) => IntervalDiagramOptions -> [(Int, Char)] -> Maybe AxisPlacement -> IntervalText a -> [([IntervalText a], [Text])] -> Either IntervalDiagramParseError (IntervalDiagram a)
+ IntervalAlgebra.IntervalDiagram: parseIntervalDiagram :: (Ord a, SizedIv (Interval a), Enum a, Num a, Enum (Moment (Interval a))) => IntervalDiagramOptions -> [(Int, Char)] -> Maybe AxisPlacement -> IntervalText a -> [([IntervalText a], [Text])] -> Either IntervalDiagramParseError (IntervalDiagram a)
- IntervalAlgebra.IntervalDiagram: simpleIntervalDiagram :: (Ord a, IntervalSizeable a b, Intervallic i, Enum b) => i a -> [i a] -> Either IntervalDiagramParseError (IntervalDiagram a)
+ IntervalAlgebra.IntervalDiagram: simpleIntervalDiagram :: (Ord a, SizedIv (Interval a), Intervallic i, Enum a, Num a, Enum (Moment (Interval a))) => i a -> [i a] -> Either IntervalDiagramParseError (IntervalDiagram a)
- IntervalAlgebra.IntervalDiagram: standardExampleDiagram :: (Num a, Ord a, Enum b, IntervalSizeable a b) => [(Interval a, String)] -> [([Interval a], String)] -> Either IntervalDiagramParseError (IntervalDiagram a)
+ IntervalAlgebra.IntervalDiagram: standardExampleDiagram :: (Num a, Enum a, Ord a, Enum (Moment (Interval a)), Ord (Moment (Interval a)), SizedIv (Interval a)) => [(Interval a, String)] -> [([Interval a], String)] -> Either IntervalDiagramParseError (IntervalDiagram a)
- IntervalAlgebra.IntervalUtilities: clip :: (Intervallic i0, Intervallic i1, IntervalSizeable a b) => i0 a -> i1 a -> Maybe (Interval a)
+ IntervalAlgebra.IntervalUtilities: clip :: (Intervallic i0, Intervallic i1, SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => i0 a -> i1 a -> Maybe (Interval a)
- IntervalAlgebra.IntervalUtilities: combineIntervals :: (Applicative f, Ord a, Intervallic i, Monoid (f (Interval a)), Foldable f) => f (i a) -> f (Interval a)
+ IntervalAlgebra.IntervalUtilities: combineIntervals :: (SizedIv (Interval a), Intervallic i, Ord a) => [i a] -> [Interval a]
- IntervalAlgebra.IntervalUtilities: combineIntervalsFromSorted :: (Applicative f, Ord a, Intervallic i, Monoid (f (Interval a)), Foldable f) => f (i a) -> f (Interval a)
+ IntervalAlgebra.IntervalUtilities: combineIntervalsFromSorted :: forall a i. (Ord a, Intervallic i, SizedIv (Interval a)) => [i a] -> [Interval a]
- IntervalAlgebra.IntervalUtilities: durations :: (Functor f, Intervallic i, IntervalSizeable a b) => f (i a) -> f b
+ IntervalAlgebra.IntervalUtilities: durations :: (Functor f, Intervallic i, SizedIv (Interval a)) => f (i a) -> f (Moment (Interval a))
- IntervalAlgebra.IntervalUtilities: gaps :: (IntervalCombinable i a, Traversable f, Monoid (f (Maybe (Interval a))), Applicative f) => f (i a) -> Maybe (f (Interval a))
+ IntervalAlgebra.IntervalUtilities: gaps :: (SizedIv (Interval a), Intervallic i, Ord a, Ord (Moment (Interval a))) => [i a] -> [Interval a]
- IntervalAlgebra.IntervalUtilities: intersect :: (Intervallic i, IntervalSizeable a b) => i a -> i a -> Maybe (Interval a)
+ IntervalAlgebra.IntervalUtilities: intersect :: (Intervallic i, SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => i a -> i a -> Maybe (Interval a)
- IntervalAlgebra.IntervalUtilities: lookahead :: (Intervallic i, IntervalSizeable a b) => b -> i a -> Interval a
+ IntervalAlgebra.IntervalUtilities: lookahead :: (Intervallic i, SizedIv (Interval a), Ord (Moment (Interval a))) => Moment (Interval a) -> i a -> Interval a
- IntervalAlgebra.IntervalUtilities: lookback :: (Intervallic i, IntervalSizeable a b) => b -> i a -> Interval a
+ IntervalAlgebra.IntervalUtilities: lookback :: (Intervallic i, SizedIv (Interval a), Ord (Moment (Interval a))) => Moment (Interval a) -> i a -> Interval a
- IntervalAlgebra.IntervalUtilities: pairGaps :: (Intervallic i, IntervalSizeable a b, IntervalCombinable i a) => [i a] -> [Maybe b]
+ IntervalAlgebra.IntervalUtilities: pairGaps :: (Intervallic i, SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => [i a] -> [Maybe (Moment (Interval a))]
- IntervalAlgebra.IntervalUtilities: rangeInterval :: (Ord a, Foldable t) => t (Interval a) -> Maybe (Interval a)
+ IntervalAlgebra.IntervalUtilities: rangeInterval :: (Foldable t, Ord a, SizedIv (Interval a)) => t (Interval a) -> Maybe (Interval a)
- IntervalAlgebra.IntervalUtilities: relations :: (Foldable f, Applicative m, Ord a, Intervallic i, Monoid (m IntervalRelation)) => f (i a) -> m IntervalRelation
+ IntervalAlgebra.IntervalUtilities: relations :: (Intervallic i, Iv (Interval a)) => [i a] -> [IntervalRelation]
- IntervalAlgebra.RelationProperties: prop_IAbefore :: IntervalRelationProperties a b => Interval a -> Interval a -> Property
+ IntervalAlgebra.RelationProperties: prop_IAbefore :: forall a. (SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => Interval a -> Interval a -> Property
- IntervalAlgebra.RelationProperties: prop_IAduring :: IntervalRelationProperties a b => Interval a -> Interval a -> Property
+ IntervalAlgebra.RelationProperties: prop_IAduring :: forall a. (SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => Interval a -> Interval a -> Property
- IntervalAlgebra.RelationProperties: prop_IAfinishes :: IntervalRelationProperties a b => Interval a -> Interval a -> Property
+ IntervalAlgebra.RelationProperties: prop_IAfinishes :: (SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => Interval a -> Interval a -> Property
- IntervalAlgebra.RelationProperties: prop_IAoverlaps :: IntervalRelationProperties a b => Interval a -> Interval a -> Property
+ IntervalAlgebra.RelationProperties: prop_IAoverlaps :: forall a. (SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => Interval a -> Interval a -> Property
- IntervalAlgebra.RelationProperties: prop_IAstarts :: IntervalRelationProperties a b => Interval a -> Interval a -> Property
+ IntervalAlgebra.RelationProperties: prop_IAstarts :: (SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => Interval a -> Interval a -> Property
- IntervalAlgebra.RelationProperties: prop_concur_predicate :: (IntervalRelationProperties a b, Ord a) => Interval a -> Interval a -> Property
+ IntervalAlgebra.RelationProperties: prop_concur_predicate :: (SizedIv (Interval a), Ord a) => Interval a -> Interval a -> Property
- IntervalAlgebra.RelationProperties: prop_disjoint_predicate :: (IntervalRelationProperties a b, Ord a) => Interval a -> Interval a -> Property
+ IntervalAlgebra.RelationProperties: prop_disjoint_predicate :: (SizedIv (Interval a), Ord a) => Interval a -> Interval a -> Property
- IntervalAlgebra.RelationProperties: prop_enclosedBy_predicate :: (IntervalRelationProperties a b, Ord a) => Interval a -> Interval a -> Property
+ IntervalAlgebra.RelationProperties: prop_enclosedBy_predicate :: (SizedIv (Interval a), Ord a) => Interval a -> Interval a -> Property
- IntervalAlgebra.RelationProperties: prop_encloses_predicate :: (IntervalRelationProperties a b, Ord a) => Interval a -> Interval a -> Property
+ IntervalAlgebra.RelationProperties: prop_encloses_predicate :: (SizedIv (Interval a), Ord a) => Interval a -> Interval a -> Property
- IntervalAlgebra.RelationProperties: prop_exclusiveRelations :: IntervalRelationProperties a b => Interval a -> Interval a -> Property
+ IntervalAlgebra.RelationProperties: prop_exclusiveRelations :: (SizedIv (Interval a), Ord a) => Interval a -> Interval a -> Property
- IntervalAlgebra.RelationProperties: prop_notdisjoint_predicate :: (IntervalRelationProperties a b, Ord a) => Interval a -> Interval a -> Property
+ IntervalAlgebra.RelationProperties: prop_notdisjoint_predicate :: (SizedIv (Interval a), Ord a) => Interval a -> Interval a -> Property
- IntervalAlgebra.RelationProperties: prop_predicate_unions :: (IntervalRelationProperties a b, Ord a) => Set IntervalRelation -> ComparativePredicateOf2 (Interval a) (Interval a) -> Interval a -> Interval a -> Property
+ IntervalAlgebra.RelationProperties: prop_predicate_unions :: (SizedIv (Interval a), Ord a) => Set IntervalRelation -> ComparativePredicateOf2 (Interval a) (Interval a) -> Interval a -> Interval a -> Property
- IntervalAlgebra.RelationProperties: prop_within_predicate :: (IntervalRelationProperties a b, Ord a) => Interval a -> Interval a -> Property
+ IntervalAlgebra.RelationProperties: prop_within_predicate :: (SizedIv (Interval a), Ord a) => Interval a -> Interval a -> Property
Files
- ChangeLog.md +24/−0
- LICENSE +2/−1
- README.md +62/−7
- interval-algebra.cabal +8/−16
- src/IntervalAlgebra.hs +8/−7
- src/IntervalAlgebra/Arbitrary.hs +62/−29
- src/IntervalAlgebra/Axioms.hs +206/−205
- src/IntervalAlgebra/Core.hs +1619/−1349
- src/IntervalAlgebra/IntervalDiagram.hs +19/−27
- src/IntervalAlgebra/IntervalUtilities.hs +102/−665
- src/IntervalAlgebra/PairedInterval.hs +19/−28
- src/IntervalAlgebra/RelationProperties.hs +100/−98
- test-axioms/AxiomsSpec.hs +11/−10
- test-relation-properties/RelationPropertiesSpec.hs +1/−1
- test/IntervalAlgebra/IntervalUtilitiesSpec.hs +40/−314
- test/IntervalAlgebra/PairedIntervalSpec.hs +4/−12
- test/IntervalAlgebraSpec.hs +77/−23
- tutorial/TutorialMain.hs +55/−45
ChangeLog.md view
@@ -1,5 +1,29 @@ # Changelog for interval-algebra +## 2.2.0++* Redesigns the core typeclasses:+ * Decouples the interval algebra functionality from that of defining and+ manipulating intervals as pairs of points.+ * New `Iv` class to implement the relation algebra from Allen 1983, over any + abstract interval type `iv`.+ * `PointedIv` class for intervals that can be cast to the canonical `Interval`.+ * `SizedIv` class for intervals that can be manipulated, providing stronger tools+ to create intervals that are consistent with the interval algebra when more+ structure is available. In effect replaces the `b` in the old+ `IntervalSizeable b a` with an associated `Moment` type of `SizedIv`.+* Reimplements various interval constructors, such as `safeInterval`, to use+ the new typeclass methods while maintaining the previous implementation's + behavior.+* Removes the `Safe` pragma.+* Removes `witch`, `witherable` and `safe` package dependencies.+* Cleans up the `IntervalAlgebra.IntervalUtilities` module, removing many+ esoteric functions and tidying the type signatures and implementations of the+ remaining ones.+* Fixes a bug in the `gaps` utility.+* Fixes the `Arbitrary` for constructing valid intervals, which was made easier+ by the switch from `IntervalSizeable` to the redesigned `SizedIv`.+ ## 2.1.3 * Removes the version constraints on the `witch` package.
LICENSE view
@@ -1,4 +1,5 @@-Copyright NoviSci, Inc (c) 2020+Copyright NoviSci, Inc (c) 2020-2022+Copyright Target RWE (c) 2023 All rights reserved.
README.md view
@@ -1,17 +1,72 @@ # interval-algebra -The `interval-algebra` package implements [Allen's interval algebra](https://www.ics.uci.edu/~alspaugh/cls/shr/allen.html) in [Haskell](https://www.haskell.org). The main module provides data types and related classes for the interval-based temporal logic described in [Allen (1983)](https://doi.org/10.1145/182.358434) and axiomatized in [Allen and Hayes (1987)](https://doi.org/10.1111/j.1467-8640.1989.tb00329.x). A good primer on Allen's algebra can be [found here](https://thomasalspaugh.org/pub/fnd/allen.html).+The `interval-algebra` package implements [Allen's interval+algebra](https://en.wikipedia.org/wiki/Allen%27s_interval_algebra) in+[Haskell](https://www.haskell.org), for a canonical representation of intervals+as a pair of points representing a begin and an end. The main module provides+data types and related classes for the interval-based temporal logic described+in [Allen (1983)](https://doi.org/10.1145/182.358434) and axiomatized in [Allen+and Hayes (1987)](https://doi.org/10.1111/j.1467-8640.1989.tb00329.x). A good+primer on Allen's algebra can be [found+here](https://thomasalspaugh.org/pub/fnd/allen.html). ## Design+The module provides an `Interval` type wrapping the most basic type of interval+needed for the relation algebra defined in the papers cited above. `Interval a`+wraps `(a, a)`, giving the interval's `begin` and `end` points. -The module is built around three typeclasses designed to separate concerns of constructing, relating, and combining types that contain `Interval`s:+However, the module provides typeclasses to generalize an `Interval` and the+interval algebra for temporal logic: -1. `Intervallic` provides an interface to the data structures which contain an `Interval`.-2. `IntervalCombinable` provides an interface to methods of combining two `Interval`s.-3. `IntervalSizeable` provides methods for measuring and modifying the size of an interval.+1. `Iv` provides an abstract interface for defining the 13 relations of the+ interval algebra. Instances are provided for the canonical `Interval a`,+ when `a` is an instance of `Ord`, as described in Allen 1983. However, + the interval algebra can be used for temporal logic on "intervals" that+ are qualitative and not represented as pairs of points in an ordered set, + as provided in examples of that paper.+2. `PointedIv` is an interface for types that, in effect, be cast to the + canonical `Interval`.+3. `SizedIv` provides a generic interface for creating and+ manipulating `PointedIv` intervals. In particular, when the interval type also + is an instance of `Iv`, it specifies class properties to ensure + intervals created or altered via its methods are valid for the purpose using the interval + algebra. +1. `Intervallic` provides an interface for data structures which contain an+ `Interval`, allowing the relation algebra to be performed relative to the+ `Interval` within. The `PairedInterval` defined here is the prototypical+ case. -An advantage of nested typeclass design is that developers can define an `Interval` of type `a` with just the amount of structure that they need.+The module defines instances of the classes above for `Interval a`, and only+provides `SizedIv (Interval a)` instances for a few common `a`. See class+documentation for examples of other possible use-cases. It also defines a+variety of ways to construct valid `Interval a` values for supported point+types `a`. +The loose naming convention is: "Bare" names such as `starts` or `contains` are+generalized over `Intervallic` and their `Iv*` class counterparts start with+`iv`, for example `ivStarts` and `ivContains`.+ ## Axiom tests -The package [includes tests](test/IntervalAlgebraSpec.hs) that the functions of the `IntervalAlgebraic` typeclass meets the axioms for _intervals_ (not points) as laid out in [Allen and Hayes (1987)](https://doi.org/10.1111/j.1467-8640.1989.tb00329.x).+The package [includes tests](test/IntervalAlgebraSpec.hs) that the functions of+the `IntervalAlgebraic` typeclass meets the axioms for _intervals_ (not points)+as laid out in [Allen and Hayes+(1987)](https://doi.org/10.1111/j.1467-8640.1989.tb00329.x).++## Comparisons++`interval-algebra` differs from `data-interval` mainly in that it is more+general and has as its starting point the relation algebra from Allen 1983. The+latter package provides an interval type that is tied to the notion of an+interval as a connected convex subset of the integer or real lines,+differentiating for example between closed and open endpoints. It provides+a `Relation` type codifying the 13 temporal relations from Allen 1983.++For use-cases where that structure is meaningful, `data-interval` might be a+more natural choice. `interval-algebra` might be used instead when more+abstract concepts are needed or there is no need for the notion of+connectedness between the starting and ending points.++An important difference is that `data-interval` supports empty+intervals. `interval-algebra` does not, since Allen's interval relations cannot+be defined for such intervals.
interval-algebra.cabal view
@@ -1,18 +1,16 @@ cabal-version: 2.2 name: interval-algebra-version: 2.1.3+version: 2.2.0 synopsis: An implementation of Allen's interval algebra for temporal logic description: Please see the README on GitHub at <https://github.com/novisci/interval-algebra> category: Algebra,Time homepage: https://github.com/novisci/interval-algebra#readme bug-reports: https://github.com/novisci/interval-algebra/issues-author: Bradley Saul- Brendan Brown-maintainer: bsaul@novisci.com, 2020-2022- bbrown@targetrwe.com, 2023+author: Bradley Saul, Brendan Brown+maintainer: <bsaul@novisci.com> 2020-2022, <bbrown@targetrwe.com> 2023 -copyright: (c) NoviSci, 2020-2022- Target RWE, 2023+copyright: (c) NoviSci 2020-2022,+ Target RWE 2023 license: BSD-3-Clause license-file: LICENSE build-type: Simple@@ -48,11 +46,8 @@ , foldl ^>= 1.4 , prettyprinter ^>= 1.7 , QuickCheck ^>= 2.14- , safe ^>= 0.3 , text ^>= 1.2 || ^>= 2.0 , time >= 1.9 && < 2- , witch- , witherable ^>= 0.4 default-language: Haskell2010 test-suite axioms@@ -65,7 +60,7 @@ ghc-options: -threaded -rtsopts -with-rtsopts=-N build-depends: base >=4.7 && <5- , hspec+ , hspec < 2.12 , interval-algebra , QuickCheck , time@@ -81,7 +76,7 @@ ghc-options: -threaded -rtsopts -with-rtsopts=-N build-depends: base >=4.7 && <5- , hspec+ , hspec < 2.12 , interval-algebra , QuickCheck , time@@ -103,12 +98,10 @@ build-depends: base >=4.7 && <5 , containers- , hspec+ , hspec < 2.12 , interval-algebra , QuickCheck- , safe , time- , witherable build-tool-depends: hspec-discover:hspec-discover >= 2.9.2 default-language: Haskell2010@@ -122,5 +115,4 @@ , interval-algebra , prettyprinter ^>= 1.7 , time >= 1.9 && < 2- , witch default-language: Haskell2010
src/IntervalAlgebra.hs view
@@ -1,9 +1,11 @@ {-| Module : Interval Algebra Description : Implementation of Allen's interval algebra-Copyright : (c) NoviSci, Inc 2020+Copyright : (c) NoviSci, Inc 2020-2022+ TargetRWE, 2023 License : BSD3-Maintainer : bsaul@novisci.com+Maintainer : bsaul@novisci.com 2020-2022+ bbrown@targetrwe.com 2023 The @IntervalAlgebra@ module provides data types and related classes for the interval-based temporal logic described in [Allen (1983)](https://doi.org/10.1145/182.358434)@@ -16,14 +18,13 @@ -} -{-# LANGUAGE Safe #-} module IntervalAlgebra ( module IntervalAlgebra.Core , module IntervalAlgebra.IntervalUtilities , module IntervalAlgebra.PairedInterval ) where -import safe IntervalAlgebra.Core-import safe IntervalAlgebra.IntervalDiagram-import safe IntervalAlgebra.IntervalUtilities-import safe IntervalAlgebra.PairedInterval+import IntervalAlgebra.Core+import IntervalAlgebra.IntervalDiagram+import IntervalAlgebra.IntervalUtilities+import IntervalAlgebra.PairedInterval
src/IntervalAlgebra/Arbitrary.hs view
@@ -1,22 +1,21 @@ {-| Module : Generate arbitrary Intervals Description : Functions for generating arbitrary intervals-Copyright : (c) NoviSci, Inc 2020+Copyright : (c) NoviSci, Inc 2020-2022+ TargetRWE, 2023 License : BSD3-Maintainer : bsaul@novisci.com+Maintainer : bsaul@novisci.com 2020-2022, bbrown@targetrwe.com 2023 Stability : experimental -}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE Safe #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-} -module IntervalAlgebra.Arbitrary- ( arbitraryWithRelation- ) where+module IntervalAlgebra.Arbitrary where import Control.Applicative (liftA2, (<$>)) import Control.Monad (liftM2)@@ -37,11 +36,10 @@ import GHC.Num import GHC.Real import IntervalAlgebra (Interval, IntervalRelation (..),- IntervalSizeable, Intervallic,- PairedInterval, beginerval, converse,+ Intervallic, PairedInterval, Point,+ SizedIv (..), beginerval, converse, duration, makePairedInterval, moment, predicate, strictWithinRelations)-import Prelude (Eq, (==)) import Test.QuickCheck (Arbitrary (arbitrary, shrink), Gen, NonNegative, arbitrarySizedNatural, elements, resize, sized, suchThat)@@ -55,21 +53,56 @@ maxDiffTime :: Int maxDiffTime = 86399 -instance Arbitrary DT.Day where- arbitrary = sized (\s -> DT.ModifiedJulianDay <$> s `resize` arbitrary)- shrink = (DT.ModifiedJulianDay <$>) . shrink . DT.toModifiedJulianDay+--instance Arbitrary DT.DiffTime where+-- arbitrary = sized+--+--+--instance Arbitrary DT.UTCTime where+-- arbitrary = liftA2 UTCTime arbitrary arbitrary -instance Arbitrary DT.NominalDiffTime where- arbitrary = sized- (\s -> fromInteger <$> (min s maxDiffTime `resize` arbitrarySizedNatural))+-- Helper+-- NOTE: You likely want to restrict the size of `dur` in a more appropriate+-- way, to be uniform over the range >= moment.+sizedIntervalGen :: (SizedIv (Interval a), Ord (Moment (Interval a))) => Int -> Gen a -> Gen (Moment (Interval a)) -> Gen (Interval a)+sizedIntervalGen s gpt gmom = do+ b <- s `resize` gpt+ dur <- s `resize` gmom+ pure $ beginerval dur b -instance Arbitrary DT.DiffTime where- arbitrary = sized- (\s -> fromInteger <$> (min s maxDiffTime `resize` arbitrarySizedNatural))+-- Generators for types that do not implement Arbitrary. This avoids creating+-- orphan instances for these types. -instance Arbitrary DT.UTCTime where- arbitrary = liftA2 UTCTime arbitrary arbitrary+genDay :: Gen DT.Day+genDay = sized (\s -> DT.ModifiedJulianDay <$> s `resize` arbitrary) +genNominalDiffTime :: Gen DT.NominalDiffTime+genNominalDiffTime = sized (\s -> fromInteger <$> (min s maxDiffTime `resize` arbitrarySizedNatural))++genDiffTime :: Gen DT.DiffTime+genDiffTime = sized (\s -> fromInteger <$> (min s maxDiffTime `resize` arbitrarySizedNatural))++genUTCTime :: Gen DT.UTCTime+genUTCTime = sized (\s -> liftA2 UTCTime genDay genDiffTime)++-- Arbitrary instances+-- for SizedIv instances defined in Core++instance Arbitrary (Interval Int) where+ arbitrary = sized (\s -> sizedIntervalGen s arbitrary arbitrary)++instance Arbitrary (Interval Integer) where+ arbitrary = sized (\s -> sizedIntervalGen s arbitrary arbitrary)++instance Arbitrary (Interval Double) where+ arbitrary = sized (\s -> sizedIntervalGen s arbitrary arbitrary)++instance Arbitrary (Interval DT.Day) where+ arbitrary = sized (\s -> sizedIntervalGen s genDay arbitrary)++instance Arbitrary (Interval DT.UTCTime) where+ arbitrary = sized (\s -> sizedIntervalGen s genUTCTime genNominalDiffTime)++ -- | Conditional generation of intervals relative to a reference. If the -- reference @iv@ is of 'moment' duration, it is not possible to generate -- intervals from the strict enclose relations StartedBy, Contains, FinishedBy.@@ -91,10 +124,10 @@ -- arbitraryWithRelation :: forall i a b- . (IntervalSizeable a b, Intervallic i, Arbitrary (i a))- => i a -- ^ reference interval+ . (SizedIv (Interval a), Ord a, Eq (Moment (Interval a)), Arbitrary (Interval a))+ => Interval a -- ^ reference interval -> Data.Set.Set IntervalRelation -- ^ set of `IntervalRelation`s, of which at least one will hold for the generated interval relative to the reference- -> Gen (Maybe (i a))+ -> Gen (Maybe (Interval a)) arbitraryWithRelation iv rs | rs == Data.Set.singleton Equals = elements [Just iv] | isEnclose && isMom = elements [Nothing]@@ -103,4 +136,4 @@ where notStrictEnclose = Data.Set.difference rs (converse strictWithinRelations) isEnclose = Data.Set.null notStrictEnclose- isMom = duration iv == moment @a+ isMom = duration iv == moment @(Interval a)
src/IntervalAlgebra/Axioms.hs view
@@ -2,39 +2,43 @@ {-| Module : Interval Algebra Axioms Description : Properties of Intervals-Copyright : (c) NoviSci, Inc 2020+Copyright : (c) NoviSci, Inc 2020-2022+ TargetRWE, 2023 License : BSD3-Maintainer : bsaul@novisci.com+Maintainer : bsaul@novisci.com 2020-2022, bbrown@targetrwe.com 2023 -This module exports a single typeclass @IntervalAxioms@ which contains-property-based tests for the axioms in section 1 of [Allen and Hayes (1987)](https://doi.org/10.1111/j.1467-8640.1989.tb00329.x).-The notation below is that of the original paper.+This module exports utilities for property-based tests for the axioms in+section 1 of [Allen and Hayes+(1987)](https://doi.org/10.1111/j.1467-8640.1989.tb00329.x). The notation+below is that of the original paper. This module is useful if creating a new instance of interval types that you want to test. -} +{-# LANGUAGE ExplicitForAll #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-} -module IntervalAlgebra.Axioms- ( IntervalAxioms(..)- , M1set(..)- , M2set(..)- , M5set(..)- ) where+module IntervalAlgebra.Axioms where -import Data.Either (isRight)-import Data.Maybe (fromJust, isJust, isNothing)-import Data.Set (Set, disjointUnion, fromList,- member)-import Data.Time as DT (Day (..), DiffTime,- NominalDiffTime, UTCTime (..))+import Data.Either (isRight)+import Data.Maybe (fromJust, isJust, isNothing)+import Data.Set (Set, disjointUnion,+ fromList, member)+import Data.Time as DT (Day (..), DiffTime,+ NominalDiffTime,+ UTCTime (..)) import IntervalAlgebra.Arbitrary import IntervalAlgebra.Core-import Test.QuickCheck (Arbitrary (arbitrary), Property,- (===), (==>))+import IntervalAlgebra.IntervalUtilities ((.+.))+import Test.QuickCheck (Arbitrary (arbitrary),+ Property, (===), (==>)) xor :: Bool -> Bool -> Bool@@ -47,13 +51,14 @@ | otherwise = x -- | A set used for testing M1 defined so that the M1 condition is true.-data M1set a = M1set- { m11 :: Interval a- , m12 :: Interval a- , m13 :: Interval a- , m14 :: Interval a- }- deriving Show+data M1set a+ = M1set+ { m11 :: Interval a+ , m12 :: Interval a+ , m13 :: Interval a+ , m14 :: Interval a+ }+ deriving (Show) instance Arbitrary (M1set Int) where arbitrary = do@@ -72,19 +77,20 @@ instance Arbitrary (M1set DT.UTCTime) where arbitrary = do x <- arbitrary- a <- arbitrary- b <- arbitrary- m1set x a b <$> arbitrary+ a <- genNominalDiffTime+ b <- genNominalDiffTime+ m1set x a b <$> genNominalDiffTime -- | A set used for testing M2 defined so that the M2 condition is true.-data M2set a = M2set- { m21 :: Interval a- , m22 :: Interval a- , m23 :: Interval a- , m24 :: Interval a- }- deriving Show+data M2set a+ = M2set+ { m21 :: Interval a+ , m22 :: Interval a+ , m23 :: Interval a+ , m24 :: Interval a+ }+ deriving (Show) instance Arbitrary (M2set Int) where arbitrary = do@@ -104,15 +110,16 @@ arbitrary = do x <- arbitrary a <- arbitrary- b <- arbitrary- m2set x a b <$> arbitrary+ b <- genNominalDiffTime+ m2set x a b <$> genNominalDiffTime -- | A set used for testing M5.-data M5set a = M5set- { m51 :: Interval a- , m52 :: Interval a- }- deriving Show+data M5set a+ = M5set+ { m51 :: Interval a+ , m52 :: Interval a+ }+ deriving (Show) instance Arbitrary (M5set Int) where arbitrary = do@@ -129,212 +136,206 @@ instance Arbitrary (M5set DT.UTCTime) where arbitrary = do x <- arbitrary- a <- arbitrary- m5set x a <$> arbitrary+ a <- genNominalDiffTime+ m5set x a <$> genNominalDiffTime --- | = "An Axiomatization of Interval Time".-class ( IntervalSizeable a b ) => IntervalAxioms a b where+-- Axiom functions - -- | Smart constructor of 'M1set'.- m1set :: (IntervalSizeable a b) => Interval a -> b -> b -> b -> M1set a- m1set x a b c = M1set p1 p2 p3 p4- where p1 = x -- interval i in prop_IAaxiomM1- p2 = beginerval a (end x) -- interval j in prop_IAaxiomM1- p3 = beginerval b (end x) -- interval k in prop_IAaxiomM1- p4 = enderval (makePos c) (begin p2)+-- | Smart constructor of 'M1set'.+m1set :: (SizedIv (Interval a), b ~ Moment (Interval a), Ord b, Num b) => Interval a -> b -> b -> b -> M1set a+m1set x a b c = M1set p1 p2 p3 p4+ where p1 = x -- interval i in prop_IAaxiomM1+ p2 = beginerval a (end x) -- interval j in prop_IAaxiomM1+ p3 = beginerval b (end x) -- interval k in prop_IAaxiomM1+ p4 = enderval (makePos c) (begin p2) - {- | - == Axiom M1+{- | - The first axiom of Allen and Hayes (1987) states that if "two periods both- meet a third, thn any period met by one must also be met by the other."- That is:+== Axiom M1 - \[- \forall \text{ i,j,k,l } s.t. (i:j \text{ & } i:k \text{ & } l:j) \implies l:k- \]- -}- prop_IAaxiomM1 :: (Ord a) => M1set a -> Property- prop_IAaxiomM1 x =- (i `meets` j && i `meets` k && l `meets` j) ==> (l `meets` k)- where i = m11 x- j = m12 x- k = m13 x- l = m14 x+The first axiom of Allen and Hayes (1987) states that if "two periods both+meet a third, thn any period met by one must also be met by the other."+That is: - -- | Smart constructor of 'M2set'.- m2set :: (IntervalSizeable a b)=> Interval a -> Interval a -> b -> b -> M2set a- m2set x y a b = M2set p1 p2 p3 p4- where p1 = x -- interval i in prop_IAaxiomM2- p2 = beginerval a (end x) -- interval j in prop_IAaxiomM2- p3 = y -- interval k in prop_IAaxiomM2- p4 = beginerval b (end y) -- interval l in prop_IAaxiomM2+\[+ \forall \text{ i,j,k,l } s.t. (i:j \text{ & } i:k \text{ & } l:j) \implies l:k+\]+-}+prop_IAaxiomM1 :: (Iv (Interval a), SizedIv (Interval a)) => M1set a -> Property+prop_IAaxiomM1 x =+ (i `meets` j && i `meets` k && l `meets` j) ==> (l `meets` k)+ where i = m11 x+ j = m12 x+ k = m13 x+ l = m14 x - {- |+-- | Smart constructor of 'M2set'.+m2set :: (SizedIv (Interval a)) => Interval a -> Interval a -> Moment (Interval a) -> Moment (Interval a) -> M2set a+m2set x y a b = M2set p1 p2 p3 p4+ where p1 = x -- interval i in prop_IAaxiomM2+ p2 = beginerval a (end x) -- interval j in prop_IAaxiomM2+ p3 = y -- interval k in prop_IAaxiomM2+ p4 = beginerval b (end y) -- interval l in prop_IAaxiomM2 - == Axiom M2+{- | - If period i meets period j and period k meets l,- then exactly one of the following holds:+== Axiom M2 - 1) i meets l;- 2) there is an m such that i meets m and m meets l;- 3) there is an n such that k meets n and n meets j.+If period i meets period j and period k meets l,+then exactly one of the following holds: - That is,+ 1) i meets l;+ 2) there is an m such that i meets m and m meets l;+ 3) there is an n such that k meets n and n meets j. - \[- \forall i,j,k,l s.t. (i:j \text { & } k:l) \implies- i:l \oplus- (\exists m s.t. i:m:l) \oplus- (\exists m s.t. k:m:j)- \]+That is, - -}+\[+ \forall i,j,k,l s.t. (i:j \text { & } k:l) \implies+ i:l \oplus+ (\exists m s.t. i:m:l) \oplus+ (\exists m s.t. k:m:j)+\] - prop_IAaxiomM2 :: (IntervalSizeable a b, Show a) =>- M2set a -> Property- prop_IAaxiomM2 x =- (i `meets` j && k `meets` l) ==>- (i `meets` l) `xor`- isRight m `xor`- isRight n- where i = m21 x- j = m22 x- k = m23 x- l = m24 x- m = parseInterval (end i) (begin l)- n = parseInterval (end k) (begin j)+-} - {- |+prop_IAaxiomM2 :: (SizedIv (Interval a), Show a, Ord a) =>+ M2set a -> Property+prop_IAaxiomM2 x =+ (i `meets` j && k `meets` l) ==>+ (i `meets` l) `xor`+ isRight m `xor`+ isRight n+ where i = m21 x+ j = m22 x+ k = m23 x+ l = m24 x+ m = parseInterval (end i) (begin l)+ n = parseInterval (end k) (begin j) - == Axiom ML1+{- | - An interval cannot meet itself.+== Axiom ML1 - \[- \forall i \lnot i:i- \]- -}+An interval cannot meet itself. - prop_IAaxiomML1 :: (Ord a) => Interval a -> Property- prop_IAaxiomML1 x = not (x `meets` x) === True+\[+ \forall i \lnot i:i+\]+-} - {- |+prop_IAaxiomML1 :: (Iv (Interval a), SizedIv (Interval a)) => Interval a -> Property+prop_IAaxiomML1 x = not (x `meets` x) === True - == Axiom ML2+{- | - If i meets j then j does not meet i.+== Axiom ML2 - \[- \forall i,j i:j \implies \lnot j:i- \]- -}+If i meets j then j does not meet i. - prop_IAaxiomML2 :: (Ord a)=> M2set a -> Property- prop_IAaxiomML2 x =- (i `meets` j) ==> not (j `meets` i)- where i = m21 x- j = m22 x+\[+\forall i,j i:j \implies \lnot j:i+\]+-} - {- |+prop_IAaxiomML2 :: (Iv (Interval a), SizedIv (Interval a))=> M2set a -> Property+prop_IAaxiomML2 x =+ (i `meets` j) ==> not (j `meets` i)+ where i = m21 x+ j = m22 x - == Axiom M3+{- | - Time does not start or stop:+== Axiom M3 - \[- \forall i \exists j,k s.t. j:i:k- \]- -}+Time does not start or stop: - prop_IAaxiomM3 :: (IntervalSizeable a b)=>- b -> Interval a -> Property- prop_IAaxiomM3 b i =- (j `meets` i && i `meets` k) === True- where j = enderval b (begin i)- k = beginerval b (end i)+\[+\forall i \exists j,k s.t. j:i:k+\]+-} - {- |- ML3 says that For all i, there does not exist m such that i meets m and- m meet i. Not testing that this axiom holds, as I'm not sure how I would- test the lack of existence easily.- -}+prop_IAaxiomM3 :: (Iv (Interval a), SizedIv (Interval a))=>+ Moment (Interval a) -> Interval a -> Property+prop_IAaxiomM3 b i =+ (j `meets` i && i `meets` k) === True+ where j = enderval b (begin i)+ k = beginerval b (end i) - {- |+{- |+ ML3 says that For all i, there does not exist m such that i meets m and+ m meet i. Not testing that this axiom holds, as I'm not sure how I would+ test the lack of existence easily.+-} - == Axiom M4+{- | - If two meets are separated by intervals, then this sequence is a longer interval.+== Axiom M4 - \[- \forall i,j i:j \implies (\exists k,m,n s.t m:i:j:n \text { & } m:k:n)- \]- -}+If two meets are separated by intervals, then this sequence is a longer interval. - prop_IAaxiomM4 :: (IntervalSizeable a b)=>- b -> M2set a -> Property- prop_IAaxiomM4 b x =- ((m `meets` i && i `meets` j && j `meets` n) &&- (m `meets` k && k `meets` n)) === True- where i = m21 x- j = m22 x- m = enderval b (begin i)- n = beginerval b (end j)- k = beginerval g (end m)- g = diff (begin n) (end m)+\[+\forall i,j i:j \implies (\exists k,m,n s.t m:i:j:n \text { & } m:k:n)+\]+-} +prop_IAaxiomM4 :: forall a. (Iv (Interval a), SizedIv (Interval a), Ord (Moment (Interval a)))=>+ Moment (Interval a) -> M2set a -> Property+prop_IAaxiomM4 b x =+ ((m `meets` i && i `meets` j && j `meets` n) &&+ (m `meets` k && k `meets` n)) === True+ where i = m21 x+ j = m22 x+ m = enderval b (begin i)+ n = beginerval b (end j)+ k = safeInterval (end m, begin n) - -- | Smart constructor of 'M5set'.- m5set :: (IntervalSizeable a b)=> Interval a -> b -> b -> M5set a- m5set x a b = M5set p1 p2- where p1 = x -- interval i in prop_IAaxiomM5- p2 = beginerval a ps -- interval l in prop_IAaxiomM5- ps = end (expandr (makePos b) x) -- creating l by shifting and expanding i - {- |+-- | Smart constructor of 'M5set'.+m5set :: (SizedIv (Interval a), Eq a, Ord (Moment (Interval a)), Num (Moment (Interval a)))=> Interval a -> Moment (Interval a) -> Moment (Interval a) -> M5set a+m5set x a b = M5set p1 p2+ where p1 = x -- interval i in prop_IAaxiomM5+ p2 = beginerval a ps -- interval l in prop_IAaxiomM5+ ps = end (expandr (makePos b) x) -- creating l by shifting and expanding i - == Axiom M5+{- | - There is only one time period between any two meeting places.+== Axiom M5 - \[- \forall i,j,k,l (i:j:l \text{ & } i:k:l) \equiv j = k- \]- -}- prop_IAaxiomM5 :: (IntervalSizeable a b) =>- M5set a -> Property- prop_IAaxiomM5 x =- ((i `meets` j && j `meets` l) &&- (i `meets` k && k `meets` l)) === (j == k)- where i = m51 x- j = beginerval g (end i)- k = beginerval g (end i)- g = diff (begin l) (end i)- l = m52 x+There is only one time period between any two meeting places. - {- |+\[+\forall i,j,k,l (i:j:l \text{ & } i:k:l) \equiv j = k+\]+-}+prop_IAaxiomM5 :: forall a. (SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) =>+ M5set a -> Property+prop_IAaxiomM5 x =+ ((i `meets` j && j `meets` l) &&+ (i `meets` k && k `meets` l)) === (j == k)+ where i = m51 x+ j = safeInterval (end i, begin l)+ k = j+ l = m52 x - == Axiom M4.1+{- | - Ordered unions:+== Axiom M4.1 - \[- \forall i,j i:j \implies (\exists m,n s.t. m:i:j:n \text{ & } m:(i+j):n)- \]- -}- prop_IAaxiomM4_1 :: (IntervalSizeable a b)=>- b -> M2set a -> Property- prop_IAaxiomM4_1 b x =- ((m `meets` i && i `meets` j && j `meets` n) &&- (m `meets` ij && ij `meets` n)) === True- where i = m21 x- j = m22 x- m = enderval b (begin i)- n = beginerval b (end j)- ij = fromJust $ i .+. j+Ordered unions: -instance IntervalAxioms Int Int-instance IntervalAxioms Day Integer-instance IntervalAxioms UTCTime NominalDiffTime+\[+\forall i,j i:j \implies (\exists m,n s.t. m:i:j:n \text{ & } m:(i+j):n)+\]+-}+prop_IAaxiomM4_1 :: (SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) =>+ Moment (Interval a) -> M2set a -> Property+prop_IAaxiomM4_1 b x =+ ((m `meets` i && i `meets` j && j `meets` n) &&+ (m `meets` ij && ij `meets` n)) === True+ where i = m21 x+ j = m22 x+ m = enderval b (begin i)+ n = beginerval b (end j)+ ij = fromJust $ i .+. j
src/IntervalAlgebra/Core.hs view
@@ -1,1349 +1,1619 @@-{-|-Module : Interval Algebra-Description : Implementation of Allen's interval algebra-Copyright : (c) NoviSci, Inc 2020-License : BSD3-Maintainer : bsaul@novisci.com--The @IntervalAlgebra@ module provides data types and related classes for the-interval-based temporal logic described in [Allen (1983)](https://doi.org/10.1145/182.358434)-and axiomatized in [Allen and Hayes (1987)](https://doi.org/10.1111/j.1467-8640.1989.tb00329.x).-A good primer on Allen's algebra can be [found here](https://thomasalspaugh.org/pub/fnd/allen.html).--= Design--The module is built around three typeclasses designed to separate concerns of-constructing, relating, and combining types that contain @'Interval'@s:--1. @'Intervallic'@ provides an interface to the data structures which contain an- @'Interval'@.-2. @'IntervalCombinable'@ provides an interface to methods of combining two- @'Interval's@.-3. @'IntervalSizeable'@ provides methods for measuring and modifying the size of- an interval.---}--{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE Safe #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}--module IntervalAlgebra.Core- (-- -- * Intervals- Interval- , Intervallic(..)- , ParseErrorInterval(..)- , begin- , end-- -- ** Create new intervals- , parseInterval- , prsi- , beginerval- , bi- , enderval- , ei- , safeInterval- , si-- -- ** Modify intervals- , expand- , expandl- , expandr-- -- * Interval Algebra-- -- ** Interval Relations and Predicates- , IntervalRelation(..)- , meets- , metBy- , before- , after- , overlaps- , overlappedBy- , finishedBy- , finishes- , contains- , during- , starts- , startedBy- , equals-- -- ** Additional predicates and utilities- , precedes- , precededBy- , disjoint- , notDisjoint- , concur- , within- , encloses- , enclosedBy- , (<|>)- , predicate- , unionPredicates- , disjointRelations- , withinRelations- , strictWithinRelations- , ComparativePredicateOf1- , ComparativePredicateOf2- , beginervalFromEnd- , endervalFromBegin- , beginervalMoment- , endervalMoment- , shiftFromBegin- , shiftFromEnd- , momentize- , toEnumInterval- , fromEnumInterval-- -- ** Algebraic operations- , intervalRelations- , relate- , compose- , complement- , union- , intersection- , converse-- -- * Combine two intervals- , IntervalCombinable(..)- , extenterval-- -- * Measure an interval- , IntervalSizeable(..)- ) where--import Control.Applicative (Applicative (pure), liftA2)-import Control.DeepSeq (NFData)-import Data.Binary (Binary)-import Data.Fixed (Pico)-import Data.Function (flip, id, ($), (.))-import Data.Ord (Ord (..), Ordering (..), max, min)-import Data.Semigroup (Semigroup ((<>)))-import qualified Data.Set (Set, difference, fromList, intersection,- map, toList, union)-import Data.Time as DT (Day, DiffTime, NominalDiffTime,- UTCTime, addDays, addUTCTime,- diffDays, diffUTCTime,- nominalDiffTimeToSeconds,- secondsToNominalDiffTime)-import Data.Tuple (fst, snd)-import GHC.Generics (Generic)-import Prelude (Bool (..), Bounded (..), Either (..),- Enum (..), Eq, Int, Integer, Maybe (..),- Num, Rational, Show, String, any, curry,- fromInteger, fromRational, map, negate,- not, otherwise, realToFrac, replicate,- show, toInteger, toRational, (!!), (&&),- (+), (++), (-), (==))-import Test.QuickCheck (Arbitrary (..), resize, sized, suchThat)--{- $setup->>> import IntervalAlgebra.IntervalDiagram--}--{- | An @'Interval' a@ is a pair \( (x, y) \text{ such that } x < y\). To create-intervals use the @'parseInterval'@, @'beginerval'@, or @'enderval'@ functions.--}-newtype Interval a = Interval (a, a) deriving (Eq, Generic)---- | A type identifying interval parsing errors.-newtype ParseErrorInterval = ParseErrorInterval String- deriving (Eq, Show)--{- | Helper defining what a valid relation is between begin and end of an-Interval.--}-isValidBeginEnd :: (Ord a) => a -> a -> Bool-isValidBeginEnd b e = b < e--{- | Safely parse a pair of @a@s to create an @'Interval' a@.-->>> parseInterval 0 1-Right (0, 1)-->>> parseInterval 1 0-Left (ParseErrorInterval "0<=1")--}-parseInterval- :: (Show a, Ord a) => a -> a -> Either ParseErrorInterval (Interval a)-parseInterval x y- | isValidBeginEnd x y = Right $ Interval (x, y)- | otherwise = Left $ ParseErrorInterval $ show y ++ "<=" ++ show x--- | A synonym for `parseInterval`-prsi :: (Show a, Ord a) => a -> a -> Either ParseErrorInterval (Interval a)-prsi = parseInterval--intervalBegin :: Interval a -> a-intervalBegin (Interval x) = fst x--intervalEnd :: Interval a -> a-intervalEnd (Interval x) = snd x--instance (Show a, Ord a) => Show (Interval a) where- show x = "(" ++ show (begin x) ++ ", " ++ show (end x) ++ ")"--instance Binary a => Binary (Interval a)-instance NFData a => NFData (Interval a)--{- | The @'Intervallic'@ typeclass defines how to get and set the 'Interval'-content of a data structure. It also includes functions for getting the-endpoints of the 'Interval' via @'begin'@ and @'end'@.-->>> getInterval (Interval (0, 10))-(0, 10)-->>> begin (Interval (0, 10))-0-->>> end (Interval (0, 10))-10--}-class Intervallic i where-- -- | Get the interval from an @i a@.- getInterval :: i a -> Interval a-- -- | Set the interval in an @i a@.- setInterval :: i a -> Interval b -> i b---- | Access the endpoints of an @i a@ .-begin, end :: (Intervallic i) => i a -> a-begin = intervalBegin . getInterval-end = intervalEnd . getInterval--{- | This *unexported* function is an internal convenience function for cases in-which @f@ is known to be strictly monotone.--}-imapStrictMonotone :: (Intervallic i) => (a -> b) -> i a -> i b-imapStrictMonotone f i = setInterval i (op f (getInterval i))- where op f (Interval (b, e)) = Interval (f b, f e)--{- | The 'IntervalRelation' type and the associated predicate functions enumerate-the thirteen possible ways that two @'Interval'@ objects may 'relate' according-to Allen's interval algebra. Constructors are shown with their corresponding-predicate function.--}-data IntervalRelation =- Before -- ^ `before`- | Meets -- ^ `meets`- | Overlaps -- ^ `overlaps`- | FinishedBy -- ^ `finishedBy`- | Contains -- ^ `contains`- | Starts -- ^ `starts`- | Equals -- ^ `equals`- | StartedBy -- ^ `startedBy`- | During -- ^ `during`- | Finishes -- ^ `finishes`- | OverlappedBy -- ^ `overlappedBy`- | MetBy -- ^ `metBy`- | After -- ^ `after`- deriving (Eq, Show, Enum)--instance Bounded IntervalRelation where- minBound = Before- maxBound = After--instance Ord IntervalRelation where- compare x y = compare (fromEnum x) (fromEnum y)---{- | Does x `meets` y? Is x `metBy` y?--Example data with corresponding diagram:-->>> x = bi 5 0->>> y = bi 5 5->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []------ <- [x]- ----- <- [y]-==========--Examples:-->>> x `meets` y-True-->>> x `metBy` y-False-->>> y `meets` x-False-->>> y `metBy` x-True--}-meets, metBy- :: (Eq a, Intervallic i0, Intervallic i1)- => ComparativePredicateOf2 (i0 a) (i1 a)-meets x y = end x == begin y-metBy = flip meets---{- | Is x `before` y? Does x `precedes` y? Is x `after` y? Is x `precededBy` y?--Example data with corresponding diagram:-->>> x = bi 3 0->>> y = bi 4 6->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []---- <- [x]- ---- <- [y]-==========--Examples:-->>> x `before` y-True->>> x `precedes` y-True-->>> x `after`y-False->>> x `precededBy` y-False-->>> y `before` x-False->>> y `precedes` x-False-->>> y `after` x-True->>> y `precededBy` x-True--}-before, after, precedes, precededBy- :: (Ord a, Intervallic i0, Intervallic i1)- => ComparativePredicateOf2 (i0 a) (i1 a)-before x y = end x < begin y-after = flip before-precedes = before-precededBy = after---{- | Does x `overlaps` y? Is x `overlappedBy` y?--Example data with corresponding diagram:-->>> x = bi 6 0->>> y = bi 6 4->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []------- <- [x]- ------ <- [y]-==========--Examples:-->>> x `overlaps` y-True-->>> x `overlappedBy` y-False-->>> y `overlaps` x-False-->>> y `overlappedBy` x-True--}-overlaps, overlappedBy- :: (Ord a, Intervallic i0, Intervallic i1)- => ComparativePredicateOf2 (i0 a) (i1 a)-overlaps x y = begin x < begin y && end x < end y && end x > begin y-overlappedBy = flip overlaps---{-| Does x `starts` y? Is x `startedBy` y?--Example data with corresponding diagram:-->>> x = bi 3 4->>> y = bi 6 4->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []- --- <- [x]- ------ <- [y]-==========--Examples:-->>> x `starts` y-True-->>> x `startedBy` y-False-->>> y `starts` x-False-->>> y `startedBy` x-True--}-starts, startedBy- :: (Ord a, Intervallic i0, Intervallic i1)- => ComparativePredicateOf2 (i0 a) (i1 a)-starts x y = begin x == begin y && end x < end y-startedBy = flip starts---{- | Does x `finishes` y? Is x `finishedBy` y?--Example data with corresponding diagram:-->>> x = bi 3 7->>> y = bi 6 4->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []- --- <- [x]- ------ <- [y]-==========--Examples:-->>> x `finishes` y-True-->>> x `finishedBy` y-False-->>> y `finishes` x-False-->>> y `finishedBy` x-True--}-finishes, finishedBy- :: (Ord a, Intervallic i0, Intervallic i1)- => ComparativePredicateOf2 (i0 a) (i1 a)-finishes x y = begin x > begin y && end x == end y-finishedBy = flip finishes---{-| Is x `during` y? Does x `contains` y?--Example data with corresponding diagram:-->>> x = bi 3 5->>> y = bi 6 4->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []- --- <- [x]- ------ <- [y]-==========--Examples:-->>> x `during` y-True-->>> x `contains` y-False-->>> y `during` x-False-->>> y `contains` x-True--}-during, contains- :: (Ord a, Intervallic i0, Intervallic i1)- => ComparativePredicateOf2 (i0 a) (i1 a)-during x y = begin x > begin y && end x < end y-contains = flip during---{- | Does x `equals` y?--Example data with corresponding diagram:-->>> x = bi 6 4->>> y = bi 6 4->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []- ------ <- [x]- ------ <- [y]-==========--Examples:-->>> x `equals` y-True-->>> y `equals` x-True--}-equals- :: (Ord a, Intervallic i0, Intervallic i1)- => ComparativePredicateOf2 (i0 a) (i1 a)-equals x y = begin x == begin y && end x == end y---- | Operator for composing the union of two predicates-(<|>)- :: (Intervallic i0, Intervallic i1)- => ComparativePredicateOf2 (i0 a) (i1 a)- -> ComparativePredicateOf2 (i0 a) (i1 a)- -> ComparativePredicateOf2 (i0 a) (i1 a)-(<|>) f g = unionPredicates [f, g]---- | The set of @IntervalRelation@ meaning two intervals are disjoint.-disjointRelations :: Data.Set.Set IntervalRelation-disjointRelations = toSet [Before, After, Meets, MetBy]---- | The set of @IntervalRelation@ meaning one interval is within the other.-withinRelations :: Data.Set.Set IntervalRelation-withinRelations = toSet [Starts, During, Finishes, Equals]---- | The set of @IntervalRelation@ meaning one interval is *strictly* within the other.-strictWithinRelations :: Data.Set.Set IntervalRelation-strictWithinRelations = Data.Set.difference withinRelations (toSet [Equals])---{- | Are x and y `disjoint` ('before', 'after', 'meets', or 'metBy')?--Example data with corresponding diagram:-->>> x = bi 3 0->>> y = bi 3 5->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []---- <- [x]- --- <- [y]-========--Examples:-->>> x `disjoint` y-True-->>> y `disjoint` x-True--Example data with corresponding diagram:-->>> x = bi 3 0->>> y = bi 3 3->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []---- <- [x]- --- <- [y]-======--Examples:-->>> x `disjoint` y-True-->>> y `disjoint` x-True--Example data with corresponding diagram:-->>> x = bi 6 0->>> y = bi 3 3->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []------- <- [x]- --- <- [y]-======--Examples:-->>> x `disjoint` y-False-->>> y `disjoint` x-False--}-disjoint- :: (Ord a, Intervallic i0, Intervallic i1)- => ComparativePredicateOf2 (i0 a) (i1 a)-disjoint = predicate disjointRelations---{-| Does x `concur` with y? Is x `notDisjoint` with y?); This is-the 'complement' of 'disjoint'.--Example data with corresponding diagram:-->>> x = bi 3 0->>> y = bi 3 4->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []---- <- [x]- --- <- [y]-=======--Examples:-->>> x `notDisjoint` y-False->>> y `concur` x-False--Example data with corresponding diagram:-->>> x = bi 3 0->>> y = bi 3 3->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []---- <- [x]- --- <- [y]-======--Examples:-->>> x `notDisjoint` y-False->>> y `concur` x-False--Example data with corresponding diagram:-->>> x = bi 6 0->>> y = bi 3 3->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []------- <- [x]- --- <- [y]-======--Examples:-->>> x `notDisjoint` y-True->>> y `concur` x-True--}-notDisjoint, concur- :: (Ord a, Intervallic i0, Intervallic i1)- => ComparativePredicateOf2 (i0 a) (i1 a)-notDisjoint = predicate (complement disjointRelations)-concur = notDisjoint---{- | Is x `within` (`enclosedBy`) y? That is, 'during', 'starts', 'finishes', or-'equals'?--Example data with corresponding diagram:-->>> x = bi 6 4->>> y = bi 6 4->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []- ------ <- [x]- ------ <- [y]-==========--Examples:-->>> x `within` y-True-->>> y `enclosedBy` x-True--Example data with corresponding diagram:-->>> x = bi 6 4->>> y = bi 5 4->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []- ------ <- [x]- ----- <- [y]-==========--Examples:-->>> x `within` y-False-->>> y `enclosedBy` x-True--Example data with corresponding diagram:-->>> x = bi 6 4->>> y = bi 4 5->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []- ------ <- [x]- ---- <- [y]-==========--Examples:-->>> x `within` y-False->>> y `enclosedBy` x-True--Example data with corresponding diagram:-->>> x = bi 2 7->>> y = bi 1 5->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []- -- <- [x]- - <- [y]-=========--Examples:-->>> x `within` y-False-->>> y `enclosedBy` x-False--}-within, enclosedBy- :: (Ord a, Intervallic i0, Intervallic i1)- => ComparativePredicateOf2 (i0 a) (i1 a)-within = predicate withinRelations-enclosedBy = within---{- | Does x `encloses` y? That is, is y 'within' x?--Example data with corresponding diagram:-->>> x = bi 6 4->>> y = bi 6 4->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []- ------ <- [x]- ------ <- [y]-==========--Examples:-->>> x `encloses` y-True-->>> y `encloses` x-True--Example data with corresponding diagram:-->>> x = bi 6 4->>> y = bi 5 4->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []- ------ <- [x]- ----- <- [y]-==========--Examples:-->>> x `encloses` y-True-->>> y `encloses` x-False--Example data with corresponding diagram:-->>> x = bi 6 4->>> y = bi 4 5->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []- ------ <- [x]- ---- <- [y]-==========--Examples:-->>> x `encloses` y-True-->>> y `encloses` x-False--Example data with corresponding diagram:-->>> x = bi 2 7->>> y = bi 1 5->>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []- -- <- [x]- - <- [y]-=========--Examples:-->>> x `encloses` y-False-->>> y `encloses` x-False--}-encloses- :: (Ord a, Intervallic i0, Intervallic i1)- => ComparativePredicateOf2 (i0 a) (i1 a)-encloses = flip enclosedBy---- | The 'Data.Set.Set' of all 'IntervalRelation's.-intervalRelations :: Data.Set.Set IntervalRelation-intervalRelations =- Data.Set.fromList (Prelude.map toEnum [0 .. 12] :: [IntervalRelation])---- | Find the converse of a single 'IntervalRelation'-converseRelation :: IntervalRelation -> IntervalRelation-converseRelation x = toEnum (12 - fromEnum x)---- | Shortcut to creating a 'Set IntervalRelation' from a list.-toSet :: [IntervalRelation] -> Data.Set.Set IntervalRelation-toSet = Data.Set.fromList---- | Compose a list of interval relations with _or_ to create a new--- @'ComparativePredicateOf1' i a@. For example,--- @unionPredicates [before, meets]@ creates a predicate function determining--- if one interval is either before or meets another interval.-unionPredicates :: [ComparativePredicateOf2 a b] -> ComparativePredicateOf2 a b-unionPredicates fs x y = any (\f -> f x y) fs---- | Maps an 'IntervalRelation' to its corresponding predicate function.-toPredicate- :: (Ord a, Intervallic i0, Intervallic i1)- => IntervalRelation- -> ComparativePredicateOf2 (i0 a) (i1 a)-toPredicate r = case r of- Before -> before- Meets -> meets- Overlaps -> overlaps- FinishedBy -> finishedBy- Contains -> contains- Starts -> starts- Equals -> equals- StartedBy -> startedBy- During -> during- Finishes -> finishes- OverlappedBy -> overlappedBy- MetBy -> metBy- After -> after---- | Given a set of 'IntervalRelation's return a list of 'predicate' functions--- corresponding to each relation.-predicates- :: (Ord a, Intervallic i0, Intervallic i1)- => Data.Set.Set IntervalRelation- -> [ComparativePredicateOf2 (i0 a) (i1 a)]-predicates x = Prelude.map toPredicate (Data.Set.toList x)---- | Forms a predicate function from the union of a set of 'IntervalRelation's.-predicate- :: (Ord a, Intervallic i0, Intervallic i1)- => Data.Set.Set IntervalRelation- -> ComparativePredicateOf2 (i0 a) (i1 a)-predicate = unionPredicates . predicates---- | The lookup table for the compositions of interval relations.-composeRelationLookup :: [[[IntervalRelation]]]-composeRelationLookup =- [ [p, p, p, p, p, p, p, p, pmosd, pmosd, pmosd, pmosd, full]- , [p, p, p, p, p, m, m, m, osd, osd, osd, fef, dsomp]- , [p, p, pmo, pmo, pmofd, o, o, ofd, osd, osd, cncr, dso, dsomp]- , [p, m, o, f', d', o, f', d', osd, fef, dso, dso, dsomp]- , [pmofd, ofd, ofd, d', d', ofd, d', d', cncr, dso, dso, dso, dsomp]- , [p, p, pmo, pmo, pmofd, s, s, ses, d, d, dfo, m', p']- , [p, m, o, f', d', s, e, s', d, f, o', m', p']- , [pmofd, ofd, ofd, d', d', ses, s', s', dfo, o', o', m', p']- , [p, p, pmosd, pmosd, full, d, d, dfomp, d, d, dfomp, p', p']- , [p, m, osd, fef, dsomp, d, f, omp, d, f, omp, p', p']- , [pmofd, ofd, cncr, dso, dsomp, dfo, o', omp, dfo, o', omp, p', p']- , [pmofd, ses, dfo, m', p', dfo, m', p', dfo, m', p', p', p']- , [full, dfomp, dfomp, p', p', dfomp, p', p', dfomp, p', p', p', p']- ]- where- p = [Before]- m = [Meets]- o = [Overlaps]- f' = [FinishedBy]- d' = [Contains]- s = [Starts]- e = [Equals]- s' = [StartedBy]- d = [During]- f = [Finishes]- o' = [OverlappedBy]- m' = [MetBy]- p' = [After]- ses = s ++ e ++ s'- fef = f' ++ e ++ f- pmo = p ++ m ++ o- pmofd = pmo ++ f' ++ d'- osd = o ++ s ++ d- ofd = o ++ f' ++ d'- omp = o' ++ m' ++ p'- dfo = d ++ f ++ o'- dfomp = dfo ++ m' ++ p'- dso = d' ++ s' ++ o'- dsomp = dso ++ m' ++ p'- pmosd = p ++ m ++ osd- cncr = o ++ f' ++ d' ++ s ++ e ++ s' ++ d ++ f ++ o'- full = p ++ m ++ cncr ++ m' ++ p'--{- | Compare two @i a@ to determine their 'IntervalRelation'.-->>> relate (Interval (0::Int, 1)) (Interval (1, 2))-Meets-->>> relate (Interval (1::Int, 2)) (Interval (0, 1))-MetBy--}-relate- :: (Ord a, Intervallic i0, Intervallic i1) => i0 a -> i1 a -> IntervalRelation-relate x y | x `before` y = Before- | x `after` y = After- | x `meets` y = Meets- | x `metBy` y = MetBy- | x `overlaps` y = Overlaps- | x `overlappedBy` y = OverlappedBy- | x `starts` y = Starts- | x `startedBy` y = StartedBy- | x `finishes` y = Finishes- | x `finishedBy` y = FinishedBy- | x `during` y = During- | x `contains` y = Contains- | otherwise = Equals--{- | Compose two interval relations according to the rules of the algebra.-The rules are enumerated according to-<https://thomasalspaugh.org/pub/fnd/allen.html#BasicCompositionsTable this table>.--}-compose- :: IntervalRelation -> IntervalRelation -> Data.Set.Set IntervalRelation-compose x y = toSet (composeRelationLookup !! fromEnum x !! fromEnum y)---- | Finds the complement of a @'Data.Set.Set' 'IntervalRelation'@.-complement :: Data.Set.Set IntervalRelation -> Data.Set.Set IntervalRelation-complement = Data.Set.difference intervalRelations---- | Find the intersection of two 'Data.Set.Set's of 'IntervalRelation's.-intersection- :: Data.Set.Set IntervalRelation- -> Data.Set.Set IntervalRelation- -> Data.Set.Set IntervalRelation-intersection = Data.Set.intersection---- | Find the union of two 'Data.Set.Set's of 'IntervalRelation's.-union- :: Data.Set.Set IntervalRelation- -> Data.Set.Set IntervalRelation- -> Data.Set.Set IntervalRelation-union = Data.Set.union---- | Find the converse of a @'Data.Set.Set' 'IntervalRelation'@.-converse :: Data.Set.Set IntervalRelation -> Data.Set.Set IntervalRelation-converse = Data.Set.map converseRelation--{- | The 'IntervalSizeable' typeclass provides functions to determine the size of-an 'Intervallic' type and to resize an 'Interval a'.--}-class (Ord a, Num b, Ord b) => IntervalSizeable a b | a -> b where-- -- | The smallest duration for an 'Interval a'.- moment :: forall a . b- moment = 1-- -- | Determine the duration of an @'i a'@.- duration :: (Intervallic i) => i a -> b- duration x = diff (end x) (begin x)-- -- | Shifts an @a@. Most often, the @b@ will be the same type as @a@.- -- But for example, if @a@ is 'Day' then @b@ could be 'Int'.- add :: b -> a -> a-- -- | Takes the difference between two @a@ to return a @b@.- diff :: a -> a -> b--{- | Resize an @i a@ to by expanding to "left" by @l@ and to the "right" by @r@.-In the case that @l@ or @r@ are less than a 'moment' the respective endpoints-are unchanged.-->>> iv2to4 = safeInterval (2::Int, 4::Int)->>> iv2to4' = expand 0 0 iv2to4->>> iv1to5 = expand 1 1 iv2to4-->>> iv2to4-(2, 4)-->>> iv2to4'-(2, 4)-->>> iv1to5-(1, 5)-->>> pretty $ standardExampleDiagram [(iv2to4, "iv2to4"), (iv1to5, "iv1to5")] []- -- <- [iv2to4]- ---- <- [iv1to5]-=====--}-expand- :: forall i a b- . (IntervalSizeable a b, Intervallic i)- => b -- ^ duration to subtract from the 'begin'- -> b -- ^ duration to add to the 'end'- -> i a- -> i a-expand l r p = setInterval p i- where- s = if l < moment @a then 0 else negate l- e = if r < moment @a then 0 else r- i = Interval (add s $ begin p, add e $ end p)--{- | Expands an @i a@ to the "left".-->>> iv2to4 = (safeInterval (2::Int, 4::Int))->>> iv0to4 = expandl 2 iv2to4-->>> iv2to4-(2, 4)-->>> iv0to4-(0, 4)-->>> pretty $ standardExampleDiagram [(iv2to4, "iv2to4"), (iv0to4, "iv0to4")] []- -- <- [iv2to4]----- <- [iv0to4]-====--}-expandl :: (IntervalSizeable a b, Intervallic i) => b -> i a -> i a-expandl i = expand i 0--{- | Expands an @i a@ to the "right".-->>> iv2to4 = (safeInterval (2::Int, 4::Int))->>> iv2to6 = expandr 2 iv2to4-->>> iv2to4-(2, 4)-->>> iv2to6-(2, 6)-->>> pretty $ standardExampleDiagram [(iv2to4, "iv2to4"), (iv2to6, "iv2to6")] []- -- <- [iv2to4]- ---- <- [iv2to6]-======--}-expandr :: (IntervalSizeable a b, Intervallic i) => b -> i a -> i a-expandr = expand 0--{- | Safely creates an 'Interval a' using @x@ as the 'begin' and adding @max-'moment' dur@ to @x@ as the 'end'.-->>> beginerval (0::Int) (0::Int)-(0, 1)-->>> beginerval (1::Int) (0::Int)-(0, 1)-->>> beginerval (2::Int) (0::Int)-(0, 2)--}-beginerval- :: forall a b- . (IntervalSizeable a b)- => b -- ^ @dur@ation to add to the 'begin'- -> a -- ^ the 'begin' point of the 'Interval'- -> Interval a-beginerval dur x = Interval (x, y)- where- i = Interval (x, x)- d = max (moment @a) dur- y = add d x-{-# INLINABLE beginerval #-}---- | A synonym for `beginerval`-bi- :: (IntervalSizeable a b)- => b -- ^ @dur@ation to add to the 'begin'- -> a -- ^ the 'begin' point of the 'Interval'- -> Interval a-bi = beginerval---{- | Safely creates an 'Interval a' using @x@ as the 'end' and adding @negate max-'moment' dur@ to @x@ as the 'begin'.-->>> enderval (0::Int) (0::Int)-(-1, 0)-->>> enderval (1::Int) (0::Int)-(-1, 0)-->>> enderval (2::Int) (0::Int)-(-2, 0)--}-enderval- :: forall a b- . (IntervalSizeable a b)- => b -- ^ @dur@ation to subtract from the 'end'- -> a -- ^ the 'end' point of the 'Interval'- -> Interval a-enderval dur x = Interval (add (negate $ max (moment @a) dur) x, x)- where i = Interval (x, x)-{-# INLINABLE enderval #-}---- | A synonym for `enderval`-ei- :: (IntervalSizeable a b)- => b -- ^ @dur@ation to subtract from the 'end'- -> a -- ^ the 'end' point of the 'Interval'- -> Interval a-ei = enderval----- | Safely creates an @'Interval'@ from a pair of endpoints.--- IMPORTANT: This function uses 'beginerval',--- thus if the second element of the pair is `<=` the first element,--- the duration will be an @"Interval"@ of 'moment' duration.------ >>> safeInterval (4, 5 ::Int)--- (4, 5)--- >>> safeInterval (4, 3 :: Int)--- (4, 5)----safeInterval :: IntervalSizeable a b => (a, a) -> Interval a-safeInterval (b, e) = beginerval (diff e b) b---- | A synonym for `safeInterval`-si :: IntervalSizeable a b => (a, a) -> Interval a-si = safeInterval---- | Creates a new Interval from the 'end' of an @i a@.-beginervalFromEnd- :: (IntervalSizeable a b, Intervallic i)- => b -- ^ @dur@ation to add to the 'end'- -> i a -- ^ the @i a@ from which to get the 'end'- -> Interval a-beginervalFromEnd d i = beginerval d (end i)---- | Creates a new Interval from the 'begin' of an @i a@.-endervalFromBegin- :: (IntervalSizeable a b, Intervallic i)- => b -- ^ @dur@ation to subtract from the 'begin'- -> i a -- ^ the @i a@ from which to get the 'begin'- -> Interval a-endervalFromBegin d i = enderval d (begin i)--{- | Safely creates a new @Interval@ with 'moment' length with 'begin' at @x@-->>> beginervalMoment (10 :: Int)-(10, 11)--}-beginervalMoment :: forall a b . (IntervalSizeable a b) => a -> Interval a-beginervalMoment x = beginerval (moment @a) x where i = Interval (x, x)--{- | Safely creates a new @Interval@ with 'moment' length with 'end' at @x@-->>> endervalMoment (10 :: Int)-(9, 10)--}-endervalMoment :: forall a b . (IntervalSizeable a b) => a -> Interval a-endervalMoment x = enderval (moment @a) x where i = Interval (x, x)--{- | Creates a new @Interval@ spanning the extent x and y.-->>> extenterval (Interval (0, 1)) (Interval (9, 10))-(0, 10)--}-extenterval :: (Ord a, Intervallic i) => i a -> i a -> Interval a-extenterval x y = Interval (s, e)- where- s = min (begin x) (begin y)- e = max (end x) (end y)--{- | Modifies the endpoints of second argument's interval by taking the difference-from the first's input's 'begin'.--Example data with corresponding diagram:-->>> a = bi 3 2 :: Interval Int->>> a-(2, 5)->>> x = bi 3 7 :: Interval Int->>> x-(7, 10)->>> y = bi 4 9 :: Interval Int->>> y-(9, 13)->>> pretty $ standardExampleDiagram [(a, "a"), (x, "x"), (y, "y")] []- --- <- [a]- --- <- [x]- ---- <- [y]-=============--Examples:-->>> x' = shiftFromBegin a x->>> x'-(5, 8)->>> y' = shiftFromBegin a y->>> y'-(7, 11)->>> pretty $ standardExampleDiagram [(x', "x'"), (y', "y'")] []- --- <- [x']- ---- <- [y']-===========--}-shiftFromBegin- :: (IntervalSizeable a b, Intervallic i1, Intervallic i0)- => i0 a- -> i1 a- -> i1 b-shiftFromBegin i = imapStrictMonotone (`diff` begin i)--{- | Modifies the endpoints of second argument's interval by taking the difference-from the first's input's 'end'.--Example data with corresponding diagram:-->>> a = bi 3 2 :: Interval Int->>> a-(2, 5)->>> x = bi 3 7 :: Interval Int->>> x-(7, 10)->>> y = bi 4 9 :: Interval Int->>> y-(9, 13)->>> pretty $ standardExampleDiagram [(a, "a"), (x, "x"), (y, "y")] []- --- <- [a]- --- <- [x]- ---- <- [y]-=============--Examples:-->>> x' = shiftFromEnd a x->>> x'-(2, 5)->>> y' = shiftFromEnd a y->>> y'-(4, 8)->>> pretty $ standardExampleDiagram [(x', "x'"), (y', "y'")] []- --- <- [x']- ---- <- [y']-========--}-shiftFromEnd- :: (IntervalSizeable a b, Intervallic i1, Intervallic i0)- => i0 a- -> i1 a- -> i1 b-shiftFromEnd i = imapStrictMonotone (`diff` end i)---- | Converts an @i a@ to an @i Int@ via @fromEnum@. This assumes the provided--- @fromEnum@ method is strictly monotone increasing: For @a@ types that are--- @Ord@ with values @x, y@, then @x < y@ implies @fromEnum x < fromEnum y@, so--- long as the latter is well-defined.-fromEnumInterval :: (Enum a, Intervallic i) => i a -> i Int-fromEnumInterval = imapStrictMonotone fromEnum---- | Converts an @i Int@ to an @i a@ via @toEnum@. This assumes the provided--- @toEnum@ method is strictly monotone increasing: For @a@ types that are--- @Ord@, then for @Int@ values @x, y@ it holds that @x < y@ implies @toEnum x--- < toEnum y@.-toEnumInterval :: (Enum a, Intervallic i) => i Int -> i a-toEnumInterval = imapStrictMonotone toEnum----{- | Changes the duration of an 'Intervallic' value to a moment starting at the-'begin' of the interval.-->>> momentize (Interval (6, 10))-(6, 7)--}-momentize- :: forall i a b . (IntervalSizeable a b, Intervallic i) => i a -> i a-momentize i = setInterval i (beginerval (moment @a) (begin i))--{- | The @'IntervalCombinable'@ typeclass provides methods for (possibly)-combining two @i a@s to form a @'Maybe' i a@, or in case of @><@, a possibly-different @Intervallic@ type.--}-class (Ord a, Intervallic i) => IntervalCombinable i a where-- -- | Maybe form a new @i a@ by the union of two @i a@s that 'meets'.- (.+.) :: i a -> i a -> Maybe (i a)- (.+.) x y- | x `meets` y = Just $ setInterval y $ Interval (b, e)- | otherwise = Nothing- where b = begin x- e = end y- {-# INLINABLE (.+.) #-}-- -- | If @x@ is 'before' @y@, then form a new @Just Interval a@ from the- -- interval in the "gap" between @x@ and @y@ from the 'end' of @x@ to the- -- 'begin' of @y@. Otherwise, 'Nothing'.- (><) :: i a -> i a -> Maybe (i a)-- -- | If @x@ is 'before' @y@, return @f x@ appended to @f y@. Otherwise,- -- return 'extenterval' of @x@ and @y@ (wrapped in @f@). This is useful for- -- (left) folding over an *ordered* container of @Interval@s and combining- -- intervals when @x@ is *not* 'before' @y@.- (<+>):: ( Semigroup (f (i a)), Applicative f) =>- i a- -> i a- -> f (i a)-{-# DEPRECATED (<+>) "A specialized function without clear use-cases." #-}--{--Misc--}---- | Defines a predicate of two objects of type @a@.-type ComparativePredicateOf1 a = (a -> a -> Bool)---- | Defines a predicate of two object of different types.-type ComparativePredicateOf2 a b = (a -> b -> Bool)---- {---- Instances--- -}---- | Imposes a total ordering on @'Interval' a@ based on first ordering the--- 'begin's then the 'end's.-instance (Ord a) => Ord (Interval a) where- (<=) x y | begin x < begin y = True- | begin x == begin y = end x <= end y- | otherwise = False- (<) x y | begin x < begin y = True- | begin x == begin y = end x < end y- | otherwise = False--instance Intervallic Interval where- getInterval = id- setInterval _ x = x--instance (Ord a) => IntervalCombinable Interval a where- (><) x y | x `before` y = Just $ Interval (end x, begin y)- | otherwise = Nothing- {-# INLINABLE (><) #-}-- (<+>) x y | x `before` y = pure x <> pure y- | otherwise = pure (extenterval x y)- {-# INLINABLE (<+>) #-}--instance IntervalSizeable Int Int where- moment = 1- add = (+)- diff = (-)--instance IntervalSizeable Integer Integer where- moment = 1- add = (+)- diff = (-)--instance IntervalSizeable DT.Day Integer where- moment = 1- add = addDays- diff = diffDays---- | Note that the @moment@ of this instance is a @'Data.Fixed.Pico'@-instance IntervalSizeable DT.UTCTime NominalDiffTime where- moment = toEnum 1 :: NominalDiffTime- add = addUTCTime- diff = diffUTCTime---- Arbitrary instances-instance (Ord a, Arbitrary a) => Arbitrary (Interval a) where- arbitrary =- sized- (\s -> liftA2 (curry Interval)- (s `resize` arbitrary)- (s `resize` arbitrary)- )- `suchThat` (\i -> isValidBeginEnd (intervalBegin i) (intervalEnd i))+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}++-- |+-- Module : Interval Algebra+-- Description : Implementation of Allen's interval algebra+-- Copyright : (c) NoviSci, Inc 2020-2022+-- TargetRWE, 2023+-- License : BSD3+-- Maintainer : bsaul@novisci.com 2020-2022, bbrown@targetrwe.com 2023+--+-- The @IntervalAlgebra@ module provides data types and related classes for the+-- interval-based temporal logic described in [Allen (1983)](https://doi.org/10.1145/182.358434)+-- and axiomatized in [Allen and Hayes (1987)](https://doi.org/10.1111/j.1467-8640.1989.tb00329.x).+-- A good primer on Allen's algebra can be [found here](https://thomasalspaugh.org/pub/fnd/allen.html).+--+-- = Design+--+-- The module provides an 'Interval' type wrapping a canonical interval to be used with the +-- relation algebra defined in the papers cited above. @'Interval' a@+-- wraps @(a, a)@, giving the interval's 'begin' and 'end' points.+-- +-- However, the module provides typeclasses to generalize an 'Interval' and the+-- interval algebra for temporal logic, such that it could be used in settings +-- where there is no need for continguity between the begin and end points, or +-- where the "intervals" are qualitative and do not have a begin or end. See +-- 'Iv' for an example.++-- Many exports of this module require `FlexibleContexts` and `TypeFamilies`+-- extensions to be enabled.+module IntervalAlgebra.Core+ ( -- * Canonical intervals+ Interval,+ PointedIv (..),+ SizedIv (..),+ Intervallic (..),+ begin,+ end,++ -- ** Create new intervals+ ParseErrorInterval (..),+ parseInterval,+ prsi,+ beginerval,+ bi,+ enderval,+ ei,+ safeInterval,+ si,++ -- ** Modify intervals within an @Intervallic@+ expand,+ expandl,+ expandr,+ + -- ** Combine two intervals+ extenterval,++ -- * Interval Algebra+ Iv (..),++ -- ** Interval Relations and Predicates+ IntervalRelation (..),+ meets,+ metBy,+ before,+ after,+ overlaps,+ overlappedBy,+ finishedBy,+ finishes,+ contains,+ during,+ starts,+ startedBy,+ equals,++ -- ** Additional predicates and utilities+ precedes,+ precededBy,+ disjoint,+ notDisjoint,+ concur,+ within,+ encloses,+ enclosedBy,+ (<|>),+ predicate,+ unionPredicates,+ disjointRelations,+ withinRelations,+ strictWithinRelations,+ ComparativePredicateOf1,+ ComparativePredicateOf2,+ beginervalFromEnd,+ endervalFromBegin,+ beginervalMoment,+ endervalMoment,+ shiftFromBegin,+ shiftFromEnd,+ momentize,+ toEnumInterval,+ fromEnumInterval,++ -- ** Algebraic operations+ intervalRelations,+ relate,+ compose,+ complement,+ union,+ intersection,+ converse,+ converseRelation,+ )+where++import Control.Applicative (Applicative (pure), liftA2)+import Control.DeepSeq (NFData)+import Data.Binary (Binary)+import Data.Fixed (Pico)+import Data.Function (flip, id, ($), (.))+import Data.Kind (Type)+import Data.Ord (Ord (..), Ordering (..), max, min)+import Data.Semigroup (Semigroup ((<>)))+import qualified Data.Set (Set, difference, fromList, intersection,+ map, toList, union)+import Data.Time as DT (Day, DiffTime, NominalDiffTime,+ UTCTime, addDays, addUTCTime,+ diffDays, diffUTCTime,+ nominalDiffTimeToSeconds,+ secondsToNominalDiffTime)+import Data.Tuple (fst, snd)+import GHC.Generics (Generic)+import GHC.IO.Handle (NewlineMode (inputNL))+import Test.QuickCheck (Arbitrary (..), resize, sized, suchThat)++-- $setup+-- >>> import IntervalAlgebra.IntervalDiagram+-- >>> :set -XTypeFamilies++-- | An @'Interval' a@ is a pair \( (x, y) \text{ such that } x < y\). To create+-- intervals use the @'parseInterval'@, @'beginerval'@, or @'enderval'@ functions.+newtype Interval a+ = Interval (a, a)+ deriving (Eq, Generic)++-- | A type identifying interval parsing errors.+newtype ParseErrorInterval+ = ParseErrorInterval String+ deriving (Eq, Show)++-- | Helper defining what a valid relation is between begin and end of an+-- Interval.+isValidBeginEnd :: (Ord a) => a -> a -> Bool+isValidBeginEnd b e = b < e++-- | Parse a pair of @a@s to create an @'Interval' a@. Note this+-- checks only that @begin < end@ and has no relation to checking+-- the conditions of 'SizedIv'.+--+-- >>> parseInterval 0 1+-- Right (0, 1)+--+-- >>> parseInterval 1 0+-- Left (ParseErrorInterval "0<=1")+parseInterval ::+ (Show a, Ord a) => a -> a -> Either ParseErrorInterval (Interval a)+parseInterval x y+ | isValidBeginEnd x y = Right $ Interval (x, y)+ | otherwise = Left $ ParseErrorInterval $ show y ++ "<=" ++ show x++-- | A synonym for `parseInterval`+prsi :: (Show a, Ord a) => a -> a -> Either ParseErrorInterval (Interval a)+prsi = parseInterval++instance (Show a, Ord a) => Show (Interval a) where+ show (Interval x) = "(" ++ show (fst x) ++ ", " ++ show (snd x) ++ ")"++instance Binary a => Binary (Interval a)++instance NFData a => NFData (Interval a)++{- INTERVALLIC -}++-- | The @'Intervallic'@ typeclass defines how to get and set the 'Interval'+-- content of a data structure. 'Intervallic' types can be compared via+-- 'IntervalRelation' s on their underlying 'Interval', and functions of this+-- module define versions of the methods from 'Iv', 'PointedIv' and 'SizedIv'+-- for instances of 'Intervallic' by applying them to the contained interval.+--+-- Only the canonical representation @'Interval'@ should define an instance of all four+-- classes.+--+-- 'PairedInterval' is the prototypical example of an 'Intervallic'.+--+-- >>> getInterval (Interval (0, 10))+-- (0, 10)+--+-- >>> begin (Interval (0, 10))+-- 0+--+-- >>> end (Interval (0, 10))+-- 10+class Intervallic i where+ -- | Get the interval from an @i a@.+ getInterval :: i a -> Interval a++ -- | Set the interval in an @i a@.+ setInterval :: i a -> Interval b -> i b++-- | Access the endpoints of an @i a@ .+begin, end :: forall i a. (SizedIv (Interval a), Intervallic i) => i a -> a+begin = ivBegin . getInterval+end = ivEnd . getInterval++-- | This *unexported* function is an internal convenience function for cases in+-- which @f@ is known to be strictly monotone.+imapStrictMonotone :: (Intervallic i) => (a -> b) -> i a -> i b+imapStrictMonotone f i = setInterval i (op f (getInterval i))+ where+ op f (Interval (b, e)) = Interval (f b, f e)++{- RELATIONS -}++-- | The 'IntervalRelation' type and the associated predicate functions enumerate+-- the thirteen possible ways that two @'SizedIv'@ objects may 'relate'+-- according to Allen's interval algebra. Constructors are shown with their+-- corresponding predicate function.+data IntervalRelation+ = -- | `before`+ Before+ | -- | `meets`+ Meets+ | -- | `overlaps`+ Overlaps+ | -- | `finishedBy`+ FinishedBy+ | -- | `contains`+ Contains+ | -- | `starts`+ Starts+ | -- | `equals`+ Equals+ | -- | `startedBy`+ StartedBy+ | -- | `during`+ During+ | -- | `finishes`+ Finishes+ | -- | `overlappedBy`+ OverlappedBy+ | -- | `metBy`+ MetBy+ | -- | `after`+ After+ deriving (Enum, Eq, Show)++instance Bounded IntervalRelation where+ minBound = Before+ maxBound = After++instance Ord IntervalRelation where+ compare x y = compare (fromEnum x) (fromEnum y)++-- | Does x `meets` y? Is x `metBy` y?+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 5 0+-- >>> y = bi 5 5+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- ----- <- [x]+-- ----- <- [y]+-- ==========+--+-- Examples:+--+-- >>> x `meets` y+-- True+--+-- >>> x `metBy` y+-- False+--+-- >>> y `meets` x+-- False+--+-- >>> y `metBy` x+-- True+meets,+ metBy ::+ (Iv (Interval a), Intervallic i0, Intervallic i1) =>+ ComparativePredicateOf2 (i0 a) (i1 a)+meets x y = ivMeets (getInterval x) (getInterval y)+metBy = flip meets++-- | Is x `before` y? Does x `precedes` y? Is x `after` y? Is x `precededBy` y?+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 3 0+-- >>> y = bi 4 6+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- --- <- [x]+-- ---- <- [y]+-- ==========+--+-- Examples:+--+-- >>> x `before` y+-- True+-- >>> x `precedes` y+-- True+--+-- >>> x `after`y+-- False+-- >>> x `precededBy` y+-- False+--+-- >>> y `before` x+-- False+-- >>> y `precedes` x+-- False+--+-- >>> y `after` x+-- True+-- >>> y `precededBy` x+-- True+before,+ after,+ precedes,+ precededBy ::+ (Iv (Interval a), Intervallic i0, Intervallic i1) =>+ ComparativePredicateOf2 (i0 a) (i1 a)+before x y = ivBefore (getInterval x) (getInterval y)+after = flip before+precedes = before+precededBy = after++-- | Aliases for 'ivBefore' and 'ivAfter'.+ivPrecedes, ivPrecededBy :: (Iv iv) => iv -> iv -> Bool+ivPrecedes = ivBefore+ivPrecededBy = ivAfter++-- | Does x `overlaps` y? Is x `overlappedBy` y?+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 6 0+-- >>> y = bi 6 4+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- ------ <- [x]+-- ------ <- [y]+-- ==========+--+-- Examples:+--+-- >>> x `overlaps` y+-- True+--+-- >>> x `overlappedBy` y+-- False+--+-- >>> y `overlaps` x+-- False+--+-- >>> y `overlappedBy` x+-- True+overlaps,+ overlappedBy ::+ (Iv (Interval a), Intervallic i0, Intervallic i1) =>+ ComparativePredicateOf2 (i0 a) (i1 a)+overlaps x y = ivOverlaps (getInterval x) (getInterval y)+overlappedBy = flip overlaps++-- | Does x `starts` y? Is x `startedBy` y?+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 3 4+-- >>> y = bi 6 4+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- --- <- [x]+-- ------ <- [y]+-- ==========+--+-- Examples:+--+-- >>> x `starts` y+-- True+--+-- >>> x `startedBy` y+-- False+--+-- >>> y `starts` x+-- False+--+-- >>> y `startedBy` x+-- True+starts,+ startedBy ::+ (Iv (Interval a), Intervallic i0, Intervallic i1) =>+ ComparativePredicateOf2 (i0 a) (i1 a)+starts x y = ivStarts (getInterval x) (getInterval y)+startedBy = flip starts++-- | Does x `finishes` y? Is x `finishedBy` y?+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 3 7+-- >>> y = bi 6 4+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- --- <- [x]+-- ------ <- [y]+-- ==========+--+-- Examples:+--+-- >>> x `finishes` y+-- True+--+-- >>> x `finishedBy` y+-- False+--+-- >>> y `finishes` x+-- False+--+-- >>> y `finishedBy` x+-- True+finishes,+ finishedBy ::+ (Iv (Interval a), Intervallic i0, Intervallic i1) =>+ ComparativePredicateOf2 (i0 a) (i1 a)+finishes x y = ivFinishes (getInterval x) (getInterval y)+finishedBy = flip finishes++-- | Is x `during` y? Does x `contains` y?+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 3 5+-- >>> y = bi 6 4+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- --- <- [x]+-- ------ <- [y]+-- ==========+--+-- Examples:+--+-- >>> x `during` y+-- True+--+-- >>> x `contains` y+-- False+--+-- >>> y `during` x+-- False+--+-- >>> y `contains` x+-- True+during,+ contains ::+ (Iv (Interval a), Intervallic i0, Intervallic i1) =>+ ComparativePredicateOf2 (i0 a) (i1 a)+during x y = ivDuring (getInterval x) (getInterval y)+contains = flip during++-- | Does x `equals` y?+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 6 4+-- >>> y = bi 6 4+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- ------ <- [x]+-- ------ <- [y]+-- ==========+--+-- Examples:+--+-- >>> x `equals` y+-- True+--+-- >>> y `equals` x+-- True+equals ::+ (Iv (Interval a), Intervallic i0, Intervallic i1) =>+ ComparativePredicateOf2 (i0 a) (i1 a)+equals x y = ivEquals (getInterval x) (getInterval y)++{- Intervallic-specific relation utilities -}++-- | Operator for composing the union of two predicates on 'Intervallic' s.+(<|>) ::+ (Intervallic i0, Intervallic i1) =>+ ComparativePredicateOf2 (i0 a) (i1 a) ->+ ComparativePredicateOf2 (i0 a) (i1 a) ->+ ComparativePredicateOf2 (i0 a) (i1 a)+(<|>) f g = unionPredicates [f, g]++-- | The set of @IntervalRelation@ meaning two intervals are disjoint.+disjointRelations :: Data.Set.Set IntervalRelation+disjointRelations = toSet [Before, After, Meets, MetBy]++-- | The set of @IntervalRelation@ meaning one interval is within the other.+withinRelations :: Data.Set.Set IntervalRelation+withinRelations = toSet [Starts, During, Finishes, Equals]++-- | The set of @IntervalRelation@ meaning one interval is *strictly* within the other.+strictWithinRelations :: Data.Set.Set IntervalRelation+strictWithinRelations = Data.Set.difference withinRelations (toSet [Equals])++-- | Are x and y `disjoint` ('before', 'after', 'meets', or 'metBy')?+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 3 0+-- >>> y = bi 3 5+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- --- <- [x]+-- --- <- [y]+-- ========+--+-- Examples:+--+-- >>> x `disjoint` y+-- True+--+-- >>> y `disjoint` x+-- True+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 3 0+-- >>> y = bi 3 3+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- --- <- [x]+-- --- <- [y]+-- ======+--+-- Examples:+--+-- >>> x `disjoint` y+-- True+--+-- >>> y `disjoint` x+-- True+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 6 0+-- >>> y = bi 3 3+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- ------ <- [x]+-- --- <- [y]+-- ======+--+-- Examples:+--+-- >>> x `disjoint` y+-- False+--+-- >>> y `disjoint` x+-- False+disjoint ::+ (SizedIv (Interval a), Ord a, Intervallic i0, Intervallic i1) =>+ ComparativePredicateOf2 (i0 a) (i1 a)+disjoint = predicate disjointRelations++-- | Does @x `concur` y@, meaning @x@ and @y@ share some support? Is @x `notDisjoint` y@? This is+-- the 'complement' of 'disjoint'.+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 3 0+-- >>> y = bi 3 4+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- --- <- [x]+-- --- <- [y]+-- =======+--+-- Examples:+--+-- >>> x `notDisjoint` y+-- False+-- >>> y `concur` x+-- False+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 3 0+-- >>> y = bi 3 3+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- --- <- [x]+-- --- <- [y]+-- ======+--+-- Examples:+--+-- >>> x `notDisjoint` y+-- False+-- >>> y `concur` x+-- False+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 6 0+-- >>> y = bi 3 3+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- ------ <- [x]+-- --- <- [y]+-- ======+--+-- Examples:+--+-- >>> x `notDisjoint` y+-- True+-- >>> y `concur` x+-- True+notDisjoint,+ concur ::+ (SizedIv (Interval a), Ord a, Intervallic i0, Intervallic i1) =>+ ComparativePredicateOf2 (i0 a) (i1 a)+notDisjoint = predicate (complement disjointRelations)+concur = notDisjoint++-- | Is x `within` (`enclosedBy`) y? That is, 'during', 'starts', 'finishes', or+-- 'equals'?+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 6 4+-- >>> y = bi 6 4+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- ------ <- [x]+-- ------ <- [y]+-- ==========+--+-- Examples:+--+-- >>> x `within` y+-- True+--+-- >>> y `enclosedBy` x+-- True+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 6 4+-- >>> y = bi 5 4+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- ------ <- [x]+-- ----- <- [y]+-- ==========+--+-- Examples:+--+-- >>> x `within` y+-- False+--+-- >>> y `enclosedBy` x+-- True+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 6 4+-- >>> y = bi 4 5+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- ------ <- [x]+-- ---- <- [y]+-- ==========+--+-- Examples:+--+-- >>> x `within` y+-- False+-- >>> y `enclosedBy` x+-- True+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 2 7+-- >>> y = bi 1 5+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- -- <- [x]+-- - <- [y]+-- =========+--+-- Examples:+--+-- >>> x `within` y+-- False+--+-- >>> y `enclosedBy` x+-- False+within,+ enclosedBy ::+ (SizedIv (Interval a), Ord a, Intervallic i0, Intervallic i1) =>+ ComparativePredicateOf2 (i0 a) (i1 a)+within = predicate withinRelations+enclosedBy = within++-- | Does x `encloses` y? That is, is y 'within' x?+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 6 4+-- >>> y = bi 6 4+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- ------ <- [x]+-- ------ <- [y]+-- ==========+--+-- Examples:+--+-- >>> x `encloses` y+-- True+--+-- >>> y `encloses` x+-- True+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 6 4+-- >>> y = bi 5 4+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- ------ <- [x]+-- ----- <- [y]+-- ==========+--+-- Examples:+--+-- >>> x `encloses` y+-- True+--+-- >>> y `encloses` x+-- False+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 6 4+-- >>> y = bi 4 5+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- ------ <- [x]+-- ---- <- [y]+-- ==========+--+-- Examples:+--+-- >>> x `encloses` y+-- True+--+-- >>> y `encloses` x+-- False+--+-- Example data with corresponding diagram:+--+-- >>> x = bi 2 7+-- >>> y = bi 1 5+-- >>> pretty $ standardExampleDiagram [(x, "x"), (y, "y")] []+-- -- <- [x]+-- - <- [y]+-- =========+--+-- Examples:+--+-- >>> x `encloses` y+-- False+--+-- >>> y `encloses` x+-- False+encloses ::+ (SizedIv (Interval a), Ord a, Intervallic i0, Intervallic i1) =>+ ComparativePredicateOf2 (i0 a) (i1 a)+encloses = flip enclosedBy++-- | The 'Data.Set.Set' of all 'IntervalRelation's.+intervalRelations :: Data.Set.Set IntervalRelation+intervalRelations =+ Data.Set.fromList (Prelude.map toEnum [0 .. 12] :: [IntervalRelation])++-- | Find the converse of a single 'IntervalRelation'+converseRelation :: IntervalRelation -> IntervalRelation+converseRelation x = toEnum (12 - fromEnum x)++-- | Shortcut to creating a 'Set IntervalRelation' from a list.+toSet :: [IntervalRelation] -> Data.Set.Set IntervalRelation+toSet = Data.Set.fromList++-- | Compose a list of interval relations with _or_ to create a new+-- @'ComparativePredicateOf1' i a@. For example,+-- @unionPredicates [before, meets]@ creates a predicate function determining+-- if one interval is either before or meets another interval.+unionPredicates :: [ComparativePredicateOf2 a b] -> ComparativePredicateOf2 a b+unionPredicates fs x y = any (\f -> f x y) fs++-- | Maps an 'IntervalRelation' to its corresponding predicate function.+toPredicate ::+ (SizedIv (Interval a), Ord a, Intervallic i0, Intervallic i1) =>+ IntervalRelation ->+ ComparativePredicateOf2 (i0 a) (i1 a)+toPredicate r = case r of+ Before -> before+ Meets -> meets+ Overlaps -> overlaps+ FinishedBy -> finishedBy+ Contains -> contains+ Starts -> starts+ Equals -> equals+ StartedBy -> startedBy+ During -> during+ Finishes -> finishes+ OverlappedBy -> overlappedBy+ MetBy -> metBy+ After -> after++-- | Given a set of 'IntervalRelation's return a list of 'predicate' functions+-- corresponding to each relation.+predicates ::+ (SizedIv (Interval a), Ord a, Intervallic i0, Intervallic i1) =>+ Data.Set.Set IntervalRelation ->+ [ComparativePredicateOf2 (i0 a) (i1 a)]+predicates x = Prelude.map toPredicate (Data.Set.toList x)++-- | Forms a predicate function from the union of a set of 'IntervalRelation's.+predicate ::+ (SizedIv (Interval a), Ord a, Intervallic i0, Intervallic i1) =>+ Data.Set.Set IntervalRelation ->+ ComparativePredicateOf2 (i0 a) (i1 a)+predicate = unionPredicates . predicates++-- | The lookup table for the compositions of interval relations.+composeRelationLookup :: [[[IntervalRelation]]]+composeRelationLookup =+ [ [p, p, p, p, p, p, p, p, pmosd, pmosd, pmosd, pmosd, full],+ [p, p, p, p, p, m, m, m, osd, osd, osd, fef, dsomp],+ [p, p, pmo, pmo, pmofd, o, o, ofd, osd, osd, cncr, dso, dsomp],+ [p, m, o, f', d', o, f', d', osd, fef, dso, dso, dsomp],+ [pmofd, ofd, ofd, d', d', ofd, d', d', cncr, dso, dso, dso, dsomp],+ [p, p, pmo, pmo, pmofd, s, s, ses, d, d, dfo, m', p'],+ [p, m, o, f', d', s, e, s', d, f, o', m', p'],+ [pmofd, ofd, ofd, d', d', ses, s', s', dfo, o', o', m', p'],+ [p, p, pmosd, pmosd, full, d, d, dfomp, d, d, dfomp, p', p'],+ [p, m, osd, fef, dsomp, d, f, omp, d, f, omp, p', p'],+ [pmofd, ofd, cncr, dso, dsomp, dfo, o', omp, dfo, o', omp, p', p'],+ [pmofd, ses, dfo, m', p', dfo, m', p', dfo, m', p', p', p'],+ [full, dfomp, dfomp, p', p', dfomp, p', p', dfomp, p', p', p', p']+ ]+ where+ p = [Before]+ m = [Meets]+ o = [Overlaps]+ f' = [FinishedBy]+ d' = [Contains]+ s = [Starts]+ e = [Equals]+ s' = [StartedBy]+ d = [During]+ f = [Finishes]+ o' = [OverlappedBy]+ m' = [MetBy]+ p' = [After]+ ses = s ++ e ++ s'+ fef = f' ++ e ++ f+ pmo = p ++ m ++ o+ pmofd = pmo ++ f' ++ d'+ osd = o ++ s ++ d+ ofd = o ++ f' ++ d'+ omp = o' ++ m' ++ p'+ dfo = d ++ f ++ o'+ dfomp = dfo ++ m' ++ p'+ dso = d' ++ s' ++ o'+ dsomp = dso ++ m' ++ p'+ pmosd = p ++ m ++ osd+ cncr = o ++ f' ++ d' ++ s ++ e ++ s' ++ d ++ f ++ o'+ full = p ++ m ++ cncr ++ m' ++ p'++-- | Compare two @i a@ to determine their 'IntervalRelation'.+--+-- >>> relate (Interval (0::Int, 1)) (Interval (1, 2))+-- Meets+--+-- >>> relate (Interval (1::Int, 2)) (Interval (0, 1))+-- MetBy+relate ::+ (Iv (Interval a), Intervallic i0, Intervallic i1) => i0 a -> i1 a -> IntervalRelation+relate x y = ivRelate (getInterval x) (getInterval y)++-- | Compose two interval relations according to the rules of the algebra.+-- The rules are enumerated according to+-- <https://thomasalspaugh.org/pub/fnd/allen.html#BasicCompositionsTable this table>.+compose ::+ IntervalRelation -> IntervalRelation -> Data.Set.Set IntervalRelation+compose x y = toSet (composeRelationLookup !! fromEnum x !! fromEnum y)++-- | Finds the complement of a @'Data.Set.Set' 'IntervalRelation'@.+complement :: Data.Set.Set IntervalRelation -> Data.Set.Set IntervalRelation+complement = Data.Set.difference intervalRelations++-- | Find the intersection of two 'Data.Set.Set's of 'IntervalRelation's.+intersection ::+ Data.Set.Set IntervalRelation ->+ Data.Set.Set IntervalRelation ->+ Data.Set.Set IntervalRelation+intersection = Data.Set.intersection++-- | Find the union of two 'Data.Set.Set's of 'IntervalRelation's.+union ::+ Data.Set.Set IntervalRelation ->+ Data.Set.Set IntervalRelation ->+ Data.Set.Set IntervalRelation+union = Data.Set.union++-- | Find the converse of a @'Data.Set.Set' 'IntervalRelation'@.+converse :: Data.Set.Set IntervalRelation -> Data.Set.Set IntervalRelation+converse = Data.Set.map converseRelation++{- Generic interval interfaces -}++-- | Generic interface for defining relations between abstract representations+-- of intervals, for the purpose of [Allen's interval algebra](https://en.wikipedia.org/wiki/Allen%27s_interval_algebra).+--+-- In general, these "intervals" need not be representable as temporal intervals with a fixed+-- beginning and ending. Specifically, the relations can be defined to provide temporal reasoning+-- in a qualitative setting, examples of which are in Allen 1983.+--+-- For intervals that can be cast in canonical form as 'Interval' s with begin and end points,+-- see 'PointedIv' and 'SizedIv'.+--+-- Instances of 'Iv' must ensure any pair of intervals satisfies exactly one+-- of the thirteen possible 'IntervalRelation' s.+--+-- When 'iv' is also an instance of 'PointedIv', with @Ord (Point iv)@,+-- the requirement implies+--+-- @+-- ivBegin i < ivEnd i+-- @+--+-- [Allen 1983](https://dl.acm.org/doi/10.1145/182.358434)+-- defines the 'IntervalRelation' s for such cases, which is provided in this module+-- for the canonical representation @'Interval' a@.+--+-- ==== __Examples__+--+-- The following example is modified from Allen 1983 to demonstrate the algebra used for temporal+-- reasoning in a qualitative setting, for a case where 'iv' does not have points.+--+-- It represents the temporal logic of the statement+--+-- > We found the letter during dinner, after we made the decision.+--+-- >>> :{+--data GoingsOn = Dinner | FoundLetter | MadeDecision+-- deriving (Show, Eq)+--instance Iv GoingsOn where+-- ivRelate MadeDecision Dinner = Before+-- ivRelate MadeDecision FoundLetter = Before+-- ivRelate FoundLetter Dinner = During+-- ivRelate x y+-- | x == y = Equals+-- | otherwise = converseRelation (ivRelate y x)+-- :}+class Iv iv where+ {-# MINIMAL ivRelate | ivBefore, ivMeets, ivOverlaps, ivStarts, ivFinishes, ivDuring, ivEquals #-}++ -- | The 'IntervalRelation' between two intervals.+ ivRelate :: iv -> iv -> IntervalRelation+ ivRelate x y+ | x `ivBefore` y = Before+ | x `ivAfter` y = After+ | x `ivMeets` y = Meets+ | x `ivMetBy` y = MetBy+ | x `ivOverlaps` y = Overlaps+ | x `ivOverlappedBy` y = OverlappedBy+ | x `ivStarts` y = Starts+ | x `ivStartedBy` y = StartedBy+ | x `ivFinishes` y = Finishes+ | x `ivFinishedBy` y = FinishedBy+ | x `ivDuring` y = During+ | x `ivContains` y = Contains+ | otherwise = Equals++ -- \| Is @'ivRelate' x y == Before@? @'ivAfter' = flip 'ivBefore'@.+ ivBefore,+ ivAfter ::+ -- | 'x'+ iv ->+ -- | 'y'+ iv ->+ Bool+ ivBefore x = (== Before) . ivRelate x+ ivAfter = flip ivBefore++ -- | Is @'ivRelate' x y == Meets@? @'ivMetBy' = flip 'ivMeets'@.+ ivMeets,+ ivMetBy ::+ -- | 'x'+ iv ->+ -- | 'y'+ iv ->+ Bool+ ivMeets x = (== Meets) . ivRelate x+ ivMetBy = flip ivMeets++ -- | Is @'ivRelate' x y == Overlaps@? @'ivOverlappedBy' = flip 'ivOverlaps'@.+ ivOverlaps,+ ivOverlappedBy ::+ -- | 'x'+ iv ->+ -- | 'y'+ iv ->+ Bool+ ivOverlaps x = (== Overlaps) . ivRelate x+ ivOverlappedBy = flip ivOverlaps++ -- | Is @'ivRelate' x y == Starts@? @'ivStartedBy' = flip 'ivStarts'@.+ ivStarts,+ ivStartedBy ::+ -- | 'x'+ iv ->+ -- | 'y'+ iv ->+ Bool+ ivStarts x = (== Starts) . ivRelate x+ ivStartedBy = flip ivStarts++ -- | Is @'ivRelate' x y == Finishes@? @'ivFinishedBy' = flip 'ivFinishes'@.+ ivFinishes,+ ivFinishedBy ::+ -- | 'x'+ iv ->+ -- | 'y'+ iv ->+ Bool+ ivFinishes x = (== Finishes) . ivRelate x+ ivFinishedBy = flip ivFinishes++ -- | Is @'ivRelate' x y == During@? @'ivContains' = flip 'ivDuring'@.+ ivDuring,+ ivContains ::+ -- | 'x'+ iv ->+ -- | 'y'+ iv ->+ Bool+ ivDuring x = (== During) . ivRelate x+ ivContains = flip ivDuring++ -- | Is @'ivRelate' x y == Equals@?+ ivEquals ::+ -- | 'x'+ iv ->+ -- | 'y'+ iv ->+ Bool+ ivEquals x = (== Equals) . ivRelate x++-- | Class representing intervals that can be cast to and from the canonical+-- representation @'Interval' a@.+--+-- When 'iv' is also an instance of 'PointedIv', with @Ord (Point iv)@, it should+-- adhere to Allen's construction of the interval algebra for intervals represented+-- by left and right endpoints. See [sections 3 and 4](https://cse.unl.edu/~choueiry/Documents/Allen-CACM1983.pdf)+-- of Allen 1983.+--+-- Specifically, the requirements for interval relations imply+--+-- @+-- ivBegin i < ivEnd i+-- @+--+-- This module provides default implementations for methods of 'Iv' in that case.+--+-- Note @iv@ should not be an instance of @Intervallic@ unless @iv ~ Interval+-- a@, since @Intervallic@ is a class for getting and setting intervals as+-- @Interval a@ in particular.+--+-- A @Vector@ whose elements are provided in strict ascending order is an example of+-- a type that could implement 'PointedIv' without being equivalent to 'Interval',+-- with @ivBegin = head@ and @ivEnd = last@.+class PointedIv iv where+ type Point iv++ -- | Access the left ("begin") and right ("end") endpoints of an interval.+ ivBegin, ivEnd :: iv -> Point iv++-- | The 'SizedIv' typeclass is a generic interface for constructing and+-- manipulating intervals. The class imposes strong requirements on its+-- methods, in large part to ensure the constructors 'ivExpandr' and 'ivExpandl'+-- return "valid" intervals, particularly in the typical case where 'iv' also+-- implements the interval algebra.+--+-- In all cases, 'ivExpandr' and 'ivExpandl' should preserve the value of the+-- point *not* shifted. That is,+--+-- @+-- ivBegin (ivExpandr d i) == ivBegin i+-- ivEnd (ivExpandl d i) == ivEnd i+-- @+--+-- In addition, using 'Interval' as example, the following must hold:+--+-- When @iv@ is @Ord@, for all @i == Interval (b, e)@,+--+-- @+-- ivExpandr d i >= i+-- ivExpandl d i <= i+-- @+--+-- When @Moment iv@ is @Ord@,+--+-- @+-- duration (ivExpandr d i) >= max moment (duration i)+-- duration (ivExpandl d i) >= max moment (duration i)+-- @+--+-- In particular, if the duration 'd' by which to expand is less than 'moment',+-- and @'duration' i >= moment@ then these constructors should return the input.+--+-- @+-- ivExpandr d i == i+-- ivExpandl d i == i+-- @+--+-- When @Moment iv@ also is @Num@, the default 'moment' value is @1@ and in all+-- cases should be positive.+--+-- @+-- moment @iv > 0+-- @+--+-- When in addition @Point iv ~ Moment iv@, the class provides a default 'duration' as+-- @duration i = ivEnd i - ivBegin i@.+--+-- This module enforces @'Point' (Interval a) = a@. However, it need not be+-- that @a ~ Moment iv@. For example @Moment (Interval UTCTime) ~+-- NominalDiffTime@.+--+-- ==== SizedIv and the interval algebra+--+-- When 'iv' is an instance of 'Iv', the methods of this class should ensure+-- the validity of the resulting interval with respect to the interval algebra.+-- For example, when @'Point' iv@ is 'Ord', they must always produce a valid+-- interval 'i' such that @'ivBegin' i < 'ivEnd' i@.+--+-- In addition, the requirements of 'SizedIv' implementations in the common case+-- where @'Moment' iv@ is 'Num' and 'Ord' require the constructors to produce intervals+-- with 'duration' of at least 'moment'.+--+-- In order to preserve the properties above, @ivExpandr, ivExpandl@ will not want to assume+-- validity of the input interval. In other words, @'ivExpandr' d i@ need not be the+-- identity when @d < 'moment'@ since it will need to ensure the result is a valid interval+-- even if 'i' is not.+--+-- These two methods can therefore be used as constructors for valid intervals.+class (PointedIv iv) => SizedIv iv where+ -- | Type of 'moment'.+ type Moment iv++ -- | The smallest duration for an 'iv'. When 'Moment iv' is an instance of+ -- 'Num', the default is 1. If @'Moment' iv@ is @Ord@ and @Num@, @'moment' > 0@+ -- is required.+ moment :: Moment iv++ -- | The duration of an 'iv'. When @Moment iv ~ Point iv@ and @Point iv@ is+ -- @Num@ this defaults to @ivEnd i - ivBegin i@.+ duration :: iv -> Moment iv++ -- | Resize @iv@ by expanding to the "left" or to the "right" by some+ -- duration. If @iv@ implements the interval algebra via @Iv@, these+ -- methods must produce valid intervals regardless of the validity of the input+ -- and thus serve as constructors for intervals. See also 'beginerval',+ -- 'endverval', 'safeInterval' and related.+ --+ -- See the class documentation for details requirements.+ --+ -- >>> ivExpandr 1 (safeInterval (0, 1) :: Interval Int) == safeInterval (0, 2)+ -- True+ -- >>> ivExpandr 0 (safeInterval (0, 1) :: Interval Int) == safeInterval (0, 1)+ -- True+ -- >>> ivExpandl 1 (safeInterval (0, 1) :: Interval Int) == safeInterval (-1, 1)+ -- True+ -- >>> ivExpandl 0 (safeInterval (0, 1) :: Interval Int) == safeInterval (0, 1)+ -- True+ ivExpandr, ivExpandl :: Moment iv -> iv -> iv++ default moment :: (Num (Moment iv)) => Moment iv+ moment = 1++ default duration :: (Point iv ~ Moment iv, Num (Point iv)) => iv -> Moment iv+ duration i = ivEnd i - ivBegin i++-- | Resize an @i a@ to by expanding to "left" by @l@ and to the "right" by @r@.+-- In the case that @l@ or @r@ are less than a 'moment' the respective endpoints+-- are unchanged.+--+-- >>> iv2to4 = safeInterval (2::Int, 4)+-- >>> iv2to4' = expand 0 0 iv2to4+-- >>> iv1to5 = expand 1 1 iv2to4+--+-- >>> iv2to4+-- (2, 4)+--+-- >>> iv2to4'+-- (2, 4)+--+-- >>> iv1to5+-- (1, 5)+--+-- >>> pretty $ standardExampleDiagram [(iv2to4, "iv2to4"), (iv1to5, "iv1to5")] []+-- -- <- [iv2to4]+-- ---- <- [iv1to5]+-- =====+expand ::+ (SizedIv (Interval a), Intervallic i) =>+ -- | duration to subtract from the 'begin'+ Moment (Interval a) ->+ -- | duration to add to the 'end'+ Moment (Interval a) ->+ i a ->+ i a+expand l r = expandl l . expandr r++-- | Expands an @i a@ to the "left".+--+-- >>> iv2to4 = (safeInterval (2::Int, 4::Int))+-- >>> iv0to4 = expandl 2 iv2to4+--+-- >>> iv2to4+-- (2, 4)+--+-- >>> iv0to4+-- (0, 4)+--+-- >>> pretty $ standardExampleDiagram [(iv2to4, "iv2to4"), (iv0to4, "iv0to4")] []+-- -- <- [iv2to4]+-- ---- <- [iv0to4]+-- ====+expandl :: (SizedIv (Interval a), Intervallic i) => Moment (Interval a) -> i a -> i a+expandl l i = setInterval i $ ivExpandl l $ getInterval i++-- | Expands an @i a@ to the "right".+--+-- >>> iv2to4 = (safeInterval (2::Int, 4::Int))+-- >>> iv2to6 = expandr 2 iv2to4+--+-- >>> iv2to4+-- (2, 4)+--+-- >>> iv2to6+-- (2, 6)+--+-- >>> pretty $ standardExampleDiagram [(iv2to4, "iv2to4"), (iv2to6, "iv2to6")] []+-- -- <- [iv2to4]+-- ---- <- [iv2to6]+-- ======+expandr :: (SizedIv (Interval a), Intervallic i) => Moment (Interval a) -> i a -> i a+expandr r i = setInterval i $ ivExpandr r $ getInterval i++-- | Safely creates an 'Interval a' using @x@ as the 'begin' and adding @max+-- 'moment' dur@ to @x@ as the 'end'. For the 'SizedIv' instances this+-- module exports, 'beginerval' is the same as 'interval'. However, it is defined+-- separately since 'beginerval' will /always/ have this behavior whereas+-- 'interval' behavior might differ by implementation.+--+-- >>> beginerval (0::Int) (0::Int)+-- (0, 1)+--+-- >>> beginerval (1::Int) (0::Int)+-- (0, 1)+--+-- >>> beginerval (2::Int) (0::Int)+-- (0, 2)+beginerval ::+ forall a.+ (SizedIv (Interval a)) =>+ -- | @dur@ation to add to the 'begin'+ Moment (Interval a) ->+ -- | the 'begin' point of the 'Interval'+ a ->+ Interval a+beginerval dur x = ivExpandr dur $ Interval (x, x)++-- | A synonym for `beginerval`+bi ::+ forall a.+ (SizedIv (Interval a)) =>+ -- | @dur@ation to add to the 'begin'+ Moment (Interval a) ->+ -- | the 'begin' point of the 'Interval'+ a ->+ Interval a+bi = beginerval++-- | Safely creates an 'Interval a' using @x@ as the 'end' and adding @negate max+-- 'moment' dur@ to @x@ as the 'begin'.+--+-- >>> enderval (0::Int) (0::Int)+-- (-1, 0)+--+-- >>> enderval (1::Int) (0::Int)+-- (-1, 0)+--+-- >>> enderval (2::Int) (0::Int)+-- (-2, 0)+enderval ::+ forall a.+ (SizedIv (Interval a)) =>+ -- | @dur@ation to subtract from the 'end'+ Moment (Interval a) ->+ -- | the 'end' point of the 'Interval'+ a ->+ Interval a+enderval dur x = ivExpandl dur $ Interval (x, x)++-- | A synonym for `enderval`+ei ::+ forall a.+ (SizedIv (Interval a)) =>+ -- | @dur@ation to subtract from the 'end'+ Moment (Interval a) ->+ -- | the 'end' point of the 'Interval'+ a ->+ Interval a+ei = enderval++-- | Safely creates an @'Interval'@ from a pair of endpoints,+-- expanding from the left endpoint if necessary to create a valid interval+-- according to the rules of 'SizedIv'. This function simply wraps+-- 'ivExpandr'.+--+-- >>> safeInterval (4, 5 ::Int)+-- (4, 5)+-- >>> safeInterval (4, 3 :: Int)+-- (4, 5)+safeInterval ::+ forall a.+ (SizedIv (Interval a), Ord (Moment (Interval a))) =>+ (a, a) ->+ Interval a+safeInterval (b, e)+ | duration i < m = ivExpandr m $ Interval (b, b)+ | otherwise = i+ where+ i = Interval (b, e)+ m = moment @(Interval a)++-- | A synonym for `safeInterval`+si ::+ (SizedIv (Interval a), Ord (Moment (Interval a))) =>+ (a, a) ->+ Interval a+si = safeInterval++-- | Creates a new 'Interval' from the 'end' of another.+beginervalFromEnd ::+ (SizedIv (Interval a), Intervallic i) =>+ -- | @dur@ation to add to the 'end'+ Moment (Interval a) ->+ -- | the @i a@ from which to get the 'end'+ i a ->+ Interval a+beginervalFromEnd d i = beginerval d (end i)++-- | Creates a new 'Interval' from the 'begin' of another.+endervalFromBegin ::+ (SizedIv (Interval a), Intervallic i) =>+ -- | @dur@ation to subtract from the 'begin'+ Moment (Interval a) ->+ -- | the @i a@ from which to get the 'begin'+ i a ->+ Interval a+endervalFromBegin d i = enderval d (begin i)++-- | Safely creates a new @Interval@ with 'moment' length with 'begin' at @x@+--+-- >>> beginervalMoment (10 :: Int)+-- (10, 11)+beginervalMoment :: forall a. (SizedIv (Interval a)) => a -> Interval a+beginervalMoment = beginerval (moment @(Interval a))++-- | Safely creates a new @Interval@ with 'moment' length with 'end' at @x@+--+-- >>> endervalMoment (10 :: Int)+-- (9, 10)+endervalMoment :: forall a. (SizedIv (Interval a)) => a -> Interval a+endervalMoment = enderval (moment @(Interval a))++-- | Creates a new @Interval@ spanning the extent x and y.+--+-- >>> extenterval (Interval (0, 1)) (Interval (9, 10))+-- (0, 10)+extenterval :: (SizedIv (Interval a), Ord a, Intervallic i) => i a -> i a -> Interval a+extenterval x y = Interval (s, e)+ where+ s = min (begin x) (begin y)+ e = max (end x) (end y)++-- | Modifies the endpoints of second argument's interval by taking the difference+-- from the first's input's 'begin'.+--+-- Example data with corresponding diagram:+--+-- >>> a = bi 3 2 :: Interval Int+-- >>> a+-- (2, 5)+-- >>> x = bi 3 7 :: Interval Int+-- >>> x+-- (7, 10)+-- >>> y = bi 4 9 :: Interval Int+-- >>> y+-- (9, 13)+-- >>> pretty $ standardExampleDiagram [(a, "a"), (x, "x"), (y, "y")] []+-- --- <- [a]+-- --- <- [x]+-- ---- <- [y]+-- =============+--+-- Examples:+--+-- >>> x' = shiftFromBegin a x+-- >>> x'+-- (5, 8)+-- >>> y' = shiftFromBegin a y+-- >>> y'+-- (7, 11)+-- >>> pretty $ standardExampleDiagram [(x', "x'"), (y', "y'")] []+-- --- <- [x']+-- ---- <- [y']+-- ===========+shiftFromBegin ::+ (Num a, SizedIv (Interval a), Intervallic i1, Intervallic i0) =>+ i0 a ->+ i1 a ->+ i1 a+shiftFromBegin i = imapStrictMonotone (\x -> x - begin i)++-- | Modifies the endpoints of second argument's interval by taking the difference+-- from the first's input's 'end'.+--+-- Example data with corresponding diagram:+--+-- >>> a = bi 3 2 :: Interval Int+-- >>> a+-- (2, 5)+-- >>> x = bi 3 7 :: Interval Int+-- >>> x+-- (7, 10)+-- >>> y = bi 4 9 :: Interval Int+-- >>> y+-- (9, 13)+-- >>> pretty $ standardExampleDiagram [(a, "a"), (x, "x"), (y, "y")] []+-- --- <- [a]+-- --- <- [x]+-- ---- <- [y]+-- =============+--+-- Examples:+--+-- >>> x' = shiftFromEnd a x+-- >>> x'+-- (2, 5)+-- >>> y' = shiftFromEnd a y+-- >>> y'+-- (4, 8)+-- >>> pretty $ standardExampleDiagram [(x', "x'"), (y', "y'")] []+-- --- <- [x']+-- ---- <- [y']+-- ========++shiftFromEnd ::+ (Num a, SizedIv (Interval a), Intervallic i1, Intervallic i0) =>+ i0 a ->+ i1 a ->+ i1 a+shiftFromEnd i = imapStrictMonotone (\x -> x - end i)++-- | Converts an @i a@ to an @i Int@ via @fromEnum@. This assumes the provided+-- @fromEnum@ method is strictly monotone increasing: For @a@ types that are+-- @Ord@ with values @x, y@, then @x < y@ implies @fromEnum x < fromEnum y@, so+-- long as the latter is well-defined.+fromEnumInterval :: (Enum a, Intervallic i) => i a -> i Int+fromEnumInterval = imapStrictMonotone fromEnum++-- | Converts an @i Int@ to an @i a@ via @toEnum@. This assumes the provided+-- @toEnum@ method is strictly monotone increasing: For @a@ types that are+-- @Ord@, then for @Int@ values @x, y@ it holds that @x < y@ implies @toEnum x+-- < toEnum y@.+toEnumInterval :: (Enum a, Intervallic i) => i Int -> i a+toEnumInterval = imapStrictMonotone toEnum++-- | Changes the duration of an 'Intervallic' value to a moment starting at the+-- 'begin' of the interval. Uses 'beginervalMoment'.+--+-- >>> momentize (Interval (6, 10))+-- (6, 7)+momentize ::+ forall i a. (SizedIv (Interval a), Intervallic i) => i a -> i a+momentize i = setInterval i $ beginervalMoment $ begin i++{-+Misc+-}++-- | Defines a predicate of two objects of type @a@.+type ComparativePredicateOf1 a = (a -> a -> Bool)++-- | Defines a predicate of two object of different types.+type ComparativePredicateOf2 a b = (a -> b -> Bool)++{- Common instance helpers -}++-- | Internal. Helper for SizedIv constructor implementations+-- defined in this module, so as to ensure the class properties.+ivExpandrI ::+ (Ord b) =>+ -- | 'moment' value to be passed here.+ b ->+ -- | 'duration'+ (a -> a -> b) ->+ -- | function for adding an amount of moments to a point.+ -- It must always satisfy addFun (dFun x y) y == x+ (b -> a -> a) ->+ -- | duration by which to expand.+ b ->+ Interval a ->+ Interval a+ivExpandrI mom dFun addFun d (Interval (b, e))+ | d < mom = Interval (b, addFun (max (dFun e b) mom) b)+ | otherwise = Interval (b, addFun d e)++ivExpandlI ::+ (Ord b) =>+ -- | 'moment' value to be passed here.+ b ->+ -- | 'duration'+ (a -> a -> b) ->+ -- | function for subtracting a amount of moments to a point.+ -- It must always satisfy subFun (dFun x y) x == y+ (b -> a -> a) ->+ -- | duration by which to expand.+ b ->+ Interval a ->+ Interval a+ivExpandlI mom dFun subFun d (Interval (b, e))+ | d < mom = Interval (subFun (max (dFun e b) mom) e, e)+ | otherwise = Interval (subFun d b, e)++{- Instances -}++-- | Imposes a total ordering on @'Interval' a@ based on first ordering the+-- 'begin's then the 'end's.+instance (Ord a) => Ord (Interval a) where+ (Interval pts1) <= (Interval pts2) = pts1 <= pts2+ (Interval pts1) < (Interval pts2) = pts1 < pts2++instance Intervallic Interval where+ getInterval = id+ setInterval _ x = x++instance PointedIv (Interval a) where+ type Point (Interval a) = a++ ivBegin (Interval (b, _)) = b+ ivEnd (Interval (_, e)) = e++-- | Implements the interval algebra for intervals represented as left and right endpoints,+-- with points in a totally ordered set, as prescribed in+-- [Allen 1983](https://dl.acm.org/doi/10.1145/182.358434).+instance (Ord a) => Iv (Interval a) where+ ivBefore x y = ivEnd x < ivBegin y+ ivMeets x y = ivEnd x == ivBegin y+ ivOverlaps x y = ivBegin x < ivBegin y && ivEnd x < ivEnd y && ivEnd x > ivBegin y+ ivStarts x y = ivBegin x == ivBegin y && ivEnd x < ivEnd y+ ivFinishes x y = ivBegin x > ivBegin y && ivEnd x == ivEnd y+ ivDuring x y = ivBegin x > ivBegin y && ivEnd x < ivEnd y+ ivEquals x y = ivBegin x == ivBegin y && ivEnd x == ivEnd y++-- TODO: Consider whether blanket instance for+-- Num a => SizedIv (Interval a) is good.++instance SizedIv (Interval Int) where+ type Moment (Interval Int) = Int+ ivExpandr = ivExpandrI (moment @(Interval Int)) (-) (+)+ ivExpandl = ivExpandlI (moment @(Interval Int)) (-) (flip (-))++instance SizedIv (Interval Integer) where+ type Moment (Interval Integer) = Integer+ ivExpandr = ivExpandrI (moment @(Interval Integer)) (-) (+)+ ivExpandl = ivExpandlI (moment @(Interval Integer)) (-) (flip (-))++instance SizedIv (Interval Double) where+ type Moment (Interval Double) = Double+ ivExpandr = ivExpandrI (moment @(Interval Double)) (-) (+)+ ivExpandl = ivExpandlI (moment @(Interval Double)) (-) (flip (-))++instance SizedIv (Interval DT.Day) where+ type Moment (Interval DT.Day) = Integer+ duration (Interval (b, e)) = diffDays e b+ moment = 1+ ivExpandr = ivExpandrI (moment @(Interval DT.Day)) diffDays addDays+ ivExpandl = ivExpandlI (moment @(Interval DT.Day)) diffDays (\d -> addDays (-d))++-- | Note this instance changes the @moment@ to 1 'Pico' second, not 1 second+-- as would be the case if the default were used.+instance SizedIv (Interval DT.UTCTime) where+ type Moment (Interval DT.UTCTime) = NominalDiffTime+ moment = toEnum 1+ duration (Interval (b, e)) = diffUTCTime e b+ ivExpandr = ivExpandrI (moment @(Interval DT.UTCTime)) diffUTCTime addUTCTime+ ivExpandl = ivExpandlI (moment @(Interval DT.UTCTime)) diffUTCTime (\d -> addUTCTime (-d))
src/IntervalAlgebra/IntervalDiagram.hs view
@@ -1,4 +1,10 @@ {-|+Module : IntervalAlgebra.IntervalDiagram+Description : Tools for visualizing intervals+Copyright : (c) NoviSci, Inc 2020-2022+ TargetRWE, 2023+License : BSD3+Maintainer : bsaul@novisci.com 2020-2022, bbrown@targetrwe.com 2023 This module provides functions for creating diagrams of intervals as text. For example,@@ -11,13 +17,6 @@ ------ ============================== ->>> let ref = bi 30 (fromGregorian 2022 5 6)->>> let ivs = [ bi 2 (fromGregorian 2022 5 6), bi 5 (fromGregorian 2022 5 10)]->>> pretty $ simpleIntervalDiagram ref ivs---- ------==============================- Such diagrams are useful for documentation, examples, and learning to reason with the interval algebra. @@ -81,7 +80,7 @@ -- $setup -- >>> :set -XTypeApplications -XFlexibleContexts -XOverloadedStrings--- >>> import IntervalAlgebra.IntervalUtilities (gapsWithin)+-- >>> import IntervalAlgebra.IntervalUtilities -- >>> import Data.Time {-@@ -118,7 +117,7 @@ getInterval (MkIntervalText x) = getInterval x setInterval (MkIntervalText x) i = MkIntervalText $ setInterval x i -instance (Enum b, IntervalSizeable a b) => Pretty (IntervalText a) where+instance (Enum (Moment (Interval a)), SizedIv (Interval a)) => Pretty (IntervalText a) where pretty (MkIntervalText x) = pretty $ replicate (fromEnum (duration i)) c where c = getPairData x@@ -507,7 +506,7 @@ | IntervalLineError IntervalTextLineParseError deriving (Eq, Show) -instance (IntervalSizeable a b) => Pretty (IntervalDiagram a) where+instance (SizedIv (Interval a)) => Pretty (IntervalDiagram a) where pretty (MkIntervalDiagram _ axis ivs opts) = do -- Create a list of pretty IntervalLines@@ -527,7 +526,7 @@ -- See use of makeIntervalLine in parseIntervalTextLine. -- This why the intervalLineEnd function is used to determine -- the end of the intervals in a line.- let labelIndents = fmap (diff refDur . intervalLineEnd) ivs+ let labelIndents = fmap ((-) refDur . intervalLineEnd) ivs -- Create a list of the line label docs let labelLines =@@ -555,7 +554,7 @@ then emptyDoc else space <> pretty ("<-" :: Text) <> space <> pretty t -instance (IntervalSizeable a b) =>+instance (SizedIv (Interval a)) => Pretty (Either IntervalDiagramParseError (IntervalDiagram a)) where pretty (Left e) = pretty $ show e pretty (Right d) = pretty d@@ -623,7 +622,7 @@ -} parseIntervalDiagram- :: (Ord a, IntervalSizeable a b, Enum b)+ :: (Ord a, SizedIv (Interval a), Enum a, Num a, Enum (Moment (Interval a))) => IntervalDiagramOptions -- ^ Document options (see 'IntervalDiagramOptions') -> [(Int, Char)]@@ -670,7 +669,7 @@ where extendsBeyond = before <|> meets <|> overlaps <|> overlappedBy <|> metBy <|> after- checkAvailableChar (AvailablePerLine i _) = fromEnum (duration ref) > i+ checkAvailableChar (AvailablePerLine i _) = fromEnum (duration $ getInterval ref) > i checkAvailableChar Unbounded = True {- Shifts the endpoints of an interval to be referenced from another interval,@@ -704,16 +703,9 @@ ----- ------ ==============================-->>> pretty $ simpleIntervalDiagram ref (fromMaybe [] (gapsWithin ref ivs))- --------- -- ---------==============================- -} simpleIntervalDiagram- :: (Ord a, IntervalSizeable a b, Intervallic i, Enum b)+ :: (Ord a, SizedIv (Interval a), Intervallic i, Enum a, Num a, Enum (Moment (Interval a))) => i a -- ^ The axis interval -> [i a] -- ^ List of intervals to be printed one per line -> Either IntervalDiagramParseError (IntervalDiagram a)@@ -734,11 +726,11 @@ and such that each row displays each interval provided in the intervals list and label pair. ->>> x1 = si (1, 5)->>> x2 = si (7, 10)->>> x3 = si (13, 15)+>>> x1 = beginerval 4 1+>>> x2 = beginerval 3 7+>>> x3 = beginerval 2 13 >>> ivs = [x1, x2, x3]->>> gaps = [si (5, 7), si (10, 13)]+>>> gaps = [beginerval 2 5, beginerval 3 10] >>> :{ pretty $ standardExampleDiagram (zip ivs ["x1", "x2", "x3"]) [(gaps, "gaps")] :}@@ -764,7 +756,7 @@ IntervalsExtendBeyondAxis -} standardExampleDiagram- :: (Num a, Ord a, Enum b, IntervalSizeable a b)+ :: (Num a, Enum a, Ord a, Enum (Moment (Interval a)), Ord (Moment (Interval a)), SizedIv (Interval a)) => [(Interval a, String)] -> [([Interval a], String)] -> Either IntervalDiagramParseError (IntervalDiagram a)
src/IntervalAlgebra/IntervalUtilities.hs view
@@ -1,132 +1,71 @@ {-| Module : Interval Algebra Utilities Description : Functions for operating on containers of Intervals.-Copyright : (c) NoviSci, Inc 2020+Copyright : (c) NoviSci, Inc 2020-2022+ TargetRWE, 2023 License : BSD3-Maintainer : bsaul@novisci.com+Maintainer : bsaul@novisci.com 2020-2022, bbrown@targetrwe.com 2023 Stability : experimental -} +{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE Safe #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-} module IntervalAlgebra.IntervalUtilities ( -- * Fold over sequential intervals combineIntervals- , combineIntervalsL , combineIntervalsFromSorted- , combineIntervalsFromSortedL , rangeInterval- , gaps- , gapsL- , gapsWithin - -- * Operations on Meeting sequences of paired intervals- , foldMeetingSafe- , formMeetingSequence-- -- * Withering functions-- -- ** Clear containers based on predicate- , nothingIf- , nothingIfNone- , nothingIfAny- , nothingIfAll-- -- ** Filter containers based on predicate- , filterBefore- , filterMeets- , filterOverlaps- , filterFinishedBy- , filterContains- , filterStarts- , filterEquals- , filterStartedBy- , filterDuring- , filterFinishes- , filterOverlappedBy- , filterMetBy- , filterAfter- , filterDisjoint- , filterNotDisjoint- , filterConcur- , filterWithin- , filterEncloses- , filterEnclosedBy+ -- * Combining intervals+ , (><)+ , (.+.) -- * Functions for manipulating intervals , lookback , lookahead -- * Gaps- , makeGapsWithinPredicate+ , gaps , pairGaps- , anyGapsWithinAtLeastDuration- , allGapsWithinLessThanDuration -- * Misc utilities , relations- , relationsL , intersect , clip , durations ) where -import safe Control.Applicative (Applicative (pure),- liftA2, (<$>), (<*>))+import Control.Applicative (Applicative (pure), liftA2,+ (<$>), (<*>)) import qualified Control.Foldl as L-import safe Control.Monad (Functor (fmap))-import safe Data.Bool (Bool (..), not, otherwise,- (&&), (||))-import safe Data.Eq (Eq ((==)))-import safe Data.Foldable (Foldable (foldl', foldr, null, toList),- all, any, or)-import safe Data.Function (flip, ($), (.))-import safe Data.List (map, reverse, sortOn)-import safe Data.Maybe (Maybe (..), maybe,- maybeToList)-import safe Data.Monoid (Monoid (mempty))-import safe Data.Ord (Ord (max, min), (<), (>=))-import safe Data.Semigroup (Semigroup ((<>)))-import safe Data.Traversable (Traversable (sequenceA))-import safe Data.Tuple (fst, uncurry)-import safe GHC.Int (Int)-import safe GHC.Show (Show)-import safe IntervalAlgebra.Core (ComparativePredicateOf1,- ComparativePredicateOf2,- Interval,- IntervalCombinable ((><)),- IntervalRelation (..),- IntervalSizeable (diff, duration),- Intervallic (..), after,- before, begin, beginerval,- beginervalFromEnd, bi,- concur, contains,- disjoint, during,- enclosedBy, encloses, end,- enderval,- endervalFromBegin, equals,- extenterval, finishedBy,- finishes, meets, metBy,- notDisjoint, overlappedBy,- overlaps, relate,- startedBy, starts, within,- (<|>))-import safe IntervalAlgebra.PairedInterval (PairedInterval,- equalPairData,- getPairData,- makePairedInterval)-import safe Safe (headMay, initSafe,- lastMay, tailSafe)-import safe Witherable (Filterable (filter),- Witherable (..),- catMaybes, mapMaybe)+import Control.Monad (Functor (fmap))+import Data.Bool (Bool (..), not, otherwise,+ (&&), (||))+import Data.Eq (Eq ((==)))+import Data.Foldable (Foldable (foldl', foldr, null, toList),+ all, any, or)+import Data.Function (flip, ($), (.))+import Data.List (map, reverse, sortOn)+import Data.Maybe (Maybe (..), mapMaybe, maybe,+ maybeToList)+import Data.Monoid (Monoid (mempty))+import Data.Ord (Ord (max, min), (<), (>=))+import Data.Semigroup (Semigroup ((<>)))+import Data.Traversable (Traversable (sequenceA))+import Data.Tuple (fst, uncurry)+import GHC.Int (Int)+import GHC.Show (Show)+import IntervalAlgebra.Core+import IntervalAlgebra.PairedInterval (PairedInterval, equalPairData,+ getPairData,+ makePairedInterval) {- $setup >>> import GHC.List ( (++), zip )@@ -138,40 +77,12 @@ -- Unexported utilties used in functions below -- ------------------------------------------------- --- An internal utility function for creating a @Fold@ that maps over a structure--- by consecutive pairs into a new structure.-makeFolder :: (Monoid (m b), Applicative m) => (a -> a -> b) -> L.Fold a (m b)-makeFolder f = L.Fold step begin done- where- begin = (mempty, Nothing)- step (fs, Nothing) y = (fs, Just y)- step (fs, Just x ) y = (fs <> pure (f x y), Just y)- done (fs, _) = fs --- | Create a predicate function that checks whether within a provided spanning--- interval, are there (e.g. any, all) gaps of (e.g. <, <=, >=, >) a specified--- duration among the input intervals?-makeGapsWithinPredicate- :: ( Monoid (t (Interval a))- , Monoid (t (Maybe (Interval a)))- , Applicative t- , Witherable.Witherable t- , IntervalSizeable a b- , Intervallic i0- , IntervalCombinable i1 a- )- => ((b -> Bool) -> t b -> Bool)- -> (b -> b -> Bool)- -> (b -> i0 a -> t (i1 a) -> Bool)-makeGapsWithinPredicate f op gapDuration interval l =- maybe False (f (`op` gapDuration) . durations) (gapsWithin interval l)---- | Gets the durations of gaps (via 'IntervalAlgebra.(><)') between all pairs--- of the input.+-- | Gets the durations of gaps (via '(><)') between all pairs of the input. pairGaps- :: (Intervallic i, IntervalSizeable a b, IntervalCombinable i a)+ :: (Intervallic i, SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => [i a]- -> [Maybe b]+ -> [Maybe (Moment (Interval a))] pairGaps es = fmap (fmap duration . uncurry (><)) (pairs es) -- Generate all pair-wise combinations of a single list. -- pairs :: [a] -> [(a, a)]@@ -189,8 +100,8 @@ -- >>> lookback 4 (beginerval 10 (1 :: Int)) -- (-3, 1) lookback- :: (Intervallic i, IntervalSizeable a b)- => b -- ^ lookback duration+ :: (Intervallic i, SizedIv (Interval a), Ord (Moment (Interval a)))+ => Moment (Interval a) -- ^ lookback duration -> i a -> Interval a lookback d x = enderval d (begin x)@@ -201,121 +112,47 @@ -- >>> lookahead 4 (beginerval 1 (1 :: Int)) -- (2, 6) lookahead- :: (Intervallic i, IntervalSizeable a b)- => b -- ^ lookahead duration+ :: (Intervallic i, SizedIv (Interval a), Ord (Moment (Interval a)))+ => Moment (Interval a) -- ^ lookahead duration -> i a -> Interval a lookahead d x = beginerval d (end x) --- | Within a provided spanning interval, are there any gaps of at least the--- specified duration among the input intervals?-anyGapsWithinAtLeastDuration- :: ( IntervalSizeable a b- , Intervallic i0- , IntervalCombinable i1 a- , Monoid (t (Interval a))- , Monoid (t (Maybe (Interval a)))- , Applicative t- , Witherable.Witherable t- )- => b -- ^ duration of gap- -> i0 a -- ^ within this interval- -> t (i1 a)- -> Bool-anyGapsWithinAtLeastDuration = makeGapsWithinPredicate any (>=)---- | Within a provided spanning interval, are all gaps less than the specified--- duration among the input intervals?------ >>> allGapsWithinLessThanDuration 30 (beginerval 100 (0::Int)) [beginerval 5 (-1), beginerval 99 10]--- True-allGapsWithinLessThanDuration- :: ( IntervalSizeable a b- , Intervallic i0- , IntervalCombinable i1 a- , Monoid (t (Interval a))- , Monoid (t (Maybe (Interval a)))- , Applicative t- , Witherable.Witherable t- )- => b -- ^ duration of gap- -> i0 a -- ^ within this interval- -> t (i1 a)- -> Bool-allGapsWithinLessThanDuration = makeGapsWithinPredicate all (<)----- Used to combine two lists by combining the last element of @x@ and the first--- element of @y@ by @f@. The combining function @f@ will generally return a--- singleton list in the case that the last of x and head of y can be combined--- or a two element list in the case they cannot.-listCombiner- :: (Maybe a -> Maybe a -> [a]) -- ^ f- -> [a] -- ^ x- -> [a] -- ^ y- -> [a]-listCombiner f x y = initSafe x <> f (lastMay x) (headMay y) <> tailSafe y-{-# INLINABLE listCombiner #-}---- | Returns a list of the 'IntervalRelation' between each consecutive pair--- of intervals. This is just a specialized 'relations' which returns a list.------ >>> relationsL [bi 1 0, bi 1 1]--- [Meets]----relationsL- :: (Foldable f, Ord a, Intervallic i) => f (i a) -> [IntervalRelation]-relationsL = relations---- | A generic form of 'relations' which can output any 'Applicative' and--- 'Monoid' structure.+-- | Returns a list of the 'IntervalRelation' between each consecutive pair of @i a@. ----- >>> (relations [bi 1 0,bi 1 1]) :: [IntervalRelation]+-- >>> relations [beginerval 1 0, beginerval 1 1] -- [Meets]------+-- >>> relations [beginerval 1 0, beginerval 1 1, beginerval 2 1]+-- [Meets,Starts]+-- >>> relations [beginerval 1 0]+-- [] relations- :: ( Foldable f- , Applicative m- , Ord a- , Intervallic i- , Monoid (m IntervalRelation)+ :: ( Intervallic i+ , Iv (Interval a) )- => f (i a)- -> m IntervalRelation-relations = L.fold (makeFolder relate)-{-# INLINABLE relations #-}+ => [i a]+ -> [IntervalRelation]+relations [] = []+relations [x] = []+relations (x : y : xs) = relate x y : relations (y : xs) --- | Forms a 'Just' new interval from the intersection of two intervals,--- provided the intervals are not disjoint.+-- | Forms 'Just' a new interval from the intersection of two intervals,+-- provided the intervals are not 'disjoint'. -- -- >>> intersect (bi 5 0) (bi 2 3) -- Just (3, 5) -- intersect- :: (Intervallic i, IntervalSizeable a b) => i a -> i a -> Maybe (Interval a)+ :: (Intervallic i, SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => i a -> i a -> Maybe (Interval a) intersect x y | disjoint x y = Nothing- | otherwise = Just $ beginerval (diff e b) b+ | otherwise = Just $ safeInterval (b, e) where b = max (begin x) (begin y) e = min (end x) (end y) --- Internal function which folds over a structure by consecutive pairs, returing--- gaps between each pair (@Nothing@ if no such gap exists).-gapsM- :: ( IntervalCombinable i a- , Traversable f- , Monoid (f (Maybe (Interval a)))- , Applicative f- )- => f (i a)- -> f (Maybe (Interval a))-gapsM = L.fold (makeFolder (\i j -> getInterval i >< getInterval j))-{-# INLINABLE gapsM #-}--{- | Returns a @Maybe@ container of intervals consisting of the gaps between-intervals in the input. __To work properly, the input should be sorted.__ See-'gapsL' for a version that always returns a list.+{- | Returns a list of intervals consisting of the gaps between+consecutive intervals in the input, after they have been sorted by+interval ordering. >>> x1 = bi 4 1 >>> x2 = bi 4 8@@ -324,7 +161,7 @@ >>> ivs [(1, 5),(8, 12),(11, 14)] >>> gaps ivs-Nothing+[(5, 8)] >>> pretty $ standardExampleDiagram (zip ivs ["x1", "x2", "x3"]) [] ---- <- [x1] ---- <- [x2]@@ -339,64 +176,10 @@ [(1, 5),(7, 10),(13, 15)] >>> gapIvs = gaps ivs >>> gapIvs-Just [(5, 7),(10, 13)]->>> :{-case gapIvs of- Nothing -> pretty ""- (Just x) -> pretty $- standardExampleDiagram (zip ivs ["x1", "x2", "x3"]) [(x, "gapIvs")]-:}- ---- <- [x1]- --- <- [x2]- -- <- [x3]- -- --- <- [gapIvs]-===============--}-gaps- :: ( IntervalCombinable i a- , Traversable f- , Monoid (f (Maybe (Interval a)))- , Applicative f- )- => f (i a)- -> Maybe (f (Interval a))-gaps = sequenceA . gapsM-{-# INLINABLE gaps #-}--{- | Returns a (possibly empty) list of intervals consisting of the gaps between-intervals in the input container.-__To work properly, the input should be sorted.__ This version outputs a list.-See 'gaps' for a version that lifts the result to same input structure @f@.-->>> x1 = bi 4 1->>> x2 = bi 4 8->>> x3 = bi 3 11->>> ivs = [x1, x2, x3]->>> ivs-[(1, 5),(8, 12),(11, 14)]->>> gapIvs = gapsL ivs->>> gapIvs-[]->>> :{-pretty $ standardExampleDiagram (zip ivs ["x1", "x2", "x3"]) []-:}- ---- <- [x1]- ---- <- [x2]- --- <- [x3]-==============-->>> x1 = bi 4 1->>> x2 = bi 3 7->>> x3 = bi 2 13->>> ivs = [x1, x2, x3]->>> ivs-[(1, 5),(7, 10),(13, 15)]->>> gapIvs = gapsL ivs->>> gapIvs [(5, 7),(10, 13)] >>> :{-pretty $- standardExampleDiagram (zip ivs ["x1", "x2", "x3"]) [(gapIvs, "gapIvs")]+ pretty $+ standardExampleDiagram (zip ivs ["x1", "x2", "x3"]) [(gapIvs, "gapIvs")] :} ---- <- [x1] --- <- [x2]@@ -404,24 +187,26 @@ -- --- <- [gapIvs] =============== -}-gapsL- :: ( IntervalCombinable i a- , Applicative f- , Monoid (f (Maybe (Interval a)))- , Traversable f- )- => f (i a)- -> [Interval a]-gapsL x = maybe [] toList (gaps x)-{-# INLINABLE gapsL #-}+gaps :: (+ SizedIv (Interval a),+ Intervallic i,+ Ord a,+ Ord (Moment (Interval a))+ ) =>+ [i a] ->+ [Interval a]+gaps xs = mapMaybe (uncurry (><)) $ pair $ sortOn getInterval xs+ where pair [] = []+ pair [x] = []+ pair (x : y : ys) = (x, y) : pair (y : ys) -- | Returns the 'duration' of each 'Intervallic i a' in the 'Functor' @f@. -- -- >>> durations [bi 9 1, bi 10 2, bi 1 5 :: Interval Int] -- [9,10,1] ---durations :: (Functor f, Intervallic i, IntervalSizeable a b) => f (i a) -> f b-durations = fmap duration+durations :: (Functor f, Intervallic i, SizedIv (Interval a)) => f (i a) -> f (Moment (Interval a))+durations = fmap (duration . getInterval) -- | In the case that x y are not disjoint, clips y to the extent of x. --@@ -432,13 +217,13 @@ -- Nothing -- clip- :: (Intervallic i0, Intervallic i1, IntervalSizeable a b)+ :: (Intervallic i0, Intervallic i1, SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => i0 a -> i1 a -> Maybe (Interval a) clip x y- | overlaps x y = Just $ enderval (diff (end x) (begin y)) (end x)- | overlappedBy x y = Just $ beginerval (diff (end y) (begin x)) (begin x)+ | overlaps x y = Just $ safeInterval (begin y, end x)+ | overlappedBy x y = Just $ safeInterval (begin x, end y) | jx x y = Just (getInterval x) | jy x y = Just (getInterval y) | otherwise = Nothing {- disjoint x y case -}@@ -447,94 +232,6 @@ jx = starts <|> during <|> finishes {-# INLINABLE clip #-} --- | Applies 'gaps' to all the non-disjoint intervals in @x@ that are /not/ disjoint--- from @i@. Intervals that 'overlaps' or are 'overlappedBy' @i@ are 'clip'ped--- to @i@, so that all the intervals are 'within' @i@. If all of the input intervals--- are disjoint from the focal interval or if the input is empty, then 'Nothing'--- is returned. When there are no gaps among the concurring intervals, then--- @Just mempty@ (e.g. @Just []@) is returned.------ >>> gapsWithin (bi 9 1) [bi 5 0, bi 2 7, bi 3 12]--- Just [(5, 7),(9, 10)]----gapsWithin- :: ( Applicative f- , Witherable f- , Monoid (f (Interval a))- , Monoid (f (Maybe (Interval a)))- , IntervalSizeable a b- , Intervallic i0- , IntervalCombinable i1 a- )- => i0 a -- ^ i- -> f (i1 a) -- ^ x- -> Maybe (f (Interval a))-gapsWithin i x | null ivs = Nothing- | otherwise = Just res- where- s = pure (endervalFromBegin 0 i)- e = pure (beginervalFromEnd 0 i)- ivs = mapMaybe (clip i) (filterNotDisjoint i x)- res = catMaybes $ gapsM (s <> ivs <> e)-{-# INLINABLE gapsWithin #-}--{- | Returns a container of intervals where any intervals that meet or share-support are combined into one interval. This functions sorts the input intervals-first. See @combineIntervalsL@ for a version that works only on lists. If you-know the input intervals are sorted, use @combineIntervalsFromSorted@ instead.-->>> x1 = bi 10 0->>> x2 = bi 5 2->>> x3 = bi 2 10->>> x4 = bi 2 13->>> ivs = [x1, x2, x3, x4]->>> ivs-[(0, 10),(2, 7),(10, 12),(13, 15)]->>> xComb = combineIntervals ivs->>> xComb-[(0, 12),(13, 15)]->>> :{-pretty $- standardExampleDiagram- (zip ivs ["x1", "x2", "x3", "x4"])- [(xComb, "xComb")]-:}----------- <- [x1]- ----- <- [x2]- -- <- [x3]- -- <- [x4]------------- -- <- [xComb]-===============--}-combineIntervals- :: (Applicative f, Ord a, Intervallic i, Monoid (f (Interval a)), Foldable f)- => f (i a)- -> f (Interval a)-combineIntervals = combineIntervalsWith combineIntervalsL--{- | Returns a container of intervals where any intervals that meet or share-support are combined into one interval. The condition is applied cumulatively,-from left to right, so-__to work properly, the input list should be sorted in increasing order__. See-@combineIntervalsLFromSorted@ for a version that works only on lists.-->>> combineIntervalsFromSorted [bi 10 0, bi 5 2, bi 2 10, bi 2 13]-[(0, 12),(13, 15)]--}-combineIntervalsFromSorted- :: (Applicative f, Ord a, Intervallic i, Monoid (f (Interval a)), Foldable f)- => f (i a)- -> f (Interval a)-combineIntervalsFromSorted = combineIntervalsWith combineIntervalsFromSortedL---- | Unexported helper-combineIntervalsWith- :: (Applicative f, Ord a, Intervallic i, Monoid (f (Interval a)), Foldable f)- => ([i a] -> [Interval a])- -> f (i a)- -> f (Interval a)-combineIntervalsWith f x = foldl' (\x y -> x <> pure y) mempty (f $ toList x)- {- | Returns a list of intervals where any intervals that meet or share support are combined into one interval. This function sorts the input. If you know the input intervals are sorted, use @combineIntervalsLFromSorted@.@@ -546,7 +243,7 @@ >>> ivs = [x1, x2, x3, x4] >>> ivs [(0, 10),(2, 7),(10, 12),(13, 15)]->>> xComb = combineIntervalsL ivs+>>> xComb = combineIntervals ivs >>> xComb [(0, 12),(13, 15)] >>> :{@@ -562,23 +259,23 @@ ------------ -- <- [xComb] =============== -}-combineIntervalsL :: (Intervallic i, Ord a) => [i a] -> [Interval a]-combineIntervalsL = combineIntervalsFromSortedL . sortOn getInterval+combineIntervals :: (SizedIv (Interval a), Intervallic i, Ord a) => [i a] -> [Interval a]+combineIntervals = combineIntervalsFromSorted . sortOn getInterval {- | Returns a list of intervals where any intervals that meet or share support are combined into one interval. The operation is applied cumulatively, from left to right, so __to work properly, the input list should be sorted in increasing order__. ->>> combineIntervalsFromSortedL [bi 10 0, bi 5 2, bi 2 10, bi 2 13]+>>> combineIntervalsFromSorted [bi 10 0, bi 5 2, bi 2 10, bi 2 13] [(0, 12),(13, 15)] ->>> combineIntervalsFromSortedL [bi 10 0, bi 5 2, bi 0 8]+>>> combineIntervalsFromSorted [bi 10 0, bi 5 2, bi 0 8] [(0, 10)] -}-combineIntervalsFromSortedL- :: forall a i . (Ord a, Intervallic i) => [i a] -> [Interval a]-combineIntervalsFromSortedL = reverse . foldl' op []+combineIntervalsFromSorted+ :: forall a i . (Ord a, Intervallic i, SizedIv (Interval a)) => [i a] -> [Interval a]+combineIntervalsFromSorted = reverse . foldl' op [] where op [] y = [getInterval y] op (x : xs) y = if x `before` y@@ -617,286 +314,26 @@ --------- <- [spanIv] =========== ->>> rangeInterval Nothing+>>> rangeInterval (Nothing :: Maybe (Interval Int)) Nothing >>> rangeInterval (Just (bi 1 0)) Just (0, 1) -}-rangeInterval :: (Ord a, L.Foldable t) => t (Interval a) -> Maybe (Interval a)+rangeInterval :: (L.Foldable t, Ord a, SizedIv (Interval a)) => t (Interval a) -> Maybe (Interval a) rangeInterval = L.fold (liftA2 extenterval <$> L.minimum <*> L.maximum) --- | Given a predicate combinator, a predicate, and list of intervals, returns--- the input unchanged if the predicate combinator is @True@. Otherwise, returns--- an empty list. See 'nothingIfAny' and 'nothingIfNone' for examples.-nothingIf- :: (Monoid (f (i a)), Filterable f)- => ((i a -> Bool) -> f (i a) -> Bool) -- ^ e.g. 'any' or 'all'- -> (i a -> Bool) -- ^ predicate to apply to each element of input list- -> f (i a)- -> Maybe (f (i a))-nothingIf quantifier predicate x =- if quantifier predicate x then Nothing else Just x---- | Returns the 'Nothing' if *none* of the element of input satisfy--- the predicate condition.------ For example, the following returns 'Nothing' because none of the intervals--- in the input list 'starts' (3, 5).------ >>> nothingIfNone (starts (bi 2 3)) [bi 1 3, bi 1 5]--- Nothing------ In the following, (3, 5) 'starts' (3, 6), so 'Just' the input is returned.------ >>> nothingIfNone (starts (bi 2 3)) [bi 3 3, bi 1 5]--- Just [(3, 6),(5, 6)]----nothingIfNone- :: (Monoid (f (i a)), Foldable f, Filterable f)- => (i a -> Bool) -- ^ predicate to apply to each element of input list- -> f (i a)- -> Maybe (f (i a))-nothingIfNone = nothingIf (\f x -> (not . any f) x)---- | Returns 'Nothing' if *any* of the element of input satisfy the predicate condition.------ >>> nothingIfAny (startedBy (bi 2 3)) [bi 3 3, bi 1 5]--- Just [(3, 6),(5, 6)]------ >>> nothingIfAny (starts (bi 2 3)) [bi 3 3, bi 1 5]--- Nothing----nothingIfAny- :: (Monoid (f (i a)), Foldable f, Filterable f)- => (i a -> Bool) -- ^ predicate to apply to each element of input list- -> f (i a)- -> Maybe (f (i a))-nothingIfAny = nothingIf any---- | Returns 'Nothing' if *all* of the element of input satisfy the predicate condition.------ >>> nothingIfAll (starts (bi 2 3)) [bi 3 3, bi 4 3]--- Nothing----nothingIfAll- :: (Monoid (f (i a)), Foldable f, Filterable f)- => (i a -> Bool) -- ^ predicate to apply to each element of input list- -> f (i a)- -> Maybe (f (i a))-nothingIfAll = nothingIf all---- | Creates a function for filtering a 'Witherable.Filterable' of @i1 a@s--- by comparing the @Interval a@s that of an @i0 a@.-makeFilter- :: (Filterable f, Intervallic i0, Intervallic i1)- => ComparativePredicateOf2 (i0 a) (i1 a)- -> i0 a- -> (f (i1 a) -> f (i1 a))-makeFilter f p = Witherable.filter (f p)--{- |-Filter 'Witherable.Filterable' containers of one @'Intervallic'@ type based by comparing to-a (potentially different) 'Intervallic' type using the corresponding interval-predicate function.--}-filterOverlaps, filterOverlappedBy, filterBefore, filterAfter, filterStarts, filterStartedBy, filterFinishes, filterFinishedBy, filterMeets, filterMetBy, filterDuring, filterContains, filterEquals, filterDisjoint, filterNotDisjoint, filterConcur, filterWithin, filterEncloses, filterEnclosedBy- :: (Filterable f, Ord a, Intervallic i0, Intervallic i1)- => i0 a- -> f (i1 a)- -> f (i1 a)-filterOverlaps = makeFilter overlaps-filterOverlappedBy = makeFilter overlappedBy-filterBefore = makeFilter before-filterAfter = makeFilter after-filterStarts = makeFilter starts-filterStartedBy = makeFilter startedBy-filterFinishes = makeFilter finishes-filterFinishedBy = makeFilter finishedBy-filterMeets = makeFilter meets-filterMetBy = makeFilter metBy-filterDuring = makeFilter during-filterContains = makeFilter contains-filterEquals = makeFilter equals-filterDisjoint = makeFilter disjoint-filterNotDisjoint = makeFilter notDisjoint-filterConcur = makeFilter concur-filterWithin = makeFilter within-filterEncloses = makeFilter encloses-filterEnclosedBy = makeFilter enclosedBy---- | Folds over a list of Paired Intervals and in the case that the 'getPairData'--- is equal between two sequential meeting intervals, these two intervals are--- combined into one. This function is "safe" in the sense that if the input is--- invalid and contains any sequential pairs of intervals with an @IntervalRelation@,--- other than 'Meets', then the function returns an empty list.-foldMeetingSafe- :: (Eq b, Ord a, Show a)- => [PairedInterval b a] -- ^ Be sure this only contains intervals- -- that sequentially 'meets'.- -> [PairedInterval b a]-foldMeetingSafe l = maybe [] (getMeeting . foldMeeting) (parseMeeting l)---- | Folds over a list of Meeting Paired Intervals and in the case that the 'getPairData'--- is equal between two sequential meeting intervals, these two intervals are--- combined into one.-foldMeeting- :: (Eq b, Ord a, Show a)- => Meeting [PairedInterval b a]- -> Meeting [PairedInterval b a]-foldMeeting (Meeting l) =- foldl' joinMeetingPairedInterval (Meeting []) (packMeeting l)---- This type identifies that @a@ contains intervals that sequentially meet one--- another.-newtype Meeting a = Meeting { getMeeting :: a } deriving (Eq, Show)---- Box up Meeting.-packMeeting :: [a] -> [Meeting [a]]-packMeeting = fmap (\z -> Meeting [z])---- Test a list of intervals to be sure they all meet; if not return Nothing.-parseMeeting :: (Ord a, Intervallic i) => [i a] -> Maybe (Meeting [i a])-parseMeeting x | all (== Meets) (relationsL x) = Just $ Meeting x- | otherwise = Nothing---- A specific case of 'joinMeeting' for @PairedIntervals@.-joinMeetingPairedInterval- :: (Eq b, Ord a, Show a)- => Meeting [PairedInterval b a]- -> Meeting [PairedInterval b a]- -> Meeting [PairedInterval b a]-joinMeetingPairedInterval = joinMeeting equalPairData---- A general function for combining any two @Meeting [i a]@ by 'listCombiner'.-joinMeeting- :: (Ord a, Intervallic i)- => ComparativePredicateOf1 (i a)- -> Meeting [i a]- -> Meeting [i a]- -> Meeting [i a]-joinMeeting f (Meeting x) (Meeting y) =- Meeting $ listCombiner (join2MeetingWhen f) x y---- The intervals @x@ and @y@ should meet! The predicate function @p@ determines--- when the two intervals that meet should be combined.-join2MeetingWhen- :: (Ord a, Intervallic i)- => ComparativePredicateOf1 (i a)- -> Maybe (i a)- -> Maybe (i a)- -> [i a]-join2MeetingWhen p Nothing Nothing = []-join2MeetingWhen p Nothing (Just y) = [y]-join2MeetingWhen p (Just x) Nothing = [x]-join2MeetingWhen p (Just x) (Just y) | p x y = [setInterval y (extenterval x y)]- | otherwise = pure x <> pure y--{- |-Takes two *ordered* events, x <= y, and "disjoins" them in the case that the-two events have different states, creating a sequence (list) of new events that-sequentially meet one another. Since x <= y, there are 7 possible interval-relations between x and y. If the states of x and y are equal and x is not-before y, then x and y are combined into a single event.--}-disjoinPaired- :: (Eq b, Monoid b, Show a, IntervalSizeable a c)- => (PairedInterval b) a- -> (PairedInterval b) a- -> Meeting [(PairedInterval b) a]-disjoinPaired o e = case relate x y of- Before -> Meeting [x, evp e1 b2 mempty, y]- Meets -> foldMeeting $ Meeting [x, y]- Overlaps -> foldMeeting $ Meeting [evp b1 b2 s1, evp b2 e1 sc, evp e1 e2 s2]- FinishedBy -> foldMeeting $ Meeting [evp b1 b2 s1, ev i2 sc]- Contains -> foldMeeting $ Meeting [evp b1 b2 s1, evp b2 e2 sc, evp e2 e1 s1]- Starts -> foldMeeting $ Meeting [ev i1 sc, evp e1 e2 s2]- _ -> Meeting [ev i1 sc] {- Equals case -}- where- x = min o e- y = max o e- i1 = getInterval x- i2 = getInterval y- s1 = getPairData x- s2 = getPairData y- sc = s1 <> s2- b1 = begin x- b2 = begin y- e1 = end x- e2 = end y- ev = flip makePairedInterval- evp b e = ev (beginerval (diff e b) b)-{-# INLINABLE disjoinPaired #-}--{- |-The internal function for converting a non-disjoint, ordered sequence of-events into a disjoint, ordered sequence of events. The function operates-by recursion on a pair of events and the input events. The first of the-is the accumulator set -- the disjoint events that need no longer be-compared to input events. The second of the pair are disjoint events that-still need to be compared to be input events.--}-recurseDisjoin- :: (Monoid b, Eq b, IntervalSizeable a c, Show a)- => ([(PairedInterval b) a], [(PairedInterval b) a])- -> [(PairedInterval b) a]- -> [(PairedInterval b) a]-recurseDisjoin (acc, o : os) [] = acc <> (o : os) -- the "final" pattern-recurseDisjoin (acc, [] ) [] = acc -- another "final" pattern-recurseDisjoin (acc, [] ) (e : es) = recurseDisjoin (acc, [e]) es -- the "initialize" pattern-recurseDisjoin (acc, o : os) (e : es)- | -- the "operating" patterns- -- If input event is equal to the first comparator, skip the comparison.- e == o = recurseDisjoin (acc, o : os) es- |-- {- If o is either before or meets e, then- the first of the combined events can be put into the accumulator.- That is, since the inputs events are ordered, once the beginning of o- is before or meets e, then we are assured that all periods up to the- beginning of o are fully disjoint and subsequent input events will- not overlap these in any way. -}- (before <|> meets) o e = recurseDisjoin- (acc <> nh, recurseDisjoin ([], nt) os)- es- |-- --The standard recursive operation.- otherwise = recurseDisjoin (acc, recurseDisjoin ([], n) os) es- where- n = getMeeting $ disjoinPaired o e- nh = maybeToList (headMay n)- nt = tailSafe n-{-# INLINABLE recurseDisjoin #-}--{- |-Convert an ordered sequence of @PairedInterval b a@. that may have any interval relation-('before', 'starts', etc) into a sequence of sequentially meeting @PairedInterval b a@.-That is, a sequence where one the end of one interval meets the beginning of-the subsequent event. The 'getPairData' of the input @PairedIntervals@ are-combined using the Monoid '<>' function, hence the pair data must be a-'Monoid' instance.--}-formMeetingSequence- :: (Eq b, Show a, Monoid b, IntervalSizeable a c)- => [PairedInterval b a]- -> [PairedInterval b a]-formMeetingSequence x- | null x = []- | allMeet x && not (hasEqData x) = x- | otherwise = formMeetingSequence (recurseDisjoin ([], []) x)- -- recurseDisjoin ([], []) (recurseDisjoin ([], []) (recurseDisjoin ([], []) x))-- -- the multiple passes of recurseDisjoin is to handle the situation where the- -- initial passes almost disjoins all the events correctly into a meeting sequence- -- but due to nesting of intervals in the input -- some of the sequential pairs have- -- the same data after the first pass. The recursive passes merges any sequential- -- intervals that have the same data.- --- -- There is probably a more efficient way to do this-{-# INLINABLE formMeetingSequence #-}+ {- Combining intervals -} -allMeet :: (Ord a) => [PairedInterval b a] -> Bool-allMeet x = all (== Meets) (relationsL x)+-- | If @x@ is 'before' @y@, then form a new @Just Interval a@ from the+-- interval in the "gap" between @x@ and @y@ from the 'end' of @x@ to the+-- 'begin' of @y@. Otherwise, 'Nothing'.+(><) :: (Iv (Interval a), Ord (Moment (Interval a)), SizedIv (Interval a), Intervallic i) => i a -> i a -> Maybe (Interval a)+(><) x y+ | x `before` y = Just $ safeInterval (end x, begin y)+ | otherwise = Nothing -hasEqData :: (Eq b) => [PairedInterval b a] -> Bool-hasEqData x = or (L.fold (makeFolder (==)) (fmap getPairData x) :: [Bool])+-- | Maybe form a new @Interval a@ by the union of two @Interval a@s that 'meets'.+(.+.) :: (Iv (Interval a), Ord (Moment (Interval a)), SizedIv (Interval a), Intervallic i) => i a -> i a -> Maybe (Interval a)+(.+.) x y+ | x `meets` y = Just $ safeInterval (begin x, end y)+ | otherwise = Nothing
src/IntervalAlgebra/PairedInterval.hs view
@@ -1,16 +1,17 @@ {-| Module : Paired interval Description : Extends the Interval Algebra to an interval paired with some data.-Copyright : (c) NoviSci, Inc 2020+Copyright : (c) NoviSci, Inc 2020-2022+ TargetRWE, 2023 License : BSD3-Maintainer : bsaul@novisci.com+Maintainer : bsaul@novisci.com 2020-2022, bbrown@targetrwe.com 2023 Stability : experimental -} {-# OPTIONS_HADDOCK prune #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE Safe #-} module IntervalAlgebra.PairedInterval ( PairedInterval@@ -23,20 +24,19 @@ , trivialize ) where -import safe Control.Applicative (liftA2)-import safe Control.DeepSeq (NFData)-import safe Data.Binary (Binary)-import safe GHC.Generics (Generic)-import safe IntervalAlgebra.Core (ComparativePredicateOf1, Interval,- IntervalCombinable (..),- IntervalSizeable, Intervallic (..),- before, extenterval)-import safe Test.QuickCheck (Arbitrary (..))-import safe Witherable (Filterable (filter))+import Control.Applicative (liftA2)+import Control.DeepSeq (NFData)+import Data.Binary (Binary)+import GHC.Generics (Generic)+import IntervalAlgebra.Core (ComparativePredicateOf1, Interval,+ Intervallic (..), SizedIv (..), before,+ extenterval)+import Test.QuickCheck (Arbitrary (..)) -- | An @Interval a@ paired with some other data of type @b@.-newtype PairedInterval b a = PairedInterval (Interval a, b)- deriving (Eq, Generic)+newtype PairedInterval b a+ = PairedInterval (Interval a, b)+ deriving (Eq, Generic) instance Intervallic (PairedInterval b) where getInterval (PairedInterval x) = fst x@@ -54,15 +54,6 @@ instance (Show b, Show a, Ord a) => Show (PairedInterval b a) where show x = "{" ++ show (getInterval x) ++ ", " ++ show (getPairData x) ++ "}" -instance (Ord a, Eq b, Monoid b) =>- IntervalCombinable (PairedInterval b) a where- (><) x y = fmap (makePairedInterval mempty) (getInterval x >< getInterval y)-- (<+>) x y- | x `before` y = pure x <> pure y- | otherwise = pure- $ makePairedInterval (getPairData x <> getPairData y) (extenterval x y)- -- | Make a paired interval. makePairedInterval :: b -> Interval a -> PairedInterval b a makePairedInterval d i = PairedInterval (i, d)@@ -80,8 +71,7 @@ intervals = fmap getInterval -- | Empty is used to trivially lift an @Interval a@ into a @PairedInterval@.-data Empty = Empty- deriving (Eq, Ord, Show)+data Empty = Empty deriving (Eq, Ord, Show) instance Semigroup Empty where x <> y = Empty instance Monoid Empty where@@ -98,6 +88,7 @@ trivialize = fmap toTrivialPair +-- TODO REFACTOR need to revisit this -- Arbitrary instance-instance (Arbitrary b, Ord a, Arbitrary a) => Arbitrary (PairedInterval b a) where- arbitrary = liftA2 makePairedInterval arbitrary arbitrary+--instance (Arbitrary b, Arbitrary (Interval a)) => Arbitrary (PairedInterval b a) where+-- arbitrary = liftA2 makePairedInterval arbitrary arbitrary
src/IntervalAlgebra/RelationProperties.hs view
@@ -1,35 +1,41 @@ {-| Module : Interval Algebra Axioms Description : Properties of Intervals-Copyright : (c) NoviSci, Inc 2020+Copyright : (c) NoviSci, Inc 2020-2022+ TargetRWE, 2023 License : BSD3-Maintainer : bsaul@novisci.com+Maintainer : bsaul@novisci.com 2020-2022, bbrown@targetrwe.com 2023 -This module exports a single typeclass @IntervalAxioms@ which contains-property-based tests for the axioms in section 1 of [Allen and Hayes (1987)](https://doi.org/10.1111/j.1467-8640.1989.tb00329.x).-The notation below is that of the original paper.+This module exports property-based tests for the axioms in section 1 of [Allen+and Hayes (1987)](https://doi.org/10.1111/j.1467-8640.1989.tb00329.x). The+notation below is that of the original paper. -This module is useful if creating a new instance of interval types that you want to test.+This module is useful if creating a new instance of interval types that you+want to test. -} {- HLINT ignore -}+{-# LANGUAGE ExplicitForAll #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-} -module IntervalAlgebra.RelationProperties- ( IntervalRelationProperties(..)- ) where+module IntervalAlgebra.RelationProperties where -import Data.Maybe (fromJust, isJust, isNothing)-import Data.Set (Set, disjointUnion, fromList,- member)-import Data.Time as DT (Day, NominalDiffTime, UTCTime)+import Data.Maybe (fromJust, isJust, isNothing)+import Data.Set (Set, disjointUnion,+ fromList, member)+import Data.Time as DT (Day, NominalDiffTime,+ UTCTime) import IntervalAlgebra.Arbitrary import IntervalAlgebra.Core-import Test.QuickCheck (Arbitrary (arbitrary), Property,- (===), (==>))+import IntervalAlgebra.IntervalUtilities ((.+.))+import Test.QuickCheck (Arbitrary (arbitrary),+ Property, (===), (==>)) -allIArelations :: (Ord a) => [ComparativePredicateOf1 (Interval a)]+allIArelations :: (SizedIv (Interval a), Ord a) => [ComparativePredicateOf1 (Interval a)] allIArelations = [ equals , meets@@ -46,98 +52,94 @@ , contains ] --- | A collection of properties for the interval algebra. Some of these come from--- figure 2 in [Allen and Hayes (1987)](https://doi.org/10.1111/j.1467-8640.1989.tb00329.x).-class ( IntervalSizeable a b ) => IntervalRelationProperties a b where-- -- | For any two pair of intervals exactly one 'IntervalRelation' should hold- prop_exclusiveRelations:: Interval a -> Interval a -> Property- prop_exclusiveRelations x y =- ( 1 == length (filter id $ map (\r -> r x y) allIArelations)) === True+-- A collection of properties for the interval algebra. Some of these come from+-- figure 2 in [Allen and Hayes+-- (1987)](https://doi.org/10.1111/j.1467-8640.1989.tb00329.x). - -- | Given a set of interval relations and predicate function, test that the- -- predicate between two interval is equivalent to the relation of two intervals- -- being in the set of relations.- prop_predicate_unions :: Ord a =>- Set IntervalRelation- -> ComparativePredicateOf2 (Interval a) (Interval a)- -> Interval a- -> Interval a- -> Property- prop_predicate_unions s pred i0 i1 =- pred i0 i1 === (relate i0 i1 `elem` s)+-- | For any two pair of intervals exactly one 'IntervalRelation' should hold+prop_exclusiveRelations:: (SizedIv (Interval a), Ord a) => Interval a -> Interval a -> Property+prop_exclusiveRelations x y =+ ( 1 == length (filter id $ map (\r -> r x y) allIArelations)) === True - prop_IAbefore :: Interval a -> Interval a -> Property- prop_IAbefore i j =- before i j ==> (i `meets` k) && (k `meets` j)- where k = beginerval (diff (begin j) (end i)) (end i)+-- | Given a set of interval relations and predicate function, test that the+-- predicate between two interval is equivalent to the relation of two intervals+-- being in the set of relations.+prop_predicate_unions :: (SizedIv (Interval a), Ord a) =>+ Set IntervalRelation+ -> ComparativePredicateOf2 (Interval a) (Interval a)+ -> Interval a+ -> Interval a+ -> Property+prop_predicate_unions s pred i0 i1 =+ pred i0 i1 === (relate i0 i1 `elem` s) - prop_IAstarts:: Interval a -> Interval a -> Property- prop_IAstarts i j- | starts i j = (j == fromJust (i .+. k)) === True- | otherwise = starts i j === False- where k = beginerval (diff (end j) (end i)) (end i)+prop_IAbefore :: forall a. (SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => Interval a -> Interval a -> Property+prop_IAbefore i j =+ before i j ==> (i `meets` k) && (k `meets` j)+ where k = safeInterval (end i, begin j) - prop_IAfinishes:: Interval a -> Interval a -> Property- prop_IAfinishes i j- | finishes i j = (j == fromJust ( k .+. i)) === True- | otherwise = finishes i j === False- where k = beginerval (diff (begin i) (begin j)) (begin j)+prop_IAstarts:: (SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => Interval a -> Interval a -> Property+prop_IAstarts i j+ | starts i j = (j == fromJust (i .+. k)) === True+ | otherwise = starts i j === False+ where k = safeInterval (end i, end j) - prop_IAoverlaps:: Interval a -> Interval a -> Property- prop_IAoverlaps i j- | overlaps i j = ((i == fromJust ( k .+. l )) &&- (j == fromJust ( l .+. m ))) === True- | otherwise = overlaps i j === False- where k = beginerval (diff (begin j) (begin i)) (begin i)- l = beginerval (diff (end i) (begin j)) (begin j)- m = beginerval (diff (end j) (end i)) (end i)+prop_IAfinishes:: (SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => Interval a -> Interval a -> Property+prop_IAfinishes i j+ | finishes i j = (j == fromJust ( k .+. i)) === True+ | otherwise = finishes i j === False+ where k = safeInterval (begin j, begin i) - prop_IAduring:: Interval a -> Interval a-> Property- prop_IAduring i j- | during i j = (j == fromJust ( fromJust (k .+. i) .+. l)) === True- | otherwise = during i j === False- where k = beginerval (diff (begin i) (begin j)) (begin j)- l = beginerval (diff (end j) (end i)) (end i)+prop_IAoverlaps:: forall a. (SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => Interval a -> Interval a -> Property+prop_IAoverlaps i j+ | overlaps i j = ((i == fromJust ( k .+. l )) &&+ (j == fromJust ( l .+. m ))) === True+ | otherwise = overlaps i j === False+ where k = safeInterval (begin i, begin j)+ l = safeInterval (begin j, end i)+ m = safeInterval (end i, end j) - prop_disjoint_predicate :: (Ord a) =>- Interval a- -> Interval a- -> Property- prop_disjoint_predicate = prop_predicate_unions disjointRelations disjoint+prop_IAduring:: forall a. (SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => Interval a -> Interval a-> Property+prop_IAduring i j+ | during i j = (j == fromJust ( fromJust (k .+. i) .+. l)) === True+ | otherwise = during i j === False+ where k = safeInterval (begin j, begin i)+ l = safeInterval (end i, end j) - prop_notdisjoint_predicate :: (Ord a) =>- Interval a- -> Interval a- -> Property- prop_notdisjoint_predicate =- prop_predicate_unions (complement disjointRelations) notDisjoint+prop_disjoint_predicate :: (SizedIv (Interval a), Ord a) =>+ Interval a+ -> Interval a+ -> Property+prop_disjoint_predicate = prop_predicate_unions disjointRelations disjoint - prop_concur_predicate :: (Ord a) =>- Interval a- -> Interval a- -> Property- prop_concur_predicate =- prop_predicate_unions (complement disjointRelations) concur+prop_notdisjoint_predicate :: (SizedIv (Interval a), Ord a) =>+ Interval a+ -> Interval a+ -> Property+prop_notdisjoint_predicate =+ prop_predicate_unions (complement disjointRelations) notDisjoint - prop_within_predicate :: (Ord a) =>- Interval a- -> Interval a- -> Property- prop_within_predicate = prop_predicate_unions withinRelations within+prop_concur_predicate :: (SizedIv (Interval a), Ord a) =>+ Interval a+ -> Interval a+ -> Property+prop_concur_predicate =+ prop_predicate_unions (complement disjointRelations) concur - prop_enclosedBy_predicate :: (Ord a) =>- Interval a- -> Interval a- -> Property- prop_enclosedBy_predicate = prop_predicate_unions withinRelations enclosedBy+prop_within_predicate :: (SizedIv (Interval a), Ord a) =>+ Interval a+ -> Interval a+ -> Property+prop_within_predicate = prop_predicate_unions withinRelations within - prop_encloses_predicate :: (Ord a) =>- Interval a- -> Interval a- -> Property- prop_encloses_predicate = prop_predicate_unions (converse withinRelations) encloses+prop_enclosedBy_predicate :: (SizedIv (Interval a), Ord a) =>+ Interval a+ -> Interval a+ -> Property+prop_enclosedBy_predicate = prop_predicate_unions withinRelations enclosedBy -instance IntervalRelationProperties Int Int-instance IntervalRelationProperties Day Integer-instance IntervalRelationProperties UTCTime NominalDiffTime+prop_encloses_predicate :: (SizedIv (Interval a), Ord a) =>+ Interval a+ -> Interval a+ -> Property+prop_encloses_predicate = prop_predicate_unions (converse withinRelations) encloses
test-axioms/AxiomsSpec.hs view
@@ -6,13 +6,14 @@ ( spec ) where -import Data.Time (Day, UTCTime)-import IntervalAlgebra.Axioms (IntervalAxioms (..))-import Test.Hspec (Spec, describe, hspec, it)-import Test.Hspec.QuickCheck (modifyMaxSuccess)-import Test.QuickCheck (Arbitrary (arbitrary), Gen (..),- Property, Testable (property),- generate, quickCheck)+import Data.Time (Day, UTCTime)+import IntervalAlgebra.Arbitrary+import IntervalAlgebra.Axioms+import Test.Hspec (Spec, describe, hspec, it)+import Test.Hspec.QuickCheck (modifyMaxSuccess)+import Test.QuickCheck (Arbitrary (arbitrary), Gen (..),+ Property, Testable (property),+ forAll, generate, quickCheck) testScale :: Int@@ -42,11 +43,11 @@ it "M3" $ property (prop_IAaxiomM3 @Int) it "M3" $ property (prop_IAaxiomM3 @Day)- it "M3" $ property (prop_IAaxiomM3 @UTCTime)+ it "M3" $ forAll genNominalDiffTime (prop_IAaxiomM3 @UTCTime) it "M4" $ property (prop_IAaxiomM4 @Int) it "M4" $ property (prop_IAaxiomM4 @Day)- it "M4" $ property (prop_IAaxiomM4 @UTCTime)+ it "M4" $ forAll genNominalDiffTime (prop_IAaxiomM4 @UTCTime) it "M5" $ property (prop_IAaxiomM5 @Int) it "M5" $ property (prop_IAaxiomM5 @Day)@@ -54,5 +55,5 @@ it "M4.1" $ property (prop_IAaxiomM4_1 @Int) it "M4.1" $ property (prop_IAaxiomM4_1 @Day)- it "M4.1" $ property (prop_IAaxiomM4_1 @UTCTime)+ it "M4.1" $ forAll genNominalDiffTime (prop_IAaxiomM4_1 @UTCTime)
test-relation-properties/RelationPropertiesSpec.hs view
@@ -7,7 +7,7 @@ ) where import Data.Time-import IntervalAlgebra.RelationProperties (IntervalRelationProperties (..))+import IntervalAlgebra.RelationProperties import Test.Hspec (Spec, describe, hspec, it) import Test.Hspec.QuickCheck (modifyMaxSuccess) import Test.QuickCheck
test/IntervalAlgebra/IntervalUtilitiesSpec.hs view
@@ -14,43 +14,22 @@ import qualified Data.Set (null) import Data.Time (Day, UTCTime) import IntervalAlgebra (Interval,- IntervalCombinable (..), IntervalRelation (..),- IntervalSizeable, Intervallic (..),- beginerval, complement,- converse, disjointRelations,- duration, intervalRelations,- moment, predicate,- rangeInterval, safeInterval,- starts,+ SizedIv (..), beginerval,+ complement, converse,+ disjointRelations, duration,+ intervalRelations, moment,+ predicate, rangeInterval,+ safeInterval, starts, strictWithinRelations,- withinRelations)+ withinRelations, (.+.),+ (><)) import IntervalAlgebra.Arbitrary (arbitraryWithRelation) import IntervalAlgebra.IntervalUtilities (clip, combineIntervals, combineIntervalsFromSorted,- durations, filterAfter,- filterBefore, filterConcur,- filterContains,- filterDisjoint,- filterDuring,- filterEnclosedBy,- filterEncloses,- filterEquals,- filterFinishedBy,- filterFinishes, filterMeets,- filterMetBy,- filterNotDisjoint,- filterOverlappedBy,- filterOverlaps,- filterStartedBy,- filterStarts, filterWithin,- foldMeetingSafe,- formMeetingSequence, gaps,- gapsL, gapsWithin,- intersect, nothingIfAll,- nothingIfAny, nothingIfNone,- relationsL)+ durations, gaps, intersect,+ relations) import IntervalAlgebra.PairedInterval (PairedInterval, getPairData, makePairedInterval, trivialize)@@ -66,7 +45,6 @@ orderedList, resize, sublistOf, suchThat, (===), (==>))-import Witherable (Filterable) -- Types for testing @@ -111,7 +89,7 @@ deriving (Eq, Show) -mkEv :: IntervalSizeable a a => (a, a) -> b -> PairedInterval b a+mkEv :: (SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => (a, a) -> b -> PairedInterval b a mkEv i s = makePairedInterval s (safeInterval i) instance Arbitrary State where@@ -137,7 +115,7 @@ -- Testing functions checkSeqStates :: (Intervallic i) => [i Int] -> Bool-checkSeqStates x = (length x > 1) || all (== Meets) (relationsL x)+checkSeqStates x = (length x > 1) || all (== Meets) (relations x) -- Creation functions iv :: Int -> Int -> Interval Int@@ -255,193 +233,49 @@ rels = refRelations ir isEnclose = Data.Set.null $ Data.Set.difference rels (converse strictWithinRelations)- isMom = duration refIv == moment @Int+ isMom = duration refIv == moment @(Interval Int) -- Check that the only relation remaining after applying a function is Before prop_before- :: (Ord a) => ([Interval a] -> [Interval a]) -> [Interval a] -> Property-prop_before f x = relationsL ci === replicate (length ci - 1) Before+ :: (SizedIv (Interval a), Ord a) => ([Interval a] -> [Interval a]) -> [Interval a] -> Property+prop_before f x = relations ci === replicate (length ci - 1) Before where ci = f (sort x) -prop_combineIntervals1 :: (Ord a, Show a, Eq a) => [Interval a] -> Property+prop_combineIntervals1 :: (SizedIv (Interval a), Ord a, Show a, Eq a) => [Interval a] -> Property prop_combineIntervals1 = prop_before combineIntervals -prop_gaps1 :: (Ord a) => [Interval a] -> Property-prop_gaps1 = prop_before gapsL---- In the case that that the input is not null, then--- * all relationsL should be `Meets` after formMeetingSequence-prop_formMeetingSequence0 :: Events Int -> Property-prop_formMeetingSequence0 x =- not (null es)- ==> all (== Meets) (relationsL $ formMeetingSequence (unEvents es))- === True- where es = getEvents x---- In the case that the input has--- * at least one Before relation between consequent pairs--- * AND does not have any empty states------ THEN the number empty states in the output should smaller than or equal to--- the number before relationsL in the output-prop_formMeetingSequence1 :: Events Int -> Property-prop_formMeetingSequence1 x =- (beforeCount > 0 && not- (any (\x -> getPairData x == State [False, False, False])- (unEvents $ getEvents x)- )- )- ==> beforeCount- >= emptyCount- where- res = formMeetingSequence (unEvents $ getEvents x)- beforeCount = lengthWhen (== Before) (relationsL (unEvents $ getEvents x))- emptyCount = lengthWhen (\x -> getPairData x == mempty) res- lengthWhen f = length . filter f---- Check that formMeetingSequence doesn't return an empty list unless input is--- empty.-prop_formMeetingSequence2 :: Events Int -> Property-prop_formMeetingSequence2 x = not (null $ getEvents x) ==> not $ null res- where res = formMeetingSequence (unEvents $ getEvents x)--class ( Ord a ) => FiltrationProperties a where- prop_filtration ::- (Interval a -> [Interval a] -> [Interval a])- -> Set IntervalRelation- -> Interval a- -> [Interval a]- -> Property- prop_filtration fltr s x l =- not (null res) ==> and (fmap (predicate s x) res) === True- where res = fltr x l-- prop_filterOverlaps :: Interval a- -> [Interval a]- -> Property- prop_filterOverlaps = prop_filtration filterOverlaps (fromList [Overlaps])-- prop_filterOverlappedBy :: Interval a- -> [Interval a]- -> Property- prop_filterOverlappedBy = prop_filtration filterOverlappedBy (fromList [OverlappedBy])-- prop_filterBefore :: Interval a- -> [Interval a]- -> Property- prop_filterBefore = prop_filtration filterBefore (fromList [Before])-- prop_filterAfter :: Interval a- -> [Interval a]- -> Property- prop_filterAfter = prop_filtration filterAfter (fromList [After])-- prop_filterStarts :: Interval a- -> [Interval a]- -> Property- prop_filterStarts = prop_filtration filterStarts (fromList [Starts])-- prop_filterStartedBy :: Interval a- -> [Interval a]- -> Property- prop_filterStartedBy = prop_filtration filterStartedBy (fromList [StartedBy])-- prop_filterFinishes :: Interval a- -> [Interval a]- -> Property- prop_filterFinishes = prop_filtration filterFinishes (fromList [Finishes])-- prop_filterFinishedBy :: Interval a- -> [Interval a]- -> Property- prop_filterFinishedBy = prop_filtration filterFinishedBy (fromList [FinishedBy])-- prop_filterMeets :: Interval a- -> [Interval a]- -> Property- prop_filterMeets = prop_filtration filterMeets (fromList [Meets])-- prop_filterMetBy :: Interval a- -> [Interval a]- -> Property- prop_filterMetBy = prop_filtration filterMetBy (fromList [MetBy])-- prop_filterDuring :: Interval a- -> [Interval a]- -> Property- prop_filterDuring = prop_filtration filterDuring (fromList [During])-- prop_filterContains :: Interval a- -> [Interval a]- -> Property- prop_filterContains = prop_filtration filterContains (fromList [Contains])-- prop_filterEquals :: Interval a- -> [Interval a]- -> Property- prop_filterEquals = prop_filtration filterEquals (fromList [Equals])-- prop_filterDisjoint :: Interval a- -> [Interval a]- -> Property- prop_filterDisjoint = prop_filtration filterDisjoint disjointRelations-- prop_filterNotDisjoint :: Interval a- -> [Interval a]- -> Property- prop_filterNotDisjoint = prop_filtration filterNotDisjoint (complement disjointRelations)-- prop_filterWithin :: Interval a- -> [Interval a]- -> Property- prop_filterWithin = prop_filtration filterWithin withinRelations-- prop_filterEnclosedBy :: Interval a- -> [Interval a]- -> Property- prop_filterEnclosedBy = prop_filtration filterEnclosedBy withinRelations-- prop_filterEncloses :: Interval a- -> [Interval a]- -> Property- prop_filterEncloses = prop_filtration filterEncloses (converse withinRelations)-- prop_filterConcur :: Interval a- -> [Interval a]- -> Property- prop_filterConcur = prop_filtration filterConcur (complement disjointRelations)+prop_gaps1 :: (SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) => [Interval a] -> Property+prop_gaps1 = prop_before gaps -instance FiltrationProperties Int+prop_filtration :: (SizedIv (Interval a), Ord a) =>+ (Interval a -> [Interval a] -> [Interval a])+ -> Set IntervalRelation+ -> Interval a+ -> [Interval a]+ -> Property+prop_filtration fltr s x l =+ not (null res) ==> and (fmap (predicate s x) res) === True+ where res = fltr x l prop_clip_intersect- :: (Show a, Ord a, IntervalSizeable a b)+ :: (Show a, Ord a, SizedIv (Interval a), Ord (Moment (Interval a))) => Interval a -> Interval a -> Property prop_clip_intersect x y = clip x y === intersect (min x y) (max x y) --- NOTE: use this instead of prop_filterEquals-prop_small_filterEquals :: SmallInterval -> [SmallInterval] -> Property-prop_small_filterEquals x l =- not (null res) ==> and (fmap (predicate s i) res) === True- where- i = unSmall x- li = map unSmall l- res = filterEquals i li- s = fromList [Equals]- -- RUNNER spec :: Spec spec = do describe "gaps tests" $ modifyMaxSuccess (* 10) $ do it "no gaps in containmentInt and noncontainmentInt"- $ gapsL [containmentInt, noncontainmentInt]+ $ gaps [containmentInt, noncontainmentInt] `shouldBe` []- it "no gaps in containmentInt" $ gapsL [containmentInt] `shouldBe` []+ it "no gaps in containmentInt" $ gaps [containmentInt] `shouldBe` [] it "single gap between containmentInt and anotherInt"- $ gapsL [containmentInt, anotherInt]+ $ gaps [containmentInt, anotherInt] `shouldBe` [gapInt] it "after gaps, only relation should be Before" $ property (prop_gaps1 @Int) @@ -465,90 +299,26 @@ `shouldBe` Just (iv 6 4) it "clip x y === intersect sort x y " $ property (prop_clip_intersect @Int) - describe "relationsL tests" $ do+ describe "relations tests" $ do it- "relationsL [(0, 10), (4, 10), (10, 15), (15, 20)] == [FinishedBy, Meets, Meets]"- $ relationsL [containmentInt, noncontainmentInt, gapInt, anotherInt]+ "relations [(0, 10), (4, 10), (10, 15), (15, 20)] == [FinishedBy, Meets, Meets]"+ $ relations [containmentInt, noncontainmentInt, gapInt, anotherInt] `shouldBe` [FinishedBy, Meets, Meets]- it "relationsL of [] shouldBe []"- $ relationsL ([] :: [Interval Int])+ it "relations of [] shouldBe []"+ $ relations ([] :: [Interval Int]) `shouldBe` []- it "relationsL of singleton shouldBe []"- $ relationsL [containmentInt]+ it "relations of singleton shouldBe []"+ $ relations [containmentInt] `shouldBe` []- it "length of relationsL result should be 1 less then length of input"+ it "length of relations result should be 1 less then length of input" $ property (\x -> not (null x)- ==> length (relationsL x)+ ==> length (relations x) === length (x :: [Interval Int]) - 1 ) - describe "gapsWithin tests" $ do- it "gapsWithin (1, 10) [(0,5), (7,9), (12,15)] should be [(5,7), (9,10)]"- $ gapsWithin (iv 9 1) [iv 5 0, iv 2 7, iv 3 12]- `shouldBe` Just [iv 2 5, iv 1 9]- it "gapsWithin (1, 10) [(-1, 0), (12,15)] should be [(5,7), (9,10)]"- $ gapsWithin (iv 9 1) [iv 1 (-1), iv 3 12]- `shouldBe` Nothing- it "gapsWithin (0, 455) [(0, 730), (731, 762), (763, 793)]"- $ gapsWithin (safeInterval (0 :: Int, 455))- (fmap safeInterval [(0, 730), (731, 762), (763, 793)])- `shouldBe` Just []- it "gapsWithin (1, 10) [] should be []"- $ gapsWithin (iv 9 1) ([] :: [Interval a])- `shouldBe` Nothing-- describe "emptyIf tests" $ do- it "emptyIfNone (starts (3, 5)) [(3,4), (5,6)] should be empty"- $ nothingIfNone (starts (iv 2 3)) [iv 1 3, iv 1 5]- `shouldBe` Nothing- it "emptyIfNone (starts (3, 5)) [(3,6), (5,6)] shoiuld be input"- $ nothingIfNone (starts (iv 2 3)) [iv 3 3, iv 1 5]- `shouldBe` Just [iv 3 3, iv 1 5]-- describe "filtration tests" $ modifyMaxDiscardRatio (* 2) $ do- it "disjoint filter should filter out noncontainment"- $ filterDisjoint containmentInt [noncontainmentInt, anotherInt]- `shouldBe` [anotherInt]- it "notDisjoint filter should keep noncontainment"- $ filterNotDisjoint containmentInt [noncontainmentInt, anotherInt]- `shouldBe` [noncontainmentInt]- it "filterBefore property" $ property (prop_filterBefore @Int)- it "filterAfter property" $ property (prop_filterAfter @Int)- it "filterOverlaps property" $ property (prop_filterOverlaps @Int)- it "filterOverlappedBy property" $ property (prop_filterOverlappedBy @Int)- it "filterStarts property" $ property (prop_filterStarts @Int)- it "filterStartedBy property" $ property (prop_filterStartedBy @Int)- it "filterFinishes property" $ property (prop_filterFinishes @Int)- it "filterFinishedBy property" $ property (prop_filterFinishedBy @Int)- it "filterMeets property" $ property (prop_filterMeets @Int)- it "filterMetBy property" $ property (prop_filterMetBy @Int)- it "filterDuring property" $ property (prop_filterDuring @Int)- it "filterContains property" $ property (prop_filterContains @Int)- it "filterEquals property" $ property prop_small_filterEquals- it "filterDisjoint property" $ property (prop_filterDisjoint @Int)- it "filterNotDisjoint property" $ property (prop_filterNotDisjoint @Int)- it "filterWithin property" $ property (prop_filterWithin @Int)- it "filterConcur property" $ property (prop_filterConcur @Int)- it "filterEncloses property" $ property (prop_filterEncloses @Int)- it "filterEnclosedBy property" $ property (prop_filterEnclosedBy @Int)-- describe "nothingIf unit tests" $ do- it "nothing from nothingIfAll"- $ nothingIfAll (starts (iv 2 3)) [iv 3 3, iv 4 3]- `shouldBe` Nothing- it "something from nothingIfAll"- $ nothingIfAll (starts (iv 2 3)) [iv 3 0, iv 4 3]- `shouldBe` Just [iv 3 0, iv 4 3]- it "nothing from nothingIfAny"- $ nothingIfAny (starts (iv 2 3)) [iv 3 3, iv 1 5]- `shouldBe` Nothing- it "something from nothingIfAny"- $ nothingIfAny (starts (iv 2 3)) [iv 3 1, iv 1 5]- `shouldBe` Just [iv 3 1, iv 1 5]- describe "intersection tests" $ do it "intersection of (0, 2) (2, 4) should be Nothing" $ intersect (iv 2 0) (iv 2 2)@@ -637,47 +407,3 @@ $ property (prop_combineIntervals1 @Day) it "after combining, only relation should be Before" $ property (prop_combineIntervals1 @UTCTime)-- describe "foldMeets unit tests" $ do- it "foldMeetingSafe meets1"- $ foldMeetingSafe (trivialize meets1)- `shouldBe` trivialize [iv 4 0]- it "foldMeetingSafe meets2"- $ foldMeetingSafe (trivialize meets2)- `shouldBe` trivialize [iv 16 0]- it "foldMeetingSafe meets3" $ foldMeetingSafe meets3 `shouldBe` meets3eq-- describe "formMeetingSequence unit tests" $ do- it "formMeetingSequence unit test 0"- $ formMeetingSequence (unEvents c0in)- `shouldBe` unEvents c0out- it "formMeetingSequence unit test 1"- $ formMeetingSequence (unEvents c1in)- `shouldBe` unEvents c1out- it "formMeetingSequence unit test 2"- $ formMeetingSequence (unEvents c2in)- `shouldBe` unEvents c2out- it "formMeetingSequence unit test 3"- $ formMeetingSequence (unEvents c3in)- `shouldBe` unEvents c3out- it "formMeetingSequence unit test 4"- $ formMeetingSequence (unEvents c4in)- `shouldBe` unEvents c4out- it "formMeetingSequence unit test 5"- $ formMeetingSequence c5in- `shouldBe` c5out- it "formMeetingSequence unit test 6"- $ formMeetingSequence ([] :: [PairedInterval State Int])- `shouldBe` []-- describe "formMeetingSequence property tests" $ modifyMaxSuccess (* 2) $ do- it "prop_formMeetingSequence0" $ property prop_formMeetingSequence0- -- 2022-05-18 - BS- -- Commmenting out this test as the execution of the test suite blows up- -- when this property check is included.- -- TODO: consider whether this check is worthwhile.- -- it "prop_formMeetingSequence1" $ property prop_formMeetingSequence1- it "prop_formMeetingSequence2" $ property prop_formMeetingSequence2-- -- describe "arbitraryWithRelation property tests" $ do- -- it "prop_withRelation_tautology" $ property prop_withRelation_tautology
test/IntervalAlgebra/PairedIntervalSpec.hs view
@@ -5,9 +5,9 @@ import Data.Bool import Data.Time (Day (ModifiedJulianDay), fromGregorian)-import IntervalAlgebra (IntervalCombinable (..),- IntervalSizeable (duration),- before, beginerval, equals,+import IntervalAlgebra (Intervallic (..),+ SizedIv (duration), before,+ beginerval, equals, toEnumInterval) import IntervalAlgebra.PairedInterval (Empty (..), PairedInterval, intervals, makePairedInterval)@@ -42,20 +42,12 @@ describe "tests on paired intervals" $ do it "t1 is before t2" $ (t1 `before` t2) `shouldBe` True- it "duration of t1 is 5" $ duration t1 `shouldBe` 5+ it "duration of t1 is 5" $ duration (getInterval t1) `shouldBe` 5 it "t1 is equal to t3" $ (t1 `equals` t3) `shouldBe` True it "t1 is LT t2" $ (t1 < t2) `shouldBe` True it "getintervals [t1, t2, t3]" $ intervals [t1, t2, t3] `shouldBe` [beginerval 5 0, beginerval 4 6, beginerval 5 0]-- describe "IntervalCombinable tests" $ do- it "" $ (t1 >< t3) `shouldBe` Nothing- it "" $ (t1 >< mkTestPr "hello" 1 6) `shouldBe` Just (mkTestPr "" 1 5)- it ""- $ (t1 <+> mkTestPr "hello" 1 6)- `shouldBe` [t1, mkTestPr "hello" 1 6]- it "" $ (t1 <+> mkTestPr "hello" 5 3) `shouldBe` [mkTestPr "hihello" 8 0] describe "tests on empty" $ do it "show empty" $ show Empty `shouldBe` "Empty"
test/IntervalAlgebraSpec.hs view
@@ -1,11 +1,14 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-} module IntervalAlgebraSpec ( spec ) where +import Control.Applicative (liftA2) import Data.Either (isRight) import Data.Fixed (Pico) import Data.Maybe (fromJust, isJust, isNothing)@@ -18,46 +21,87 @@ secondsToDiffTime) import GHC.Real (Rational (..), Real (..)) import IntervalAlgebra as IA-import IntervalAlgebra.Arbitrary ()+import IntervalAlgebra.Arbitrary (genDay) import Test.Hspec (Spec, describe, hspec, it, shouldBe) import Test.Hspec.QuickCheck (modifyMaxDiscardRatio, modifyMaxSuccess) import Test.QuickCheck (Arbitrary (arbitrary), Gen (..), Property, Testable (property),- generate, quickCheck, (===), (==>))+ forAll, generate, quickCheck,+ (.&&.), (===), (==>)) +-- Convenience aliases+interval :: (iv ~ Interval a, SizedIv iv, Ord a, Ord (Moment iv)) => a -> a -> iv+interval = curry safeInterval+ mkIntrvl :: Int -> Int -> Interval Int mkIntrvl = beginerval prop_expandl_end- :: (IntervalSizeable a b, Show a) => b -> Interval a -> Property+ :: (SizedIv (Interval a), Show a, Eq a) => Moment (Interval a) -> Interval a -> Property prop_expandl_end d i = end (expandl d i) === end i prop_expandr_begin- :: (IntervalSizeable a b, Show a) => b -> Interval a -> Property+ :: (SizedIv (Interval a), Show a, Eq a) => Moment (Interval a) -> Interval a -> Property prop_expandr_begin d i = begin (expandr d i) === begin i -- | The relation between x and z should be an element of the set of the -- composed relations between x y and between y z.-prop_compose :: Ord a => Interval a -> Interval a -> Interval a -> Property+prop_compose :: (Ord a, SizedIv (Interval a)) => Interval a -> Interval a -> Interval a -> Property prop_compose x y z = member (relate x z) (compose (relate x y) (relate y z)) === True -- | If two intervals are disjoint and not meeting, then there should be a gap -- between the two (by ><), after the intervals are sorted.-prop_combinable_gap_exists :: Ord a => Interval a -> Interval a -> Property+prop_combinable_gap_exists :: (Ord a, SizedIv (Interval a), Ord (Moment (Interval a))) => Interval a -> Interval a -> Property prop_combinable_gap_exists x y = (before <|> after) x y ==> isJust ((><) (min x y) (max x y)) -- | If two intervals are not disjoint or meeting, then there should be NO gap -- between the two (by ><), after the intervals are sorted.-prop_combinable_nogap_exists :: Ord a => Interval a -> Interval a -> Property+prop_combinable_nogap_exists :: (Ord a, SizedIv (Interval a), Ord (Moment (Interval a))) => Interval a -> Interval a -> Property prop_combinable_nogap_exists x y = (predicate $ complement $ fromList [Before, After]) x y ==> isNothing ((><) (min x y) (max x y)) + {- Properties of SizedIv -} +-- When @Point iv@ is @Ord@,+--+-- prop> ivBegin i < ivEnd i+prop_validIv :: forall a. (SizedIv (Interval a), Ord a, Show a, Ord (Moment (Interval a))) => a -> a -> Property+prop_validIv b e = (ivBegin i < ivEnd i) === True where i = safeInterval (b, e)++-- When @iv@ is @Eq@,+--+-- prop> interval (ivBegin i) (ivEnd i) == i+prop_validIv' :: forall a. (SizedIv (Interval a), Ord a, Show a, Ord (Moment (Interval a))) => a -> a -> Property+prop_validIv' b e = interval (ivBegin i) (ivEnd i) === i where i = interval b e++-- When @iv@ is @Ord@, for all @i == interval b e@,+--+-- prop> ivExpandr d i >= i+-- prop> ivExpandl d i <= i+prop_ivExpandr, prop_ivExpandl :: forall a. (SizedIv (Interval a), Ord (Interval a), Show (Interval a)) => Moment (Interval a) -> Interval a -> Property+prop_ivExpandr d i = (ivExpandr d i >= i) === True+prop_ivExpandl d i = (ivExpandl d i <= i) === True++-- When @Moment iv@ is @Ord@,+--+-- prop> duration (interval b e) >= moment+-- prop> duration (ivExpandr d i) >= duration i+-- prop> duration (ivExpandl d i) >= duration i+prop_duration :: forall a. (SizedIv (Interval a), Ord a, Ord (Moment (Interval a)), Show (Interval a)) => Moment (Interval a) -> a -> a -> Property+prop_duration d b e = p1 .&&. p2 .&&. p3+ where i = interval b e+ m = moment @(Interval a)+ dur = duration i+ p1 = (dur >= m) === True+ p2 = (duration (ivExpandr d i) >= dur) === True+ p3 = (duration (ivExpandl d i) >= dur) === True++{- Specs -} spec :: Spec spec = do describe "Basic Interval unit tests of typeclass and creation methods" $ do@@ -80,10 +124,10 @@ it "beginervalMoment duration is moment" $ duration (beginervalMoment (-13 :: Int))- `shouldBe` (moment @Int)+ `shouldBe` (moment @(Interval Int)) it "endervalMoment duration is moment" $ duration (endervalMoment (26 :: Int))- `shouldBe` (moment @Int)+ `shouldBe` (moment @(Interval Int)) it "parsing fails on bad inputs" $ parseInterval 10 0 `shouldBe` Left (IA.ParseErrorInterval "0<=10")@@ -180,8 +224,8 @@ $ intersection (fromList [Before]) (fromList [After]) `shouldBe` fromList [] - describe "IntervalSizeable tests" $ do- it "moment is 1" $ moment @Int `shouldBe` 1+ describe "SizedIv tests" $ do+ it "moment is 1" $ moment @(Interval Int) `shouldBe` 1 it "expandl doesn't change end" $ property (prop_expandl_end @Int) it "expandr doesn't change begin" $ property (prop_expandr_begin @Int) it "expand 0 5 Interval (0, 1) should be Interval (0, 6)"@@ -200,7 +244,7 @@ $ expand 5 (-5) (beginerval (1 :: Int) (0 :: Int)) `shouldBe` beginerval (6 :: Int) (-5 :: Int) it "expand moment 0 Interval (0, 1) should be Interval (-1, 1)"- $ expand (moment @Int) 0 (beginerval (1 :: Int) (0 :: Int))+ $ expand (moment @(Interval Int)) 0 (beginerval (1 :: Int) (0 :: Int)) `shouldBe` beginerval (2 :: Int) (-1 :: Int) it "beginerval 2 10 should be Interval (10, 12)"@@ -230,21 +274,30 @@ $ shiftFromEnd (beginerval 2 (4 :: Int)) (beginerval 2 10) `shouldBe` beginerval 2 4 -- (4, 6) - it "shiftFromBegin can convert Interval Day to Interval Integer"- $ shiftFromBegin (beginerval 2 (fromGregorian 2001 1 1))- (beginerval 2 (fromGregorian 2001 1 10))- `shouldBe` beginerval 2 9 -- (9, 11)-- it "shiftFromEnd can convert Interval Day to Interval Integer"- $ shiftFromEnd (beginerval 2 (fromGregorian 2001 1 1))- (beginerval 2 (fromGregorian 2001 1 10))- `shouldBe` beginerval 2 7 -- (7, 9)- it "momentize works" $ momentize (beginerval 2 (fromGregorian 2001 1 1)) `shouldBe` beginerval 1 (fromGregorian 2001 1 1) + describe "SizedIv properties for Interval Int" $ do+ it "validIv" $ property (prop_validIv @Int)+ it "validIv'" $ property (prop_validIv' @Int)+ it "ivExpandr" $ property (prop_ivExpandr @Int)+ it "ivExpandl" $ property (prop_ivExpandl @Int)+ it "duration" $ property (prop_duration @Int) + describe "SizedIv properties for Interval Day" $ do+ it "validIv" $ forAll (liftA2 (,) genDay genDay) (uncurry prop_validIv)+ it "validIv'" $ forAll (liftA2 (,) genDay genDay) (uncurry prop_validIv')+ it "ivExpandr" $ property (prop_ivExpandr @Day)+ it "ivExpandl" $ property (prop_ivExpandl @Day)+ it "duration" $ forAll (do+ m <- arbitrary+ b <- genDay+ e <- genDay+ pure (m, b, e)+ ) (\(m, b, e) -> prop_duration m b e)++ describe "Intervallic tests" $ -- modifyMaxSuccess (*10000) $ do@@ -267,7 +320,7 @@ (mkIntrvl 2 3) it "prop_compose holds" $ property (prop_compose @Int) - describe "IntervalCombinable tests" $ do+ describe "(.+.) tests" $ do it "join non-meeting intervals is Nothing" $ beginerval 2 (0 :: Int) .+. beginerval 6 5@@ -311,3 +364,4 @@ it "concur matches notDisjoint" $ concur (beginerval 1 0) (beginerval 10 (0 :: Int)) `shouldBe` notDisjoint (beginerval 1 0) (beginerval 10 (0 :: Int))+
tutorial/TutorialMain.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-}@@ -18,12 +19,22 @@ import Data.Time ( Day , UTCTime(..) , addDays+ , diffDays , fromGregorian , secondsToDiffTime ) import Witch ( into ) -- end::import-declarations[] +-- tag::safeInterval-alias[]+interval ::+ (SizedIv (Interval a), Ord a, Ord (Moment (Interval a))) =>+ a ->+ a ->+ Interval a+interval = curry safeInterval+-- end::safeInterval-alias[]+ main :: IO () main = do @@ -182,9 +193,9 @@ print $ getInterval pairListstringInteger putStr- "\nprint $ setInterval pairListstringInteger (safeInterval (4, 9) :: Interval Integer)\n---> "+ "\nprint $ setInterval pairListstringInteger (interval 4 9 :: Interval Integer)\n---> " print $ setInterval pairListstringInteger- (safeInterval (4, 9) :: Interval Integer)+ (interval 4 9 :: Interval Integer) putStr "\nprint $ intervals [pairListstringInteger, pairListstringInteger]\n---> "@@ -239,29 +250,38 @@ putStrLn "-- end::intervallic-interval-instance-print[]" - -- IntervalSizeable instance examples ------------------------------------------------------+ -- SizedIv instance examples ------------------------------------------------------ putStrLn "-- tag::intervalsizeable-instance-print[]" putStr "\nprint ivDay\n---> " print ivDay - putStr "\nprint $ moment @Day\n---> "- print $ moment @Day+ putStr "\nprint $ moment @(Interval Day)\n---> "+ print $ moment @(Interval Day) + putStr "\nprint $ interval (ivBegin ivDay) (ivEnd ivDay)\n---> "+ print $ interval (ivBegin ivDay) (ivEnd ivDay)++ putStr "\nprint $ interval (ivEnd ivDay) (ivBegin ivDay)\n---> "+ print $ interval (ivEnd ivDay) (ivBegin ivDay)+ putStr "\nprint $ duration ivDay\n---> " print $ duration ivDay - putStr "\nprint $ add 15 (begin ivDay)\n---> "- print $ add 15 (begin ivDay)+ putStr "\nprint $ ivExpandr 15 ivDay\n---> "+ print $ ivExpandr 15 ivDay - putStr "\nprint $ diff (add 15 (begin ivDay)) (begin ivDay)\n---> "- print $ diff (add 15 (begin ivDay)) (begin ivDay)+ putStr "\nprint $ ivExpandl 0 ivDay\n---> "+ print $ ivExpandl 0 ivDay + putStr "\nprint $ ivExpandl 10 ivDay\n---> "+ print $ ivExpandl 10 ivDay+ putStrLn "-- end::intervalsizeable-instance-print[]" - -- IntervalCombineable Interval examples -------------------------------------+ -- "Combining" utility examples ------------------------------------- putStrLn "-- tag::intervalcombinable-interval-print[]" @@ -286,7 +306,7 @@ putStrLn "-- end::intervalcombinable-interval-print[]" - -- IntervalCombineable PairedInterval examples -------------------------------+ -- "Combining" utilities for PairedInterval examples ------------------------------- putStrLn "-- tag::intervalcombinable-pairedinterval-print[]" @@ -375,21 +395,12 @@ putStr "\nprint [iv2to4, iv5to8]\n---> " print [iv2to4, iv5to8] - putStr "\nprint ivDay\n---> "- print ivDay- putStr "\nprint $ shiftFromBegin iv2to4 iv5to8\n---> " print $ shiftFromBegin iv2to4 iv5to8 - putStr "\nprint $ shiftFromBegin ivDay ivDay\n---> "- print $ shiftFromBegin ivDay ivDay- putStr "\nprint $ shiftFromEnd iv2to4 iv5to8\n---> " print $ shiftFromEnd iv2to4 iv5to8 - putStr "\nprint $ shiftFromEnd ivDay ivDay\n---> "- print $ shiftFromEnd ivDay ivDay- putStrLn "-- end::shifting-intervals-print[]" @@ -509,34 +520,33 @@ -- end::parseinterval-examples[] --- tag::safeinterval-examples[]+-- tag::interval-examples[] ivInteger :: Interval Integer-ivInteger = safeInterval (2, 6)+ivInteger = interval 2 6 ivMinDurInteger :: Interval Integer-ivMinDurInteger = safeInterval (2, 2)+ivMinDurInteger = interval 2 2 ivDay :: Interval Day-ivDay = safeInterval (fromGregorian 1967 01 18, fromGregorian 1967 01 24)+ivDay = interval (fromGregorian 1967 01 18) (fromGregorian 1967 01 24) ivUTC :: Interval UTCTime-ivUTC = safeInterval- ( UTCTime (fromGregorian 1967 01 18) (secondsToDiffTime 32400)- , UTCTime (fromGregorian 1967 01 18) (secondsToDiffTime 33200)- )--- end::safeinterval-examples[]+ivUTC = interval+ (UTCTime (fromGregorian 1967 01 18) (secondsToDiffTime 32400))+ (UTCTime (fromGregorian 1967 01 18) (secondsToDiffTime 33200))+-- end::interval-examples[] -- tag::ivXtoY-examples[] ---------------- iv0to2, iv2to4, iv2to5, iv4to5, iv5to8, iv6to8, iv3to6 :: Interval Integer-iv0to2 = safeInterval (0, 2)-iv2to4 = safeInterval (2, 4)-iv2to5 = safeInterval (2, 5)-iv3to6 = safeInterval (3, 6)-iv4to5 = safeInterval (4, 5)-iv5to8 = safeInterval (5, 8)-iv6to8 = safeInterval (6, 8)+iv0to2 = interval 0 2+iv2to4 = interval 2 4+iv2to5 = interval 2 5+iv3to6 = interval 3 6+iv4to5 = interval 4 5+iv5to8 = interval 5 8+iv6to8 = interval 6 8 -- end::ivXtoY-examples[] @@ -586,14 +596,14 @@ -- We can construct a predicate function from a 'Set IntervalRelation' endedPrior- :: (Ord a, Intervallic i0, Intervallic i1)+ :: (SizedIv (Interval a), Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a) endedPrior = predicate (fromList [Before, Meets]) -- We can also construct a predicate function directly from a list of predicate -- functions endedPrior'- :: (Ord a, Intervallic i0, Intervallic i1)+ :: (SizedIv (Interval a), Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a) endedPrior' = unionPredicates [before, meets] @@ -601,7 +611,7 @@ -- using the <|> operator. If we had multiple predicates we could use e.g.: -- p1 <|> p2 <|> p3 endedPrior''- :: (Ord a, Intervallic i0, Intervallic i1)+ :: (SizedIv (Interval a), Ord a, Intervallic i0, Intervallic i1) => ComparativePredicateOf2 (i0 a) (i1 a) endedPrior'' = before <|> meets @@ -799,21 +809,21 @@ -- Calculate the difference between the start endpoint of the first Intervallic -- and the start endpoint of the second Intervallic calcDiff- :: (IntervalSizeable a b, Intervallic i0, Intervallic i1)+ :: (SizedIv (Interval a), Num a, Intervallic i0, Intervallic i1) => Maybe (i0 a) -> Maybe (i1 a)- -> Maybe b-calcDiff (Just y) (Just x) = Just $ diff (begin y) (end x)+ -> Maybe a+calcDiff (Just y) (Just x) = Just $ (-) (begin y) (end x) calcDiff _ _ = Nothing -- Calculate the difference between the end endpoint of the first Intervallic -- and the start endpoint of the second Intervallic calcAtRisk- :: (IntervalSizeable a b, Intervallic i0, Intervallic i1)+ :: (SizedIv (Interval a), Num a, Intervallic i0, Intervallic i1) => Maybe (i0 a) -> Maybe (i1 a)- -> Maybe b-calcAtRisk (Just y) (Just x) = Just $ diff (end y) (end x)+ -> Maybe a+calcAtRisk (Just y) (Just x) = Just $ (-) (end y) (end x) calcAtRisk _ _ = Nothing -- end::extended-example-1-processing-functions[]