content: module ContextFilter (filterContext) where
  
  -- | Like 'filter' except you can request the elements before
  -- and after the matched elements.
  --
  -- >>> filterContext 2 1 (\x -> 4<=x&&x<=6) [0..10::Int]
  -- [2,3,4,5,6,7]
  --
  -- >>> filterContext 2 1 even [0..10::Int]
  -- [0,1,2,3,4,5,6,7,8,9,10]
  --
  -- >>> filterContext 2 1 (==10) [0..10::Int]
  -- [8,9,10]
  --
  -- >>> filterContext 2 1 (==0) [0..10::Int]
  -- [0,1]
  --
  -- >>> filterContext 0 0 (==0) [0..10::Int]
  -- [0]
  --
  -- >>> filterContext 2 1 (==5) [0..10::Int]
  -- [3,4,5,6]
  --
  -- >>> filterContext 1 2 (==5) [0..10::Int]
  -- [4,5,6,7]
  filterContext ::
    Int         {- ^ context before       -} ->
    Int         {- ^ context after        -} ->
    (a -> Bool) {- ^ predicate            -} ->
    [a]         {- ^ inputs               -} ->
    [a]         {- ^ matches with context -}
  filterContext before after p xs0
    | before < 0 = error "filterContext: bad before"
    | after  < 0 = error "filterContext: bad after"
    | otherwise  = go 0 0 xs0 xs0
    where
      width = before + after
  
      -- i: index
      -- m: current match window
      -- xs: list to match
      -- ys: offset list to generate results from
      go i m (x:xs) yys@(y:ys) =
        if (m > 0 || px) && i >= before then y : rest else rest
        where
          rest = go (i+1) m' xs ys'
          px = p x
          m' = max (m-1) (if px then width else 0)
          ys' = if i >= before then ys else yys
  
      -- no more matches, so just return the remaining m window
      go _ m _ ys = take m ys
  