-- | The 'FPPrac.Prelude' defines the 'Number' type (which is like Amanda's
-- 'num' type), and hides the intricacies of Haskell's Type Classes
-- from new users when dealing with number. Also defines corresponding
-- 'Prelude' functions that use this new 'Number' type.
module FPPrac.Prelude
  ( module Prelude
  , Number
  , ord
  , chr
  , length
  , (!!)
  , replicate
  , take
  , drop
  , splitAt
  )
where

import Prelude hiding (length,(!!),replicate,take,drop,splitAt)
import qualified Prelude as P
import qualified Data.Char (ord,chr)

import FPPrac.Prelude.Number

default (Number)
infixl 9 !!

ord :: Char -> Number
ord = I . toInteger . Data.Char.ord

chr :: Number -> Char
chr (I i) = Data.Char.chr (fromInteger i)
chr n     = error $ "ord undefined for float: " ++ show n

-- | /O(n)/. 'length' returns the length of a finite list as a 'Number'.
length :: [a] -> Number
length = I . toInteger . P.length

-- | List index (subscript) operator, starting from 0.
(!!) :: [a] -> Number -> a
xs !! (I i) = xs P.!! fromInteger i
_  !! _     = error "trying to index (!!) using a floating number"

-- | 'replicate' @n x@ is a list of length @n@ with @x@ the value of
-- every element.
--
-- Fails when @n@ is not an integral number
replicate :: Number -> a -> [a]
replicate (I i) a = P.replicate (fromInteger i) a
replicate _     _ = error "replicate undefined for float"

-- | 'take' @n@, applied to a list @xs@, returns the prefix of @xs@
-- of length @n@, or @xs@ itself if @n > 'length' xs@:
--
-- > take 5 "Hello World!" == "Hello"
-- > take 3 [1,2,3,4,5] == [1,2,3]
-- > take 3 [1,2] == [1,2]
-- > take 3 [] == []
-- > take (-1) [1,2] == []
-- > take 0 [1,2] == []
--
-- Fails when @n@ is not an integral number
take :: Number -> [a] -> [a]
take (I i) xs = P.take (fromInteger i) xs
take _     _  = error "take undefined for float"

-- | 'drop' @n xs@ returns the suffix of @xs@
-- after the first @n@ elements, or @[]@ if @n > 'length' xs@:
--
-- > drop 6 "Hello World!" == "World!"
-- > drop 3 [1,2,3,4,5] == [4,5]
-- > drop 3 [1,2] == []
-- > drop 3 [] == []
-- > drop (-1) [1,2] == [1,2]
-- > drop 0 [1,2] == [1,2]
--
-- Fails when @n@ is not an integral number
drop :: Number -> [a] -> [a]
drop (I i) xs = P.drop (fromInteger i) xs
drop _     _  = error "drop undefined for float"

-- | 'splitAt' @n xs@ returns a tuple where first element is @xs@ prefix of
-- length @n@ and second element is the remainder of the list:
--
-- > splitAt 6 "Hello World!" == ("Hello ","World!")
-- > splitAt 3 [1,2,3,4,5] == ([1,2,3],[4,5])
-- > splitAt 1 [1,2,3] == ([1],[2,3])
-- > splitAt 3 [1,2,3] == ([1,2,3],[])
-- > splitAt 4 [1,2,3] == ([1,2,3],[])
-- > splitAt 0 [1,2,3] == ([],[1,2,3])
-- > splitAt (-1) [1,2,3] == ([],[1,2,3])
--
-- It is equivalent to @('take' n xs, 'drop' n xs)@ when @n@ is not @_|_@
-- (@splitAt _|_ xs = _|_@).
--
-- Fails when @n@ is not an integral number
splitAt :: Number -> [a] -> ([a],[a])
splitAt (I i) xs = P.splitAt (fromInteger i) xs
splitAt _     _  = error "splitAt undefined for float"