This file is indexed.

/usr/share/acl2-6.3/books/paco/simplify.lisp is in acl2-books-source 6.3-5.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
(in-package "PACO")

; In this section we develop rewrite-clause.

(defun split-on-assumptions (assumptions cl ans)

; Cl is a clause and ans is a set of clauses that will be our answer.
; Assumptions is a list of literals.  For each lit in assumptions
; we add a new clause to ans, obtained by adding lit to cl.

  (cond ((endp assumptions) ans)
        (t (split-on-assumptions
            (cdr assumptions)
            cl
            (conjoin-clause-to-clause-set
             (add-literal (car assumptions) cl nil)
             ans)))))

(defun rewrite-clause-action (lit branches)

; Lit is a term. Branches is the result of clausifying the result of
; rewriting lit.  We want to know if anything has happened.  The table
; below indicates our result:

; branches      result            meaning
;  {}          'shown-true      Lit was rewritten and clausified to
;                               the empty set, i.e., lit is T under our
;                               assumptions.
;  {NIL}       'shown-false     Lit was rewritten and clausified to
;                               the set containing the empty clause, i.e.,
;                               lit is NIL under our assumptions.
;  {{lit}}     'no-change       Neither rewrite nor clausify did anything.

;  otherwise   'change

  (cond ((consp branches)
         (cond ((null (cdr branches))
                (cond ((null (car branches))
                       'shown-false)
                      ((and (null (cdr (car branches)))
                            (equal lit (car (car branches))))
                       'no-change)
                      (t 'change)))
               (t 'change)))
        (t 'shown-true)))

(defun rewrite-clause-type-alist (tail new-clause ens wrld)

; We construct a type alist in which we assume (a) the falsity of
; every literal in tail except the first, and (b) the falsity of every
; literal in new-clause.

; We return a pair, (mv contradictionp type-alist), where
; contradictionp is t or nil and indicates whether we derived a contradiction.
; Type-alist is the constructed type-alist (or nil if we got a contradiction).

  (type-alist-clause (append new-clause (cdr tail)) nil ens wrld))

(defun rewrite-atm (atm not-flg type-alist wrld rcnst)

; This function rewrites atm in the given context, maintaining iff.

  (mv-let (knownp nilp)
          (known-whether-nil atm type-alist
                             (access rewrite-constant rcnst :ens)
                             wrld)
          (cond
           ((and knownp not-flg nilp)

; So we have reduced the atm to nil but it occurs negated in the
; clause and so we have reduced the literal to t, proving the clause.
; We report this reduction.  We don't report reductions of the literal
; to nil because that sometimes erases hypotheses added by hints.  See
; the comment in ACL2's rewrite-atm.  Paco doesn't support hints per
; se, but this rather subtle interaction of hints with the rewriter
; would be hard to track down if Paco didn't handle it this way.

            *nil*)
           ((and knownp (not not-flg) (not nilp))
            *t*)
           (t
            (rewrite-entry
             (rewrite atm nil)
             :type-alist type-alist
             :obj '?
             :iff-flg t
             :wrld wrld
             :fnstack nil
             :ancestors nil
             :rcnst rcnst
             :nnn *rewrite-nnn*)))))

; Now we develop the code for removing trivial equivalences.

(defun dumb-occurs-in-some-other-lit (term n cl i)

; We determine whether term occurs in some lit of clause cl other than
; the nth.  The number of the first literal of cl is i.

  (cond ((endp cl) nil)
        ((int= n i)
         (dumb-occurs-in-some-other-lit term n (cdr cl) (1+ i)))
        ((dumb-occur term (car cl)) t)
        (t (dumb-occurs-in-some-other-lit term n (cdr cl) (1+ i)))))


(defun find-trivial-equivalence1 (not-just-quotep-flg tail i cl avoid-lst)

; Cl is a clause.  Tail is a tail of cl and i is the position number
; of its first literal, starting from 0 for the first lit in cl.  See
; find-trivial-equivalence for the rest of the spec.

; It is important to keep in mind that the clause upon which we are working has
; not necessarily been rewritten.  Indeed, it is often the product of previous
; substitutions by the driver of this very function.  (Aside: once upon a time,
; the driver did not evaluate literals as they got stuffed with constants.  At
; the moment it does evaluate fns on constant args.  But that may
; change and so this function is written in a way that assumes the worst: there
; may be reducible terms in the clause.)  Thus, for example, a clause like
;    {(not (equal x 'a)) (not (equal y 'b)) (not (equal x y)) y ...}
; may first produce
;    {(not (equal y 'b)) (not (equal 'a y)) y ...}
; and then
;    {(not (equal 'a 'b)) 'b ...}
; which contains two unexpected sorts of literals: an equivalence with
; constant args and a constant literal.  We must therefore not be
; surprised by such literals.  However, we do not expect them to arise
; often enough to justify making our caller cope with the possibility
; that we've proved the clause.  So if we find such a literal and can
; decide the clause, we will just immediately report that there are no
; more usable equivalences and let the simplifier rediscover the
; literal.  If we find such a literal and can't decide the clause
; quickly based on equal and iff facts then we will just continue
; looking for usable equivalences.  The idea is that if the discovered
; lit makes the clause true, we don't expect to have screwed it up by
; continuing to substitute; and if the discovered lit just drops out,
; then our continued substitution is what we should have done.
; (Aside: If we persist in our decision to reduce literals when they
; are stuffed with constants, then these cases will not arise and all
; of the above is irrelevant.)

; Recall our output spec from find-trivial-equivalence.  The five results we
; return are the name of the condition detected (disposable or keeper), the
; location i of the literal, lhs, rhs and the literal itself.  Otherwise
; we return 5 nils.  (When we succeed, the "lhs" of our result is the term for
; which we are to substitute and "rhs" is the term by which we replace lhs.
; They may in fact come from the opposite sides of the equivalence term.)

  (cond ((endp tail) (mv nil nil nil nil nil))
        ((member-equal (car tail) avoid-lst)
         (find-trivial-equivalence1
          not-just-quotep-flg (cdr tail) (1+ i) cl avoid-lst))

; Below we handle variable V as though it were the literal (not (equal
; V nil)).  ACL2 handles (NOT V) as though it were (NOT (IFF V t)),
; but ACL2 supports equivalence relations (like IFF) and Paco doesn't.
; So we cannot handle (NOT V) as an equivalence.

        ((quotep (car tail))

; If the discovered lit is nil, then we just ignore it because it will drop
; out.  If the discovered lit is non-nil, this clause is true.  So we signal
; that there are no more usable equivs and let the simplifier get its hands
; on the clause to rediscover that it is true.

         (if (equal (car tail) *nil*)
             (find-trivial-equivalence1
              not-just-quotep-flg (cdr tail) (1+ i) cl avoid-lst)
             (mv nil nil nil nil nil)))
        ((or (variablep (car tail))
             (and (eq (ffn-symb (car tail)) 'NOT)
                  (and (nvariablep (fargn (car tail) 1))
                       (not (fquotep (fargn (car tail) 1)))
                       (eq (ffn-symb (fargn (car tail) 1)) 'EQUAL))))
         (let* ((atm
                 (if (variablep (car tail))
                     (fcons-term* 'equal (car tail) *nil*)
                   (fargn (car tail) 1)))
                (lhs (fargn atm 1))
                (rhs (fargn atm 2)))

; We have discovered an equiv hyp (NOT (EQUAL lhs rhs)) that is not on
; avoid-lst.

           (cond ((and (quotep lhs)
                       (quotep rhs))

; Oh! It has constant args.  We decide which way this lit goes and act
; accordingly, as we did for a quotep lit above.

                  (if (equal lhs rhs)
                      (find-trivial-equivalence1
                       not-just-quotep-flg (cdr tail) (1+ i) cl avoid-lst)
                    (mv nil nil nil nil nil)))

; So below we know that if one side is a quotep then the other side is
; not (but we don't yet know that either side is a quotep).  Observe
; that if one side is a quotep we are always safe in answering that we
; can substitute for the other side and it is only a question of
; whether we have a disposable lit or a keeper.

                 ((and not-just-quotep-flg
                       (variablep lhs)
                       (not (dumb-occur lhs rhs)))

; The 'disposable condition is met: lhs is a variable not in rhs.  But
; it could be that rhs is also a variable not in lhs.  If so, we'll
; substitute the term-order smaller for the bigger just so the user
; knows which way the substitutions will go.

                  (cond ((variablep rhs)
                         (cond
                          ((term-order lhs rhs)
                           (mv 'disposable i rhs lhs (car tail)))
                          (t (mv 'disposable i lhs rhs (car tail)))))
                        (t (mv 'disposable i lhs rhs (car tail)))))
                 ((and not-just-quotep-flg
                       (variablep rhs)
                       (not (dumb-occur rhs lhs)))
                  (mv 'disposable i rhs lhs (car tail)))
                 ((and (quotep rhs)
                       (dumb-occurs-in-some-other-lit lhs i cl 0))

; The 'keeper conditions are met: lhs is a non-quote with at least one
; occurrence in another lit and rhs is a quote.  Note that in the case
; that not-just-quotep-flg is nil, we might be giving the ``wrong''
; first answer, since if lhs is a variable then 'keeper should be
; 'disposable.  However, if not-just-quotep-flg is nil, then we will
; be ignoring that answer anyhow; see the call of
; subst-equiv-and-maybe-delete-lit in remove-trivial-equivalences.

                  (mv 'keeper i lhs rhs (car tail)))
                 ((and (quotep lhs) ; thus rhs is a non-quotep
                       (dumb-occurs-in-some-other-lit rhs i cl 0))
                  (mv 'keeper i rhs lhs (car tail)))
                 (t (find-trivial-equivalence1
                     not-just-quotep-flg
                     (cdr tail) (1+ i) cl avoid-lst)))))
        (t (find-trivial-equivalence1
            not-just-quotep-flg
            (cdr tail) (1+ i) cl avoid-lst))))

(defun find-trivial-equivalence (not-just-quotep-flg cl avoid-lst)

; We look for a literal of cl of the form (NOT (EQUAL lhs rhs)) where
; either of two conditions apply.
;    name          condition
; disposable:    lhs is a variable and rhs does not contain lhs.
; keeper:        lhs is any non-quotep and rhs is a quotep and lhs has
;                 an occurrence in some other literal of the clause

; In addition, we accept commuted version of the equivalence and we
; treat each variablep literal, var, as the trivial equivalence (NOT
; (EQUAL var 'NIL)).

; If we find such a literal we return 5 values: the name of the condition
; detected, the location i of the literal, lhs, rhs and the literal
; itself.  Otherwise we return 5 nils.

; The driver of this function, remove-trivial-equivalences, will substitute rhs
; for lhs throughout cl, possibly delete the literal, and then call us again to
; look for the next trivial equivalence.  This raises a problem.  If the driver
; doesn't delete the literal, then we will return the same one again and loop.
; So the driver supplies us with a list of literals to avoid, avoid-lst, and
; will put onto it each of the literals that has been used but not deleted.

; So consider a clause like
; (implies (and (equal (foo b) 'evg)   [1]
;               (equal a b))           [2]
;          (p (foo a) (foo b)))

; The first trivial equivalence is [1].  The driver substitutes 'evg
; for (foo b) but doesn't delete the literal.  So we get:
; (implies (and (equal (foo b) 'evg)   [1]
;               (equal a b))           [2]
;          (p (foo a) 'evg))
; and the admotion against using (equal (foo b) 'evg).  But now we see
; [2] and the driver substitutes a for b (because a is smaller) and deletes
; [2].  So we get:
; (implies (equal (foo a) 'evg)        [1]
;          (p (foo a) 'evg))
; and the old admotion against using (equal (foo b) 'evg).  Here we find [1]
; ``again'' because it is no longer on the list of things to avoid.  Indeed, we
; can even use it to good effect.  Of course, once it is used both it and the
; old avoided literal are to be avoided.

; So can this loop?  No.  Every substitution reduces term-order.

  (find-trivial-equivalence1 not-just-quotep-flg cl 0 cl avoid-lst))

(defun subst-equiv-and-maybe-delete-lit (new old n cl i delete-flg ens wrld)

; Substitutes new for old (which are EQUAL) in every literal of cl
; except the nth one.  The nth literal is deleted if delete-flg is t
; and is skipped but included if delete-flg is nil.  We return the new
; clause.  It is possible that this fn will return a clause
; dramatically shorter than cl, because lits may evaporate upon
; evaluation or merge with other literals.  We may also prove the
; clause.

  (cond
   ((endp cl) nil)
   ((int= n i)
    (let ((cl1
           (subst-equiv-and-maybe-delete-lit new old n (cdr cl) (1+ i)
                                             delete-flg ens wrld)))
      (cond
       (delete-flg cl1)
       (t (add-literal (car cl) cl1 nil)))))
   ((dumb-occur old (car cl))
    (mv-let (hitp lit)
            (subst-equiv-expr new old (car cl) ens wrld)
            (declare (ignore hitp))
            (let ((cl1
                   (subst-equiv-and-maybe-delete-lit new old n (cdr cl) (1+ i)
                                                     delete-flg ens wrld)))
              (add-literal lit cl1 nil))))
   (t (let ((cl1
             (subst-equiv-and-maybe-delete-lit new old n
                                               (cdr cl) (1+ i)
                                               delete-flg ens wrld)))
        (add-literal (car cl) cl1 nil)))))

; In the ACL2 version of remove-trivial-equivalences there is no nnn.
; The argument that it terminates is based on the observation, above,
; that every substitution reduces term order.  But I don't want to
; prove that and it is not possible to prove the termination of this
; function without carefully analyzing the conditions on the choice of
; the trivial equivalence and what happens during substitution and
; evaluation.  In particular, there is no argument based solely on the
; growth of avoid-lst and the fact that the choice of trivial
; equivalence comes from cl and is not in avoid-lst.  Imagine that
; literals are the natural numbers and that the first literal in the
; initial cl is 0.  Imagine that find-trivial-equivalence chooses the
; first literal.  Imagine that subst-equiv-and-maybe-delete-lit
; deletes the first literal and replaces it by the integer one
; greater.  Then this function will repeatedly select ever increasing
; naturals and avoid-lst will always contain all naturals below the
; next one selected.

(defun remove-trivial-equivalences1 (cl remove-flg ens wrld hitp avoid-lst nnn)

; This function looks for two kinds of equivalence hypotheses in cl and uses
; them to do substitutions.  By "equivalence hypothesis" we mean a literal of
; the form (not (EQUAL lhs rhs)) that is not on avoid-lst.  The two kinds are
; called "trivial var equivalences" and "trivial quote equivalences."  If we
; find an equation of the first sort we substitute one side for the other and
; delete the equivalence (provided remove-flg is t).  If we find an equation of
; the second sort, we substitute one side for the other but do not delete the
; equivalence.  See find-trivial-equivalence for more details, especially
; concerning avoid-lst.  Hitp is an accumulator that records whether we did
; anything.

; The justification for deleting (not (EQUAL var term)) when var occurs nowhere
; in the clause is (a) it is always sound to throw out a literal, and (b) it is
; heuristically safe here because var is isolated and EQUAL is reflexive: if
; (implies (EQUAL var term) p) is a theorem so is p because (EQUAL term term).

; We return (mv hitp' cl').

; No Change Loser.

  (declare (xargs :measure (acl2-count nnn)))
  (if (zp nnn)
      (mv hitp cl)
    (mv-let (condition lit-position lhs rhs lit)
            (find-trivial-equivalence remove-flg cl avoid-lst)
            (cond
             (lit-position
              (let ((new-cl (subst-equiv-and-maybe-delete-lit
                             rhs lhs lit-position cl 0
                             (and remove-flg (eq condition 'disposable))
                             ens wrld)))
                (remove-trivial-equivalences1 new-cl remove-flg ens wrld t
                                              (cons lit avoid-lst)
                                              (- nnn 1))))
             (t (mv hitp cl))))))

(defun remove-trivial-equivalences (cl remove-flg ens wrld hitp avoid-lst)
  (remove-trivial-equivalences1 cl remove-flg ens wrld hitp avoid-lst
                                (len cl)))

; The termination argument for the next clique was interesting.  It
; took me a while to find it!  Ultimately what I did was imagine
; eliminating subsumes entirely by expanding the call to it in
; subsumes1.  Then I recognized the lex order on <tl1,tl2> and so then
; added the counter to give subsumes1 permission to call subsumes.

(mutual-recursion

(defun subsumes (cl1 cl2 alist)

  (declare (xargs :measure (cons (cons (+ 1 (acl2-count cl1)) 1)
                                 (acl2-count cl2))))

; We return t iff some instance of clause cl1 is a subset of
; clause cl2 (where the instance is an extension of alist).

  (cond ((endp cl1) t)
        (t (subsumes1 (car cl1) (cdr cl1) cl2 cl2 alist))))

(defun subsumes1 (lit tl1 tl2 cl2 alist)

  (declare (xargs :measure (cons (cons (+ 1 (acl2-count tl1)) 2)
                                 (acl2-count tl2))))

; If we can extend alist to an alist1 so that lit/alist1 is a member
; of tl2 and tl1/alist1 is a subset of cl2, we return t.  Else we
; return nil.

  (cond ((endp tl2) nil)
        (t (mv-let (wonp alist1)
             (one-way-unify1 lit (car tl2) alist)
             (cond ((and wonp (subsumes tl1 cl2 alist1))
                    t)
                   (t (subsumes1 lit tl1 (cdr tl2) cl2 alist)))))))

)

(defun some-member-subsumes (cl-set cl)
  (cond ((endp cl-set) nil)
        ((subsumes (car cl-set) cl nil) t)
        (t (some-member-subsumes (cdr cl-set) cl))))

(defrec built-in-clause (nume all-fnnames . clause))

; The world global, (global-val 'built-in-clauses wrld), will be an
; alist pairing function symbols with lists of built-in-clause
; records.  The global is set from the corresponding ACL2 global when
; the Paco world is built.  See database.lisp.

(defun built-in-clausep2 (bic-lst cl fns ens)
  (cond ((endp bic-lst) nil)
        ((and (enabled-numep (access built-in-clause (car bic-lst) :nume)
                             ens)
              (subsetp-eq (access built-in-clause (car bic-lst) :all-fnnames)
                          fns)
              (subsumes (access built-in-clause (car bic-lst) :clause)
                        cl nil))
         (<built-in-clausep2-id>
          t))
        (t (built-in-clausep2 (cdr bic-lst) cl ens fns))))
                         
(defun built-in-clausep1 (bic-alist cl ens fns)

; Bic-alist is the alist of built-in clauses, organized via top fnname.  Cl is
; a clause and fns is the all-fnnames-lst of cl.  This function is akin to
; some-member-subsumes in the sense of some built-in clause subsumes cl.  We
; only try subsumption on built-in clauses whose :all-fnnames field is
; a subset of fns.

  (cond ((endp bic-alist) nil)
        ((or (null (caar bic-alist))
             (member-eq (caar bic-alist) fns))

; All the built-in clauses in this pot have the same top-fnname and that name
; occurs in cl.  So these guys are all candidate subsumers.  Note:  if (car
; bic-alist) is null then this is the special pot into which we have put all
; the built-in clauses that have no "function symbols" in them, as computed by
; all-fnnames-subsumer.  I don't see how this can happen, but if it does we're
; prepared!

         (or (built-in-clausep2 (cdr (car bic-alist)) cl ens fns)
             (built-in-clausep1 (cdr bic-alist) cl ens fns)))
        (t (built-in-clausep1 (cdr bic-alist) cl ens fns))))

(defun trivial-clause-p (cl ens wrld)
  (or (member-equal *t* cl)
      (tautologyp (disjoin cl) ens wrld)))

(defun built-in-clausep (cl ens wrld)

; We return t iff cl is ``built in''.

  (cond ((trivial-clause-p cl ens wrld) t)
        (t (built-in-clausep1 (global-val 'built-in-clauses wrld)
                              cl
                              ens
                              (all-fnnames-lst cl)))))
           
(defun crunch-clause-segments1 (seg1 cl)

; This function reverses seg1 and appends it to cl, like (revappend seg1
; cl), However, if a literal in seg1 already occurs in cl, it is merged
; into that literal.

  (cond ((endp seg1) cl)
        (t (crunch-clause-segments1 (cdr seg1)
                                    (add-literal (car seg1) cl nil)))))

(defun crunch-clause-segments2 (cl seg1)

; See crunch-clause-segments.  We scan down cl until we see the marker
; literal, accumulating the earlier lits into seg1. We return (mv seg1
; cl).

  (cond ((endp cl) (mv seg1 nil))
        ((and (consp (car cl))
              (eq (ffn-symb (car cl)) 'car)
              (eq (fargn (car cl) 1) :crunch-clause-segments-marker))
         (mv seg1 (cdr cl)))
        (t (crunch-clause-segments2 (cdr cl)
                                    (cons (car cl) seg1)))))

(defun crunch-clause-segments (seg1 seg2 ens wrld)

; This function is a special purpose subroutine of rewrite-clause.  Seg1 and
; seg2 are just lists of literals.  Pts1 is in weak 1:1 correspondence with
; seg1 and enumerates the parent trees of the corresponding literals of seg1.
; Consider the clause obtained by appending these two segments.

; {lit4 ... lit7 lit1' ... lit2' lit3a ... lit3z}    ; cl

; |  <- seg1 -> | <- seg2 ->                   |

;  unrewritten  |  rewritten

; Context: The rewriter is sweeping through this clause, rewriting each literal
; and assembling a new clause.  It has rewritten none of the seg1 literals and
; all of the seg2 literals.  It has just rewritten some literal called lit3.
; After clausifying the result (and getting in this case lit3a ... lit3z) it is
; about to start rewriting the first literal of seg1, lit4.  It has already
; rewritten lit1'...lit2'.  The rewriter actually keeps the unrewritten part of
; the clause (seg1) separate from the rewritten part (seg2) so that it knows
; when it is done.  In the old days, it would just proceed to rewrite the first
; literal of seg1.

; But suppose lit3 was something like (not (member x '(...))).  Then
; we will get lots of segs, each of the form (equal x '...).  We are
; trying to optimize our handling of this by actually stuffing the
; constant into the clause and running any terms we can.  We do this
; in what we think is a very elegant way: We actually create cl and
; call remove-trivial-equivalences on it.  Then we recover the two
; parts, unrewritten and rewritten.  The trick is how we figure out
; which is which.  We put a marker literal into the clause, after seg1
; and before seg2.  Remove-trivial-equivalences may do a lot of
; literal evaluation and deletion.  But then we find the marker
; literal and consider everything to its left unrewritten and
; everything else rewritten.

; We return two values: The unrewritten part of cl and the rewritten
; part of cl.

  (let* ((marker '(car :crunch-clause-segments-marker))
         (cl (crunch-clause-segments1 seg1 (cons marker seg2))))
    (mv-let (hitp cl)
            (remove-trivial-equivalences cl nil ;;; see Note below
                                         ens wrld nil nil)

; Note: In the call of remove-trivial-equivalences above we use
; remove-flg = nil.  At one time, we used remove-flg = t.  But if
; there is auxiliary information around -- e.g., the linear arithmetic
; database in ACL2 -- that data may mention the eliminated variable.
; So we keep them until we are at the top of the process again.

            (cond
             ((null hitp)
              (mv seg1 seg2))
             (t (mv-let (seg1 seg2)
                        (crunch-clause-segments2 cl nil)
                        (mv seg1 seg2)))))))

(defthm rewrite-clause-speedup-1
  (implies (not (zp nnn))
           (E0-ORD-<
            (CONS
             (CONS (+ 1 (nfix (+ -1 nnn))) 2) x)
            (CONS (CONS (+ 1 (nfix nnn)) 1) y)))
  :rule-classes nil)

(acl2::set-well-founded-relation e0-ord-<)

(mutual-recursion

(defun rewrite-clause (tail new-clause wrld rcnst flg ans nnn)

; Note: When rewrite-clause is called, nnn should be set to (len
; tail), where tail is the first argument supplied to rewrite-clause.
; See the note below about the admission of this function.

  (declare (xargs :measure (cons (cons (+ 1 (nfix nnn)) 1) 0)
                  :hints (("Goal" :in-theory
                           (disable remove-trivial-equivalences
                                    crunch-clause-segments
                                    DISJOIN-CLAUSE-SEGMENT-TO-CLAUSE-SET
                                    NORMALIZE))
                          ("Subgoal 1" :do-not '(preprocess)
                           :by rewrite-clause-speedup-1)
                          )))

; We are to rewrite the literals of the clause cl formed by appending
; tail to new-clause.  We assume rcnst has the correct top-clause and
; current-clause.  We return (mv flg ans), where flg indicates whether
; anything was done and ans is a set of clauses whose conjunction
; implies cl.

  (cond
   ((or (endp tail)
        (zp nnn))

; We choose nnn large enough so that (endp tail) will always be true
; before or when (zp nnn) becomes true.  But just in case, if we run
; out of nnn before rewriting all the literals in tail, we just tack
; them onto the rewritten part, new-clause, and call that our result.
; We never expect this to happen.  See the discussion below about the
; termination of rewrite-clause.

    (let ((new-clause (crunch-clause-segments1 tail new-clause)))
      (cond
       ((built-in-clausep1 (global-val 'built-in-clauses wrld)
                           new-clause
                           (access rewrite-constant rcnst :ens)
                           (all-fnnames-lst new-clause))
        (mv t ans))
       (t (mv flg (cons new-clause ans))))))
   (t
    (mv-let
     (not-flg atm)
     (strip-not (car tail))
     (let ((local-rcnst
            (change rewrite-constant rcnst
                    :current-literal
                    (make current-literal
                          :not-flg not-flg
                          :atm atm))))
       (mv-let
        (contradictionp type-alist)
        (rewrite-clause-type-alist tail new-clause
                                   (access rewrite-constant rcnst :ens)
                                   wrld)
        (cond
         (contradictionp
          (mv t ans))
         (t
          (let* ((val1 (rewrite-atm atm not-flg type-alist wrld local-rcnst))
                 (val2 (if not-flg (dumb-negate-lit val1) val1))
                 (branches (clausify val2
                                     (access rewrite-constant rcnst :ens)
                                     wrld))
                 (action (rewrite-clause-action (car tail) branches))
                 (segs

; Perhaps we can simply use branches below.  But that requires some thought,
; because the form below handles true clauses (including *true-clause*) with
; special care.  This issue arose as we removed old-style-forcing from the
; code.

                  (disjoin-clause-segment-to-clause-set nil branches)))
            (rewrite-clause-lst segs
                                (cdr tail)
                                new-clause
                                wrld
                                rcnst
                                (or flg (not (eq action 'no-change)))
                                ans
                                (- nnn 1)))))))))))
             

(defun rewrite-clause-lst (segs cdr-tail new-clause wrld rcnst flg ans nnn)
  (declare (xargs :measure (cons (cons (+ 1 (nfix nnn)) 2) (len segs))))
  (cond
   ((endp segs) (mv flg ans))
   (t
    (mv-let
     (flg1 ans1)
     (rewrite-clause-lst (cdr segs) cdr-tail new-clause wrld rcnst flg ans nnn)
     (mv-let (unrewritten rewritten)
             (crunch-clause-segments
              cdr-tail
              (append new-clause
                      (set-difference-equal (car segs) new-clause))
              (access rewrite-constant rcnst :ens)
              wrld)
             (rewrite-clause unrewritten
                             rewritten
                             wrld
                             rcnst
                             flg1
                             ans1
                             nnn))))))

)

; Essay on the Admission of Rewrite-clause

; In ACL2, rewrite-clause does not have the nnn argument.  It is there in
; Paco only to make the termination argument easier.  Consider the definition
; of rewrite-clause and rewrite-clause-lst obtained by removing all mention of
; nnn.  I could get that definition admitted if I could prove:

; (defthm len-car-crunch-clause-segments
;   (<= (len (car (crunch-clause-segments seg1 seg2 ens wrld)))
;       (len seg1))
;  :rule-classes :linear)

; But this cannot be proved without a lot of reasoning about the
; world. Here is the problem.  The definition of
; crunch-clause-segments conses a marker literal between the two
; segments, runs remove-trivial-equivalences on the resulting clause,
; and then extracts the two segments.  The "theorem" above says that
; the first extracted segment is no longer than the original input
; segment.  But what happens if remove-trivial-equivalences eliminates
; the marker literal, e.g., by evaluating it to NIL?  Then the
; extracted segment is the whole clause, which is in general longer
; than seg1 alone.  Why do I know remove-trivial-equivalences cannot
; eliminate the marker?  The marker is (CAR
; :CRUNCH-CLAUSE-SEGMENTS-MARKER).  Call this (CAR :C) for short.  I
; could probably prove that (CAR :C) will not evaluate to NIL if I
; knew :C doesn't occur elsewhere in the clause.  But for all I know,
; there is a literal elsewhere that says (CAR :C) is NIL.  What stops
; that?  :C is not a legal variable symbol.  But where have I
; established that?  I don't want to go this deeply into the problem.

; Instead, I introduced nnn.  It should be set initially to the length
; of the first argument, tail, in rewrite-clause.  That is the number
; of unrewritten literals.  It will not grow.  It could shrink if some
; unrewritten literals evaluate to NIL in remove-trivial-equivalences.
; But no new unrewritten literals are ever introduced.  So this definition
; is easier to admit, just as powerful as ACL2's, and only marginally
; less efficient.

(defun simplify-clause1 (top-clause rcnst wrld)

; Top-clause is a clause with history hist.  We assume that rcnst
; contains appropriate settings (i.e., from induct) and we install the
; rewrite-specific fields below.

; We return 2 values.  The first indicates whether we changed
; top-clause.  If we did not, the second is nil.  If we did change
; top-clause, the second is a list of new clauses whose conjunction
; implies top-clause.

  (mv-let (hitp current-clause)
          (remove-trivial-equivalences top-clause t
                                       (access rewrite-constant rcnst :ens)
                                       wrld nil nil)
          (let ((local-rcnst (change rewrite-constant rcnst
                                     :top-clause top-clause
                                     :current-clause current-clause)))

; When we call rewrite-clause, below, we pass in as the initial value
; of its ``did we change anything?'' accumulator the flg, hitp, that
; indicates whether remove-trivial-equations changed anything.  Thus,
; this call may answer ``yes, something was changed'' when in fact,
; nothing was done by rewrite-clause itself.
                     
            (rewrite-clause current-clause nil wrld local-rcnst
                            hitp nil (len current-clause)))))

(defun some-element-dumb-occur-lst (lst1 lst2)
  (cond ((endp lst1) nil)
        ((dumb-occur-lst (car lst1) lst2) t)
        (t (some-element-dumb-occur-lst (cdr lst1) lst2))))

; The Spec Vars of Prove -- pspv

; The theorem prover, prove, uses so many special variables that are rarely
; modified that we bundle them up.  Because simplify-clause, below, is a
; clause processor in the waterfall, it is the first function in this
; development that is high enough up in the hierarchy to see prove's
; bundle of variables.  So it is time to lay out prove's spec vars:

(defrec prove-spec-var
  (((rewrite-constant . induct-hint-val)
    .
    (induction-hyp-terms . induction-concl-terms))
   .
   (((do-not-processes . hint-settings)
     .
     (global-ens . unused-slot)))))

;=================================================================

; Clause Histories

; Clauses carry with them their histories, which describe which
; processes have produced them.  A clause history is a list of
; history-entry records.  It is in reverse chronological order.  A
; process, such as simplify-clause, might inspect the history of its
; input clause to help decide whether to perform a certain
; transformation.

(defrec history-entry
  (processor clause . alist))

; Processor is a waterfall processor (e.g., 'simplify-clause).  Clause
; is its input.  The alist is an alist produced by the processor on
; clause used to record what that processor did, in a format that is
; essentially unique for each processor.  However, any processor that
; introduces new variables into a clause must include in its alist
; a pair of the form (:VARIABLES . vars), where vars are the newly
; introduced variables.

; Each history-entry is built in the waterfall, but we inspect them
; for the first time in this file.

(defun settled-since-inductionp (hist)

; Scan back to the last induct-clause (or to the beginning of hist)
; and determine whether there is a settled-down-clause since then.
; Below are some examples, where simp means a simplify-clause history
; entry, etc.

; hist                                     ; result

; (simp simp)                                nil
; (simp settled simp)                        t
; (simp induct simp settled simp)            nil
; (settled simp induct simp settled simp)    t

  (cond ((endp hist) nil)
        ((eq (access history-entry (car hist) :processor)
             'settled-down-clause)
         t)
        ((eq (access history-entry (car hist) :processor)
             'induct-clause)
         nil)
        (t (settled-since-inductionp (cdr hist)))))

(defun simplify-clause (id cl hist pspv wrld)

; This is the main "clause processor" of the waterfall.  Its input and
; output spec is consistent with that of all such processors.  See the
; waterfall for a general discussion.

  (declare (ignore id))

; Cl is a clause with clause id id and history hist.  We can obtain a
; rewrite-constant from the prove spec var pspv.  We assume nothing
; about the rewrite-constant except that it has the appropriate
; contextual settings (e.g., from induct).  We install
; rewrite-specific fields when necessary.

; We return 4 values.  The first is a signal that in general is 'HIT,
; 'MISS, 'ABORT, or anything else, indicating an unexpected error.  In
; this case, the signal is always either 'HIT or 'MISS.  When the
; signal is 'MISS, the other 3 values are irrelevant.  When the signal
; is 'HIT, the second result is the list of new clauses, the third is
; an alist (in this case nil) that will become the :alist component of
; the history-entry for this simplification, and the fourth is the new
; (but in this case unmodified) pspv.  The third and fourth return
; values here are present only to follow the standard calling
; conventions for waterfall processors.

; If the first result is 'HIT then the conjunction of the new clauses
; returned implies cl.  Equivalently, under the assumption that cl is
; false, cl is equivalent to the conjunction of the new clauses.

  (cond ((settled-since-inductionp hist)

; The clause has settled down under rewriting with the
; induction-hyp-terms ignored and the induction-concl-terms forcibly
; expanded.  In general, we now want to stop treating those terms
; specially and continue simplifying.  However, there is a special
; case that will save a little time.  Suppose that the clause just
; settled down -- i.e., the most recent hist entry is the one we just
; found.  And suppose that none of the specially treated terms occurs
; in the clause we're to simplify.  Then we needn't simplify it again.

         (cond ((and (eq 'settled-down-clause
                         (access history-entry (car hist) :processor))
                     (not (some-element-dumb-occur-lst
                           (access prove-spec-var
                                   pspv
                                   :induction-hyp-terms)
                           cl)))

; Since we know the induction-concl-terms couldn't occur in the clause --
; they would have been expanded -- we are done.  This test should
; speed up base cases and preinduction simplification at least.

                (mv 'miss nil nil nil))
               (t

; We now cease interfering and let the heuristics do their job.

                (mv-let (changedp clauses)
                        (simplify-clause1
                         cl
                         (access prove-spec-var pspv :rewrite-constant)
                         wrld)
                        (cond (changedp

; Note: It is possible that our input, cl, is a member-equal of our
; output, clauses!  Such simplifications are said to be "specious."
; But we do not bother to detect that here because we want to report
; the specious simplification as though everything were ok and then
; pretend nothing happened.  This gives the user some indication of
; where the loop is.  In the old days, we just signalled a 'miss if
; (member-equal cl clauses) and that caused a lot of confusion among
; experienced users, who saw simplifiable clauses being passed on to
; elim, etc.  See :DOC specious-simplification.

                               (mv 'hit clauses nil pspv))
                              (t (mv 'miss nil nil nil)))))))
        (t

; The clause has not settled down yet.  So we arrange to ignore the
; induction-hyp-terms and to expand the induction-concl-terms without
; question.  The local-rcnst created below is not passed out of this
; function.

         (let* ((rcnst (access prove-spec-var pspv :rewrite-constant))
                (local-rcnst
                 (change rewrite-constant
                         rcnst
                         :terms-to-be-ignored-by-rewrite
                         (append
                          (access prove-spec-var
                                  pspv :induction-hyp-terms)
                          (access rewrite-constant
                                  rcnst :terms-to-be-ignored-by-rewrite))
                         :expand-lst
                         (append (access prove-spec-var
                                          pspv :induction-concl-terms)
                                 (access rewrite-constant
                                         rcnst :expand-lst)))))
           (mv-let (changedp clauses)
                   (simplify-clause1 cl local-rcnst wrld)
                   (cond
                    (changedp (mv 'hit clauses nil pspv))
                    (t (mv 'miss nil nil nil))))))))

; Inside the waterfall, the settled-down-clause processor immediately
; follows simplify-clause.  Its presence in the waterfall causes us to
; add a 'settled-down-clause hist-entry to the history of the clause
; when simplify-clause finishes with it.  The "hit state" of the
; waterfall leads back to the simplifier, where the code above detects
; this settling down and turns off the handling of the induction hyp
; and concl terms.  The "miss state" of the waterfall leads to the
; next processor.

(defun hit-it-againp (hist)

; This function should only be called in settled-down-clause, i.e.,
; when the clause has settled down.  The question is: do we want to
; hit the clause with another round of simplification, by claiming a
; HIT now?  Why would we do that?  If we've come from induction,
; simplify-clause has short-circuited the normal heuristics.  Claiming
; a HIT here will cause simplify-clause to behave ``normally.''  There
; is no point in going back to the top of the waterfall if (a) we
; haven't seen an induction yet (i.e., the user's input didn't change
; under the first simplification), or (b) there is an induction in the
; history but the clause already settled-down after it.

  (cond
   ((endp hist) nil)
   ((eq (access history-entry (car hist) :processor) 'induct-clause)
    t)
   ((eq (access history-entry (car hist) :processor) 'settled-down-clause)
    nil)
   (t (hit-it-againp (cdr hist)))))

(defun settled-down-clause (id clause hist pspv wrld)

; This is the processor in the waterfall just after simplify-clause.
; If we get here, simplify-clause has MISSed.

  (declare (ignore id wrld))
  (cond ((hit-it-againp hist)
         (mv 'HIT (list clause) nil pspv))
        (t (mv 'MISS nil nil nil))))