packages feed

allen-0.1.0.0: src/Data/Allen/Relation.hs

-- |
-- Module      : Data.Allen.Relation
-- Description : Functions for working with Allen's interval algebra relations.
-- Maintainer  : Archaversine 
--
-- This module provides functions for working with relations. Note that almost 
-- all exposed functions only work with relation bitsets. This is done mainly 
-- to optimize the speed in calculations involving relations. 
--
-- The 'RelationBits' type is a synonym for a 16 bit unsigned integer. Note that 
-- since Allen's interval algebra only defines 13 relations, the remaining 3 bits 
-- are unused. So the bit representation of every possible relation looks like 
-- this: 
--
-- @ 
-- 0b0001111111111111
-- @ 
--
-- Modifying the extra 3 bits will not affect the result of any calculations.
-- To view in exact detail how a `Relation` converted to a bit representation, 
-- see the `toBits` function.

module Data.Allen.Relation ( converse
                           , testRelation
                           , testRelationSet 
                           , testRelationBits
                           , composeSingle
                           , compose
                           , bitsFromString
                           ) where

import Data.Allen.Types
import Data.Bits

import qualified Data.Map.Strict as Map
import qualified Data.Vector.Unboxed as U

-- | Lookup table for converse function.
converseLookup :: [(RelationBits, RelationBits)]
converseLookup = zip bits (reverse bits)
    where bits = map toBits allRelations

-- | Return the converse of a Relation bitset.
converse :: RelationBits -> RelationBits 
converse 0 = 0
converse x = relationUnion $ map func [0 .. fromEnum (maxBound :: Relation)]
    where func i | testBit x i = snd $ head $ filter ((== bit i) . fst) converseLookup
                 | otherwise = 0

-- | Return if a relation exists between two intervals.
testRelation :: Relation -> IntervalID -> IntervalID -> Allen Bool
testRelation r id1 id2 = do 
    relations <- Map.findWithDefault 0 id2 . intervalRelations <$> fromID id1 
    return $ toBits r .&. relations /= 0

-- | Return if all relations in a set exist between two intervals. 
--
-- IF the set of relations between interval @a@ and interval @b@ is @full@, 
-- then the function will always return @True@.
testRelationSet :: [Relation] -> IntervalID -> IntervalID -> Allen Bool 
testRelationSet r = testRelationBits (relationUnion $ map toBits r)

-- | Return if all relations in a set exist between two intervals.
--  
-- If the set of relations between interval @a@ and interval @b@ is @full@, 
-- then the function will always return @True@.
testRelationBits :: RelationBits -> IntervalID -> IntervalID -> Allen Bool
testRelationBits r id1 id2 = do 
    relations <- Map.findWithDefault 0 id2 . intervalRelations <$> fromID id1 
    return $ r .&. relations >= r

-- | Valid Chars: pmoFDseSdfoMP.
relationFromChar :: Char -> Relation
relationFromChar x = case x of 
    'p' -> Precedes 
    'm' -> Meets 
    'o' -> Overlaps 
    'F' -> FinishedBy 
    'D' -> Contains 
    's' -> Starts 
    'e' -> Equals 
    'S' -> StartedBy 
    'd' -> During 
    'f' -> Finishes 
    'O' -> OverlappedBy 
    'M' -> MetBy 
    'P' -> PrecededBy 
    _   -> error $ "relationFromChar: invalid relation " <> [x]


-- | Given a string, return the bit representation of the set of relations.
-- Valid characters: pmoFDseSdfoMP.
--
-- You may also use @full@ to represent all relations, or @concur@ to represent
-- all relations excluding Precedes and PrecededBy.
--
-- Example:
-- 
-- @
-- let x = 'bitsFromString' "pms"    -- [Precedes, Meets, StartedBy]
--     y = 'bitsFromString' "full"   -- [Precedes .. PrecededBy]
--     z = 'bitsFromString' "concur" -- [Overlaps .. OverlappedBy]
-- @
bitsFromString :: String -> RelationBits
bitsFromString x | x == "full"   = rBits allRelations 
                 | x == "concur" = rBits [Overlaps .. OverlappedBy]
                 | otherwise = rBits $ map relationFromChar x
    where rBits = relationUnion . map toBits

-- Table referenced from here: https://www.ics.uci.edu/~alspaugh/cls/shr/allen.html
composeLookup :: U.Vector RelationBits
composeLookup = U.fromList $ map bitsFromString table 
--                |    p   |    m   |   o     |  F     |  D     |  s     |  e |   S    |     d   |    f   |     O   |    M   |    P
-- ---------------+--------+--------+---------+--------+--------+--------+----+--------+---------+--------+---------+--------+-------------
    where table = [     "p",     "p",      "p",     "p",     "p",     "p", "p",     "p",  "pmosd", "pmosd",  "pmosd", "pmosd",  "full" -- p
                  ,     "p",     "p",      "p",     "p",     "p",     "m", "m",     "m",    "osd",   "osd",    "osd",   "Fef", "DSOMP" -- m
                  ,     "p",     "p",    "pmo",   "pmo", "pmoFD",     "o", "o",   "oFD",    "osd",   "osd", "concur",   "DSO", "DSOMP" -- o
                  ,     "p",     "m",      "o",     "F",     "D",     "o", "F",     "D",    "osd",   "Fef",    "DSO",   "DSO", "DSOMP" -- F
                  , "pmoFD",   "oFD",    "oFD",     "D",     "D",   "oFD", "D",     "D", "concur",   "DSO",    "DSO",   "DSO", "DSOMP" -- D
                  ,     "p",     "p",    "pmo",   "pmo", "pmoFD",     "s", "s",   "seS",      "d",     "d",    "dfO",     "M",     "P" -- s
                  ,     "p",     "m",      "o",     "F",     "D",     "s", "e",     "S",      "d",     "f",      "O",     "M",     "P" -- e
                  , "pmoFD",   "oFD",    "oFD",     "D",     "D",   "seS", "S",     "S",    "dfO",     "O",      "O",     "M",     "P" -- S
                  ,     "p",     "p",  "pmosd", "pmosd",  "full",     "d", "d", "dfOMP",      "d",     "d",  "dfOMP",     "P",     "P" -- d
                  ,     "p",     "m",    "osd",   "Fef", "DSOMP",     "d", "f",   "OMP",      "d",     "f",    "OMP",     "P",     "P" -- f
                  , "pmoFD",   "oFD", "concur",   "DSO", "DSOMP",   "dfO", "O",   "OMP",    "dfO",     "O",    "OMP",     "P",     "P" -- O
                  , "pmoFD",   "seS",    "dfO",     "M",     "P",   "dfO", "M",     "P",    "dfO",     "M",      "P",     "P",     "P" -- M
                  ,  "full", "dfOMP", "dfOMOP",     "P",     "P", "dfOMP", "P",     "P",  "dfOMP",     "P",      "P",     "P",     "P" -- P
                  ]

-- | Compose two relations.
--
-- Composition table available at <https://www.ics.uci.edu/~alspaugh/cls/shr/allen.html>.
composeSingle :: Relation -> Relation -> RelationBits 
composeSingle r1 r2 = composeLookup U.! index
    where index = 13 * fromEnum r1 + fromEnum r2

-- | Compose two sets of relations.
--
-- Composition table available at <https://www.ics.uci.edu/~alspaugh/cls/shr/allen.html>.
compose :: RelationBits -> RelationBits -> RelationBits
compose r1 r2 = relationUnion [composeSingle a b | a <- fromBits r1, b <- fromBits r2]