aboutsummaryrefslogtreecommitdiffstats
path: root/src/lfsc/converter.ml
blob: 8753bd049ed66e8ae1ae5c9e0b5b29b7bb27cec4 (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
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
(**************************************************************************)
(*                                                                        *)
(*     SMTCoq                                                             *)
(*     Copyright (C) 2011 - 2021                                          *)
(*                                                                        *)
(*     See file "AUTHORS" for the list of authors                         *)
(*                                                                        *)
(*   This file is distributed under the terms of the CeCILL-C licence     *)
(*                                                                        *)
(**************************************************************************)


open Ast
open Builtin
open Format
open Translator_sig


module Make (T : Translator_sig.S) = struct

  open T

  module MTerm = Map.Make (Term)


  (** Environment for {!lem} *)
  type env = {
    clauses : int list;     (** Accumulated clauses *)
    ax : bool;              (** Force use of axiomatic rules? *)
    mpred : bool MTerm.t;   (** map for positivity of predicates in cong *)
    assum : Hstring.t list; (** Assumptions that were not used *)
  }


  (** Empty environment *)
  let empty = {
    clauses = [];
    ax = false;
    mpred = MTerm.empty;
    assum = [];
  }


  (** Returns the formula of which p is a proof of *)
  let th_res p = match app_name (deref p).ttype with
    | Some (n, [r]) when n == H.th_holds -> r
    | _ -> assert false

  
  (** Ignore declarations at begining of proof *)
  let rec ignore_all_decls p = match value p with
    | Lambda (s, p) -> ignore_all_decls p
    | _ -> p

  
  (** Ignore declarations but keep assumptions *)
  let rec ignore_decls p = match value p with
    | Lambda (s, pr) ->
      (match s.sname with
       | Name n when (Hstring.view n).[0] = 'A' -> p
       | _ -> ignore_decls pr
      )
    | _ -> p

  
  (** Ignore result of preprocessing *)
  let rec ignore_preproc p = match app_name p with
    | Some (n, [_; _; p]) when n == H.th_let_pf ->
      begin match value p with
        | Lambda (_, p) -> ignore_preproc p
        | _ -> assert false
      end
    | _ -> p

  
  (** Produce input clauses from the result of CVC4's pre-processing. This may
      not match the actual inputs in the original SMT2 file but they correspond
      to what the proof uses. *)
  let rec produce_inputs_preproc p = match app_name p with
    | Some (n, [_; _; p]) when n == H.th_let_pf ->
      begin match value p with
        | Lambda ({sname = Name h; stype}, p) ->
          begin match app_name stype with
            | Some (n, [formula]) when n == H.th_holds ->
              mk_input h formula;
              produce_inputs_preproc p
            | _ -> assert false
          end
        | _ -> assert false
      end
    | _ -> p


  (** Produce inputs from the assumptions *)
  let rec produce_inputs p = match value p with
    | Lambda ({sname = Name h; stype}, p) ->
      begin match app_name stype with
        | Some (n, [formula])
          when n == H.th_holds &&
               (match name formula with
                | Some f when f == H.ttrue -> false | _ -> true)
          ->
          mk_input h formula;
          produce_inputs p
        | _ -> produce_inputs p
      end
    | _ -> p


  let dvar_of_v t = match app_name t with
    | Some (n, [_; v]) when n == H.a_var_bv -> v
    | _ -> t

  
  let trust_vareq_as_alias formula = match app_name formula with
    | Some (n, [ty; alias; t]) when n == H.eq ->
      (match name (dvar_of_v alias) with
      | Some n -> register_alias n t; true
      | None -> false)
    | _ -> false

  
  let rec admit_preproc p = match app_name p with
    | Some (n, [_; tr; p]) when n == H.th_let_pf ->
      begin match app_name tr with
        | Some (n, _) when n == H.trust_f ->
          eprintf "Warning: hole for trust_f.@."
        | Some (rule, _) ->
          eprintf "Warning: hole for unsupported rule %a.@." Hstring.print rule
        | None -> eprintf "Warning: hole@."
      end;
      let formula = th_res tr in
      begin match value p with
        | Lambda ({sname = Name h}, p) ->
          if not (trust_vareq_as_alias formula) then
            mk_admit_preproc h formula;
          admit_preproc p
        | _ -> assert false
      end
    | _ -> p



  (** Handle deferred declarations in LFSC (for extensionality rule atm.) *)
  let rec deferred p = match app_name p with
    | Some (n, [ty_i; ty_e; a; b; p]) when n == H.ext ->
      begin match value p with
        | Lambda ({sname = Name index_diff}, p) ->
          begin match value p with
            | Lambda ({sname = Name h}, p) ->
              let diff_a_b = (apply_diff ty_i ty_e a b) in
              register_alias index_diff diff_a_b;
              let f =
                or_ (eq (array ty_i ty_e) a b)
                  (not_ (eq ty_e
                           (apply_read ty_i ty_e a diff_a_b)
                           (apply_read ty_i ty_e b diff_a_b))) in
              let cid = mk_clause_cl Exte [f] [] in
              register_decl_id h cid;
              deferred p
            | _ -> assert false
          end
        | _ -> assert false
      end
    | _ -> p


   
  (** Registers a propositional variable as an abstraction for a
      formula. Proofs in SMTCoq have to be given in terms of formulas. *)
  let rec register_prop_vars p = match app_name p with
    | Some (n, [formula; p]) when n == H.decl_atom ->
      begin match value p with
        | Lambda (v, p) ->
          let vt = (symbol_to_const v) in
          (* eprintf "register prop var: %a@." print_term_type vt; *)
          register_prop_abstr vt formula;
          begin match value p with
            | Lambda (_, p) -> register_prop_vars p
            | _ -> assert false
          end
        | _ -> assert false
      end
    | _ -> p


  (** Returns the name of the local assumptions made in [satlem] *)
  let rec get_assumptions acc p = match app_name p with
    | Some (n, [_; _; _; _; p]) when n == H.ast || n == H.asf ->
      begin match value p with
        | Lambda ({sname = Name n}, p) -> get_assumptions (n :: acc) p
        | _ -> assert false
      end
    | _ -> acc, p



  let rec rm_used' assumptions t = match name t with
    | Some x -> List.filter (fun y -> y != x) assumptions
    | None -> match app_name t with
      | Some (_, l) -> List.fold_left rm_used' assumptions l
      | None -> assumptions

  (** Remove used assumptions from the environment *)
  let rm_used env t = { env with assum = rm_used' env.assum t }


  let rm_duplicates eq l =
    let rec aux acc = function
      | x :: r -> if List.exists (eq x) acc then aux acc r else aux (x :: acc) r
      | [] -> acc in
    aux [] (List.rev l)
  
  (** Create an intermediate resolution step in [satlem] with the accumulated
      clauses. {!Reso} ignores the resulting clause so we can just give the
      empty clause here. *)
  let mk_inter_resolution clauses = match clauses with
    | [] -> (* not false *)
      mk_clause_cl Fals [not_ tfalse] []
      (* assert false *)
    | [id] -> id
    | _ -> mk_clause ~reuse:false Reso [] clauses



  let is_ty_Bool ty = match name ty with
    | Some n -> n == H.tBool
    | _ -> false


  (** Accumulates equalities for congruence. This is useful for when [f] takes
      multiples arguments. *)
  let rec cong neqs env p = match app_name p with
    | Some (n, [ty; rty; f; f'; x; y; p_f_eq_f'; r]) when n == H.cong ->

      let ne = not_ (eq ty x y) in
      let neqs, env =
        if List.exists (Term.equal ne) neqs then neqs, env
        else ne :: neqs, lem env r in

      begin match name f, name f' with
        | Some n, Some n' when n == n' -> neqs, env
        | None, None -> cong neqs env p_f_eq_f'
        | _ -> assert false
      end

    | Some (n, [_; _; _; r])
      when n == H.symm || n == H.negsymm ->
      cong neqs (rm_used env r) r

    (* | Some (n, [t; x; y; z; r1; r2]) when n == H.trans *)
    (* | Some (n, [t; x; z; y; r1; r2]) when n == H.negtrans || n == H.negtrans1 *)
    (* | Some (n, [t; y; x; z; r1; r2]) when n == H.negtrans2 *)

    | Some (n, [t; x1; x2; x3; r1; r2])
      when n == H.trans || n == H.negtrans ||
           n == H.negtrans1 || n == H.negtrans2 ->

      let x, y, z =
        if n == H.trans then x1, x2, x3
        else if n == H.negtrans || n == H.negtrans1 then x1, x3, x2
        else if n == H.negtrans2 then x2, x1, x3
        else assert false
      in

      (* ignore useless transitivity *)
      if term_equal x z then
        match app_name x with
        | Some (n, [t; _; _; x]) when n == H.apply ->
          let x_x = eq t x x in
          not_ x_x :: neqs,
          { env with clauses = mk_clause_cl Eqre [x_x] [] :: env.clauses }
        | _ ->
          let x_x = eq t x x in
          not_ x_x :: neqs,
          { env with clauses = mk_clause_cl Eqre [x_x] [] :: env.clauses }
      else if term_equal x y then cong neqs (rm_used env r2) r2
      else if term_equal y z then cong neqs (rm_used env r1) r1
      else
        let neqs1, env1 = cong neqs (rm_used env r1) r1 in
        cong neqs1 (rm_used env1 r2) r2

    (* | Some ("refl", [_; r]) -> neqs, rm_used env r *)

    | _ -> neqs, env
      (* eprintf "something went wrong in congruence@."; *)
      (* neqs, lem env p (\* env *\) *)


  (** Accumulates equalities for transitivity to chain them together. *)
  and trans neqs env p = match app_name p with

    | Some (n, [ty; x; y; z; p1; p2]) when n == H.trans ->
    (* | Some (("negtrans"|"negtrans1") as r, [ty; x; z; y; p1; p2]) *)
    (* | Some ("negtrans2" as r, [ty; y; x; z; p1; p2]) *)

      let merge = true in
      
      (* let clauses = lem mpred assum (lem mpred assum clauses p1) p2 in *)

      (* let x_y = th_res p1 in *)
      (* let y_z = th_res p2 in *)
      (* let x_y = match r with "negtrans2" -> eq ty y x | _ -> eq ty x y in *)
      (* let y_z = match r with "negtrans"|"negtrans1" -> eq ty z y | _ -> eq ty y z in *)
      let n_x_y = not_ (eq ty x y) in
      let n_y_z = not_ (eq ty y z) in

      let neqs2, env = if merge then trans neqs env p2 else [], lem env p2 in
      let neqs1, env = if merge then trans neqs env p1 else [], lem env p1 in

      let neqs = match neqs1, neqs2 with
        | [], [] -> [n_x_y; n_y_z]
        | [], _ -> n_x_y :: neqs2
        | _, [] -> neqs1 @ [n_y_z]
        | _, _ -> neqs1 @ neqs2
      in

      (* rm_duplicates Term.equal neqs *)
      neqs, env

    | Some (n, [_; _; _; r]) when n == H.symm || n == H.negsymm ->
      let neqs, env = trans neqs (rm_used env r) r in
      List.rev neqs, env
      
    | Some (n, [_; r]) when n == H.refl -> neqs, rm_used env r
  
    | _ -> neqs, lem env p


  

  (** Convert the local proof of a [satlem]. We use decductive style rules when
      possible but revert to axiomatic ones when the context forces us to. *)
  and lem ?(toplevel=false) env p = match app_name p with
    | Some (n, [l1; l2; x; r])
      when (n == H.or_elim_1 || n == H.or_elim_2) &&
        (match app_name r with
          | Some (n, _) -> n == H.iff_elim_1 || n == H.iff_elim_2
          | _ -> false)
      ->

      let el, rem = if n == H.or_elim_1 then l1, l2 else l2, l1 in
      
      let env = lem env r in
      let env = lem env x in
      (match env.clauses with
       | ci1 :: ci2 :: cls ->
         { env with clauses = mk_clause_cl Reso [rem] [ci1; ci2] :: cls }
       | _ -> env
      )

    | Some (n, [_; _; x; r])
      when (n == H.or_elim_1 || n == H.or_elim_2) &&
        (match app_name r with
          | Some (n, _) -> n == H.impl_elim ||
                           n == H.not_and_elim ||
                           n == H.iff_elim_1 ||
                           n == H.iff_elim_2 ||
                           n == H.xor_elim_1 ||
                           n == H.xor_elim_2 ||
                           n == H.ite_elim_1 ||
                           n == H.ite_elim_2 ||
                           n == H.ite_elim_3 ||
                           n == H.not_ite_elim_1 ||
                           n == H.not_ite_elim_2 ||
                           n == H.not_ite_elim_3
          | _ -> false)
      ->
      let env = rm_used env x in
      let env = lem env r in
      { env with ax = true }

    | Some (n, [a; b; x; r]) when n == H.or_elim_1 || n == H.or_elim_2 ->
      let env = rm_used env x in
      let env = lem env r in
      let clauses = match env.clauses with
        | [_] when not env.ax -> mk_clause_cl Or [a; b] env.clauses :: []
        | _ ->
          let a_or_b = th_res r in
          mk_clause_cl Orp [not_ a_or_b; a; b] [] :: env.clauses
      in
      { env with clauses; ax = true }

    | Some (n, [a; b; r]) when n == H.impl_elim ->
      let env = lem env r in
      let clauses = match env.clauses with
        | [_] when not env.ax -> mk_clause_cl Imp [not_ a; b] env.clauses :: []
        | _ ->
          let a_impl_b = th_res r in
          mk_clause_cl Impp [not_ a_impl_b; not_ a; b] [] :: env.clauses
      in
      { env with clauses }

    | Some (n, [a; b; r]) when n == H.xor_elim_1 ->
      let env = lem env r in
      let clauses = match env.clauses with
        | [_] when not env.ax ->
          mk_clause_cl Xor2 [not_ a; not_ b] env.clauses :: []
        | _ ->
          let a_xor_b = xor_ a b in
          mk_clause_cl Xorp2 [not_ a_xor_b; not_ a; not_ b] [] :: env.clauses
      in
      { env with clauses }

    | Some (n, [a; b; r]) when n == H.xor_elim_2 ->
      let env = lem env r in
      let clauses = match env.clauses with
        | [_] when not env.ax ->
          mk_clause_cl Xor1 [a; b] env.clauses :: []
        | _ ->
          let a_xor_b = xor_ a b in
          mk_clause_cl Xorp1 [not_ a_xor_b; a; b] [] :: env.clauses
      in
      { env with clauses }

    | Some (n, [a; b; c; r]) when n == H.ite_elim_1 ->
      let env = lem env r in
      let clauses = match env.clauses with
        | [_] when not env.ax ->
          mk_clause_cl Ite2 [not_ a; b] env.clauses :: []
        | _ ->
          let ite_a_b_c = ifte_ a b c in
          mk_clause_cl Itep2 [not_ ite_a_b_c; not_ a; b] [] :: env.clauses
      in
      { env with clauses }

    | Some (n, [a; b; c; r]) when n == H.ite_elim_2 ->
      let env = lem env r in
      let clauses = match env.clauses with
        | [_] when not env.ax ->
          mk_clause_cl Ite1 [a; c] env.clauses :: []
        | _ ->
          let ite_a_b_c = ifte_ a b c in
          mk_clause_cl Itep1 [not_ ite_a_b_c; a; c] [] :: env.clauses
      in
      { env with clauses }

    | Some (n, [a; b; c; r]) when n == H.not_ite_elim_1 ->
      let env = lem env r in
      let clauses = match env.clauses with
        | [_] when not env.ax ->
          mk_clause_cl Nite2 [not_ a; not_ b] env.clauses :: []
        | _ ->
          let ite_a_b_c = ifte_ a b c in
          mk_clause_cl Iten2 [ite_a_b_c; not_ a; not_ b] [] :: env.clauses
      in
      { env with clauses }

    | Some (n, [a; b; c; r]) when n == H.not_ite_elim_2 ->
      let env = lem env r in
      let clauses = match env.clauses with
        | [_] when not env.ax ->
          mk_clause_cl Nite1 [a; not_ c] env.clauses :: []
        | _ ->
          let ite_a_b_c = ifte_ a b c in
          mk_clause_cl Iten1 [ite_a_b_c; a; not_ c] [] :: env.clauses
      in
      { env with clauses }

    | Some (n, [a; b; c; r]) when n == H.ite_elim_3 ->
      let env = lem env r in
      let ite_a_b_c = ifte_ a b c in
      { env with
        clauses =
          mk_clause_cl Itep1 [not_ ite_a_b_c; a; c] [] ::
          mk_clause_cl Itep2 [not_ ite_a_b_c; not_ a; b] [] ::
          env.clauses;
        ax = true }

    | Some (n, [a; b; c; r]) when n == H.not_ite_elim_3 ->
      let env = lem env r in
      let ite_a_b_c = ifte_ a b c in
      { env with
        clauses =
          mk_clause_cl Iten1 [ite_a_b_c; a; not_ c] [] ::
          mk_clause_cl Iten2 [ite_a_b_c; not_ a; not_ b] [] ::
          env.clauses;
        ax = true }

    | Some (n, [a; b; r]) when n == H.iff_elim_1 ->
      begin match app_name r with
        | Some (n, [a; b; r]) when n == H.not_iff_elim ->
          let env = lem env r in
          let clauses = match env.clauses with
            | [_] when not env.ax ->
              mk_clause_cl Nequ2 [not_ a; not_ b] env.clauses :: []
            | _ ->
              let a_iff_b = iff_ a b in
              mk_clause_cl Equn1 [a_iff_b; not_ a; not_ b] [] :: env.clauses
          in
          { env with clauses }
        | Some (n, [a; b; r]) when n == H.not_xor_elim ->
          let env = lem env r in
          let clauses = match env.clauses with
            | [_] when not env.ax ->
              mk_clause_cl Nxor2 [not_ a; b] env.clauses :: []
            | _ ->
              let a_xor_b = xor_ a b in
              mk_clause_cl Xorn2 [a_xor_b; not_ a; b] [] :: env.clauses
          in
          { env with clauses }
        | _ ->
          let env = lem env r in
          let clauses = match env.clauses with
            | [_] when not env.ax ->
              mk_clause_cl Equ1 [not_ a; b] env.clauses :: []
            | _ ->
              let a_iff_b = th_res r in
              mk_clause_cl Equp2 [not_ a_iff_b; not_ a; b] [] :: env.clauses
          in
          { env with clauses }
      end

      | Some (n, [a; b; r]) when n == H.iff_elim_2 ->
      begin match app_name r with
        | Some (n, [a; b; r]) when n == H.not_iff_elim ->
          let env = lem env r in
          let clauses = match env.clauses with
            | [_] when not env.ax ->
              mk_clause_cl Nequ1 [a; b] env.clauses :: []
            | _ ->
              let a_iff_b = iff_ a b in
              mk_clause_cl Equn2 [a_iff_b; a; b] [] :: env.clauses
          in
          { env with clauses }
        | Some (n, [a; b; r]) when n == H.not_xor_elim ->
          let env = lem env r in
          let clauses = match env.clauses with
            | [_] when not env.ax ->
              mk_clause_cl Nxor1 [a; not_ b] env.clauses :: []
            | _ ->
              let a_xor_b = xor_ a b in
              mk_clause_cl Xorn1 [a_xor_b; a; not_ b] [] :: env.clauses
          in
          { env with clauses }
        | _ ->
          let env = lem env r in
          let clauses = match env.clauses with
            | [_] when not env.ax ->
              mk_clause_cl Equ2 [a; not_ b] env.clauses :: []
            | _ ->
              let a_iff_b = th_res r in
              mk_clause_cl Equp1 [not_ a_iff_b; a; not_ b] [] :: env.clauses
          in
          { env with clauses }
      end

    | Some (n, [a; b; r]) when n == H.not_and_elim ->
      let env = lem env r in
      let clauses = match env.clauses with
        | [_] when not env.ax ->
          mk_clause_cl Nand [not_ a; not_ b] env.clauses :: []
        | _ ->
          let a_and_b = and_ a b in
          mk_clause_cl Andn [a_and_b; not_ a; not_ b] [] :: env.clauses
      in
      { env with clauses }

    | Some (n, [a; _; r]) when n == H.and_elim_1 ->
      begin match app_name r with
        | Some (n, [a; b; r]) when n == H.not_impl_elim ->
          let env = lem env r in
          let clauses = match env.clauses with
            | [_] when not env.ax -> mk_clause_cl Nimp1 [a] env.clauses :: []
            | _ ->
              let a_impl_b = impl_ a b in
              mk_clause_cl Impn1 [a_impl_b; a] [] :: env.clauses
          in
          { env with clauses }

        | Some (n, [a; b; r]) when n == H.not_or_elim ->
          let env = lem env r in
          let clauses = match env.clauses with
            | [id] when not env.ax -> mk_clause_cl Nor [not_ a] [id; 0] :: []
            | _ ->
              let a_or_b = or_ a b in
              mk_clause_cl Orn [a_or_b; not_ a] [0] :: env.clauses
          in
          { env with clauses }

        | _ ->
          let env = lem env r in
          let clauses = match env.clauses with
            | [id] when not env.ax -> mk_clause_cl And [a] [id; 0] :: []
            | _ ->
              let a_and_b = th_res r in
              mk_clause_cl Andp [not_ a_and_b; a] [0] :: env.clauses
          in
          { env with clauses }
      end

    | Some (n, [a; b; r]) when n == H.and_elim_2 ->
      begin match app_name r with
        | Some (n, [a; b; r]) when n == H.not_impl_elim ->
          let env = lem env r in
          let clauses = match env.clauses with
            | [_] when not env.ax ->
              mk_clause_cl Nimp2 [not_ b] env.clauses :: []
            | _ ->
              let a_impl_b = impl_ a b in
              mk_clause_cl Impn2 [a_impl_b; not_ b] [] :: env.clauses
          in
          { env with clauses }

        | Some (n, [a; b; r]) when n == H.not_or_elim ->
          let env = lem env r in
          let clauses = match env.clauses with
            | [id] when not env.ax -> mk_clause_cl Nor [not_ b] [id; 1] :: []
            | _ ->
              let a_or_b = or_ a b in
              mk_clause_cl Orn [a_or_b; not_ b] [1] :: env.clauses
          in
          { env with clauses }

        | _ ->
          let env = lem env r in
          let clauses = match env.clauses with
            | [id] when not env.ax -> mk_clause_cl And [b] [id; 1] :: []
            | _ ->
              let a_and_b = th_res r in
              mk_clause_cl Andp [not_ a_and_b; b] [1] :: env.clauses
          in
          { env with clauses }
      end

    (* Only handle symmetry rules when they are the only rule of the lemma *)
      
    | Some (n, [ty; a; b; r])
      when n == H.symm && toplevel && name r <> None ->
      let env = lem env r in
      let a_b = eq ty a b in
      let b_a = eq ty b a in

      { env with
        clauses = mk_clause_cl Eqtr [not_ a_b; b_a] [] :: env.clauses;
        ax = true }

    | Some (n, [ty; a; b; r])
      when n == H.negsymm && toplevel && name r <> None ->
      let env = lem env r in
      let a_b = eq ty a b in
      let b_a = eq ty b a in

      { env with
        clauses = mk_clause_cl Eqtr [a_b; not_ b_a] [] :: env.clauses;
        ax = true }

    (* Ignore other symmetry of equlity rules *)
    | Some (n, [_; _; _; r]) when n == H.symm || n == H.negsymm ->
      lem (rm_used env r) r

    (* Ignore double negation *)
    | Some (n, [_; r]) when n == H.not_not_elim || n == H.not_not_intro ->
      lem env r

    (* Should not be traversed anyway *)
    | Some (n, [_; r]) when n == H.pred_eq_t || n == H.pred_eq_f ->
      lem env r


    | Some (n, [f]) when n == H.trust_f ->
      begin match app_name f with
        | Some (n, ty :: _)
          when n == H.eq &&
               (match name ty with Some i -> i == H.tInt | None -> false) ->
          (* trust are for lia lemma if equality between integers *)
          { env with clauses = mk_clause_cl Lage [f] [] :: env.clauses }
        | Some (n, [x]) when n == H.not_ ->
          begin match app_name x with
            | Some (n, ty :: _)
              when n == H.eq &&
                   (match name ty with Some i -> i == H.tInt | None -> false) ->
              (* trust are for lia lemma if disequality between integers *)
              { env with clauses = mk_clause_cl Lage [f] [] :: env.clauses }
            | _ -> { env with clauses = mk_clause_cl Hole [f] [] :: env.clauses }
          end
        | _ -> { env with clauses = mk_clause_cl Hole [f] [] :: env.clauses }
      end
      
    | Some (n, [_; _; _; _; r; w])
      when n == H.trans &&
           (match app_name w with
            | Some (n, _) -> n == H.pred_eq_t || n == H.pred_eq_f
            | _ -> false)
      ->
      (* Remember which direction of the implication we want for congruence over
         predicates *)
      let env = match app_name w with
        | Some (n, [pt; x]) when n == H.pred_eq_t ->
          let env = rm_used env x in
          { env with mpred = MTerm.add pt false env.mpred }
        | Some (n, [pt; x]) when n == H.pred_eq_f ->
          let env = rm_used env x in
          { env with mpred = MTerm.add pt true env.mpred }
        | _ -> assert false
      in

      lem env r


    | Some (n, [ty; x; y; z; p1; p2])
      when n == H.negtrans || n == H.negtrans1 ->

      if term_equal x y || term_equal x z || term_equal y z then env
      else 
        let env = lem env p2 in
        let env = lem env p1 in

        let x_y = eq ty x y in
        let y_z = eq ty y z in
        let x_z = eq ty x z in

        { env with
          clauses = mk_clause_cl Eqtr [x_y; not_ y_z; not_ x_z] [] :: env.clauses;
          ax = true }

    | Some (n, [ty; x; y; z; p1; p2]) when n == H.negtrans2 ->

      if term_equal x y || term_equal x z || term_equal y z then env
      else 
        let env = lem env p2 in
        let env = lem env p1 in

        let x_y = eq ty x y in
        let y_z = eq ty y z in
        let x_z = eq ty x z in

        { env with
          clauses = mk_clause_cl Eqtr [not_ x_y; y_z; not_ x_z] [] :: env.clauses;
          ax = true }

    | Some (n, [ty; x; y; z; p1; p2]) when n == H.trans ->
    (* | Some (("negtrans"|"negtrans1"), [ty; x; z; y; p1; p2]) *)
    (* | Some ("negtrans2", [ty; y; x; z; p1; p2]) *)

      (* if Term.equal x y || Term.equal x z || Term.equal y z then env *)
      (* else  *)
      
      let neqs, env = trans [] env p in
      let x_z = eq ty x z in
      let cl = (neqs @ [x_z]) in
      let id = mk_clause_cl Eqtr cl [] in
      let id = mk_clause_cl ~reuse:false Weak cl [id] in
      { env with 
        clauses = id :: env.clauses;
        ax = true }

    (* | Some ("trans", [ty; x; y; z; p1; p2]) ->

       (* let clauses1 = lem mpred assum clauses p1 in *)
       (* let clauses2 = lem mpred assum clauses p2 in *)

       (* TODO: intermediate resolution step *)
       let clauses = lem mpred assum (lem mpred assum clauses p1) p2 in

       let x_y = th_res p1 in
       let y_z = th_res p2 in
       let x_z = eq ty x z in
       let clauses = mk_clause_cl "eq_transitive" [not_ x_y; not_ y_z; x_z] [] :: clauses in


       (* let cl1 = [th_res p1] in *)
       (* let cl2 = [th_res p2] in *)
       (* let clauses = [ *)
       (*   mk_inter_resolution cl1 clauses1; *)
       (*   mk_inter_resolution cl2 clauses2] *)
       (* in *)
       clauses
    *)

    (* Congruence with predicates *)
    | Some (n, [_; rty; pp; _; x; y; _; _])
      when n == H.cong && is_ty_Bool rty ->
      
      let neqs, env = cong [] env p in
      let cptr, cpfa = match app_name (th_res p) with
        | Some (n, [_; apx; apy]) when n == H.eq ->
          (match MTerm.find apx env.mpred, MTerm.find apy env.mpred with
           | true, false -> p_app apx, not_ (p_app apy)
           | false, true -> p_app apy, not_ (p_app apx)
           | true, true -> p_app apx, p_app apy
           | false, false -> not_ (p_app apx), not_ (p_app apy)
          )
        | _ -> assert false
      in
      let cl = neqs @ [cpfa; cptr] in
      { env with
        clauses = mk_clause_cl Eqcp cl [] :: env.clauses;
        ax = true }

    (* Congruence *)
    | Some (n, [_; _; _; _; _; _; _; _]) when n == H.cong ->
      let neqs, env = cong [] env p in
      let fx_fy = th_res p in
      let cl = neqs @ [fx_fy] in
      { env with
        clauses = mk_clause_cl Eqco cl [] :: env.clauses;
        ax = true }

    | Some (n, [_; _]) when n == H.refl ->
      let x_x = th_res p in
      { env with clauses = mk_clause_cl Eqre [x_x] [] :: env.clauses }

    | Some (n, [_; _; a; i; v]) when n == H.row1 ->
      let raiwaiv = th_res p in
      { env with clauses = mk_clause_cl Row1 [raiwaiv] [] :: env.clauses }

    | Some (n, [ti; _; i; j; a; v; r]) when n == H.row ->
      let env = lem env r in
      let i_eq_j = eq ti i j in
      let pr1 = th_res p in
      { env with
        clauses = mk_clause_cl Row2 [i_eq_j; pr1] [] :: env.clauses;
        ax = true}

    | Some (n, [ti; _; i; j; a; v; npr1]) when n == H.negativerow ->
      let env = lem env npr1 in
      let i_eq_j = eq ti i j in
      let pr1 = match app_name (th_res p) with
        | Some (n, [pr1]) when n == H.not_ -> pr1
        | _ -> assert false
      in
      { env with clauses = mk_clause_cl Row2 [i_eq_j; pr1] [] :: env.clauses }

    | Some (n, [_; x; y]) when n == H.bv_disequal_constants ->
      { env with clauses = mk_clause_cl Bbdis [th_res p] [] :: env.clauses }
  
    | Some (rule, args) ->
      eprintf "Warning: Introducing hole for unsupported rule %a@."
        Hstring.print rule;
      { env with clauses = mk_clause_cl Hole [th_res p] [] :: env.clauses }

    | None ->

      match name p with

      | Some n when n == H.truth ->
        { env with clauses = mk_clause_cl True [ttrue] [] :: env.clauses }
      
      | Some h ->
        (* should be an input clause *)
        (try { env with clauses = get_input_id h :: env.clauses }
         with Not_found ->
           { env with
             ax = true;
             assum = List.filter (fun a -> a <> h) env.assum }
        )

      | None -> { env with ax = true }


  
  (** Returns the name given to this lemma, its type and the continuation. *)
  let result_satlem p = match value p with
    | Lambda ({sname=Name n} as s, r) ->

      begin match app_name s.stype with
        | Some (n, [cl]) when n == H.holds -> n, cl, r
        | _ -> assert false
      end

    | _ -> assert false


  (** Returns the clause used in a resolution step *)
  let clause_qr p =
    try match name p with
      | Some n -> get_input_id n
      | _ -> raise Not_found
    with Not_found -> match app_name (deref p).ttype with
      | Some (n, [cl]) when n == H.holds ->
        (* eprintf "get_clause id : %a@." print_term cl; *)
        get_clause_id (to_clause cl)
      | _ -> raise Not_found
             

  (* let rec reso_of_QR acc qr = match app_name qr with
   *   | Some (n, [_; _; u1; u2; _]) when n == H.q || n == H.r ->
   *     reso_of_QR (reso_of_QR acc u1) u2
   *   | _ -> clause_qr qr :: acc *)

  (** Returns clauses used in a linear resolution chain *)
  (* let reso_of_QR qr = reso_of_QR [] qr |> List.rev *)


  (* let rec reso_of_QR qr = match app_name qr with
   *   | Some (n, [_; _; u1; u2; _]) when n == H.q || n == H.r ->
   *     reso_of_QR u1 @ reso_of_QR u2
   *   | _ -> [clause_qr qr] *)

  let rec reso_of_QR depth acc qr = match app_name qr with
    | Some (n, [_; _; u1; u2; _]) when n == H.q || n == H.r ->
      let depth = depth + 1 in
      reso_of_QR depth (reso_of_QR depth acc u1) u2
    | _ -> (depth, clause_qr qr) :: acc

  (** Returns clauses used in a linear resolution chain *)
  let reso_of_QR qr =
    reso_of_QR 0 [] qr
    |> List.rev
    |> List.stable_sort (fun (d1, _) (d2, _) -> d2 - d1)
    |> List.map snd
  
  
  (** convert resolution proofs of [satlem_simplify] *)
  let satlem_simplify p = match app_name p with
    | Some (n, [_; _; _; qr; p]) when n == H.satlem_simplify ->
      let clauses = reso_of_QR qr in
      let lem_name, res, p = result_satlem p in
      let cl_res = to_clause res in
      let id = mk_clause ~reuse:false Reso cl_res clauses in
      register_clause_id cl_res id;
      register_decl_id lem_name id;
      Some id, p
    | _ -> raise Exit

  
  let rec many_satlem_simplify lastid p =
    try
      let lastid, p = satlem_simplify p in
      many_satlem_simplify lastid p
    with Exit -> lastid, p


  (* can be empty, returns continuation *)
  let satlem_simplifies_c p =
    many_satlem_simplify None p |> snd


  (* There must be at least one, returns id of last deduced clause *)
  let reso_of_satlem_simplify p =
    match many_satlem_simplify None p with
    | Some id, _ -> id
    | _ -> assert false


  let rec bb_trim_intro_unit env p = match app_name p with
    | Some (n, [_; _; _; ullit; _; l])
      when n == H.intro_assump_f || n == H.intro_assump_t ->
      let env = rm_used env ullit in
      (match value l with
       | Lambda (_, p) -> bb_trim_intro_unit env p
       | _ -> assert false)
    | _ -> env, p

  
  let is_last_bbres p = match app_name p with
    | Some (n, [_; _; _; _; l]) when n == H.satlem_simplify ->
      (match value l with
       | Lambda ({sname=Name e}, pe) ->
         (match name pe with Some ne -> ne = e | None -> false)
       | _ -> false)
    | _ -> false


  let rec bb_lem_res lastid p =
    try
      if is_last_bbres p then raise Exit;
      let lastid, p = satlem_simplify p in
      bb_lem_res lastid p
    with Exit -> match lastid with
      | Some id -> id
      | None -> assert false


  let bb_lem env p =
    let env, p = bb_trim_intro_unit env p in
    let id = bb_lem_res None p in
    { env with clauses = id :: env.clauses }
  


  exception ArithLemma

  (** Remove superfluous applications at the top of [satlem] and returns a list
      of proofs whose resulting clauses need to be resolved.

      @raises {!ArithLemma} if the proof is a trust statement (we assume it is
      the case for now).  *)
  let rec trim_junk_satlem p = match app_name p with
    | Some (n, [p]) when n == H.clausify_false ->
      (match name p with
       | Some n when n == H.trust -> raise ArithLemma
       | _ -> trim_junk_satlem p
      )
    | Some (n, [_; p1; p2]) when n == H.contra ->
      trim_junk_satlem p1 @ trim_junk_satlem p2
    | _ -> [p]

  
  (** Returns the continuation of a [satlem]. *)
  let continuation_satlem p = match value p with
    | Lambda ({sname=Name n}, r) -> n, r
    | _ -> assert false


  let is_bbr_satlem_lam p = match value p with
    | Lambda ({sname = Name h}, _) ->
      (try String.sub (Hstring.view h) 0 5 = "bb.cl"
       with Invalid_argument _ -> false)
    | _ -> false 
  
  let has_intro_bv p = match app_name p with
    | Some (n, _) when n == H.intro_assump_f || n == H.intro_assump_t -> true
    | _ -> false


  let has_prefix p s =
    try
      for i = 0 to String.length p - 1 do
        if p.[i] <> s.[i] then raise Exit
      done;
      true
    with Exit | Invalid_argument _ -> false
      
  
  (** Convert [satlem]. Clauses are chained together with an intermediate
      resolution step when needed, and when CVC4 uses superfluous local
      assumption, the clause is weakened. *)
  let rec satlem ?(prefix_cont) p =
    let old_p = p in
    match app_name p with

    | Some (n, [c; _; l; p]) when n == H.satlem ->
      (* eprintf "SATLEM ---@."; *)
      let lem_name, lem_cont = continuation_satlem p in
      begin match prefix_cont with
        | Some pref when not (has_prefix pref (Hstring.view lem_name)) -> old_p
        | _ ->
          let cl = to_clause c in
          (try
             let assumptions, l = get_assumptions [] l in
             let l = trim_junk_satlem l in
             let env = { empty with assum = assumptions } in
             let lem =
               if is_bbr_satlem_lam p || List.exists has_intro_bv l then bb_lem
               else lem ~toplevel:true in
             let env =
               List.fold_left (fun env p ->
                   let local_env =
                     { env with
                       clauses = [];
                       ax = false;
                       mpred = MTerm.empty;
                     } in
                   let local_env = lem local_env p in
                   { env with
                     clauses = List.rev_append local_env.clauses env.clauses;
                     assum = local_env.assum
                   }
                 ) env l
             in
             let clauses = List.rev env.clauses in
             let id = mk_inter_resolution clauses in
             (* eprintf "remaining assumptions:"; *)
             (* List.iter (eprintf "%s, ") env.assu; *)
             (* eprintf "@."; *)
             (* if env.assum = [] then id else *) 
             let satlem_id = mk_clause Weak cl [id] in
             register_clause_id cl satlem_id;
             register_decl_id lem_name satlem_id;
             (* eprintf "--- SATLEM@."; *)

           with ArithLemma ->
             let satlem_id = mk_clause Lage cl [] in
             register_clause_id cl satlem_id

          );

          satlem ?prefix_cont lem_cont
      end

    | Some (n, [_; _; _; _; l]) when n == H.satlem_simplify ->
      (match value l with
       | Lambda ({sname=Name _}, r) ->
         (match name r with
          | Some _ -> p
          | None -> match app_name r with
            | Some (n, _) when n == H.satlem_simplify -> p
            | _ ->
              (* Intermediate satlem_simplify *)
              (* eprintf ">>>>>> intermediate satlemsimplify@."; *)
              snd (satlem_simplify p) |> satlem ?prefix_cont
         )
       | _ -> p)

    | _ -> p


  let rec bbt p = match app_name p with
    | Some (b, [n; v; bb]) when b == H.bv_bbl_var ->
      let res = bblast_term n (a_var_bv n v) bb in
      Some (mk_clause_cl Bbva [res] [])
    | Some (b, [n; bb; bv]) when b == H.bv_bbl_const ->
      let res = bblast_term n (a_bv n bv) bb in
      Some (mk_clause_cl Bbconst [res] [])
    | Some (rop, [n; x; y; _; _; rb; xbb; ybb])
      when rop == H.bv_bbl_bvand ||
           rop == H.bv_bbl_bvor ||
           rop == H.bv_bbl_bvxor ||
           rop == H.bv_bbl_bvadd ||
           rop == H.bv_bbl_bvmul ||
           rop == H.bv_bbl_bvult ||
           rop == H.bv_bbl_bvslt
      ->
      let bvop, rule =
        if rop == H.bv_bbl_bvand then bvand, Bbop
        else if rop == H.bv_bbl_bvor then bvor, Bbop
        else if rop == H.bv_bbl_bvxor then bvxor, Bbop
        else if rop == H.bv_bbl_bvadd then bvadd, Bbadd
        else if rop == H.bv_bbl_bvmul then bvmul, Bbmul
        else if rop == H.bv_bbl_bvult then bvult, Bbult
        else if rop == H.bv_bbl_bvslt then bvslt, Bbslt
        else assert false
      in
      let res = bblast_term n (bvop n x y) rb in
      (match bbt xbb, bbt ybb with
       | Some idx, Some idy ->
         Some (mk_clause_cl rule [res] [idx; idy])
       | _ -> assert false
      )
      
    | Some (c, [n; x; _; rb; xbb]) when c == H.bv_bbl_bvnot ->
      let res = bblast_term n (bvnot n x) rb in
      (match bbt xbb with
       | Some idx ->
         Some (mk_clause_cl Bbnot [res] [idx])
       | _ -> assert false
      )
    | Some (c, [n; x; _; rb; xbb]) when c == H.bv_bbl_bvneg ->
      let res = bblast_term n (bvneg n x) rb in
      (match bbt xbb with
       | Some idx ->
         Some (mk_clause_cl Bbneg [res] [idx])
       | _ -> assert false
      )
        
    | Some (c, [n; m; m'; x; y; _; _; rb; xbb; ybb])
      when c == H.bv_bbl_concat ->
      let res = bblast_term n (concat n m m' x y) rb in
      (match bbt xbb, bbt ybb with
       | Some idx, Some idy ->
         Some (mk_clause_cl Bbconc [res] [idx; idy])
       | _ -> assert false
      )
        
    | Some (c, [n; i; j; m; x; _; rb; xbb])
      when c == H.bv_bbl_extract ->
      let res = bblast_term n (extract n i j m x) rb in
      (match bbt xbb with
       | Some idx ->
         Some (mk_clause_cl Bbextr [res] [idx])
       | _ -> assert false
      )

    | Some (c, [n; k; m; x; _; rb; xbb])
      when c == H.bv_bbl_zero_extend ->
      let res = bblast_term n (zero_extend n k m x) rb in
      (match bbt xbb with
       | Some idx ->
         Some (mk_clause_cl Bbzext [res] [idx])
       | _ -> assert false
      )

    | Some (c, [n; k; m; x; _; rb; xbb])
      when c == H.bv_bbl_sign_extend ->
      let res = bblast_term n (sign_extend n k m x) rb in
      (match bbt xbb with
       | Some idx ->
         Some (mk_clause_cl Bbsext [res] [idx])
       | _ -> assert false
      )
        
    | None ->
      begin match name p with
      | Some h -> (* should be an declared clause *)
        Some (try get_input_id h with Not_found -> assert false)
      | None -> assert false
      end
      
    | Some (rule, args) ->
      eprintf "Warning: Introducing hole for unsupported rule %a@."
        Hstring.print rule;
      Some (mk_clause_cl Hole [ttype p] [])

  

  let rec bblast_decls p = match app_name p with
    | Some (d, [n; b; t; bb; l]) when d == H.decl_bblast ->
      (* let res = bblast_term n t b in *)
      let id = match bbt bb with Some id -> id | None -> assert false in
      begin match value l with
        | Lambda ({sname = Name h}, p) ->
          register_decl_id h id;
          bblast_decls p
        | _ -> assert false
      end
      
    | Some (d, [n; b; t; a; bb; _; l]) when d == H.decl_bblast_with_alias ->
      (* register_termalias a t; *)
      (* begin match name a with *)
      (*   | Some n -> register_alias n t *)
      (*   | None -> () *)
      (* end; *)
      let id = match bbt bb with Some id -> id | None -> assert false in
      begin match value l with
        | Lambda ({sname = Name h}, p) ->
          register_decl_id h id;
          bblast_decls p
        | _ -> assert false
      end

    | _ -> p


  let bv_pred n =
    if n == H.bv_bbl_eq then Bbeq
    else if n == H.bv_bbl_eq_swap then Bbeq
    else if n == H.bv_bbl_bvult then Bbult
    else if n == H.bv_bbl_bvslt then Bbslt
    else assert false

  
  let rec bblast_eqs p = match app_name p with
    | Some (n, [f; pf; l]) when n == H.th_let_pf ->
      begin match app_name pf with
        | Some (rule_name, [_; _; _; _; _; _; a; b]) ->
          begin match name a, name b with
          | Some na, Some nb ->
            let id1, id2 =
              try get_input_id na, get_input_id nb
              with Not_found -> assert false in
            let clid = mk_clause_cl (bv_pred rule_name) [f] [id1; id2] in
            begin match value l with
              | Lambda ({sname = Name h}, p) ->
                register_decl_id h clid;
                bblast_eqs p
              | _ -> assert false
            end
          | _ -> assert false
          end

        | _ -> assert false
      end
    | _ -> p

  
  (** Bit-blasting and bitvector proof conversion (returns rest of the sat
      proof) *)
  let bb_proof p = match app_name p with
    | Some (n, _) when n == H.decl_bblast || n == H.decl_bblast_with_alias ->
      p
      |> bblast_decls
      |> bblast_eqs
      |> register_prop_vars
      |> satlem ~prefix_cont:"bb."
      |> satlem_simplifies_c
      |> satlem
      
    | _ -> p
  

  (** Convert an LFSC proof (this is the entry point) *)
  let convert p =
    p
      
    (* |> ignore_all_decls *)
    (* |> produce_inputs_preproc *)

    |> ignore_decls
    |> produce_inputs

    |> deferred
  
    |> admit_preproc

    |> register_prop_vars
    |> satlem

    |> bb_proof
    
    |> reso_of_satlem_simplify

  

  let convert_pt p =
    eprintf "Converting LFSC proof to SMTCoq...@?";
    let t0 = Sys.time () in
    let r = convert p in
    let t1 = Sys.time () in
    eprintf " Done [%.3f s]@." (t1 -. t0);
    r

  

  (** Clean global environments *)
  let clear () =
    Ast.clear_sc ();
    T.clear ()


end