aboutsummaryrefslogtreecommitdiffstats
path: root/src/Misc.v
blob: 14a55eaf4dbbdf0c2ec7e6aabbcee7acabdfcdd9 (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
(**************************************************************************)
(*                                                                        *)
(*     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     *)
(*                                                                        *)
(**************************************************************************)


Require Import Bool List PArray Int63 ZArith Psatz.
Local Open Scope int63_scope.
Local Open Scope array_scope.


(** Lemmas about Bool *)

Lemma implb_true_r : forall a, implb a true = true.
Proof. intros [ | ]; reflexivity. Qed.


(** Lemmas about Int63 *)

Lemma le_eq : forall i j,
  (j <= i) = true -> (j + 1 <= i) = false -> i = j.
Proof.
  intros i j; rewrite leb_spec; destruct (dec_Zle [|j+1|] [|i|]) as [H|H].
  rewrite <- leb_spec in H; rewrite H; discriminate.
  intros H1 _; apply to_Z_inj; rewrite add_spec, to_Z_1 in H.
  assert (H2: (([|j|] + 1)%Z < wB)%Z \/ ([|j|] + 1)%Z = wB).
  pose (H3 := to_Z_bounded j); lia.
  destruct H2 as [H2|H2].
  rewrite Zmod_small in H.
  lia.
  split.
  pose (H3 := to_Z_bounded j); lia.
  assumption.
  rewrite H2, Z_mod_same_full in H; elim H; destruct (to_Z_bounded i) as [H3 _]; assumption.
Qed.


Lemma lt_eq : forall i j,
  (i < j + 1) = true -> (i < j) = false -> i = j.
Proof.
  intros i j. rewrite ltb_spec. destruct (dec_Zlt [|i|] [|j|]) as [H|H].
  rewrite <- ltb_spec in H; rewrite H; discriminate.
  intros H1 _; apply to_Z_inj. rewrite add_spec in H1. rewrite to_Z_1 in H1.
  assert (H2: (([|j|] + 1)%Z < wB)%Z \/ ([|j|] + 1)%Z = wB).
  pose (H3 := to_Z_bounded j); lia.
  destruct H2 as [H2|H2].
  rewrite Zmod_small in H1.
  lia.
  split.
  pose (H3 := to_Z_bounded j); lia.
  assumption.
  rewrite H2, Z_mod_same_full in H1; elimtype False. destruct (to_Z_bounded i) as [H3 _]. lia.
Qed.


Lemma minus_1_lt i : (i == 0) = false -> i - 1 < i = true.
Proof.
  intro Hl. rewrite ltb_spec, (to_Z_sub_1 _ 0).
  - lia.
  - rewrite ltb_spec. rewrite eqb_false_spec in Hl.
    assert (0%Z <> [|i|])
      by (change 0%Z with [|0|]; intro H; apply to_Z_inj in H; auto).
    destruct (to_Z_bounded i) as [H1 _].
    clear -H H1. change [|0|] with 0%Z. lia.
Qed.


Lemma foldi_down_ZInd2 :
   forall A (P: Z -> A -> Prop) (f:int -> A -> A) max min a,
   (max < min = true -> P ([|min|])%Z a) ->
   (P ([|max|]+1)%Z a) ->
   (forall i a, min <= i = true -> i <= max = true -> P ([|i|]+1)%Z a -> P [|i|] (f i a)) ->
   P [|min|] (foldi_down f max min a).
Proof.
 unfold foldi_down;intros A P f max min a Hlt;intros.
 set (P' z cont :=
       if Zlt_bool z ([|min|]+1)%Z then cont = (fun a0 : A => a0)
       else forall a, P z a -> P [|min|] (cont a)).
 assert (H1: P' ([|max|]+1)%Z (foldi_down_cont (fun (i : int) (cont : A -> A) (a0 : A) => cont (f i a0)) max
                       min (fun a0 : A => a0))).
 apply foldi_down_cont_ZInd;intros;red.
 assert (H20: (z+1 < [|min|]+1)%Z).
 lia.
 rewrite Zlt_is_lt_bool in H20; rewrite H20;trivial.
 case_eq (Zlt_bool ([|i|]+1) ([|min|]+1));intros.
  rewrite <- Zlt_is_lt_bool in H4;rewrite leb_spec in H1;elimtype False;lia.
 clear H4;revert H3;unfold P'.
 case_eq (Zlt_bool ([|i|] - 1 + 1) ([|min|]+1));intros;auto.
  rewrite <- Zlt_is_lt_bool in H3; assert ([|i|] = [|min|]) by (rewrite leb_spec in H1;lia).
  rewrite H4, <- H6. apply H0;trivial.
  apply H4. replace ([|i|] - 1 + 1)%Z with [|i|] by lia. auto.
 revert H1;unfold P'.
 case_eq (Zlt_bool ([|max|]+1)%Z ([|min|]+1)%Z);auto.
 rewrite <- Zlt_is_lt_bool.
 intro H22; assert (H21: ([|max|] < [|min|])%Z). lia.
 rewrite <- ltb_spec in H21. intros;rewrite foldi_down_cont_lt;auto.
Qed.


Lemma foldi_down_Ind2 : forall A (P : int -> A -> Prop) f max min a,
  (max < max_int = true) ->
  (max < min = true -> P min a) ->
  P (max+1) a ->
  (forall i a, min <= i = true -> i <= max = true ->
    P (i+1) a -> P i (f i a)) ->
  P min (foldi_down f max min a).
