--
import Shape

len :: [a] -> Int
len [] = 0
len (x:xs) = 1 + len xs

tag1 x = (1,x)

-- Parameterized datatypes

data Option a = NONE | SOME a

pos :: Eq a => a -> [a] -> Option Int
pos a l =
  let find a [] n = NONE
      find a (x:xs) n =
          if a==x
             then SOME n
             else find a xs (n+1)
  in find a l 0

-- multiple parameterized datatypes

data Pair a b  = Pair (a,b)
data Pair2 a b  = Pair2 a b


-- Recursive datatypes

data Mylist a = Nil
              | Cons  (a,Mylist a)

-- Recursive datatypes with multiple parameters

data Twolist a b = Twonil
                 | Acons  (a,Twolist a b)
                 | Bcons  (b,Twolist a b)

append2 Twonil ys = ys
append2 (Acons(a,xs)) ys =  Acons(a,append2 xs ys)
append2 (Bcons(b,xs)) ys = Bcons(b,append2 xs ys)

rev2 Twonil = Twonil
rev2 (Acons(a,t)) = append2 (rev2 t) (Acons(a,Twonil))
rev2 (Bcons(b,t)) = append2 (rev2 t) (Bcons(b,Twonil))

-- Recursive datatypes with no parameters

data Nat = Zero | Succ  Nat
  deriving Show

toNat 0 = Zero
toNat n = Succ(toNat (n-1))

-- Functions with functions as parameters

mymap f [] = []
mymap f (x:xs) = (f x):(mymap f xs)

myfoldr op e [] = e
myfoldr op e (x:xs) =
      op x (myfoldr op e xs)

-- Polymorphism from functions as arguments

applyTwice f x = f(f x)

z f x y = (f x, f y)

concat1 :: [[a]] -> [a]
concat1 = foldr (++) []

concat2 :: [[a]] -> [a]
concat2 = foldl (++) []

-----------------------------------------------------------
-- Perimiters of Shapes
-----------------------------------------------------------

perimeter :: Shape -> Float
perimeter (Rectangle  s1 s2) = 2*(s1+s2)
perimeter (RtTriangle s1 s2) = s1 + s2 + sqrt(s1^2+s2^2)
perimeter (Polygon pts)      = foldl (+) 0 (sides pts)
perimeter (Ellipse r1 r2)
   | r1 > r2   = ellipsePerim r1 r2
   | otherwise = ellipsePerim r2 r1
   where ellipsePerim r1 r2
           = let e = sqrt (r1^2 - r2^2) / r1
                 s = scanl aux (0.25*e^2)
                             (map intToFloat [2..])
                 aux s i = nextEl e s i
                 test x = x > epsilon
                 sSum = foldl (+) 0 (takeWhile test s)
             in 2*r1*pi*(1 - sSum)



sides        :: [Vertex] -> [Side]
sides  []     = []
sides (p:pts) = aux (p:pts)
  where aux (p1:p2:pts) =  (distBetween p1 p2) : (aux (p2:pts))
        aux (pn:[])     = distBetween pn p  : []

sides2    :: [Vertex] -> [Side]
sides2 pts = zipWith distBetween pts
                (tail pts ++ [head pts])

nextEl:: Float -> Float -> Float -> Float
nextEl e s i =  s*(2*i-1)*(2*i-3)*(e^2) / (4*i^2)

epsilon :: Float
epsilon = 0.00001



intToFloat   :: Int -> Float
intToFloat  n = fromInteger (toInteger n)

--