aboutsummaryrefslogtreecommitdiffhomepage
path: root/lib/rtree.ml
blob: 9361a2858989e255a4025663fa6472be5af75be0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
(***********************************************************************)
(*  v      *   The Coq Proof Assistant  /  The Coq Development Team    *)
(* <O___,, *        INRIA-Rocquencourt  &  LRI-CNRS-Orsay              *)
(*   \VV/  *************************************************************)
(*    //   *      This file is distributed under the terms of the      *)
(*         *       GNU Lesser General Public License Version 2.1       *)
(***********************************************************************)

(*i $Id$ i*)


(* Type of regular trees:
   - Param denotes tree variables (like de Bruijn indices)
   - Node denotes the usual tree node, labelles with 'a
   - Rec(j,v1..vn) introduces infinite tree. It denotes
     v(j+1) with parameters 0..n-1 replaced by
     Rec(0,v1..vn)..Rec(n-1,v1..vn) respectively.
     Parameters n and higher denote parameters globals to the
     current Rec node (as usual in de Bruijn binding system)
 *)
type 'a t =
    Param of int 
  | Node of 'a * 'a t array
  | Rec of int * 'a t array

(* Building trees *)
let mk_param i = Param i
let mk_node lab sons = Node (lab, sons)

(* Given a vector of n bodies, builds the n mutual recursive trees.
   Recursive calls are made with parameters 0 to n-1. We check
   the bodies actually build something by checking it is not
   directly one of the parameters 0 to n-1. *)
let mk_rec defs =
  Array.mapi
    (fun i d ->
      (match d with
          Param k when k < Array.length defs -> failwith "invalid rec call"
        | _ -> ());
      Rec(i,defs))
    defs

(* The usual lift operation *)
let rec lift_rtree_rec depth n = function
    Param i -> if i < depth then Param i else Param (i+n)
  | Node (l,sons) -> Node (l,Array.map (lift_rtree_rec depth n) sons)
  | Rec(j,defs) ->
      Rec(j, Array.map (lift_rtree_rec (depth+Array.length defs) n) defs)

let lift n t = if n=0 then t else lift_rtree_rec 0 n t

(* The usual subst operation *)
let rec subst_rtree_rec depth sub = function
    Param i ->
      if i < depth then Param i
      else if i-depth < Array.length sub then lift depth sub.(i-depth)
      else Param (i-Array.length sub)
  | Node (l,sons) -> Node (l,Array.map (subst_rtree_rec depth sub) sons)
  | Rec(j,defs) ->
      Rec(j, Array.map (subst_rtree_rec (depth+Array.length defs) sub) defs)

let subst_rtree sub t = subst_rtree_rec 0 sub t

(* To avoid looping, we must check that every body introduces a node 
   or a parameter *)
let rec expand_rtree = function
  | Rec(j,defs) ->
      let sub = Array.init (Array.length defs) (fun i -> Rec(i,defs)) in
      expand_rtree (subst_rtree sub defs.(j))
  | t -> t

(* Tree destructors, expanding loops when necessary *)
let dest_param t = 
  match expand_rtree t with
      Param i -> i
    | _ -> failwith "dest_param"

let dest_node t = 
  match expand_rtree t with
      Node (l,sons) -> (l,sons)
    | _ -> failwith "dest_node"

(* Tests if a given tree is infinite or not. It proceeds *)
let rec is_infinite = function
    Param i -> i = (-1)
  | Node(_,sons) -> Util.array_exists is_infinite sons
  | Rec(j,defs) ->
      let newdefs =
        Array.mapi (fun i def -> if i=j then Param (-1) else def) defs in
      let sub =
        Array.init (Array.length defs)
          (fun i -> if i=j then Param (-1) else Rec(i,newdefs)) in
      is_infinite (subst_rtree sub defs.(j))

(* Pretty-print a tree (not so pretty) *)
open Pp

let rec pp_tree prl t =
  match t with
      Param k -> str"#"++int k
    | Node(lab,[||]) -> hov 2 (str"("++prl lab++str")")
    | Node(lab,v) ->
        hov 2 (str"("++prl lab++str","++brk(1,0)++
               Util.prvect_with_sep Util.pr_coma (pp_tree prl) v++str")")
    | Rec(i,v) ->
        if Array.length v = 0 then str"Rec{}"
        else if Array.length v = 1 then
          hov 2 (str"Rec{"++pp_tree prl v.(0)++str"}")
        else
          hov 2 (str"Rec{"++int i++str","++brk(1,0)++
                 Util.prvect_with_sep Util.pr_coma (pp_tree prl) v++str"}")