summaryrefslogtreecommitdiff
path: root/contrib/subtac/equations.ml4
blob: 9d1200197ad5041c682c628168a7170c3386ad45 (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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
(* -*- compile-command: "make -C ../.. bin/coqtop.byte" -*- *)
(************************************************************************)
(*  v      *   The Coq Proof Assistant  /  The Coq Development Team     *)
(* <O___,, * CNRS-Ecole Polytechnique-INRIA Futurs-Universite Paris Sud *)
(*   \VV/  **************************************************************)
(*    //   *      This file is distributed under the terms of the       *)
(*         *       GNU Lesser General Public License Version 2.1        *)
(************************************************************************)

(*i camlp4deps: "parsing/grammar.cma" i*)
(*i camlp4use: "pa_extend.cmo" i*) 

(* $Id: subtac_cases.ml 11198 2008-07-01 17:03:43Z msozeau $ *)

open Cases
open Util
open Names
open Nameops
open Term
open Termops
open Declarations
open Inductiveops
open Environ
open Sign
open Reductionops
open Typeops
open Type_errors

open Rawterm
open Retyping
open Pretype_errors
open Evarutil
open Evarconv
open List
open Libnames

type pat =
  | PRel of int
  | PCstr of constructor * pat list
  | PInac of constr

let coq_inacc = lazy (Coqlib.gen_constant "equations" ["Program";"Equality"] "inaccessible_pattern")
  
let mkInac env c =
  mkApp (Lazy.force coq_inacc, [| Typing.type_of env Evd.empty c ; c |])
    
let rec constr_of_pat ?(inacc=true) env = function
  | PRel i -> mkRel i
  | PCstr (c, p) -> 
      let c' = mkConstruct c in
	mkApp (c', Array.of_list (constrs_of_pats ~inacc env p))
  | PInac r -> 
      if inacc then try mkInac env r with _ -> r else r
      
and constrs_of_pats ?(inacc=true) env l = map (constr_of_pat ~inacc env) l

let rec pat_vars = function
  | PRel i -> Intset.singleton i
  | PCstr (c, p) -> pats_vars p
  | PInac _ -> Intset.empty

and pats_vars l = 
  fold_left (fun vars p -> 
    let pvars = pat_vars p in
    let inter = Intset.inter pvars vars in
      if inter = Intset.empty then
	Intset.union pvars vars
      else error ("Non-linear pattern: variable " ^
		     string_of_int (Intset.choose inter) ^ " appears twice"))
    Intset.empty l

let rec pats_of_constrs l = map pat_of_constr l
and pat_of_constr c = 
  match kind_of_term c with
  | Rel i -> PRel i
  | App (f, [| a ; c |]) when eq_constr f (Lazy.force coq_inacc) ->
      PInac c
  | App (f, args) when isConstruct f ->
      PCstr (destConstruct f, pats_of_constrs (Array.to_list args))
  | Construct f -> PCstr (f, [])
  | _ -> PInac c

let inaccs_of_constrs l = map (fun x -> PInac x) l

exception Conflict

let rec pmatch p c =
  match p, c with
  | PRel i, t -> [i, t]
  | PCstr (c, pl), PCstr (c', pl') when c = c' -> pmatches pl pl'
  | PInac _, _ -> []
  | _, PInac _ -> []
  | _, _ -> raise Conflict

and pmatches pl l =
  match pl, l with
  | [], [] -> []
  | hd :: tl, hd' :: tl' -> 
      pmatch hd hd' @ pmatches tl tl'
  | _ -> raise Conflict
      
let pattern_matches pl l = try Some (pmatches pl l) with Conflict -> None

let rec pinclude p c =
  match p, c with
  | PRel i, t -> true
  | PCstr (c, pl), PCstr (c', pl') when c = c' -> pincludes pl pl'
  | PInac _, _ -> true
  | _, PInac _ -> true
  | _, _ -> false
      
and pincludes pl l =
  match pl, l with
  | [], [] -> true
  | hd :: tl, hd' :: tl' -> 
      pinclude hd hd' && pincludes tl tl'
  | _ -> false
      
let pattern_includes pl l = pincludes pl l

(** Specialize by a substitution. *)

let subst_tele s = replace_vars (List.map (fun (id, _, t) -> id, t) s)

let subst_rel_subst k s c = 
  let rec aux depth c =
    match kind_of_term c with
    | Rel n -> 
	let k = n - depth in 
	  if k >= 0 then 
	    try lift depth (snd (assoc k s))
	    with Not_found -> c
	  else c
    | _ -> map_constr_with_binders succ aux depth c
  in aux k c
    
let subst_context s ctx =
  let (_, ctx') = fold_right 
    (fun (id, b, t) (k, ctx') ->
      (succ k, (id, Option.map (subst_rel_subst k s) b, subst_rel_subst k s t) :: ctx'))
    ctx (0, [])
  in ctx'

let subst_rel_context k cstr ctx = 
  let (_, ctx') = fold_right 
    (fun (id, b, t) (k, ctx') ->
      (succ k, (id, Option.map (substnl [cstr] k) b, substnl [cstr] k t) :: ctx'))
    ctx (k, [])
  in ctx'

let rec lift_pat n k p = 
  match p with
  | PRel i ->
      if i >= k then PRel (i + n)
      else p
  | PCstr(c, pl) -> PCstr (c, lift_pats n k pl)
  | PInac r -> PInac (liftn n k r)
      
and lift_pats n k = map (lift_pat n k)

let rec subst_pat env k t p = 
  match p with
  | PRel i -> 
      if i = k then t
      else if i > k then PRel (pred i)
      else p
  | PCstr(c, pl) ->
      PCstr (c, subst_pats env k t pl)
  | PInac r -> PInac (substnl [constr_of_pat ~inacc:false env t] (pred k) r)

and subst_pats env k t = map (subst_pat env k t)

let rec specialize s p = 
  match p with
  | PRel i -> 
      if mem_assoc i s then
	let b, t = assoc i s in
	  if b then PInac t
	  else PRel (destRel t)
      else p
  | PCstr(c, pl) ->
      PCstr (c, specialize_pats s pl)
  | PInac r -> PInac (specialize_constr s r)

and specialize_constr s c = subst_rel_subst 0 s c
and specialize_pats s = map (specialize s)

let specialize_patterns = function
  | [] -> fun p -> p
  | s -> specialize_pats s

let specialize_rel_context s ctx =
  snd (fold_right (fun (n, b, t) (k, ctx) -> 
    (succ k, (n, Option.map (subst_rel_subst k s) b, subst_rel_subst k s t) :: ctx))
	  ctx (0, []))
    
let lift_contextn n k sign =
  let rec liftrec k = function
    | (na,c,t)::sign ->
	(na,Option.map (liftn n k) c,liftn n k t)::(liftrec (k-1) sign)
    | [] -> []
  in
  liftrec (rel_context_length sign + k) sign

type program = 
  signature * clause list

and signature = identifier * rel_context * constr

and clause = lhs * (constr, int) rhs

and lhs = rel_context * identifier * pat list

and ('a, 'b) rhs = 
  | Program of 'a
  | Empty of 'b

type splitting = 
  | Compute of clause
  | Split of lhs * int * inductive_family *
      unification_result array * splitting option array
      
and unification_result = 
  rel_context * int * constr * pat * substitution option

and substitution = (int * (bool * constr)) list

type problem = identifier * lhs

let rels_of_tele tele = rel_list 0 (List.length tele)

let patvars_of_tele tele = map (fun c -> PRel (destRel c)) (rels_of_tele tele)

let split_solves split prob =
  match split with
  | Compute (lhs, rhs) -> lhs = prob
  | Split (lhs, id, indf, us, ls) -> lhs = prob

let ids_of_constr c = 
  let rec aux vars c = 
    match kind_of_term c with
    | Var id -> Idset.add id vars
    | _ -> fold_constr aux vars c
  in aux Idset.empty c

let ids_of_constrs = 
  fold_left (fun acc x -> Idset.union (ids_of_constr x) acc) Idset.empty

let idset_of_list =
  fold_left (fun s x -> Idset.add x s) Idset.empty

let intset_of_list =
  fold_left (fun s x -> Intset.add x s) Intset.empty

let solves split (delta, id, pats as prob) = 
  split_solves split prob && 
  Intset.equal (pats_vars pats) (intset_of_list (map destRel (rels_of_tele delta)))

let check_judgment ctx c t =
  ignore(Typing.check (push_rel_context ctx (Global.env ())) Evd.empty c t); true

let check_context env ctx =
  fold_right
    (fun (_, _, t as decl) env -> 
      ignore(Typing.sort_of env Evd.empty t); push_rel decl env)
    ctx env

let split_context n c =
  let after, before = list_chop n c in
    match before with
    | hd :: tl -> after, hd, tl
    | [] -> raise (Invalid_argument "split_context")
	
let split_tele n (ctx : rel_context) =
  let rec aux after n l =
    match n, l with
    | 0, decl :: before -> before, decl, List.rev after
    | n, decl :: before -> aux (decl :: after) (pred n) before
    | _ -> raise (Invalid_argument "split_tele")
  in aux [] n ctx

let rec add_var_subst env subst n c =
  if mem_assoc n subst then
    let t = assoc n subst in
      if eq_constr t c then subst
      else unify env subst t c
  else 
    let rel = mkRel n in
      if rel = c then subst
      else if dependent rel c then raise Conflict
      else (n, c) :: subst
    
and unify env subst x y =
  match kind_of_term x, kind_of_term y with
  | Rel n, _ -> add_var_subst env subst n y
  | _, Rel n -> add_var_subst env subst n x
  | App (c, l), App (c', l') when eq_constr c c' ->
      unify_constrs env subst (Array.to_list l) (Array.to_list l')
  | _, _ -> if eq_constr x y then subst else raise Conflict

and unify_constrs (env : env) subst l l' = 
  if List.length l = List.length l' then
    fold_left2 (unify env) subst l l'
  else raise Conflict

let fold_rel_context_with_binders f ctx init =
  snd (List.fold_right (fun decl (depth, acc) ->
    (succ depth, f depth decl acc)) ctx (0, init))
    
let dependent_rel_context (ctx : rel_context) k =
  fold_rel_context_with_binders
    (fun depth (n,b,t) acc -> 
      let r = mkRel (depth + k) in
	acc || dependent r t ||
	  (match b with
	  | Some b -> dependent r b
	  | None -> false))
    ctx false

let liftn_between n k p c =
  let rec aux depth c = match kind_of_term c with
    | Rel i -> 
	if i <= depth then c
	else if i-depth > p then c
	else mkRel (i - n)
    | _ -> map_constr_with_binders succ aux depth c
  in aux k c
    
let liftn_rel_context n k sign =  
  let rec liftrec k = function
    | (na,c,t)::sign ->
	(na,Option.map (liftn n k) c,liftn n k t)::(liftrec (k-1) sign)
    | [] -> []
  in
    liftrec (k + rel_context_length sign) sign

let substnl_rel_context n l =
  map_rel_context_with_binders (fun k -> substnl l (n+k-1))

let reduce_rel_context (ctx : rel_context) (subst : (int * (bool * constr)) list) =
  let _, s, ctx' =
    fold_left (fun (k, s, ctx') (n, b, t as decl) ->
      match b with
      | None -> (succ k, mkRel k :: s, ctx' @ [decl])
      | Some t -> (k, lift (pred k) t :: map (substnl [t] (pred k)) s, subst_rel_context 0 t ctx'))
      (1, [], []) ctx
  in
  let s = rev s in
  let s' = map (fun (korig, (b, knew)) -> korig, (b, substl s knew)) subst in
    s', ctx'
      
(* Compute the transitive closure of the dependency relation for a term in a context *)

let rec dependencies_of_rel ctx k =
  let (n,b,t) = nth ctx (pred k) in
  let b = Option.map (lift k) b and t = lift k t in
  let bdeps = match b with Some b -> dependencies_of_term ctx b | None -> Intset.empty in
    Intset.union (Intset.singleton k) (Intset.union bdeps (dependencies_of_term ctx t))
      
and dependencies_of_term ctx t =
  let rels = free_rels t in
    Intset.fold (fun i -> Intset.union (dependencies_of_rel ctx i)) rels Intset.empty

let subst_telescope k cstr ctx = 
  let (_, ctx') = fold_left
    (fun (k, ctx') (id, b, t) ->
      (succ k, (id, Option.map (substnl [cstr] k) b, substnl [cstr] k t) :: ctx'))
    (k, []) ctx
  in rev ctx'

let lift_telescope n k sign =
  let rec liftrec k = function
    | (na,c,t)::sign ->
	(na,Option.map (liftn n k) c,liftn n k t)::(liftrec (succ k) sign)
    | [] -> []
  in liftrec k sign
    
type ('a,'b) either = Inl of 'a | Inr of 'b
  
let strengthen (ctx : rel_context) (t : constr) : rel_context * rel_context * (int * (int, int) either) list =
  let rels = dependencies_of_term ctx t in
  let len = length ctx in
  let nbdeps = Intset.cardinal rels in
  let lifting = len - nbdeps in (* Number of variables not linked to t *)
  let rec aux k n acc m rest s = function
    | decl :: ctx' ->
	if Intset.mem k rels then
	  let rest' = subst_telescope 0 (mkRel (nbdeps + lifting - pred m)) rest in
	    aux (succ k) (succ n) (decl :: acc) m rest' ((k, Inl n) :: s) ctx'
	else aux (succ k) n (subst_telescope 0 mkProp acc) (succ m) (decl :: rest) ((k, Inr m) :: s) ctx'
    | [] -> rev acc, rev rest, s
  in aux 1 1 [] 1 [] [] ctx
    
let merge_subst (ctx', rest, s) =
  let lenrest = length rest in
    map (function (k, Inl x) -> (k, (false, mkRel (x + lenrest))) | (k, Inr x) -> k, (false, mkRel x)) s

(* let simplify_subst s =  *)
(*   fold_left (fun s (k, t) ->  *)
(*     match kind_of_term t with *)
(*     | Rel n when n = k -> s *)
(*     | _ -> (k, t) :: s) *)
(*     [] s *)

let compose_subst s' s =
  map (fun (k, (b, t)) -> (k, (b, specialize_constr s' t))) s

let substitute_in_ctx n c ctx =
  let rec aux k after = function
    | [] -> []
    | (name, b, t as decl) :: before ->
	if k = n then rev after @ (name, Some c, t) :: before
	else aux (succ k) (decl :: after) before
  in aux 1 [] ctx
    
let rec reduce_subst (ctx : rel_context) (substacc : (int * (bool * constr)) list) (cursubst : (int * (bool * constr)) list) =
  match cursubst with
  | [] -> ctx, substacc
  | (k, (b, t)) :: rest ->
      if t = mkRel k then reduce_subst ctx substacc rest
      else if noccur_between 1 k t then
	(* The term to substitute refers only to previous variables. *)
	let t' = lift (-k) t in
	let ctx' = substitute_in_ctx k t' ctx in
	  reduce_subst ctx' substacc rest
      else (* The term refers to variables declared after [k], so we have 
	      to move these dependencies before [k]. *)
	let (minctx, ctxrest, subst as str) = strengthen ctx t in
	  match assoc k subst with
	  | Inl _ -> error "Occurs check in substituted_context"
	  | Inr k' ->
	      let s = merge_subst str in
	      let ctx' = ctxrest @ minctx in
	      let rest' =
		let substsubst (k', (b, t')) =
		  match kind_of_term (snd (assoc k' s)) with
		  | Rel k'' -> (k'', (b, specialize_constr s t'))
		  | _ -> error "Non-variable substituted for variable by strenghtening"
		in map substsubst ((k, (b, t)) :: rest)
	      in
		reduce_subst ctx' (compose_subst s substacc) rest' (* (compose_subst s ((k, (b, t)) :: rest)) *)
		  
		  
let substituted_context (subst : (int * constr) list) (ctx : rel_context) =
  let _, subst =
    fold_left (fun (k, s) _ ->
      try let t = assoc k subst in
	    (succ k, (k, (true, t)) :: s)
      with Not_found ->
	(succ k, ((k, (false, mkRel k)) :: s)))
      (1, []) ctx
  in
  let ctx', subst' = reduce_subst ctx subst subst in
    reduce_rel_context ctx' subst'
    
let unify_type before ty =
  try
    let envb = push_rel_context before (Global.env()) in
    let IndType (indf, args) = find_rectype envb Evd.empty ty in
    let ind, params = dest_ind_family indf in
    let vs = map (Reduction.whd_betadeltaiota envb) args in
    let cstrs = Inductiveops.arities_of_constructors envb ind in
    let cstrs = 
      Array.mapi (fun i ty ->
	let ty = prod_applist ty params in
	let ctx, ty = decompose_prod_assum ty in
	let ctx, ids = 
	  let ids = ids_of_rel_context ctx in
	    fold_right (fun (n, b, t as decl) (acc, ids) ->
	      match n with Name _ -> (decl :: acc), ids
	      | Anonymous -> let id = next_name_away Anonymous ids in
			       ((Name id, b, t) :: acc), (id :: ids))
	      ctx ([], ids)
	in
	let env' = push_rel_context ctx (Global.env ()) in
	let IndType (indf, args) = find_rectype env' Evd.empty ty in
	let ind, params = dest_ind_family indf in
	let constr = applist (mkConstruct (ind, succ i), params @ rels_of_tele ctx) in
	let constrpat = PCstr ((ind, succ i), inaccs_of_constrs params @ patvars_of_tele ctx) in
	  env', ctx, constr, constrpat, (* params @  *)args)
	cstrs
    in
    let res = 
      Array.map (fun (env', ctxc, c, cpat, us) -> 
	let _beforelen = length before and ctxclen = length ctxc in
	let fullctx = ctxc @ before in
	  try
	    let fullenv = push_rel_context fullctx (Global.env ()) in
	    let vs' = map (lift ctxclen) vs in
	    let subst = unify_constrs fullenv [] vs' us in
	    let subst', ctx' = substituted_context subst fullctx in
	      (ctx', ctxclen, c, cpat, Some subst')
	  with Conflict -> 
	    (fullctx, ctxclen, c, cpat, None)) cstrs
    in Some (res, indf)
  with Not_found -> (* not an inductive type *)
    None

let rec id_of_rel n l =
  match n, l with
  | 0, (Name id, _, _) :: tl -> id
  | n, _ :: tl -> id_of_rel (pred n) tl
  | _, _ -> raise (Invalid_argument "id_of_rel")

let constrs_of_lhs ?(inacc=true) env (ctx, _, pats) = 
  constrs_of_pats ~inacc (push_rel_context ctx env) pats
      
let rec valid_splitting (f, delta, t, pats) tree = 
  split_solves tree (delta, f, pats) && 
    valid_splitting_tree (f, delta, t) tree
    
and valid_splitting_tree (f, delta, t) = function
  | Compute (lhs, Program rhs) -> 
      let subst = constrs_of_lhs ~inacc:false (Global.env ()) lhs in 
	ignore(check_judgment (pi1 lhs) rhs (substl subst t)); true

  | Compute ((ctx, id, lhs), Empty split) -> 
      let before, (x, _, ty), after = split_context split ctx in
      let unify = 
	match unify_type before ty with
	| Some (unify, _) -> unify 
	| None -> assert false
      in
	array_for_all (fun (_, _, _, _, x) -> x = None) unify
	  
  | Split ((ctx, id, lhs), rel, indf, unifs, ls) -> 
      let before, (id, _, ty), after = split_tele (pred rel) ctx in
      let unify, indf' = Option.get (unify_type before ty) in
	assert(indf = indf');
	if not (array_exists (fun (_, _, _, _, x) -> x <> None) unify) then false
	else
	  let ok, splits = 
	    Array.fold_left (fun (ok, splits as acc) (ctx', ctxlen, cstr, cstrpat, subst) -> 
	      match subst with
	      | None -> acc
	      | Some subst ->
(* 		  let env' = push_rel_context ctx' (Global.env ()) in *)
(* 		  let ctx_correct =  *)
(* 		    ignore(check_context env' (subst_context subst ctxc)); *)
(* 		    ignore(check_context env' (subst_context subst before)); *)
(* 		    true *)
(* 		  in  *)
		  let newdelta = 
		    subst_context subst (subst_rel_context 0 cstr 
					    (lift_contextn ctxlen 0 after)) @ before in
		  let liftpats = lift_pats ctxlen rel lhs in
		  let newpats = specialize_patterns subst (subst_pats (Global.env ()) rel cstrpat liftpats) in
		    (ok, (f, newdelta, newpats) :: splits))
	      (true, []) unify
	  in
	  let subst = List.map2 (fun (id, _, _) x -> out_name id, x) delta 
	    (constrs_of_pats ~inacc:false (Global.env ()) lhs) 
	  in
	  let t' = replace_vars subst t in
	    ok && for_all 
	      (fun (f, delta', pats') -> 
		array_exists (function None -> false | Some tree -> valid_splitting (f, delta', t', pats') tree) ls) splits
	      
let valid_tree (f, delta, t) tree = 
  valid_splitting (f, delta, t, patvars_of_tele delta) tree

let is_constructor c =
  match kind_of_term (fst (decompose_app c)) with
  | Construct _ -> true
  | _ -> false

let find_split (_, _, curpats : lhs) (_, _, patcs : lhs) =
  let rec find_split_pat curpat patc =
    match patc with
    | PRel _ -> None
    | PCstr (f, args) ->
	(match curpat with
	| PCstr (f', args') when f = f' -> (* Already split at this level, continue *)
	    find_split_pats args' args
	| PRel i -> (* Split on i *) Some i
	| PInac c when isRel c -> Some (destRel c)
	| _ -> None)
    | PInac _ -> None

  and find_split_pats curpats patcs =
    assert(List.length curpats = List.length patcs);
    fold_left2 (fun acc -> 
      match acc with
      | None -> find_split_pat | _ -> fun _ _ -> acc)
      None curpats patcs
  in find_split_pats curpats patcs
      
open Pp
open Termops

let pr_constr_pat env c =
  let pr = print_constr_env env c in
    match kind_of_term c with
    | App _ -> str "(" ++ pr ++ str ")"
    | _ -> pr

let pr_pat env c =
  try 
    let patc = constr_of_pat env c in
      try pr_constr_pat env patc with _ -> str"pr_constr_pat raised an exception"
  with _ -> str"constr_of_pat raised an exception"
    
let pr_context env c =
  let pr_decl (id,b,_) = 
    let bstr = match b with Some b -> str ":=" ++ spc () ++ print_constr_env env b | None -> mt() in
    let idstr = match id with Name id -> pr_id id | Anonymous -> str"_" in
      idstr ++ bstr
  in
    prlist_with_sep pr_spc pr_decl (List.rev c)
(*   Printer.pr_rel_context env c *)

let pr_lhs env (delta, f, patcs) =
  let env = push_rel_context delta env in
  let ctx = pr_context env delta in
    (if delta = [] then ctx else str "[" ++ ctx ++ str "]" ++ spc ())
    ++ pr_id f ++ spc () ++ prlist_with_sep spc (pr_pat env) patcs

let pr_rhs env = function
  | Empty var -> spc () ++ str ":=!" ++ spc () ++ print_constr_env env (mkRel var)
  | Program rhs -> spc () ++ str ":=" ++ spc () ++ print_constr_env env rhs
      
let pr_clause env (lhs, rhs) =
  pr_lhs env lhs ++ 
    (let env' = push_rel_context (pi1 lhs) env in
       pr_rhs env' rhs)
    
(* let pr_splitting env = function *)
(*   | Compute cl -> str "Compute " ++ pr_clause env cl *)
(*   | Split (lhs, n, indf, results, splits) -> *)

(* let pr_unification_result (ctx, n, c, pat, subst) = *)
  
(*       unification_result array * splitting option array *)

let pr_clauses env =
  prlist_with_sep fnl (pr_clause env)

let lhs_includes (delta, _, patcs : lhs) (delta', _, patcs' : lhs) =
  pattern_includes patcs patcs'
    
let lhs_matches (delta, _, patcs : lhs) (delta', _, patcs' : lhs) =
  pattern_matches patcs patcs'

let rec split_on env var (delta, f, curpats as lhs) clauses =
  let before, (id, _, ty), after = split_tele (pred var) delta in
  let unify, indf = 
    match unify_type before ty with 
    | Some r -> r
    | None -> assert false (* We decided... so it better be inductive *)
  in
  let clauses = ref clauses in
  let splits = 
    Array.map (fun (ctx', ctxlen, cstr, cstrpat, s) ->
      match s with
      | None -> None
      | Some s -> 
	  (* ctx' |- s cstr, s cstrpat *)
	  let newdelta =
	    subst_context s (subst_rel_context 0 cstr 
				(lift_contextn ctxlen 1 after)) @ ctx' in
	  let liftpats = 
	    (* delta |- curpats -> before; ctxc; id; after |- liftpats *)
	    lift_pats ctxlen (succ var) curpats 
	  in
	  let liftpat = (* before; ctxc |- cstrpat -> before; ctxc; after |- liftpat *)
	    lift_pat (pred var) 1 cstrpat
	  in
	  let substpat = (* before; ctxc; after |- liftpats[id:=liftpat] *)
	    subst_pats env var liftpat liftpats 
	  in
	  let lifts = (* before; ctxc |- s : newdelta ->
			 before; ctxc; after |- lifts : newdelta ; after *)
	    map (fun (k,(b,x)) -> (pred var + k, (b, lift (pred var) x))) s
	  in
	  let newpats = specialize_patterns lifts substpat in
	  let newlhs = (newdelta, f, newpats) in
	  let matching, rest = 
	    fold_right (fun (lhs, rhs as clause) (matching, rest) -> 
	      if lhs_includes newlhs lhs then
		(clause :: matching, rest)
	      else (matching, clause :: rest))
	      !clauses ([], [])
	  in
	    clauses := rest;
	    if matching = [] then (
	      (* Try finding a splittable variable *)
	      let (id, _) = 
		fold_right (fun (id, _, ty as decl) (accid, ctx) -> 
		  match accid with 
		  | Some _ -> (accid, ctx)
		  | None -> 
		      match unify_type ctx ty with
		      | Some (unify, indf) ->
			  if array_for_all (fun (_, _, _, _, x) -> x = None) unify then
			    (Some id, ctx)
			  else (None, decl :: ctx)
		      | None -> (None, decl :: ctx))
		  newdelta (None, [])
	      in 
		match id with
		| None ->
		    errorlabstrm "deppat"
	      	      (str "Non-exhaustive pattern-matching, no clause found for:" ++ fnl () ++
	      		  pr_lhs env newlhs)
		| Some id -> 
		    Some (Compute (newlhs, Empty (fst (lookup_rel_id (out_name id) newdelta))))
	    ) else (
	      let splitting = make_split_aux env newlhs matching in
		Some splitting))
      unify
  in
(*     if !clauses <> [] then *)
(*       errorlabstrm "deppat" *)
(* 	(str "Impossible clauses:" ++ fnl () ++ pr_clauses env !clauses); *)
    Split (lhs, var, indf, unify, splits)
      
and make_split_aux env lhs clauses =
  let split = 
    fold_left (fun acc (lhs', rhs) -> 
      match acc with 
      | None -> find_split lhs lhs'
      | _ -> acc) None clauses
  in 
    match split with
    | Some var -> split_on env var lhs clauses
    | None ->
	(match clauses with
	| [] -> error "No clauses left"
	| [(lhs', rhs)] ->
	    (* No need to split anymore, fix the environments so that they are correctly aligned. *)
	    (match lhs_matches lhs' lhs with
	    | Some s ->
		let s = map (fun (x, p) -> x, (true, constr_of_pat ~inacc:false env p)) s in
		let rhs' = match rhs with
		  | Program c -> Program (specialize_constr s c)
		  | Empty i -> Empty (destRel (snd (assoc i s)))
		in Compute ((pi1 lhs, pi2 lhs, specialize_patterns s (pi3 lhs')), rhs')
	    | None -> anomaly "Non-matching clauses at a leaf of the splitting tree")
	| _ ->
	    errorlabstrm "make_split_aux"
	      (str "Overlapping clauses:" ++ fnl () ++ pr_clauses env clauses))

let make_split env (f, delta, t) clauses =
  make_split_aux env (delta, f, patvars_of_tele delta) clauses
    
open Evd
open Evarutil

let lift_substitution n s = map (fun (k, x) -> (k + n, x)) s
let map_substitution s t = map (subst_rel_subst 0 s) t

let term_of_tree status isevar env (i, delta, ty) ann tree =
(*   let envrec = match ann with *)
(*     | None -> [] *)
(*     | Some (loc, i) ->  *)
(* 	let (n, t) = lookup_rel_id i delta in *)
(* 	let t' = lift n t in *)
	  
	  
(*   in *)
  let rec aux = function
    | Compute ((ctx, _, pats as lhs), Program rhs) -> 
	let ty' = substl (rev (constrs_of_lhs ~inacc:false env lhs)) ty in
	let body = it_mkLambda_or_LetIn rhs ctx and typ = it_mkProd_or_LetIn ty' ctx in
	  mkCast(body, DEFAULTcast, typ), typ

    | Compute ((ctx, _, pats as lhs), Empty split) ->
	let ty' = substl (rev (constrs_of_lhs ~inacc:false env lhs)) ty in
	let split = (Name (id_of_string "split"), 
		    Some (Class_tactics.coq_nat_of_int (1 + (length ctx - split))),
		    Lazy.force Class_tactics.coq_nat)
	in
	let ty' = it_mkProd_or_LetIn ty' ctx in
	let let_ty' = mkLambda_or_LetIn split (lift 1 ty') in
	let term = e_new_evar isevar env ~src:(dummy_loc, QuestionMark (Define true)) let_ty' in
	  term, ty'
	    
    | Split ((ctx, _, pats as lhs), rel, indf, unif, sp) -> 
	let before, decl, after = split_tele (pred rel) ctx in
	let ty' = substl (rev (constrs_of_lhs ~inacc:false env lhs)) ty in
	let branches = 
	  array_map2 (fun (ctx', ctxlen, cstr, cstrpat, subst) split -> 
	    match split with
	    | Some s -> aux s
	    | None -> 
		(* dead code, inversion will find a proof of False by splitting on the rel'th hyp *)
		Class_tactics.coq_nat_of_int rel, Lazy.force Class_tactics.coq_nat)
	    unif sp 
	in
	let branches_ctx =
	  Array.mapi (fun i (br, brt) -> (id_of_string ("m_" ^ string_of_int i), Some br, brt))
	    branches
	in
	let n, branches_lets = 
	  Array.fold_left (fun (n, lets) (id, b, t) -> 
	    (succ n, (Name id, Option.map (lift n) b, lift n t) :: lets))
	    (0, []) branches_ctx
	in
	let liftctx = lift_contextn (Array.length branches) 0 ctx in
	let case =
	  let ty = it_mkProd_or_LetIn ty' liftctx in
	  let ty = it_mkLambda_or_LetIn ty branches_lets in
	  let nbbranches = (Name (id_of_string "branches"), 
			   Some (Class_tactics.coq_nat_of_int (length branches_lets)),
			   Lazy.force Class_tactics.coq_nat)
	  in
	  let nbdiscr = (Name (id_of_string "target"), 
			Some (Class_tactics.coq_nat_of_int (length before)),
			Lazy.force Class_tactics.coq_nat)
	  in
	  let ty = it_mkLambda_or_LetIn (lift 2 ty) [nbbranches;nbdiscr] in
	  let term = e_new_evar isevar env ~src:(dummy_loc, QuestionMark status) ty in
	    term
	in       
	let casetyp = it_mkProd_or_LetIn ty' ctx in
	  mkCast(case, DEFAULTcast, casetyp), casetyp

  in aux tree

open Topconstr
open Constrintern
open Decl_kinds

type equation = constr_expr * (constr_expr, identifier located) rhs

let locate_reference qid =
  match Nametab.extended_locate qid with
    | TrueGlobal ref -> true
    | SyntacticDef kn -> true

let is_global id =
  try 
    locate_reference (make_short_qualid id)
  with Not_found -> 
    false

let is_freevar ids env x =
  try
    if Idset.mem x ids then false
    else
      try ignore(Environ.lookup_named x env) ; false
      with _ -> not (is_global x)
  with _ -> true
  
let ids_of_patc c ?(bound=Idset.empty) l = 
  let found id bdvars l =
    if not (is_freevar bdvars (Global.env ()) (snd id)) then l
    else if List.exists (fun (_, id') -> id' = snd id) l then l 
    else id :: l 
  in
  let rec aux bdvars l c = match c with
    | CRef (Ident lid) -> found lid bdvars l
    | CNotation (_, "{ _ : _ | _ }", ((CRef (Ident (_, id))) :: _, _)) when not (Idset.mem id bdvars) ->
	fold_constr_expr_with_binders (fun a l -> Idset.add a l) aux (Idset.add id bdvars) l c
    | c -> fold_constr_expr_with_binders (fun a l -> Idset.add a l) aux bdvars l c
  in aux bound l c

let interp_pats i isevar env impls pat sign recu =
  let bound = Idset.singleton i in
  let vars = ids_of_patc pat ~bound [] in
  let varsctx, env' = 
    fold_right (fun (loc, id) (ctx, env) ->
      let decl =
	let ty = e_new_evar isevar env ~src:(loc, BinderType (Name id)) (new_Type ()) in
	  (Name id, None, ty) 
      in
	decl::ctx, push_rel decl env)
      vars ([], env)
  in
  let pats =
    let patenv = match recu with None -> env' | Some ty -> push_named (i, None, ty) env' in
    let patt, _ = interp_constr_evars_impls ~evdref:isevar patenv ~impls:([],[]) pat in
      match kind_of_term patt with
      | App (m, args) -> 
	  if not (eq_constr m (mkRel (succ (length varsctx)))) then
	    user_err_loc (constr_loc pat, "interp_pats",
			 str "Expecting a pattern for " ++ pr_id i)
	  else Array.to_list args
      | _ -> user_err_loc (constr_loc pat, "interp_pats",
			  str "Error parsing pattern: unnexpected left-hand side")
  in
    isevar := nf_evar_defs !isevar;
    (nf_rel_context_evar (Evd.evars_of !isevar) varsctx, 
    nf_env_evar (Evd.evars_of !isevar) env',
    rev_map (nf_evar (Evd.evars_of !isevar)) pats)
      
let interp_eqn i isevar env impls sign arity recu (pats, rhs) =
  let ctx, env', patcs = interp_pats i isevar env impls pats sign recu in
  let rhs' = match rhs with
    | Program p -> 
	let ty = nf_isevar !isevar (substl patcs arity) in
	  Program (interp_casted_constr_evars isevar env' ~impls p ty)
    | Empty lid -> Empty (fst (lookup_rel_id (snd lid) ctx))
  in ((ctx, i, pats_of_constrs (rev patcs)), rhs')	

open Entries

open Tacmach
open Tacexpr
open Tactics
open Tacticals

let contrib_tactics_path =
  make_dirpath (List.map id_of_string ["Equality";"Program";"Coq"])

let tactics_tac s =
  make_kn (MPfile contrib_tactics_path) (make_dirpath []) (mk_label s)
    
let equations_tac = lazy 
  (Tacinterp.eval_tactic 
      (TacArg(TacCall(dummy_loc, 
		     ArgArg(dummy_loc, tactics_tac "equations"), []))))

let define_by_eqs with_comp i (l,ann) t nt eqs =
  let env = Global.env () in
  let isevar = ref (create_evar_defs Evd.empty) in
  let (env', sign), impls = interp_context_evars isevar env l in
  let arity = interp_type_evars isevar env' t in
  let sign = nf_rel_context_evar (Evd.evars_of !isevar) sign in
  let arity = nf_evar (Evd.evars_of !isevar) arity in
  let arity = 
    if with_comp then
      let compid = add_suffix i "_comp" in
      let ce =
	{ const_entry_body = it_mkLambda_or_LetIn arity sign;
	  const_entry_type = None;
	  const_entry_opaque = false;
	  const_entry_boxed = false} 
      in
      let c =
	Declare.declare_constant compid (DefinitionEntry ce, IsDefinition Definition)
      in mkApp (mkConst c, rel_vect 0 (length sign))
    else arity
  in
  let env = Global.env () in
  let ty = it_mkProd_or_LetIn arity sign in
  let data = Command.compute_interning_datas env Constrintern.Recursive [] [i] [ty] [impls] in
  let fixdecls = [(Name i, None, ty)] in
  let fixenv = push_rel_context fixdecls env in
  let equations = 
    States.with_heavy_rollback (fun () -> 
      Option.iter (Command.declare_interning_data data) nt;
      map (interp_eqn i isevar fixenv data sign arity None) eqs) ()
  in
  let sign = nf_rel_context_evar (Evd.evars_of !isevar) sign in
  let arity = nf_evar (Evd.evars_of !isevar) arity in
  let prob = (i, sign, arity) in
  let fixenv = nf_env_evar (Evd.evars_of !isevar) fixenv in
  let fixdecls = nf_rel_context_evar (Evd.evars_of !isevar) fixdecls in
    (*   let ce = check_evars fixenv Evd.empty !isevar in *)
    (*   List.iter (function (_, _, Program rhs) -> ce rhs | _ -> ()) equations; *)
  let is_recursive, env' =
    let occur_eqn ((ctx, _, _), rhs) =
      match rhs with
      | Program c -> dependent (mkRel (succ (length ctx))) c
      | _ -> false
    in if exists occur_eqn equations then true, fixenv else false, env
  in
  let split = make_split env' prob equations in
    (* if valid_tree prob split then *)
  let status = (* if is_recursive then Expand else *) Define false in
  let t, ty = term_of_tree status isevar env' prob ann split in
  let undef = undefined_evars !isevar in
  let t, ty = if is_recursive then 
    (it_mkLambda_or_LetIn t fixdecls, it_mkProd_or_LetIn ty fixdecls)
    else t, ty
  in
  let obls, t', ty' = 
    Eterm.eterm_obligations env i !isevar (Evd.evars_of undef) 0 ~status t ty
  in
    if is_recursive then
      ignore(Subtac_obligations.add_mutual_definitions [(i, t', ty', impls, obls)] [] 
		~tactic:(Lazy.force equations_tac)
		(Command.IsFixpoint [None, CStructRec]))
    else
      ignore(Subtac_obligations.add_definition
		~implicits:impls i t' ty' ~tactic:(Lazy.force equations_tac) obls)
      
module Gram = Pcoq.Gram
module Vernac = Pcoq.Vernac_
module Tactic = Pcoq.Tactic

module DeppatGram =
struct
  let gec s = Gram.Entry.create ("Deppat."^s)

  let deppat_equations : equation list Gram.Entry.e = gec "deppat_equations"

  let binders_let2 : (local_binder list * (identifier located option * recursion_order_expr)) Gram.Entry.e = gec "binders_let2"

(*   let where_decl : decl_notation Gram.Entry.e = gec "where_decl" *)

end

open Rawterm
open DeppatGram 
open Util
open Pcoq
open Prim
open Constr
open G_vernac

GEXTEND Gram
  GLOBAL: (* deppat_gallina_loc *) deppat_equations binders_let2;
 
  deppat_equations:
    [ [ l = LIST1 equation SEP ";" -> l ] ]
  ;

  binders_let2:
    [ [ l = binders_let_fixannot -> l ] ]
  ;

  equation:
    [ [ c = Constr.lconstr; r=rhs -> (c, r) ] ]
  ;

  rhs:
    [ [ ":=!"; id = identref -> Empty id
      |":="; c = Constr.lconstr -> Program c
    ] ]
  ;
  
  END

type 'a deppat_equations_argtype = (equation list, 'a) Genarg.abstract_argument_type

let (wit_deppat_equations : Genarg.tlevel deppat_equations_argtype),
  (globwit_deppat_equations : Genarg.glevel deppat_equations_argtype),
  (rawwit_deppat_equations : Genarg.rlevel deppat_equations_argtype) =
  Genarg.create_arg "deppat_equations"

type 'a binders_let2_argtype = (local_binder list * (identifier located option * recursion_order_expr), 'a) Genarg.abstract_argument_type

let (wit_binders_let2 : Genarg.tlevel binders_let2_argtype),
  (globwit_binders_let2 : Genarg.glevel binders_let2_argtype),
  (rawwit_binders_let2 : Genarg.rlevel binders_let2_argtype) =
  Genarg.create_arg "binders_let2"

type 'a decl_notation_argtype = (Vernacexpr.decl_notation, 'a) Genarg.abstract_argument_type

let (wit_decl_notation : Genarg.tlevel decl_notation_argtype),
  (globwit_decl_notation : Genarg.glevel decl_notation_argtype),
  (rawwit_decl_notation : Genarg.rlevel decl_notation_argtype) =
  Genarg.create_arg "decl_notation"

let equations wc i l t nt eqs =
  try define_by_eqs wc i l t nt eqs
  with e -> msg (Cerrors.explain_exn e)

VERNAC COMMAND EXTEND Define_equations
| [ "Equations" ident(i) binders_let2(l) ":" lconstr(t) ":=" deppat_equations(eqs)
      decl_notation(nt) ] ->
    [ equations true i l t nt eqs ]
      END

VERNAC COMMAND EXTEND Define_equations2
| [ "Equations_nocomp" ident(i) binders_let2(l) ":" lconstr(t) ":=" deppat_equations(eqs)
      decl_notation(nt) ] ->
    [ equations false i l t nt eqs ]
END
    
let rec int_of_coq_nat c = 
  match kind_of_term c with
  | App (f, [| arg |]) -> succ (int_of_coq_nat arg)
  | _ -> 0

let solve_equations_goal destruct_tac tac gl =
  let concl = pf_concl gl in
  let targetn, branchesn, targ, brs, b =
    match kind_of_term concl with
    | LetIn (Name target, targ, _, b) ->
	(match kind_of_term b with
	| LetIn (Name branches, brs, _, b) ->
	    target, branches, int_of_coq_nat targ, int_of_coq_nat brs, b
	| _ -> error "Unnexpected goal")
    | _ -> error "Unnexpected goal"
  in
  let branches, b = 
    let rec aux n c =
      if n = 0 then [], c
      else match kind_of_term c with
      | LetIn (Name id, br, brt, b) -> 
	  let rest, b = aux (pred n) b in
	    (id, br, brt) :: rest, b
      | _ -> error "Unnexpected goal"
    in aux brs b
  in 
  let ids = targetn :: branchesn :: map pi1 branches in
  let cleantac = tclTHEN (intros_using ids) (thin ids) in
  let dotac = tclDO (succ targ) intro in
  let subtacs = 
    tclTHENS destruct_tac
      (map (fun (id, br, brt) -> tclTHEN (letin_tac None (Name id) br (Some brt) onConcl) tac) branches)
  in tclTHENLIST [cleantac ; dotac ; subtacs] gl
    
TACTIC EXTEND solve_equations
  [ "solve_equations" tactic(destruct) tactic(tac) ] -> [ solve_equations_goal (snd destruct) (snd tac) ]
    END

let coq_eq = Lazy.lazy_from_fun Coqlib.build_coq_eq
let coq_eq_refl = lazy ((Coqlib.build_coq_eq_data ()).Coqlib.refl)

let coq_heq = lazy (Coqlib.coq_constant "mkHEq" ["Logic";"JMeq"] "JMeq")
let coq_heq_refl = lazy (Coqlib.coq_constant "mkHEq" ["Logic";"JMeq"] "JMeq_refl")

let specialize_hyp id gl =
  let env = pf_env gl in
  let ty = pf_get_hyp_typ gl id in
  let evars = ref (create_evar_defs (project gl)) in
  let rec aux in_eqs acc ty =
    match kind_of_term ty with
    | Prod (_, t, b) -> 
	(match kind_of_term t with
	| App (eq, [| eqty; x; y |]) when eq_constr eq (Lazy.force coq_eq) ->
	    let pt = mkApp (Lazy.force coq_eq, [| eqty; x; x |]) in
	    let p = mkApp (Lazy.force coq_eq_refl, [| eqty; x |]) in
	      if e_conv env evars pt t then
		aux true (mkApp (acc, [| p |])) (subst1 p b)
	      else error "Unconvertible members of an homogeneous equality"
	| App (heq, [| eqty; x; eqty'; y |]) when eq_constr heq (Lazy.force coq_heq) ->
	    let pt = mkApp (Lazy.force coq_heq, [| eqty; x; eqty; x |]) in
	    let p = mkApp (Lazy.force coq_heq_refl, [| eqty; x |]) in
	      if e_conv env evars pt t then
		aux true (mkApp (acc, [| p |])) (subst1 p b)
	      else error "Unconvertible members of an heterogeneous equality"
	| _ -> 
	    if in_eqs then acc, in_eqs, ty
	    else 
	      let e = e_new_evar evars env t in
		aux false (mkApp (acc, [| e |])) (subst1 e b))
    | t -> acc, in_eqs, ty
  in 
  try 
    let acc, worked, ty = aux false (mkVar id) ty in
    let ty = Evarutil.nf_isevar !evars ty in
      if worked then
	tclTHENFIRST
	  (fun g -> Tacmach.internal_cut true id ty g)
	  (exact_no_check (Evarutil.nf_isevar !evars acc)) gl
      else tclFAIL 0 (str "Nothing to do in hypothesis " ++ pr_id id) gl
  with e -> tclFAIL 0 (Cerrors.explain_exn e) gl
      
TACTIC EXTEND specialize_hyp
[ "specialize_hypothesis" constr(c) ] -> [ 
  match kind_of_term c with
  | Var id -> specialize_hyp id
  | _ -> tclFAIL 0 (str "Not an hypothesis") ]
END