; APT (Automated Program Transformations) Library
;
; Copyright (C) 2020 Kestrel Institute (http://www.kestrel.edu)
;
; License: A 3-clause BSD license. See the LICENSE file distributed with ACL2.
;
; Author: Alessandro Coglio (coglio@kestrel.edu)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(in-package "ACL2")

(include-book "kestrel/soft/defunvar" :dir :system)
(include-book "kestrel/soft/defund-sk2" :dir :system)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; This file contains macros to generate template functions and theorems
; of the kind processed and generated by SOLVE.
; These templates are useful to construct generic tests
; and to explicate proof generation strategies.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Generate a function variable.

(defmacro gen-funvar (&key name arity)
  `(defunvar ,name ,(repeat arity '*) => *))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Generate an I/O relation (matrix of OLD) that rewrites to
; (EQUAL OUTPUT TERM).

(defmacro gen-matrix-rw-eqterm (&key name inputs output term rwrule)
  `(encapsulate
     (((,name ,@(repeat (1+ (len inputs)) '*)) => *)
      ((,term ,@(repeat (len inputs) '*)) => *))
     (local
      (defun ,term (,@inputs)
        (declare (ignore ,@inputs))
        nil))
     (local
      (defun ,name (,@inputs ,output)
        (equal ,output (,term ,@inputs))))
     (defthmd ,rwrule
       (equal (,name ,@inputs ,output)
              (equal ,output (,term ,@inputs))))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Generate an I/O relation (matrix of OLD) that rewrites to
; (IF ... (EQUAL OUTPUT TERM) T).

(defmacro gen-matrix-rw-if-eqterm-t (&key name inputs output term test rwrule)
  `(encapsulate
     (((,name ,@(repeat (1+ (len inputs)) '*)) => *)
      ((,term ,@(repeat (len inputs) '*)) => *)
      ((,test) => *))
     (local
      (defun ,term (,@inputs)
        (declare (ignore ,@inputs))
        nil))
     (local
      (defun ,name (,@inputs ,output)
        (equal ,output (,term ,@inputs))))
     (local
      (defun ,test ()
        t))
     (defthmd ,rwrule
       (equal (,name ,@inputs ,output)
              (if (,test)
                  (equal ,output (,term ,@inputs))
                t)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Generate an I/O relation (matrix of OLD) that rewrites to
; (IF ... T (EQUAL OUTPUT TERM)).

(defmacro gen-matrix-rw-if-t-eqterm (&key name inputs output term test rwrule)
  `(encapsulate
     (((,name ,@(repeat (1+ (len inputs)) '*)) => *)
      ((,term ,@(repeat (len inputs) '*)) => *)
      ((,test) => *))
     (local
      (defun ,term (,@inputs)
        (declare (ignore ,@inputs))
        nil))
     (local
      (defun ,name (,@inputs ,output)
        (equal ,output (,term ,@inputs))))
     (local
      (defun ,test ()
        nil))
     (defthmd ,rwrule
       (equal (,name ,@inputs ,output)
              (if (,test)
                  t
                (equal ,output (,term ,@inputs)))))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Generate an I/O relation (matrix of OLD) that rewrites to T.

(defmacro gen-matrix-rw-t (&key name inputs output rwrule)
  `(encapsulate
     (((,name ,@(repeat (1+ (len inputs)) '*)) => *))
     (local
      (defun ,name (,@inputs ,output)
        (declare (ignore ,@inputs ,output))
        t))
     (defthmd ,rwrule
       (equal (,name ,@inputs ,output)
              t))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Generate an I/O relation (matrix of OLD) that rewrites to (IF ... T T).

(defmacro gen-matrix-rw-if-t-t (&key name inputs output test rwrule)
  `(encapsulate
     (((,name ,@(repeat (1+ (len inputs)) '*)) => *)
      ((,test) => *))
     (local
      (defun ,name (,@inputs ,output)
        (declare (ignore ,@inputs ,output))
        t))
     (local
      (defun ,test ()
        nil))
     (defthmd ,rwrule
       (equal (,name ,@inputs ,output)
              (if (,test) t t)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Generate an I/O relation (matrix of OLD) that
; is satisfied by a solution function or term.

(defmacro gen-matrix-sat (&key name inputs output sol satrule)
  `(encapsulate
     (((,name ,@(repeat (1+ (len inputs)) '*)) => *)
      ((,sol ,@(repeat (len inputs) '*)) => *))
     (local
      (defun ,sol (,@inputs)
        (declare (ignore ,@inputs))
        nil))
     (local
      (defun ,name (,@inputs ,output)
        (declare (ignore ,@inputs ,output))
        t))
     (defthmd ,satrule
       (,name ,@inputs (,name ,@inputs)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Generate an I/O relation (matrix of OLD) that
; is satisfied when the output is equal to a term.

(defmacro gen-matrix-sat-eqterm (&key name inputs output term sat)
  `(encapsulate
     (((,name ,@(repeat (1+ (len inputs)) '*)) => *)
      ((,term ,@(repeat (len inputs) '*)) => *))
     (local
      (defun ,term (,@inputs)
        (declare (ignore ,@inputs))
        nil))
     (local
      (defun ,name (,@inputs ,output)
        (equal ,output (,term ,@inputs))))
     (defthmd ,sat
       (implies (equal ,output (,term ,@inputs))
                (,name ,@inputs ,output)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Generate the function OLD described in the user documentation.

(defmacro gen-old (&key name vars matrix ?f)
  `(defund-sk2 ,name ()
     (declare (xargs :guard t))
     (forall (,@vars) (,matrix ,@vars (,?f ,@vars)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Generate the function F described in the user documentation.

(defmacro gen-f (&key name inputs body guard guard-hints)
  `(defund ,name (,@inputs)
     (declare (ignorable ,@inputs))
     (declare (xargs :guard ,guard
                ,@(and guard-hints (list :guard-hints guard-hints))))
     ,body))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Generate the function NEW described in the user documentation.

(defmacro gen-new (&key name vars ?f f guard-hints)
  `(defund-sk2 ,name ()
     (declare (xargs :guard t :guard-hints ,guard-hints))
     (forall (,@vars)
             (equal (,?f ,@vars)
                    (,f ,@vars)))
     :rewrite :direct))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Generate the function OLD-IF-NEW described in the user documentation.

(defmacro gen-old-if-new (&key name old new hints)
  `(defthmd ,name
     (implies (,new)
              (,old))
     :hints ,hints))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Generate generic inputs of the transformation,
; for different arities and different kinds of solutions.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defmacro gen-inputs-1-rw-eqterm (&key (?f '?f)
                                       (matrix 'm)
                                       (inputs '(x))
                                       (output 'y)
                                       (term 'term)
                                       (rwrule 'rwrule)
                                       (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 1)
     (gen-matrix-rw-eqterm :name ,matrix
                           :inputs ,inputs
                           :output ,output
                           :term ,term
                           :rwrule ,rwrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-1-rw-if-eqterm-t (&key (?f '?f)
                                            (matrix 'm)
                                            (inputs '(x))
                                            (output 'y)
                                            (term 'term)
                                            (test 'test)
                                            (rwrule 'rwrule)
                                            (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 1)
     (gen-matrix-rw-if-eqterm-t :name ,matrix
                                :inputs ,inputs
                                :output ,output
                                :term ,term
                                :test ,test
                                :rwrule ,rwrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-1-rw-if-t-eqterm (&key (?f '?f)
                                            (matrix 'm)
                                            (inputs '(x))
                                            (output 'y)
                                            (term 'term)
                                            (test 'test)
                                            (rwrule 'rwrule)
                                            (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 1)
     (gen-matrix-rw-if-t-eqterm :name ,matrix
                                :inputs ,inputs
                                :output ,output
                                :term ,term
                                :test ,test
                                :rwrule ,rwrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-1-rw-t (&key (?f '?f)
                                  (matrix 'm)
                                  (inputs '(x))
                                  (output 'y)
                                  (rwrule 'rwrule)
                                  (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 1)
     (gen-matrix-rw-t :name ,matrix
                      :inputs ,inputs
                      :output ,output
                      :rwrule ,rwrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-1-rw-if-t-t (&key (?f '?f)
                                       (matrix 'm)
                                       (inputs '(x))
                                       (output 'y)
                                       (test 'test)
                                       (rwrule 'rwrule)
                                       (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 1)
     (gen-matrix-rw-if-t-t :name ,matrix
                           :inputs ,inputs
                           :output ,output
                           :test ,test
                           :rwrule ,rwrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-1-sat (&key (?f '?f)
                                 (matrix 'm)
                                 (inputs '(x))
                                 (output 'y)
                                 (sol 'sol)
                                 (satrule 'satrule)
                                 (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 1)
     (gen-matrix-sat :name ,matrix
                     :inputs ,inputs
                     :output ,output
                     :sol ,sol
                     :satrule ,satrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-1-sat-eqterm (&key (?f '?f)
                                        (matrix 'm)
                                        (inputs '(x))
                                        (output 'y)
                                        (term 'term)
                                        (sat 'sat)
                                        (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 1)
     (gen-matrix-sat-eqterm :name ,matrix
                            :inputs ,inputs
                            :output ,output
                            :term ,term
                            :sat ,sat)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defmacro gen-inputs-2-rw-eqterm (&key (?f '?f)
                                       (matrix 'm)
                                       (inputs '(x1 x2))
                                       (output 'y)
                                       (term 'term)
                                       (rwrule 'rwrule)
                                       (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 2)
     (gen-matrix-rw-eqterm :name ,matrix
                           :inputs ,inputs
                           :output ,output
                           :term ,term
                           :rwrule ,rwrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-2-rw-if-eqterm-t (&key (?f '?f)
                                            (matrix 'm)
                                            (inputs '(x1 x2))
                                            (output 'y)
                                            (term 'term)
                                            (test 'test)
                                            (rwrule 'rwrule)
                                            (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 2)
     (gen-matrix-rw-if-eqterm-t :name ,matrix
                                :inputs ,inputs
                                :output ,output
                                :term ,term
                                :test ,test
                                :rwrule ,rwrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-2-rw-if-t-eqterm (&key (?f '?f)
                                            (matrix 'm)
                                            (inputs '(x1 x2))
                                            (output 'y)
                                            (term 'term)
                                            (test 'test)
                                            (rwrule 'rwrule)
                                            (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 2)
     (gen-matrix-rw-if-t-eqterm :name ,matrix
                                :inputs ,inputs
                                :output ,output
                                :term ,term
                                :test ,test
                                :rwrule ,rwrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-2-rw-t (&key (?f '?f)
                                  (matrix 'm)
                                  (inputs '(x1 x2))
                                  (output 'y)
                                  (rwrule 'rwrule)
                                  (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 2)
     (gen-matrix-rw-t :name ,matrix
                      :inputs ,inputs
                      :output ,output
                      :rwrule ,rwrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-2-rw-if-t-t (&key (?f '?f)
                                       (matrix 'm)
                                       (inputs '(x1 x2))
                                       (output 'y)
                                       (test 'test)
                                       (rwrule 'rwrule)
                                       (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 2)
     (gen-matrix-rw-if-t-t :name ,matrix
                           :inputs ,inputs
                           :output ,output
                           :test ,test
                           :rwrule ,rwrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-2-sat (&key (?f '?f)
                                 (matrix 'm)
                                 (inputs '(x1 x2))
                                 (output 'y)
                                 (sol 'sol)
                                 (satrule 'satrule)
                                 (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 2)
     (gen-matrix-sat :name ,matrix
                     :inputs ,inputs
                     :output ,output
                     :sol ,sol
                     :satrule ,satrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-2-sat-eqterm (&key (?f '?f)
                                        (matrix 'm)
                                        (inputs '(x1 x2))
                                        (output 'y)
                                        (term 'term)
                                        (sat 'sat)
                                        (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 2)
     (gen-matrix-sat-eqterm :name ,matrix
                            :inputs ,inputs
                            :output ,output
                            :term ,term
                            :sat ,sat)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defmacro gen-inputs-3-rw-eqterm (&key (?f '?f)
                                       (matrix 'm)
                                       (inputs '(x1 x2 x3))
                                       (output 'y)
                                       (term 'term)
                                       (rwrule 'rwrule)
                                       (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 3)
     (gen-matrix-rw-eqterm :name ,matrix
                           :inputs ,inputs
                           :output ,output
                           :term ,term
                           :rwrule ,rwrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-3-rw-if-eqterm-t (&key (?f '?f)
                                            (matrix 'm)
                                            (inputs '(x1 x2 x3))
                                            (output 'y)
                                            (term 'term)
                                            (test 'test)
                                            (rwrule 'rwrule)
                                            (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 3)
     (gen-matrix-rw-if-eqterm-t :name ,matrix
                                :inputs ,inputs
                                :output ,output
                                :term ,term
                                :test ,test
                                :rwrule ,rwrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-3-rw-if-t-eqterm (&key (?f '?f)
                                            (matrix 'm)
                                            (inputs '(x1 x2 x3))
                                            (output 'y)
                                            (term 'term)
                                            (test 'test)
                                            (rwrule 'rwrule)
                                            (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 3)
     (gen-matrix-rw-if-t-eqterm :name ,matrix
                                :inputs ,inputs
                                :output ,output
                                :term ,term
                                :test ,test
                                :rwrule ,rwrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-3-rw-t (&key (?f '?f)
                                  (matrix 'm)
                                  (inputs '(x1 x2 x3))
                                  (output 'y)
                                  (rwrule 'rwrule)
                                  (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 3)
     (gen-matrix-rw-t :name ,matrix
                      :inputs ,inputs
                      :output ,output
                      :rwrule ,rwrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-3-rw-if-t-t (&key (?f '?f)
                                       (matrix 'm)
                                       (inputs '(x1 x2 x3))
                                       (output 'y)
                                       (test 'test)
                                       (rwrule 'rwrule)
                                       (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 3)
     (gen-matrix-rw-if-t-t :name ,matrix
                           :inputs ,inputs
                           :output ,output
                           :test ,test
                           :rwrule ,rwrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-3-sat (&key (?f '?f)
                                 (matrix 'm)
                                 (inputs '(x1 x2 x3))
                                 (output 'y)
                                 (sol 'sol)
                                 (satrule 'satrule)
                                 (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 3)
     (gen-matrix-sat :name ,matrix
                     :inputs ,inputs
                     :output ,output
                     :sol ,sol
                     :satrule ,satrule)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))

(defmacro gen-inputs-3-sat-eqterm (&key (?f '?f)
                                        (matrix 'm)
                                        (inputs '(x1 x2 x3))
                                        (output 'y)
                                        (term 'term)
                                        (sat 'sat)
                                        (old 'old))
  `(encapsulate ()
     (gen-funvar :name ,?f :arity 3)
     (gen-matrix-sat-eqterm :name ,matrix
                            :inputs ,inputs
                            :output ,output
                            :term ,term
                            :sat ,sat)
     (gen-old :name ,old :vars ,inputs :matrix ,matrix :?f ,?f)))
