(* Cours numero 2: les types de donnees *)

(* enregistrements *)


type complex = {im: float; re: float};;

let zc = {re=0.;im=0.};;

let addc = fun {im=a; re=b} {im=c; re=d} -> 
           {im=a+.c; re=b+.d};;


(* Types sommes *)

(* constructeurs constants *)


type colors = Blue | Red | Green;;

let perm = function Blue -> Red
           |        Red -> Green
           |        Green -> Blue;;


(* constructeurs avec parametres *)


type num = Entier of int | Flottant of float;;

let addnum = 
    function (Entier a, Entier b) -> Entier (a+b)
    |        (Entier a, Flottant b) -> Flottant (float a+.b);;

type intlist = Nil | Cons of int * intlist;;

(* listes d'entiers *)


let rec append = 
    function (Nil,l)  -> l
    |        (Cons(a,r), l) -> Cons(a, append (r,l));;

(* pouvoir des types recursifs: un programme qui diverge *)


type d = Plie of (d->d);;

let plie = fun f -> Plie f;;
let deplie = fun (Plie f) -> f;;

let delta = (deplie x) x;;

let boom = delta (plie delta);;   (* tapez ctrl-c ! *)

(* types POLYMORPHES: listes et operations sur listes *)


type 'a list = Nil | Cons of 'a * 'a list;;

let rec append = 
        function (Nil,l)  -> l
        |        (Cons(a,r), l) -> Cons(a, append (r,l));;

let rec list_hom f e = 
        function Nil -> e
        | Cons(a,l)  -> f a (list_hom f e l);;

(* exemples de homomorphismes *)


let length = list_hom (fun x y -> y+1) 0;;

let reverse = list_hom (fun x y -> append (y,(Cons(x,Nil)))) Nil;;

let filter p = list_hom 
               (fun x l -> if p x then Cons(x,l) else l) Nil;;


(* les listes sont aussi predefinies!!! *)


open List;;

[];;

1::[];;

[1];;

[1;2;3;4];;


let rec list_hom f e = 
        function [] -> e
        | a::l  -> f a (list_hom f e l);;

let filter p = list_hom (fun x l -> if p x then x::l else l) [];;

(* on peut aussi l'ecrire directement : *)


let rec filter p = 
        function [] -> []
        |    (a::r) -> if p a then a::(filter p r)
                              else (filter p r);;

filter (fun x -> x mod 2 =0) [1;2;3;4;5;6];;


(* les TRI sur les listes *)

(* quicksort *)


let rec partition p = 
        function []   -> [],[]
        |      (a::r) -> let (l1,l2) = partition p r
                         in if p a then ((a::l1),l2)
                                   else (l1,(a::l2));;

partition (fun x -> x mod 2 =0) [1;2;3;4;5;6];;

let rec qsort cmp = 
        function []   -> []
        |  (a::r) -> let (p,g) = partition (fun x -> cmp a x) r
                     in (qsort cmp p)@[a]@(qsort cmp g);;

qsort (fun x y -> x < y) [1;4;3;2;9;5;10];; 

qsort (fun x y -> x > y) [1;4;3;2;9;5;10];; 

(* mergesort *)


let rec partition_merge = 
        function []      -> [],[]
        |    [a]         -> [a],[] 
        |   (a::(b::r))  -> let (l1,l2) = partition_merge r
                            in (a::l1),(b::l2);;

partition_merge [1;2;3;4;5;6;7;8;9];; 

let rec merge cmp = 
        function (a::r,b::s) -> if cmp a b 
                                then a::(merge cmp (r, (b::s)))
                                else b::(merge cmp ((a::r), s))
        |        ([],l) -> l
        |        (l,[]) -> l;;

let rec mergesort cmp = 
        function [] -> []
        |        [a] -> [a] (* necessaire pour la terminaison *)
        |         l -> let (l1,l2) = partition_merge l
                       in merge cmp (mergesort cmp l1,
                                     mergesort cmp l2);;

mergesort (fun x y -> x > y) [1;4;3;2;9;5;10];; 
mergesort (fun x y -> x < y) [1;4;3;2;9;5;10];; 


(* les arbres binaires *)


type 'a arbre = Empty | Node of 'a arbre *'a*'a arbre;;

(* parcours d'arbre *)


let rec infixe = 
     function Empty           -> []
     |        (Node(ag,i,ad)) -> (infixe ag)@[i]@(infixe ad);;

let rec prefixe = 
     function Empty           -> []
     |        (Node(ag,i,ad)) -> [i]@(prefixe ag)@(prefixe ad);;

let rec postfixe = 
     function Empty           -> []
     |        (Node(ag,i,ad)) -> (postfixe ag)@(postfixe ad)@[i];;

(* homomorphismes *)


let rec hom_tree f e =
        function Empty    -> e
        | (Node(ag,i,ad)) -> f (hom_tree f e ag) i 
                               (hom_tree f e ad);; 

(* exemples *)


let maptree f = hom_tree (fun g i d -> Node(g,f i,d)) Empty;;

maptree (fun x -> x+1) 
        (Node(Node(Empty,3,Empty),4,Node(Empty,5,Empty)));;

let hauteur = hom_tree (fun g i d -> 1+(max g d)) 0;;

hauteur (Node(Node(Empty,3,Empty),4,Node(Empty,5,Empty)));;

let taille = hom_tree  (fun g i d -> 1+g+d) 0;;

taille (Node(Node(Empty,3,Empty),4,Node(Empty,5,Empty)));;

let miroir = hom_tree (fun g i d -> Node(d,i,g)) Empty;;

miroir (Node(Node(Empty,3,Empty),4,Node(Empty,5,Empty)));;


(* parcours infixe generalises, pliages *)


let rec plie_droite f e =
        function Empty  -> e
        | (Node(g,i,d)) -> plie_droite 
                              f 
                              (f (plie_droite f e g) i)
                              d;;

let infixe a = plie_droite (fun l x -> x::l) [] a;;

infixe (Node(Node(Empty,3,Empty),4,Node(Empty,5,Empty)));;