interval-algebra-1.4.0: src/IntervalAlgebra/Axioms.hs
{- HLINT ignore -}
{-|
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 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
)
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