Proof.
  intros A P f max min a H H0 H1 H2.
  set (P' z a := (0 <= z < wB)%Z -> P (of_Z z) a).
  assert (W:= to_Z_add_1 _ _ H).
  assert (P' ([|min|])%Z (foldi_down f max min a)).
  apply foldi_down_ZInd2;unfold P';intros.
  rewrite of_to_Z;auto.
  rewrite <- W, of_to_Z;auto.
  rewrite of_to_Z. apply H2; trivial.
  assert (i < max_int = true).
  apply leb_ltb_trans with max; trivial.
  rewrite <- (to_Z_add_1 _ _ H7) in H5. rewrite of_to_Z in H5. apply H5. apply to_Z_bounded.
  unfold P' in H3; rewrite of_to_Z in H3;apply H3;apply to_Z_bounded.
Qed.


(** Lemmas about PArray.to_list *)

Lemma to_list_In : forall {A} (t: array A) i,
  i < length t = true -> In (t.[i]) (to_list t).
Proof.
  intros A t i H; unfold to_list; case_eq (0 == length t); intro Heq.
  unfold is_true in H; rewrite ltb_spec in H; rewrite eqb_spec in Heq; rewrite <- Heq in H; rewrite to_Z_0 in H; pose (H1 := to_Z_bounded i); elimtype False; lia.
  pose (P := fun j a => i < j = true \/ In (t .[ i]) a).
  pose (H1:= foldi_down_Ind2 _ P); unfold P in H1.
  assert (H2: i < 0 = true \/ In (t .[ i]) (foldi_down (fun (i0 : int) (l : list A) => t .[ i0] :: l) (length t - 1) 0 nil)).
  apply H1.
  rewrite ltb_spec; erewrite to_Z_sub_1; try eassumption.
  pose (H2 := to_Z_bounded (length t)); change [|max_int|] with (wB-1)%Z; lia.
  intro H2; elimtype False; rewrite ltb_spec, to_Z_0 in H2; pose (H3 := to_Z_bounded (length t - 1)); lia.
  left; unfold is_true; rewrite ltb_spec; rewrite (to_Z_add_1 _ max_int).
  erewrite to_Z_sub_1; try eassumption.
  unfold is_true in H; rewrite ltb_spec in H; lia.
  rewrite ltb_spec; erewrite to_Z_sub_1; try eassumption.
  pose (H2 := to_Z_bounded (length t)); change [|max_int|] with (wB-1)%Z; lia.
  intros j a H2 H3 [H4|H4].
  case_eq (i < j); intro Heq2.
  left; reflexivity.
  right; rewrite (lt_eq _ _ H4 Heq2); constructor; reflexivity.
  right; constructor 2; assumption.
  destruct H2 as [H2|H2]; try assumption.
  unfold is_true in H2; rewrite ltb_spec, to_Z_0 in H2; pose (H3 := to_Z_bounded i); elimtype False; lia.
Qed.

Lemma to_list_In_eq : forall {A} (t: array A) i x,
  i < length t = true -> x = t.[i] -> In x (to_list t).
Proof.
  intros A t i x Hi ->. now apply to_list_In.
Qed.

Lemma In_to_list : forall {A} (t: array A) x,
  In x (to_list t) -> exists i, i < length t = true /\ x = t.[i].
Proof.
  intros A t x; unfold to_list; case_eq (0 == length t); intro Heq.
  intro H; inversion H.
  rewrite eqb_false_spec in Heq.
  pose (P (_:int) l := In x l ->
   exists i : int, (i < length t) = true /\ x = t .[ i]).
  pose (H1 := foldi_down_Ind2 _ P (fun (i : int) (l : list A) => t .[ i] :: l) (length t - 1) 0); unfold P in H1; apply H1.
  rewrite ltb_spec, to_Z_sub_1_diff; auto; change [|max_int|] with (wB-1)%Z; pose (H2 := to_Z_bounded (length t)); lia.
  intros _ H; inversion H.
  intro H; inversion H.
  simpl; intros i a _ H2 IH [H3|H3].
  exists i; split; auto; rewrite ltb_spec; rewrite leb_spec, to_Z_sub_1_diff in H2; auto; lia.
  destruct (IH H3) as [j [H4 H5]]; exists j; auto.
Qed.


(** Lemmas about PArray.mapi *)

Lemma length_mapi : forall {A B} (f:int -> A -> B) t,
  length (mapi f t) = length t.
Proof.
  unfold mapi; intros A B f t; case_eq (length t == 0).
  rewrite Int63Properties.eqb_spec; intro Heq; rewrite Heq, length_make; auto.
  rewrite eqb_false_spec; intro Heq; apply foldi_ind.
  rewrite length_make, ltb_length; auto.
  intros i a _ H1 H2; rewrite length_set; auto.
Qed.


Lemma default_mapi : forall {A B} (f:int -> A -> B) t,
  default (mapi f t) = f (length t) (default t).
Proof.
  unfold mapi; intros A B f t; case (length t == 0).
  rewrite default_make; auto.
  apply foldi_ind.
  rewrite default_make; auto.
  intros; rewrite default_set; auto.
Qed.

Lemma get_mapi : forall {A B} (f:int -> A -> B) t i,
  i < length t = true -> (mapi f t).[i] = f i (t.[i]).
Proof.
  intros A B f t i Hi; generalize (length_mapi f t); unfold mapi; case_eq (length t == 0).
  rewrite Int63Properties.eqb_spec; intro Heq; rewrite Heq in Hi; eelim ltb_0; eassumption.
  rewrite eqb_false_spec; intro Heq; pose (Hi':=Hi); replace (length t) with ((length t - 1) + 1) in Hi'.
  generalize Hi'; apply (foldi_Ind _ (fun j a => (i < j) = true -> length a = length t -> a.[i] = f i (t.[i]))).
  rewrite ltb_spec, (to_Z_sub_1 _ i); auto; destruct (to_Z_bounded (length t)) as [_ H]; change [|max_int|] with (wB-1)%Z; lia.
  intros H _; eelim ltb_0; eassumption.
  intros H; eelim ltb_0; eassumption.
  intros j a _ H1 IH H2 H3; rewrite length_set in H3; case_eq (j == i).
  rewrite Int63Properties.eqb_spec; intro Heq2; subst i; rewrite get_set_same; auto; rewrite H3; auto.
  rewrite eqb_false_spec; intro Heq2; rewrite get_set_other; auto; apply IH; auto; rewrite ltb_spec; rewrite ltb_spec, (to_Z_add_1 _ (length t)) in H2.
  assert (H4: [|i|] <> [|j|]) by (intro H4; apply Heq2, to_Z_inj; auto); lia.
  rewrite ltb_spec; rewrite leb_spec, (to_Z_sub_1 _ _ Hi) in H1; lia.
  apply to_Z_inj; rewrite (to_Z_add_1 _ max_int).
  rewrite to_Z_sub_1_diff; auto; lia.
  rewrite ltb_spec, to_Z_sub_1_diff; auto; destruct (to_Z_bounded (length t)) as [_ H]; change [|max_int|] with (wB-1)%Z; lia.
Qed.


Lemma get_mapi_outofbound : forall {A B} (f:int -> A -> B) t i,
  i < length t = false -> (mapi f t).[i] = f (length t) (default t).
Proof.
  intros A B f t i H1; rewrite get_outofbound.
  apply default_mapi.
  rewrite length_mapi; auto.
Qed.


(** Custom fold_left and fold_right *)

Definition afold_left A B default (OP : A -> A -> A) (F : B -> A) (V : array B) :=
  let n := PArray.length V in
  if n == 0 then default
  else foldi (fun i a => OP a (F (V.[i]))) 1 (n-1) (F (V.[0])).


Definition afold_right A B default (OP : A -> A -> A) (F : B -> A) (V : array B) :=
  let n := PArray.length V in
  if n == 0 then default else
    if n <= 1 then F (V.[0])
    else foldi_down (fun i b => OP (F (V.[i])) b) (n-2) 0 (F (V.[n-1])).


(** Some properties about afold_left *)

Lemma afold_left_eq :
  forall A B OP def (F1 F2 : A -> B) V1 V2,
    length V1 = length V2 ->
    (forall i, i < length V1 = true -> F1 (V1.[i]) = F2 (V2.[i])) ->
    afold_left _ _ def OP F1 V1 = afold_left _ _ def OP F2 V2.
Proof.
  unfold afold_left;intros. rewrite <- H.
  destruct (Int63Properties.reflect_eqb (length V1) 0);trivial.
  rewrite (H0 0); [ |  unfold is_true;rewrite <- not_0_ltb;trivial].
  apply foldi_eq_compat;intros;rewrite H0;trivial.
  unfold is_true;rewrite ltb_leb_sub1;trivial.
Qed.


Definition afoldi_left {A B:Type} default (OP : int -> A -> A -> A) (F : B -> A) (V : array B) :=
  let n := PArray.length V in
    if n == 0 then default
      else foldi (fun i a => OP i a (F (V.[i]))) 1 (n-1) (F (V.[0])).


Lemma afoldi_left_Ind :
  forall {A B: Type} (P : int -> A -> Prop) default (OP : int -> A -> A -> A) (F : B -> A) (t:array B),
    if length t == 0 then
      True
    else
      (forall a i, i < length t = true -> P i a -> P (i+1) (OP i a (F (t.[i])))) ->
      P 1 (F (t.[0])) ->
      P (length t) (afoldi_left default OP F t).
Proof.
  intros A B P default OP F t; case_eq (length t == 0).
  intros; exact I.
  intros Heq H1 H2; unfold afoldi_left; rewrite Heq;
    assert (H: (length t - 1) + 1 = length t) by ring.
  rewrite <- H at 1; apply foldi_Ind; auto.
  assert (W:= leb_max_int (length t)); rewrite leb_spec in W.
  rewrite ltb_spec, to_Z_sub_1_diff; auto with zarith.
  intro H3; rewrite H3 in Heq; discriminate.
  intro Hlt; assert (H3: length t - 1 = 0).
  rewrite ltb_spec, to_Z_1 in Hlt; apply to_Z_inj; rewrite to_Z_0; pose (H3 := to_Z_bounded (length t - 1)); lia.
  rewrite H3; assumption.
  intros i a H3 H4; apply H1; trivial.
  rewrite ltb_leb_sub1; auto.
  intro H5; rewrite H5 in Heq; discriminate.
Qed.


Lemma afold_left_Ind :
  forall A B (P : int -> A -> Prop) default (OP : A -> A -> A) (F : B -> A) (t:array B),
    if length t == 0 then
      True
    else
      (forall a i, i < length t = true -> P i a -> P (i+1) (OP a (F (t.[i])))) ->
      P 1 (F (t.[0])) ->
      P (length t) (afold_left A B default OP F t).
Proof.
  intros A B P default OP F t;
    apply (afoldi_left_Ind P default (fun _ => OP)); trivial.
Qed.


Lemma afold_left_ind :
  forall A B (P : A -> Prop) default (OP : A -> A -> A) (F : B -> A) (t:array B),
    (forall a i, i < length t = true -> P a -> P (OP a (F (t.[i])))) ->
    P default -> P (F (t.[0])) ->
    P (afold_left _ _ default OP F t).
Proof.
  intros A B P default OP F t H1 H2 H4.
  pose (H3 := afold_left_Ind A B (fun _ => P) default OP F t).
  case_eq (length t == 0); intro Heq.
  unfold afold_left; rewrite Heq; assumption.
  rewrite Heq in H3; apply H3; trivial.
Qed.


Lemma afold_left_spec : forall A B (f:B -> A) args op e,
  (forall a, op e a = a) ->
  afold_left _ _ e op f args =
  fold_left (fun a v  => op a (f v)) e args.
  Proof.
    unfold afold_left, fold_left;intros A B f args op neu H10.
    destruct (reflect_eqb (length args) 0) as [e|n].
    rewrite e, eqb_refl;trivial.
    apply not_eq_sym in n;rewrite (eqb_false_complete _ _ n).
    case_eq (0 < (length args - 1));intros H.
    rewrite foldi_lt with (from := 0);trivial.
    rewrite H10; auto.
    assert (H0: (0 <> [|length args|])%Z).
    intros Heq;apply n;apply to_Z_inj;trivial.
    assert (H1: length args = 1).
    generalize (to_Z_bounded (length args)).
    rewrite <- not_true_iff_false, ltb_spec, to_Z_0, to_Z_sub_1_diff in H;auto.
    intros;apply to_Z_inj;rewrite to_Z_1;lia.
    rewrite H1; change (1 - 1) with 0; rewrite (foldi_eq _ _ 0 0); auto.
  Qed.


(** Some properties about afold_right *)

Lemma afold_right_eq :
  forall A B OP def (F1 F2 : A -> B) V1 V2,
    length V1 = length V2 ->
    (forall i, i < length V1 = true -> F1 (V1.[i]) = F2 (V2.[i])) ->
    afold_right _ _ def OP F1 V1 = afold_right _ _ def OP F2 V2.
Proof.
  unfold afold_right;intros.
  rewrite <- H.
  destruct (Int63Properties.reflect_eqb (length V1) 0);trivial.
  destruct (reflect_leb (length V1) 1);intros.
  apply H0;unfold is_true;rewrite ltb_leb_sub1;trivial. apply leb_0.
  assert (length V1 - 1 < length V1 = true).
  rewrite ltb_leb_sub1;auto using leb_refl.
  rewrite (H0 (length V1 - 1));trivial.
  apply foldi_down_eq_compat;intros;rewrite H0;trivial.
  unfold is_true;rewrite ltb_leb_sub1;[ | trivial].
  apply ltb_leb_sub1;trivial.
  revert n0 H3;rewrite ltb_spec, leb_spec, to_Z_1, sub_spec.
  change [|2|] with 2%Z.
  intros HH;assert (W:= to_Z_bounded (length V1));rewrite Zmod_small;lia.
Qed.


Definition afoldi_right {A B:Type} default (OP : int -> A -> A -> A) (F : B -> A) (V : array B) :=
  let n := PArray.length V in
    if n == 0 then default
      else if n <= 1 then F (V .[ 0])
        else foldi_down (fun i a => OP i (F (V.[i])) a) (n-2) 0 (F (V.[n-1])).


Lemma afoldi_right_Ind :
  forall {A B: Type} (P : int -> A -> Prop) default (OP : int -> A -> A -> A) (F : B -> A) (t:array B),
    if length t <= 1 then
      True
    else
      (forall a i, i < length t - 1 = true -> P (i+1) a -> P i (OP i (F (t.[i])) a)) ->
      P ((length t)-1) (F (t.[(length t)-1])) ->
      P 0 (afoldi_right default OP F t).
Proof.
  intros A B P default OP F t; case_eq (length t <= 1).
  intros; exact I.
  intros Heq H1 H2; unfold afoldi_right. replace (length t == 0) with false.
  rewrite Heq.
  set (P' z a := P (of_Z (z + 1)) a).
  change (P' ([|0|] - 1)%Z (foldi_down (fun (i : int) (a : A) => OP i (F (t .[ i])) a) (length t - 2) 0 (F (t .[ length t - 1])))).
  apply foldi_down_ZInd;unfold P'.
  intros Hlt;elim (ltb_0 _ Hlt).
  replace (length t - 2) with (length t - 1 - 1) by ring.
  rewrite to_Z_sub_1_diff.
  ring_simplify ([|length t - 1|] - 1 + 1)%Z;rewrite of_to_Z;trivial.
  assert (H10: (1 < length t) = true) by (rewrite ltb_negb_geb, Heq; auto).
  intro H11. rewrite ltb_spec in H10. assert (H12: [|length t - 1|] = 0%Z) by (rewrite H11; auto). change [|1|] with (1%Z) in H10. rewrite to_Z_sub_1_diff in H12; [lia| ]. intro H13. assert (H14: [|length t|] = 0%Z) by (rewrite H13; auto). lia.
  intros;ring_simplify ([|i|] - 1 + 1)%Z;rewrite of_to_Z;auto.
  assert (i < length t - 1 = true).
    rewrite ltb_spec. rewrite leb_spec in H0. replace (length t - 2) with (length t - 1 - 1) in H0 by ring. rewrite to_Z_sub_1_diff in H0; [lia| ]. intro H4. assert (H5: [|length t - 1|] = 0%Z) by (rewrite H4; auto). assert (H6: 1 < length t = true) by (rewrite ltb_negb_geb, Heq; auto). rewrite ltb_spec in H6. change ([|1|]) with (1%Z) in H6. rewrite to_Z_sub_1_diff in H5; [lia| ]. intro H7. assert (H8: [|length t|] = 0%Z) by (rewrite H7; auto). lia.
  apply H1; [trivial| ].
  rewrite <-(to_Z_add_1 _ _ H4), of_to_Z in H3;auto.
  symmetry. rewrite eqb_false_spec. intro H. rewrite H in Heq. discriminate.
Qed.


Lemma afold_right_Ind :
  forall A B (P : int -> A -> Prop) default (OP : A -> A -> A) (F : B -> A) (t:array B),
    if length t <= 1 then
      True
    else
      (forall a i, i < length t - 1 = true -> P (i+1) a -> P i (OP (F (t.[i])) a)) ->
      P ((length t)-1) (F (t.[(length t)-1])) ->
      P 0 (afold_right A B default OP F t).
Proof.
  intros A B P default OP F t;
    apply (afoldi_right_Ind P default (fun _ => OP) F).
Qed.


Lemma afold_right_ind :
  forall A B (P : A -> Prop) default (OP : A -> A -> A) (F : B -> A) (t:array B),
    (forall a i, i < length t - 1 = true -> P a -> P (OP (F (t.[i])) a)) ->
    P default -> P (F (t.[length t - 1])) ->
    P (afold_right _ _ default OP F t).
Proof.
  intros A B P default OP F t H1 H2 H4.
  pose (H3 := afold_right_Ind A B (fun _ => P) default OP F t).
  unfold afold_right. case_eq (length t == 0); auto. intro H10. assert (H := H10). rewrite eqb_false_spec in H. case_eq (length t <= 1); intro Heq.
  replace 0 with (length t - 1); auto. apply to_Z_inj. rewrite to_Z_sub_1_diff; auto. rewrite leb_spec in Heq. assert (H5 := leb_0 (length t)). rewrite leb_spec in H5. change [|0|] with 0%Z in *. change [|1|] with 1%Z in Heq. assert (H6 : [|length t|] <> 0%Z) by (intro H6; elim H; apply to_Z_inj; auto). lia. rewrite Heq in H3. unfold afold_right in H3. rewrite H10, Heq in H3. apply H3; auto.
Qed.


Lemma afold_right_ind_nonempty :
  forall A B (P : A -> Prop) default (OP : A -> A -> A) (F : B -> A) (t:array B),
    (forall a i, i < length t - 1 = true -> P a -> P (OP (F (t.[i])) a)) ->
    0 < length t = true -> P (F (t.[length t - 1])) ->
    P (afold_right _ _ default OP F t).
Proof.
  intros A B P default OP F t H1 H2 H4.
  pose (H3 := afold_right_Ind A B (fun _ => P) default OP F t).
  unfold afold_right. assert (H10 : length t == 0 = false) by (rewrite eqb_false_spec; intro H; rewrite H in H2; discriminate). rewrite H10. assert (H := H10). rewrite eqb_false_spec in H. case_eq (length t <= 1); intro Heq.
  replace 0 with (length t - 1); auto. apply to_Z_inj. rewrite to_Z_sub_1_diff; auto. rewrite leb_spec in Heq. assert (H5 := leb_0 (length t)). rewrite leb_spec in H5. change [|0|] with 0%Z in *. change [|1|] with 1%Z in Heq. assert (H6 : [|length t|] <> 0%Z) by (intro H6; elim H; apply to_Z_inj; auto). lia. rewrite Heq in H3. unfold afold_right in H3. rewrite H10, Heq in H3. apply H3; auto.
Qed.


Lemma afold_right_spec : forall A B (f:B -> A) args op e,
  (forall a, op a e = a) ->
  afold_right _ _ e op f args =
  fold_right (fun v a => op (f v) a) args e.
  Proof.
    unfold afold_right, fold_right;intros A B f args op neu H10.
    destruct (reflect_eqb (length args) 0) as [e|n].
    rewrite e, eqb_refl;trivial.
    apply not_eq_sym in n;rewrite (eqb_false_complete _ _ n).
    case_eq (length args <= 1); intro Heq.
    assert (H11: length args = 1).
    apply to_Z_inj. rewrite leb_spec in Heq. assert (H11: 0%Z <> [|length args|]) by (intro H; elim n; apply to_Z_inj; auto). change [|1|] with (1%Z) in *. assert (H12 := leb_0 (length args)). rewrite leb_spec in H12. change [|0|] with 0%Z in H12. lia.
    rewrite H11, foldi_down_eq; auto.
    assert (H11: 1 < length args = true) by (rewrite ltb_negb_geb, Heq; auto). replace (foldi_down (fun (i : int) (b : A) => op (f (args .[ i])) b) (length args - 1) 0 neu) with (foldi_down (fun (i : int) (b : A) => op (f (args .[ i])) b) (length args - 1 - 1) 0 (op (f (args .[ length args - 1])) neu)).
    replace (length args - 1 - 1) with (length args - 2) by ring. rewrite H10. auto.
    symmetry. apply foldi_down_gt. rewrite ltb_spec. change [|0|] with 0%Z. rewrite to_Z_sub_1_diff; auto. rewrite ltb_spec in H11. change [|1|] with 1%Z in H11. lia.
  Qed.


(** Application to our uses of afold_left and afold_right *)
(* Case andb *)

Lemma afold_left_andb_false : forall A i a f,
  i < length a = true ->
  f (a .[ i]) = false ->
  afold_left bool A true andb f a = false.
Proof.
  intros A i a f; rewrite afold_left_spec; auto; apply (fold_left_Ind _ _ (fun j t => (i < j) = true -> f (a .[ i]) = false -> t = false)).
  intros b j H1 H2 H3 H4; case_eq (i == j).
  rewrite Int63Properties.eqb_spec; intro; subst i; rewrite H4, andb_false_r; auto.
  rewrite eqb_false_spec; intro Heq; rewrite H2; auto; rewrite ltb_spec; rewrite ltb_spec in H3; rewrite (to_Z_add_1 _ (length a)) in H3; auto; assert (H5: [|i|] <> [|j|]) by (intro H5; apply Heq, to_Z_inj; auto); lia.
  intro H; eelim ltb_0; eassumption.
Qed.


Lemma afold_left_andb_false_inv : forall A a f,
  afold_left bool A true andb f a = false ->
  exists i, (i < length a = true) /\ (f (a .[ i]) = false).
Proof.
  intros A a f; rewrite afold_left_spec; auto; apply fold_left_ind; try discriminate.
  intros b i H1; case b; simpl.
  intros _ H2; exists i; auto.
  intros H2 _; destruct (H2 (refl_equal false)) as [j [H3 H4]]; exists j; auto.
Qed.


Lemma afold_left_andb_true : forall A a f,
  (forall i, i < length a = true -> f (a.[i]) = true) ->
  afold_left bool A true andb f a = true.
Proof.
  intros A a f H; rewrite afold_left_spec; auto; apply fold_left_ind; trivial; intros b j H1 H2; rewrite H2; simpl; rewrite H; trivial.
Qed.


Lemma afold_left_andb_true_inv : forall A a f,
  afold_left bool A true andb f a = true ->
  forall i, i < length a = true -> f (a.[i]) = true.
Proof.
  intros A a f; rewrite afold_left_spec; auto; apply (fold_left_Ind _ _ (fun j t => t = true -> forall i, (i < j) = true -> f (a .[ i]) = true)).
  intros b i H1; case b; simpl; try discriminate; intros H2 H3 j Hj; case_eq (j == i); intro Heq.
  rewrite Int63Properties.eqb_spec in Heq; subst j; auto.
  apply H2; auto; rewrite eqb_false_spec in Heq; rewrite ltb_spec; rewrite ltb_spec in Hj; assert (H4: [|j|] <> [|i|]) by (intro H; apply Heq, to_Z_inj; auto); rewrite (to_Z_add_1 _ (length a)) in Hj; auto; lia.
  intros _ i H; eelim ltb_0; eassumption.
Qed.


Lemma afold_left_and p a :
  afold_left bool int true andb p a =
  List.forallb p (to_list a).
Proof.
  rewrite afold_left_spec; auto.
  rewrite fold_left_to_list.
  assert (H:forall l acc, List.fold_left (fun (a0 : bool) (v : int) => a0 && p v) l acc =
  acc && List.forallb p l).
  {
    clear a. induction l; simpl.
    - intros; now rewrite andb_true_r.
    - intro acc. rewrite IHl. now rewrite andb_assoc.
  }
  now apply H.
Qed.


(* Case orb *)

Lemma afold_left_orb_true : forall A i a f,
  i < length a = true ->
  f (a .[ i]) = true ->
  afold_left bool A false orb f a = true.
Proof.
  intros A i a f; rewrite afold_left_spec; auto; apply (fold_left_Ind _ _ (fun j t => (i < j) = true -> f (a .[ i]) = true -> t = true)).
  intros b j H1 H2 H3 H4; case_eq (i == j).
  rewrite Int63Properties.eqb_spec; intro; subst i; rewrite H4, orb_true_r; auto.
  rewrite eqb_false_spec; intro Heq; rewrite H2; auto; rewrite ltb_spec; rewrite ltb_spec in H3; rewrite (to_Z_add_1 _ (length a)) in H3; auto; assert (H5: [|i|] <> [|j|]) by (intro H5; apply Heq, to_Z_inj; auto); lia.
  intro H; eelim ltb_0; eassumption.
Qed.


Lemma afold_left_orb_true_inv : forall A a f,
  afold_left bool A false orb f a = true ->
  exists i, (i < length a = true) /\ (f (a .[ i]) = true).
Proof.
  intros A a f; rewrite afold_left_spec; auto; apply fold_left_ind; try discriminate.
  intros b i H1; case b; simpl.
  intros H2 _; destruct (H2 (refl_equal true)) as [j [H3 H4]]; exists j; auto.
  intros _ H2; exists i; auto.
Qed.


Lemma afold_left_orb_false : forall A a f,
  (forall i, i < length a = true -> f (a.[i]) = false) ->
  afold_left bool A false orb f a = false.
Proof.
  intros A a f H; rewrite afold_left_spec; auto; apply fold_left_ind; trivial; intros b j H1 H2; rewrite H2; simpl; rewrite H; trivial.
Qed.


Lemma afold_left_orb_false_inv : forall A a f,
  afold_left bool A false orb f a = false ->
  forall i, i < length a = true -> f (a.[i]) = false.
Proof.
  intros A a f; rewrite afold_left_spec; auto; apply (fold_left_Ind _ _ (fun j t => t = false -> forall i, (i < j) = true -> f (a .[ i]) = false)).
  intros b i H1; case b; simpl; try discriminate; intros H2 H3 j Hj; case_eq (j == i); intro Heq.
  rewrite Int63Properties.eqb_spec in Heq; subst j; auto.
  apply H2; auto; rewrite eqb_false_spec in Heq; rewrite ltb_spec; rewrite ltb_spec in Hj; assert (H4: [|j|] <> [|i|]) by (intro H; apply Heq, to_Z_inj; auto); rewrite (to_Z_add_1 _ (length a)) in Hj; auto; lia.
  intros _ i H; eelim ltb_0; eassumption.
Qed.


Lemma afold_left_or p a :
  afold_left bool int false orb p a =
  List.existsb p (to_list a).
Proof.
  rewrite afold_left_spec; auto.
  rewrite fold_left_to_list.
  assert (H:forall l acc, List.fold_left (fun (a0 : bool) (v : int) => a0 || p v) l acc =
  acc || List.existsb p l).
  {
    clear a. induction l; simpl.
    - intros; now rewrite orb_false_r.
    - intro acc. rewrite IHl. now rewrite orb_assoc.
  }
  now apply H.
Qed.


(* Case implb *)

Lemma afold_right_implb_false : forall A a f,
  0 < length a = true /\
  (forall i, i < length a - 1 = true -> f (a .[ i]) = true) /\
  f (a.[length a - 1]) = false ->
  afold_right bool A true implb f a = false.
Proof.
  intros A a f; intros [H1 [H2 H3]]; apply afold_right_ind_nonempty; auto; intros b i H4 H5; rewrite H5, H2; auto.
Qed.


Lemma afold_right_implb_false_inv : forall A a f,
  afold_right bool A true implb f a = false ->
  0 < length a = true /\
  (forall i, i < length a - 1 = true -> f (a .[ i]) = true) /\
  f (a.[length a - 1]) = false.
Proof.
  intros A a f; case_eq (length a == 0); intro Heq1.
  unfold afold_right; rewrite Heq1; discriminate.
  intro H; split.
  rewrite eqb_false_spec in Heq1; rewrite <- not_0_ltb; auto.
  generalize H; clear H; case_eq (length a <= 1); intro Heq2.
  unfold afold_right; rewrite Heq1, Heq2; intro H; replace (length a - 1) with 0.
  split; auto; intros i Hi; elim (ltb_0 i); auto.
  rewrite eqb_false_spec in Heq1; apply to_Z_inj; rewrite to_Z_sub_1_diff; auto; rewrite leb_spec in Heq2; change [|1|] with 1%Z in Heq2; assert ([|length a|] <> 0%Z) by (intro H1; apply Heq1, to_Z_inj; auto); generalize (leb_0 (length a)); rewrite leb_spec; change [|0|] with 0%Z; lia.
  pose (P j k := k = false -> (forall i : int, (j <= i) = true -> (i < length a - 1) = true -> f (a .[ i]) = true) /\ f (a .[ length a - 1]) = false); assert (H: P 0 (afold_right bool A true implb f a)).
  generalize (afold_right_Ind _ _ P true implb f a); rewrite Heq2; intro IH; apply IH; clear IH; unfold P.
  intros b i H1 H2 H3; case_eq b; intro Heq3.
  rewrite Heq3 in H3; generalize H3; case (f (a .[ i])); discriminate.
  destruct (H2 Heq3) as [H4 H5]; split; auto; intros j H6 H7; case_eq (i == j); intro Heq4.
  rewrite eqb_spec in Heq4; subst j b; generalize H3; case (f (a .[ i])); auto; discriminate.
  apply H4; auto; rewrite leb_spec in *; rewrite (to_Z_add_1 _ _ H1); rewrite eqb_false_spec in Heq4; assert ([|i|] <> [|j|]) by (intro H; apply Heq4, to_Z_inj; auto); lia.
  intro H; split; auto; intros i H1 H2; elimtype False; rewrite leb_spec in H1; rewrite ltb_spec in H2; lia.
  unfold P in H; intro H1; destruct (H H1) as [H2 H3]; split; auto; intro i; apply H2, leb_0.
Qed.


Lemma afold_right_implb_true_aux : forall A a f,
  (exists i, i < length a - 1 = true /\ f (a.[i]) = false) ->
  afold_right bool A true implb f a = true.
Proof.
  intros A a f; case_eq (length a == 0); intro Heq1.
  intros _; unfold afold_right; rewrite Heq1; auto.
  case_eq (length a <= 1); intro Heq2.
  intros [i [Hi _]]; elim (ltb_0 i); replace 0 with (length a - 1); auto; rewrite eqb_false_spec in Heq1; apply to_Z_inj; rewrite to_Z_sub_1_diff; auto; assert (H1: [|length a|] <> 0%Z) by (intro H; apply Heq1, to_Z_inj; auto); rewrite leb_spec in Heq2; generalize (leb_0 (length a)); rewrite leb_spec; change [|0|] with 0%Z; change [|1|] with 1%Z in Heq2; lia.
  pose (P j k := (exists i : int, (j <= i) = true /\ (i < length a - 1) = true /\ f (a .[ i]) = false) -> k = true); assert (H: P 0 (afold_right bool A true implb f a)).
  generalize (afold_right_Ind _ _ P true implb f a); rewrite Heq2; intro IH; apply IH; clear IH; unfold P.
  intros b i H1 H2 [j [H3 [H4 H5]]]; case_eq (i == j); intro Heq3.
  rewrite eqb_spec in Heq3; subst i; rewrite H5; case b; auto.
  rewrite H2.
  case (f (a .[ i])); auto.
  exists j; repeat split; auto; assert (H: i < j = true).
  rewrite ltb_spec; rewrite leb_spec in H3; rewrite eqb_false_spec in Heq3; assert (H: [|i|] <> [|j|]) by (intro H; apply Heq3, to_Z_inj; auto); lia.
  rewrite leb_spec, (to_Z_add_1 _ _ H); rewrite ltb_spec in H; lia.
  intros [i [H1 [H2 _]]]; elimtype False; rewrite leb_spec in H1; rewrite ltb_spec in H2; lia.
  unfold P in H; intros [i Hi]; apply H; exists i; split; auto; apply leb_0.
Qed.


Lemma afold_right_implb_true : forall A a f,
  length a = 0 \/ (exists i, i < length a - 1 = true /\ f (a.[i]) = false) \/
  (forall i, i < length a = true -> f (a.[i]) = true) ->
  afold_right bool A true implb f a = true.
Proof.
  intros A a f; case_eq (length a == 0).
  intros H _; unfold afold_right; rewrite H; auto.
  intros Heq [H1|[H1|H1]].
  rewrite H1 in Heq; discriminate.
  apply afold_right_implb_true_aux; auto.
  apply afold_right_ind_nonempty.
  intros b i H2 H3; subst b; case (f (a .[ i])); auto.
  apply not_0_ltb; intro H; rewrite H in Heq; discriminate.
  apply H1; rewrite ltb_spec, to_Z_sub_1_diff; [lia| ]; intro H; rewrite H in Heq; discriminate.
Qed.


Lemma afold_right_implb_true_inv : forall A a f,
  afold_right bool A true implb f a = true ->
  length a = 0 \/ (exists i, i < length a - 1 = true /\ f (a.[i]) = false) \/
  (forall i, i < length a = true -> f (a.[i]) = true).
Proof.
  intros A a f; case_eq (length a == 0); intro Heq1.
  intros _; left; rewrite eqb_spec in Heq1; auto.
  case_eq (length a <= 1); intro Heq2.
  unfold afold_right; rewrite Heq1, Heq2; intro H; right; right; intros i Hi; replace i with 0; auto; apply to_Z_inj; rewrite ltb_spec in Hi; rewrite eqb_false_spec in Heq1; assert (H1: [|length a|] <> 0%Z) by (intro H1; apply Heq1, to_Z_inj; auto); rewrite leb_spec in Heq2; change [|1|] with 1%Z in Heq2; generalize (leb_0 (length a)) (leb_0 i); rewrite !leb_spec; change [|0|] with 0%Z;  lia.
  pose (P j k := k = true -> (exists i : int, (j <= i) = true /\ (i < length a - 1) = true /\ f (a .[ i]) = false) \/ (forall i : int, (j <= i) = true -> (i < length a) = true -> f (a .[ i]) = true)); assert (H: P 0 (afold_right bool A true implb f a)).
  generalize (afold_right_Ind _ _ P true implb f a); rewrite Heq2; intro IH; apply IH; clear IH; unfold P.
  intros b i H1 H2 H3; case_eq b; intro Heq3.
  destruct (H2 Heq3) as [[j [H4 [H5 H6]]]|H4].
  left; exists j; repeat split; auto; rewrite leb_spec in *; rewrite (to_Z_add_1 _ _ H1) in H4; lia.
  case_eq (f (a.[i])); intro Heq4.
  right; intros j H5 H6; case_eq (i == j); intro Heq5.
  rewrite eqb_spec in Heq5; subst j; auto.
  apply H4; auto; rewrite leb_spec in *; rewrite (to_Z_add_1 _ _ H1); rewrite eqb_false_spec in Heq5; assert ([|i|] <> [|j|]) by (intro H; apply Heq5, to_Z_inj; auto); lia.
  left; exists i; repeat split; auto; apply leb_refl.
  rewrite Heq3 in H3; case_eq (f (a .[ i])); intro Heq4; rewrite Heq4 in H3; try discriminate; left; exists i; repeat split; auto; apply leb_refl.
  intros H1; right; intros i H2 H3; replace i with (length a - 1); auto; apply to_Z_inj; rewrite leb_spec in H2; rewrite (to_Z_sub_1 _ _ H3) in *; rewrite ltb_spec in H3; lia.
  unfold P in H; intro H1; right; destruct (H H1) as [[i [_ H2]]|H2].
  left; exists i; auto.
  right; intro i; apply H2, leb_0.
Qed.


(* Other cases *)

Lemma afold_left_length_2 : forall A B default OP F t,
  (length t == 2) = true ->
  afold_left A B default OP F t = OP (F (t.[0])) (F (t.[1])).
Proof.
  intros A B default OP F t H; unfold afold_left; rewrite eqb_spec in H; rewrite H; change (2 == 0) with false; simpl; change (2-1) with 1; rewrite foldi_eq; trivial.
Qed.


Lemma afold_right_length_2 : forall A B default OP F t,
  (length t == 2) = true ->
  afold_right A B default OP F t = OP (F (t.[0])) (F (t.[1])).
Proof.
  intros A B default OP F t H; unfold afold_right; rewrite eqb_spec in H; rewrite H; change (2 == 0) with false; simpl; change (2<=1) with false; simpl; change (2-2) with 0; rewrite foldi_down_eq; trivial.
Qed.


Ltac tac_left :=
  intros t f H H1 H2; rewrite afold_left_length_2;
    [rewrite H1, H2| ]; trivial.


Ltac tac_right :=
  try (intros t f H H1 H2; rewrite afold_right_length_2;
    [rewrite H1, H2| ]; trivial);
  try (intros t f H H1; rewrite afold_right_length_2;
    [rewrite H1| ]; trivial);
  try (rewrite implb_true_r; trivial).


Lemma afold_left_xorb_false1 : forall t f,
  (PArray.length t == 2) = true ->
  f (t .[ 0]) = false -> f (t .[ 1]) = false ->
  afold_left bool int false xorb f t = false.
Proof. tac_left. Qed.


Lemma afold_left_xorb_false2 : forall t f,
  (PArray.length t == 2) = true ->
  f (t .[ 0]) = true -> f (t .[ 1]) = true ->
  afold_left bool int false xorb f t = false.
Proof. tac_left. Qed.


Lemma afold_left_xorb_true1 : forall t f,
  (PArray.length t == 2) = true ->
  f (t .[ 0]) = false -> f (t .[ 1]) = true ->
  afold_left bool int false xorb f t = true.
Proof. tac_left. Qed.


Lemma afold_left_xorb_true2 : forall t f,
  (PArray.length t == 2) = true ->
  f (t .[ 0]) = true -> f (t .[ 1]) = false ->
  afold_left bool int false xorb f t = true.
Proof. tac_left. Qed.


(* Lemma afold_right_implb_false : forall t f, *)
(*   (PArray.length t == 2) = true -> *)
(*   f (t .[ 0]) = true -> f (t .[ 1]) = false -> *)
(*   afold_right bool int true implb f t = false. *)
(* Proof. tac_right. Qed. *)


(* Lemma afold_right_implb_true1 : forall t f, *)
(*   (PArray.length t == 2) = true -> *)
(*   f (t .[ 0]) = false -> *)
(*   afold_right bool int true implb f t = true. *)
(* Proof. tac_right. Qed. *)


(* Lemma afold_right_implb_true2 : forall t f, *)
(*   (PArray.length t == 2) = true -> *)
(*   f (t.[1]) = true -> *)
(*   afold_right bool int true implb f t = true. *)
(* Proof. tac_right. Qed. *)


Lemma afold_left_eqb_false1 : forall t f,
  (PArray.length t == 2) = true ->
  f (t .[ 0]) = false -> f (t .[ 1]) = true ->
  afold_left bool int true eqb f t = false.
Proof. tac_left. Qed.


Lemma afold_left_eqb_false2 : forall t f,
  (PArray.length t == 2) = true ->
  f (t .[ 0]) = true -> f (t .[ 1]) = false ->
  afold_left bool int true eqb f t = false.
Proof. tac_left. Qed.


Lemma afold_left_eqb_true1 : forall t f,
  (PArray.length t == 2) = true ->
  f (t .[ 0]) = true -> f (t .[ 1]) = true ->
  afold_left bool int true eqb f t = true.
Proof. tac_left. Qed.


Lemma afold_left_eqb_true2 : forall t f,
  (PArray.length t == 2) = true ->
  f (t .[ 0]) = false -> f (t .[ 1]) = false ->
  afold_left bool int true eqb f t = true.
Proof. tac_left. Qed.


(** Two elements in a list *)

Section List2.

  Variable A : Type.

  Inductive In2 (i j : A) : list A -> Prop :=
  | In2_hd : forall l, In j l -> In2 i j (i::l)
  | In2_tl : forall k l, In2 i j l -> In2 i j (k::l).

  Local Hint Constructors In2.


  Lemma In2_app : forall i j l m, In2 i j (l ++ m) <->
    In2 i j l \/ (In i l /\ In j m) \/ In2 i j m.
  Proof.
    intros i j; induction l as [ |t l IHl]; simpl; intro m; split; auto.
    intros [H|[[H _]|H]]; auto.
    inversion H.
    elim H.
    intro H; inversion H; clear H.
    subst i l0; rewrite in_app_iff in H1; destruct H1 as [H1|H1]; auto.
    subst k l0; rewrite IHl in H1; destruct H1 as [H1|[[H1 H2]|H1]]; auto.
    intros [H|[[[H|H] H1]|H]].
    inversion H; clear H.
    subst i l0; constructor 1; rewrite in_app_iff; auto.
    subst k l0; constructor 2; rewrite IHl; left; auto.
    subst t; constructor 1; rewrite in_app_iff; auto.
    constructor 2; rewrite IHl; right; left; auto.
    constructor 2; rewrite IHl; right; right; auto.
  Qed.


  Fixpoint rev_aux acc (l:list A) :=
    match l with
      | nil => acc
      | t::q => rev_aux (t::acc) q
    end.


  Lemma In2_rev_aux : forall i j l acc, In2 i j (rev_aux acc l) <->
    In2 i j acc \/ (In i l /\ In j acc) \/ In2 j i l.
  Proof.
    intros i j; induction l as [ |t q IHq]; simpl; intro acc; split; auto.
    intros [H|[[H _]|H]]; auto.
    elim H.
    inversion H.
    rewrite IHq; clear IHq; intros [H|[[H1 H2]|H]]; auto.
    inversion H; auto.
    inversion H2; auto; clear H2; subst t; right; right; auto.
    intros [H|[[[H1|H1] H2]|H]]; rewrite IHq; clear IHq; auto.
    subst t; auto.
    right; left; split; auto; constructor 2; auto.
    inversion H; clear H; auto; subst j l; right; left; split; auto; constructor 1; auto.
  Qed.


  Definition rev := rev_aux nil.


  Lemma In2_rev : forall i j l, In2 i j (rev l) <-> In2 j i l.
  Proof.
    intros i j l; unfold rev; rewrite In2_rev_aux; split; auto; intros [H|[[_ H]|H]]; auto; inversion H.
  Qed.


  Lemma In2_In : forall i j, i <> j -> forall l, (In i l /\ In j l) <-> (In2 i j l \/ In2 j i l).
  Proof.
    intros i j H l; split.
    intros [H1 H2]; generalize H1 H2; clear H1 H2; induction l as [ |t q IHq].
    intro H1; inversion H1.
    intros H1 H2; inversion H1; clear H1.
    subst t; inversion H2; auto; elim H; auto.
    inversion H2; clear H2.
    subst t; auto.
    destruct (IHq H0 H1) as [H2|H2]; auto.
    intros [H1|H1]; induction H1 as [H1|t q H1 [IH1 IH2]].
    split; [constructor 1|constructor 2]; auto.
    split; constructor 2; auto.
    split; [constructor 2|constructor 1]; auto.
    split; constructor 2; auto.
  Qed.

End List2.

Arguments In2 [A] i j _.
Arguments rev [A] l.
Arguments In2_In [A i j] _ l.


(** List of distinct elements *)

Section Distinct.

  Variable A : Type.
  Variable eq : A -> A -> bool.

  Fixpoint distinct_aux2 acc ref l :=
    match l with
      | nil => acc
      | t::q => distinct_aux2 (acc && (negb (eq ref t))) ref q
    end.

  Lemma distinct_aux2_spec : forall ref l acc, distinct_aux2 acc ref l = true
    <->
    acc = true /\ (forall i, In i l -> eq ref i = false).
  Proof.
    intro ref; induction l as [ |t q IHq]; simpl.
    intro acc; split.
    intro H; split; auto; intros i H1; elim H1.
    intros [H _]; auto.
    intro acc; rewrite (IHq (acc && negb (eq ref t))); split.
    rewrite andb_true_iff; intros [[H1 H2] H3]; split; auto; intros i [Hi|Hi]; auto; subst i; generalize H2; case (eq ref t); auto; discriminate.
    intros [H1 H2]; rewrite andb_true_iff; repeat split; auto; rewrite (H2 t); auto.
  Qed.

  Lemma distinct_aux2_spec_neg : forall ref l acc,
    distinct_aux2 acc ref l = false <->
    acc = false \/ (exists i, In i l /\ eq ref i = true).
  Proof.
    intro ref; induction l as [ |t q IHq]; simpl.
    intro acc; split; auto; intros [H|[i [H _]]]; auto; elim H.
    intro acc; rewrite (IHq (acc && negb (eq ref t))); rewrite andb_false_iff;split.
    intros [[H|H]|[i [H1 H2]]]; auto.
    right; exists t; split; auto; generalize H; case (eq ref t); auto.
    right; exists i; split; auto.
    intros [H|[i [[H1|H1] H2]]]; auto.
    subst t; left; right; generalize H2; case (eq ref i); auto.
    right; exists i; auto.
  Qed.

  Fixpoint distinct_aux acc l :=
    match l with
      | nil => acc
      | t::q =>
        let acc' := distinct_aux2 acc t q in
        distinct_aux acc' q
    end.

  Local Hint Constructors In2.

  Lemma distinct_aux_spec : forall l acc, distinct_aux acc l = true <->
    acc = true /\ (forall i j, In2 i j l -> eq i j = false).
  Proof.
    induction l as [ |t q IHq]; simpl.
    intro acc; split.
    intro H; split; auto; intros i j H1; inversion H1.
    intros [H _]; auto.
    intro acc; rewrite (IHq (distinct_aux2 acc t q)), distinct_aux2_spec; split.
    intros [[H1 H2] H3]; split; auto; intros i j H; inversion H; auto.
    intros [H1 H2]; repeat split; auto.
  Qed.

  Lemma distinct_aux_spec_neg : forall l acc, distinct_aux acc l = false <->
    acc = false \/ (exists i j, In2 i j l /\ eq i j = true).
  Proof.
    induction l as [ |t q IHq]; simpl.
    intro acc; split; auto; intros [H|[i [j [H _]]]]; auto; inversion H.
    intro acc; rewrite (IHq (distinct_aux2 acc t q)), distinct_aux2_spec_neg; split.
    intros [[H|[i [H1 H2]]]|[i [j [H1 H2]]]]; auto.
    right; exists t; exists i; auto.
    right; exists i; exists j; auto.
    intros [H|[i [j [H1 H2]]]]; auto; inversion H1; clear H1.
    subst i l; left; right; exists j; auto.
    subst k l; right; exists i; exists j; auto.
  Qed.

  Definition distinct := distinct_aux true.

  Lemma distinct_spec : forall l, distinct l = true <->
    (forall i j, In2 i j l -> eq i j = false).
  Proof.
    unfold distinct; intro l; rewrite distinct_aux_spec; split; auto; intros [_ H]; auto.
  Qed.

  Lemma distinct_false_spec : forall l, distinct l = false <->
    (exists i j, In2 i j l /\ eq i j = true).
  Proof.
    unfold distinct; intro l; rewrite distinct_aux_spec_neg; split; auto; intros [H|H]; auto; discriminate.
  Qed.

End Distinct.

Arguments distinct [A] eq l.


(** Specification of existsb *)

Lemma existsb_false_spec : forall f from to,
  existsb f from to = false <->
  forall i, ((from <= i) = true /\ (i <= to) = true) -> f i = false.
Proof.
  unfold existsb;intros; setoid_rewrite leb_spec; apply foldi_cont_ZInd.
  intros z Hz; split; auto; intros _ i [H1 H2]; assert (H3 := Z.le_trans _ _ _ H1 H2); elimtype False; lia.
  intros i cont H1 H2 H3; case_eq (f i); intro Heq.
  split; try discriminate; intro H; rewrite <- Heq; apply H; split; try lia; rewrite leb_spec in H2; auto.
  rewrite H3; split; intros H j [Hj1 Hj2].
  case_eq (i == j); intro Heq2.
  rewrite eqb_spec in Heq2; subst j; auto.
  apply H; split; auto; rewrite eqb_false_spec in Heq2; assert ([|i|] <> [|j|]) by (intro; apply Heq2, to_Z_inj; auto); lia.
  apply H; lia.
Qed.


Lemma array_existsbi_false_spec : forall A (f : int -> A -> bool) t,
  existsbi f t = false <->
  forall i, i < length t = true -> f i (t.[i]) = false.
Proof.
  unfold existsbi;intros A f t; destruct (reflect_eqb 0 (length t)).
  split; auto. intros _ i Hi. elim (ltb_0 i). rewrite e. auto.
  rewrite existsb_false_spec. split.
  intros H i Hi. apply H. split; [apply leb_0| ]. rewrite leb_spec. rewrite (to_Z_sub_1 _ _ Hi). rewrite ltb_spec in Hi. lia.
  intros H i [_ Hi]. apply H. rewrite ltb_spec. rewrite leb_spec in Hi. rewrite to_Z_sub_1_diff in Hi; auto; lia.
Qed.


Lemma array_existsb_false_spec : forall A (f : A -> bool) t,
  PArray.existsb f t = false <->
  forall i, i < length t = true -> f (t.[i]) = false.
Proof.
  intros A f t; unfold PArray.existsb; case_eq (0 == length t).
  rewrite eqb_spec; intro H; split; auto; intros _ i Hi; elim (ltb_0 i); rewrite H; auto.
  intro H; rewrite existsb_false_spec; split.
  intros H1 i Hi; apply H1; split; [apply leb_0| ]; rewrite leb_spec, (to_Z_sub_1 _ _ Hi); rewrite ltb_spec in Hi; lia.
  intros H1 i [_ H2]; apply H1; rewrite ltb_spec; rewrite leb_spec in H2; rewrite to_Z_sub_1_diff in H2; [lia| ]; intro H3; rewrite H3 in H; discriminate.
Qed.


(** Forall of two lists at the same time *)

Section Forall2.

  Variables (A B:Type) (f:A->B->bool).

  Fixpoint forallb2 l1 l2 :=
    match l1, l2 with
      | nil, nil => true
      | a::l1, b::l2 => f a b && forallb2 l1 l2
      | _, _ => false
    end.

End Forall2.

Arguments forallb2 {A B} f l1 l2.


(* Compatibility between native-coq and Coq 8.5 *)

Definition Nat_eqb :=
  fix eqb (n m : nat) {struct n} : bool :=
    match n with
    | O => match m with
           | O => true
           | S _ => false
           end
    | S n' => match m with
              | O => false
              | S m' => eqb n' m'
              end
    end.

Definition List_map_ext_in
  : forall (A B : Type) (f g : A -> B) (l : list A),
    (forall a : A, In a l -> f a = g a) -> List.map f l = List.map g l :=
  fun (A B : Type) (f g : A -> B) (l : list A) =>
    list_ind
      (fun l0 : list A =>
         (forall a : A, In a l0 -> f a = g a) -> List.map f l0 = List.map g l0)
      (fun _ : forall a : A, False -> f a = g a => eq_refl)
      (fun (a : A) (l0 : list A)
           (IHl : (forall a0 : A, In a0 l0 -> f a0 = g a0) -> List.map f l0 = List.map g l0)
           (H : forall a0 : A, a = a0 \/ In a0 l0 -> f a0 = g a0) =>
         eq_ind_r (fun b : B => b :: List.map f l0 = g a :: List.map g l0)
                  (eq_ind_r (fun l1 : list B => g a :: l1 = g a :: List.map g l0) eq_refl
                            (IHl (fun (a0 : A) (H0 : In a0 l0) => H a0 (or_intror H0))))
                  (H a (or_introl eq_refl))) l.


(* Misc lemmas *)

Lemma neg_eq_true_eq_false b : b = false <-> b <> true.
Proof. destruct b; intuition. Qed.

Lemma is_true_iff e :  e = true <-> is_true e.
Proof. now unfold is_true. Qed.


(* 
   Local Variables:
   coq-load-path: ((rec "." "SMTCoq"))
   End: 
*)