--1. Find the last element of a list
getLast :: [a] -> a
getLast [] = error "Got an empty list"
getLast [x] = x
getLast (x:xs) = getLast xs

--2. Find the last but one element of a list
getLastButOne :: [a] -> a
getLastButOne []  = error "Got an empty list"
getLastButOne [x] = error "Too few elements"
getLastButOne x = reverse x !! 1

--3. Find the n'th element of a list. The first element in the list is number 1
elementAt :: [a] -> Int -> a
elementAt [] a = error "Got an empty list"
elementAt list i = list !! (i - 1)

elementAt' :: [a] -> Int -> a
elementAt' (x:_) 1 = x
elementAt' (_:xs) i = elementAt' xs (i - 1)
elementAt' _ _ = error "Index out of bounds"

--4. Find the number of elements of a list
getNumOfElements        :: [a] -> Int
getNumOfElements []     = 0
getNumOfElements (x:xs) = 1 + (getNumOfElements xs) 

--5. Reverse a list
reverseList             :: [a] -> [a]
reverseList []          = []
reverseList (x:xs)      = reverseList(xs) ++ [x]

--6. Find out whether a list is a palindrome or not
isPalindrome            :: (Eq a) => [a] -> Bool
isPalindrome []         = True
isPalindrome [_]        = True
isPalindrome list       = list == (reverseList list) 

--7. Flatten a nested list structure

data NestedList a = Elem a | List  [NestedList a]

flatten :: NestedList a -> [a]
flatten (Elem x) = [x]
flatten (List x) = concatMap flatten x

--8. Eliminate consecutive duplicates of list elements
--compress                :: [a] -> [a]



--9. Pack consecutive duplicates of list elements into sublists. If a list contains repeated elements they should be placed in separate sublists. 
--10. Run-length encoding of a list. Use the result of problem P09 to implement the so-called run-length encoding data compression method. Consecutive duplicates of elements are encoded as lists (N E) where N is the number of duplicates of the element E. 
--11


--12



--14

duplicate'        :: [a] -> [a]
duplicate' []     = []
duplicate' (x:xs) = x : x : duplicate' xs

--17
split'            :: [a] -> Int -> ([a], [a])
split' [] _       = ([], [])
split' list 0     = ([], list)
split' list i     = (take i list, drop i list)