interval-algebra-2.2.0: src/IntervalAlgebra/Axioms.hs
{- HLINT ignore -}
{-|
Module : Interval Algebra Axioms
Description : Properties of Intervals
Copyright : (c) NoviSci, Inc 2020-2022
TargetRWE, 2023
License : BSD3
Maintainer : bsaul@novisci.com 2020-2022, bbrown@targetrwe.com 2023
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 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 IntervalAlgebra.Arbitrary
import IntervalAlgebra.Core
import IntervalAlgebra.IntervalUtilities ((.+.))
import Test.QuickCheck (Arbitrary (arbitrary),
Property, (===), (==>))
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 <- 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)
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 <- genNominalDiffTime
m2set x a b <$> genNominalDiffTime
-- | 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 <- genNominalDiffTime
m5set x a <$> genNominalDiffTime
-- Axiom functions
-- | 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:
\[
\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:
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 :: (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.
\[
\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.
\[
\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:
\[
\forall i \exists j,k s.t. j:i:k
\]
-}
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.
\[
\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 :: (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.
\[
\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:
\[
\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