```module Sound.Tidal.Time where

import Sound.Tidal.Utils

-- | Time is represented by a rational number. Each natural number
-- represents both the start of the next rhythmic cycle, and the end
-- of the previous one. Rational numbers are used so that subdivisions
-- of each cycle can be accurately represented.
type Time = Rational

-- | @(s,e) :: Arc@ represents a time interval with a start and end value.
-- @ { t : s <= t && t < e } @
type Arc = (Time, Time)

-- | An Event is a value that occurs during the period given by the
-- first @Arc@. The second one indicates the event's "domain of
-- influence". These will often be the same, but many temporal
-- transformations, such as rotation and scaling time, may result in
-- arcs being split or truncated. In such cases, the first arc is
-- preserved, but the second arc reflects the portion of the event
-- which is relevant.
type Event a = (Arc, Arc, a)

-- | The starting point of the current cycle. A cycle occurs from each
-- natural number to the next, so this is equivalent to @floor@.
sam :: Time -> Time
sam = fromIntegral . floor

-- | The end point of the current cycle (and starting point of the next cycle)
nextSam :: Time -> Time
nextSam = (1+) . sam

-- | The position of a time value relative to the start of its cycle.
cyclePos :: Time -> Time
cyclePos t = t - sam t

-- | @isIn a t@ is @True@ iff @t@ is inside
-- the arc represented by @a@.
isIn :: Arc -> Time -> Bool
isIn (s,e) t = t >= s && t < e

-- | Splits the given @Arc@ into a list of @Arc@s, at cycle boundaries.
arcCycles :: Arc -> [Arc]
arcCycles (s,e) | s >= e = []
| sam s == sam e = [(s,e)]
| otherwise = (s, nextSam s) : (arcCycles (nextSam s, e))

-- | @subArc i j@ is the arc that is the intersection of @i@ and @j@.
subArc :: Arc -> Arc -> Maybe Arc
subArc (s, e) (s',e') | s'' < e'' = Just (s'', e'')
| otherwise = Nothing
where s'' = max s s'
e'' = min e e'

-- | Map the given function over both the start and end @Time@ values
-- of the given @Arc@.
mapArc :: (Time -> Time) -> Arc -> Arc
mapArc f (s,e) = (f s, f e)

-- | Returns the `mirror image' of an @Arc@, used by @Sound.Tidal.Pattern.rev@.
mirrorArc :: Arc -> Arc
mirrorArc (s, e) = (sam s + (nextSam s - e), nextSam s - (s - sam s))

-- | The start time of the given @Event@
eventStart :: Event a -> Time
eventStart = fst . snd'

-- | The start time of the given @Event@
eventOnset :: Event a -> Time
eventOnset = fst . fst'

-- | The midpoint of an @Arc@
midPoint :: Arc -> Time
midPoint (s,e) = s + ((e - s) / 2)

hasOnset :: Event a -> Bool
hasOnset ((s,_), (s',_), _) = s == s'
```