{-| Module : Interval Algebra Axioms Description : Properties of Intervals Copyright : (c) NoviSci, Inc 2020 License : BSD3 Maintainer : bsaul@novisci.com 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 is useful if creating a new instance of interval types that you want to test. -} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} module IntervalAlgebra.Axioms ( IntervalAxioms(..) , M1set(..) , M2set(..) , M5set(..) ) where import Test.QuickCheck ( (===) , (==>) , Property , Arbitrary (arbitrary) ) import Data.Either ( isRight ) import Data.Maybe ( fromJust, isJust, isNothing ) import Data.Time as DT ( Day(..) , UTCTime(..) , DiffTime , NominalDiffTime ) import Data.Set ( Set , member , disjointUnion , fromList ) import IntervalAlgebra.Core import IntervalAlgebra.Arbitrary xor :: Bool -> Bool -> Bool xor a b = a /= b -- | Internal function for converting a number to a strictly positive value. makePos :: (Ord b, Num b) => b -> b makePos x | x == 0 = x + 1 | x < 0 = negate x | 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) instance Arbitrary (M1set Int) where arbitrary = do x <- arbitrary a <- arbitrary b <- arbitrary m1set x a b <$> arbitrary instance Arbitrary (M1set DT.Day) where arbitrary = do x <- arbitrary a <- arbitrary b <- arbitrary m1set x a b <$> arbitrary instance Arbitrary (M1set DT.UTCTime) where arbitrary = do x <- arbitrary a <- arbitrary b <- arbitrary m1set x a b <$> arbitrary -- | 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) instance Arbitrary (M2set Int) where arbitrary = do x <- arbitrary a <- arbitrary b <- arbitrary m2set x a b <$> arbitrary instance Arbitrary (M2set DT.Day) where arbitrary = do x <- arbitrary a <- arbitrary b <- arbitrary m2set x a b <$> arbitrary instance Arbitrary (M2set DT.UTCTime) where arbitrary = do x <- arbitrary a <- arbitrary b <- arbitrary m2set x a b <$> arbitrary -- | A set used for testing M5. data M5set a = M5set { m51 :: Interval a , m52 :: Interval a } deriving (Show) instance Arbitrary (M5set Int) where arbitrary = do x <- arbitrary a <- arbitrary m5set x a <$> arbitrary instance Arbitrary (M5set DT.Day) where arbitrary = do x <- arbitrary a <- arbitrary m5set x a <$> arbitrary instance Arbitrary (M5set DT.UTCTime) where arbitrary = do x <- arbitrary a <- arbitrary m5set x a <$> arbitrary -- | = "An Axiomatization of Interval Time". class ( IntervalSizeable a b ) => IntervalAxioms a b where -- | 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) {- | == 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: \[ \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 -- | 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 {- | == Axiom M2 If period i meets period j and period k meets l, then exactly one of the following holds: 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. 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) {- | == Axiom ML1 An interval cannot meet itself. \[ \forall i \lnot i:i \] -} prop_IAaxiomML1 :: (Ord a) => Interval a -> Property prop_IAaxiomML1 x = not (x `meets` x) === True {- | == Axiom ML2 If i meets j then j does not meet i. \[ \forall i,j i:j \implies \lnot j: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 {- | == Axiom M3 Time does not start or stop: \[ \forall i \exists j,k s.t. j:i:k \] -} 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) {- | 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. \[ \forall i,j i:j \implies (\exists k,m,n s.t m:i:j:n \text { & } m:k:n) \] -} 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) -- | 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 {- | == Axiom M5 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 :: (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 {- | == Axiom M4.1 Ordered unions: \[ \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 instance IntervalAxioms Int Int instance IntervalAxioms Day Integer instance IntervalAxioms UTCTime NominalDiffTime