module ProjectM36.DataTypes.Interval where
import ProjectM36.AtomFunctionBody
import ProjectM36.Base
import ProjectM36.AtomType
import ProjectM36.DataTypes.Primitive
import ProjectM36.AtomFunctionError
import qualified Data.HashSet as HS
supportsInterval :: AtomType -> Bool
supportsInterval typ = case typ of
IntAtomType -> True
IntegerAtomType -> True
DoubleAtomType -> True
TextAtomType -> False
DayAtomType -> True
DateTimeAtomType -> True
ByteStringAtomType -> False
BoolAtomType -> False
IntervalAtomType _ -> False
RelationAtomType _ -> False
ConstructedAtomType _ _ -> False
TypeVariableType _ -> False
supportsOrdering :: AtomType -> Bool
supportsOrdering typ = case typ of
IntAtomType -> True
IntegerAtomType -> True
DoubleAtomType -> True
TextAtomType -> True
DayAtomType -> True
DateTimeAtomType -> True
ByteStringAtomType -> False
BoolAtomType -> False
IntervalAtomType _ -> False
RelationAtomType _ -> False
ConstructedAtomType _ _ -> False
TypeVariableType _ -> False
atomCompare :: Atom -> Atom -> Either AtomFunctionError Ordering
atomCompare a1 a2 = let aType = atomTypeForAtom a1
go a b = Right (compare a b)
typError = Left (AtomTypeDoesNotSupportOrderingError (prettyAtomType aType)) in
if atomTypeForAtom a1 /= atomTypeForAtom a2 then
Left AtomFunctionTypeMismatchError
else if not (supportsOrdering aType) then
typError
else
case (a1, a2) of
(IntegerAtom a, IntegerAtom b) -> go a b
(IntAtom a, IntAtom b) -> go a b
(DoubleAtom a, DoubleAtom b) -> go a b
(TextAtom a, TextAtom b) -> go a b
(DayAtom a, DayAtom b) -> go a b
(DateTimeAtom a, DateTimeAtom b) -> go a b
_ -> typError
createInterval :: Atom -> Atom -> OpenInterval -> OpenInterval -> Either AtomFunctionError Atom
createInterval atom1 atom2 bopen eopen = do
cmp <- atomCompare atom1 atom2
case cmp of
GT -> Left InvalidIntervalOrderingError
EQ -> if bopen || eopen then
Left InvalidIntervalBoundariesError
else
Right valid
LT -> Right valid
where valid = IntervalAtom atom1 atom2 bopen eopen
intervalAtomFunctions :: AtomFunctions
intervalAtomFunctions = HS.fromList [
AtomFunction { atomFuncName = "interval",
atomFuncType = [TypeVariableType "a",
TypeVariableType "a",
BoolAtomType,
BoolAtomType,
IntervalAtomType (TypeVariableType "a")],
atomFuncBody = compiledAtomFunctionBody $ \(atom1:atom2:BoolAtom bopen:BoolAtom eopen:_) -> do
let aType = atomTypeForAtom atom1
if supportsInterval aType then
createInterval atom1 atom2 bopen eopen
else
Left (AtomTypeDoesNotSupportIntervalError (prettyAtomType aType))
},
AtomFunction {
atomFuncName = "interval_overlaps",
atomFuncType = [IntervalAtomType (TypeVariableType "a"),
IntervalAtomType (TypeVariableType "a"),
BoolAtomType],
atomFuncBody = compiledAtomFunctionBody $ \(i1@IntervalAtom{}:i2@IntervalAtom{}:_) -> do
res <- intervalOverlaps i1 i2
pure (BoolAtom res)
}]
intervalOverlaps :: Atom -> Atom -> Either AtomFunctionError Bool
intervalOverlaps (IntervalAtom i1start i1end i1startopen i1endopen) (IntervalAtom i2start i2end i2startopen i2endopen) = do
cmp1 <- atomCompare i1start i2end
cmp2 <- atomCompare i2start i1end
let startcmp = if i1startopen || i2endopen then oplt else oplte
endcmp = if i2startopen || i1endopen then oplt else oplte
oplte op = op == LT || op == EQ
oplt op = op == LT
pure (startcmp cmp1 && endcmp cmp2)
intervalOverlaps _ _ = Left AtomFunctionTypeMismatchError