module ListUtils
  where

--To remove all the elements of the list either before or after x
strip :: Eq a => a -> [a] -> (a -> [a] -> [a] -> [a]) -> (a -> [a] -> [a] -> [a]) -> [a]
strip x [] combYes combNo = []
strip x (f: r) combYes combNo = 
 let comb = (if x == f then combYes else combNo) in
   comb f r (strip x r combYes combNo)
--strip "FCST" [] (\f rest rec -> []) (\f rest rec -> f:rec) => []
--strip "FCST" ["HI","FCST","FCST"] (\f rest rec -> []) (\f rest rec -> f:rec) => ["HI"]

--To remove all of the elements in ls after x
stripAfter :: Eq a => a -> [a] -> [a]
stripAfter x ls = strip x ls (\f rest rec -> []) (\f rest rec -> f:rec)
--stripAfter "FCST" ["HI","FCST","FCST"] => ["HI"]
--stripAfter "FCST" [] => []

--To remove all of the elements in ls before x (and including x)
stripTo :: Eq a => a -> [a] -> [a]
stripTo x ls = strip x ls (\f rest rec -> rest) (\f rest rec -> rec)
--stripTo "FCST" ["HI","FCST","FCST"] => ["FCST"]
--stripTo "FCST" [] => []

{- To separate the given list into a list of lists where all the
   elements are the same as by. Also returns the remainder of the
   given list -}
separateToLists :: Eq a => a -> [a] -> ([[a]],[a])
separateToLists by [] = ([],[])
separateToLists by (f:r) = 
  let rest = separateToLists by r in
    case rest of
      (ret,rem) -> if (by == f) 
                    then ([by]:ret,rem)
                   else (ret,(f:r))
--separateToLists "F" [] => ([],[])
--separateToLists "F" ["F","F","F","G","G","G"] => ([["F"],["F"],["F"]],["G","G","G"])


--To add 'add' to the list specified by which in ls, ls should be non-empty
addToIth :: a -> Int -> [[a]] -> [[a]]
addToIth add which ls =
  let adder eltNum [] = []
      adder eltNum (f:r) = if (eltNum == which)
                              then ((add:f):r)
                           else f:(adder (eltNum + 1) r) in
  adder 1 ls
--addToIth "a" 1 [[]] => [["a"]]
--addToIth "a" 2 [["a"],["b"]] => [["a"],["a","b"]]

--To add one element of the first list into each of addTo's lists
addOneToEach :: [a] -> [[a]] -> Int -> ([[a]],[a])
addOneToEach [] n i = (n,[])
addOneToEach (f:r) addTo i = 
  if (i > (length addTo))
   then (addTo,(f:r))
  else addOneToEach r (addToIth f i addTo) (i+1)
-- Tests
-- addOneToEach [] ["a"] 1 => (["a"],"")
-- addOneToEach ["A","b","c","d","e","f","g"] [[],[],[]] 1 => 
--              ([["A"],["b"],["c"]],["d","e","f","g"])

--To add two elements of the first list into each of addTo's lists
addTwoToEach :: [a] -> [[a]] -> Int -> ([[a]],[a])
addTwoToEach [] n i = (n,[])
addTwoToEach (f:s:r) addTo i = 
  if ( i > (length addTo))
   then (addTo,(f:r))
  else addTwoToEach r (addToIth f i (addToIth s i addTo)) (i + 1) 
addTwoToEach (f:[]) addTo i = (addTo,f:[])
--Tests
--addTwoToEach [] ["a"] 1 => (["a"],"")
--addTwoToEach ["A","b","c","d","e","f","g"] [[],[],[]] 1 => 
--             ([["A","b"],["c","d"],["e","f"]],["g"])
--addTwoToEach "a" ["a"] 1 => (["a"],"a")
