; The "tests" function tests EVERYTHING! (defun tests () (progn (princ "-----------------------------------------") (terpri) (easy-cnf) (hard-cnf) (easy-simplification) (hard-simplification) (combination) ) ) ; The "easy-cnf" function tests simple cases of conversion to CNF, ; where one conversion rule has to be applied once only. (defun easy-cnf () (progn (princ "Single-rule CNF tests:") (terpri) (terpri) (test-cnf '(implies p q)) (test-cnf '(implies p (not q))) (test-cnf '(implies true false)) (test-cnf '(not (and p q))) (test-cnf '(not (and p q r))) (test-cnf '(not (or p q))) (test-cnf '(not (or p q r))) (test-cnf '(not (and (not p) (not q)))) (test-cnf '(not (or true) false)) (test-cnf '(or p (and q r))) (test-cnf '(or (and p q) (and r s))) ) ) ; The "hard-cnf" function tests hard cases of conversion to CNF, ; where multiple conversion rules may be needed, in some order, ; and rules may be needed more than once. (defun hard-cnf () (progn (princ "Multiple-rule CNF tests:") (terpri) (terpri) (test-cnf '(and (implies a b) (implies c d))) (test-cnf '(implies p (implies q r))) (test-cnf '(not (and (or a b) (or c d)))) (test-cnf '(not (or (and p q) (and r s)))) (test-cnf '(or (and a b) (and c d))) (test-cnf '(and (and a b) (or c d))) (test-cnf '(or a (or b c))) ) ) ; The "easy-simplification" function tests easy cases of simplification, ; where one simplification rule has to be applied once only. (defun easy-simplification () (progn (princ "Single-step simplifications:") (terpri) (terpri) (test-simplify '(and p p)) (test-simplify '(or p p)) (test-simplify '(not (not p))) (test-simplify '(and false p)) (test-simplify '(or true p)) (test-simplify '(and true p)) (test-simplify '(or false p)) (test-simplify '(or p (not p))) (test-simplify '(or (not p) p)) (test-simplify '(and p (not p))) (test-simplify '(and (not p) p)) (test-simplify '(and (or p q) (not (or p q)))) ) ) ; The "hard-simplification" function tests hard cases of function ; simplification, where multiple simplification rules may be needed, ; in some order, and rules may be needed more than once. (defun hard-simplification () (progn (princ "Multiple-step simplification:") (terpri) (terpri) (test-simplify '(and (or p p) (or p p))) (test-simplify '(and p (not (not p)))) (test-simplify '(or (and true (not true)) (or false (not false)))) (test-simplify '(or (and (not p) true) (and true p))) ) ) ; The "combination" function tests expressions that should be ; converted to CNF and also simplified. (defun combination () (progn (princ "Conversion to CNF and simplification:") (terpri) (terpri) (test-combination '(implies (not (and p q)) (or (not p) (not q)))) (test-combination '(and (implies (and p (not q)) q) (not p))) (test-combination '(implies (and (implies p q) (implies q r)) (implies p r)) ) (test-combination '(and (implies p q) (not (or (not p) q)))) ) ) ; The "test-cnf" function prints out its argument, then (on a ; separate line) prints out the result of calling "to-cnf" with ; its argument, and finally prints a blank line. If your function ; for converting an expression to CNF form is named "to-cnf", ; you can use this function as is; otherwise, change the call ; in the fourth line to call your function rather than "to-cnf". (defun test-cnf (expr) (progn (princ "input: ") (prin1 expr) (terpri) (princ "CNF: ") (prin1 (to-cnf expr)) (terpri) (terpri) ) ) ; The "test-simplify" function prints out its argument, then (on a ; separate line) prints out the result of calling "simplify" with ; its argument, and finally prints a blank line. If your function ; for simplifying an expression is named "simplify", you can use ; this function as is; otherwise, change the call in the fourth line ; to call your function rather than "simplify". (defun test-simplify (expr) (progn (princ "input: ") (prin1 expr) (terpri) (princ "simpler: ") (prin1 (simplify expr)) (terpri) (terpri) ) ) ; The "combination" function prints out its argument, then (on a ; separate line) prints out the result of calling "to-cnf" with ; its argument, then the result of calling "simplify" with the ; CNF expression, and finally prints a blank line. If your functions ; are named "to-cnf" and "simplify", you can use this function as is, ; otherwise chang the names of the functions that this one calls (defun test-combination (expr) (progn (princ "input: ") (prin1 expr) (terpri) (princ "CNF: ") (prin1 (to-cnf expr)) (terpri) (princ "simpler: ") (prin1 (simplify (to-cnf expr))) (terpri) (terpri) ) ) ; This is the BEGINNING of a function to turn an arbitrary logical ; expression into a logical expression in Conjunctive Normal Form ; (CNF). At the moment, all it does is replace (IMPLIES P Q) with ; (OR (NOT P) Q). (defun to-cnf (expr) (recursive-apply 'remove-implies expr) ) ; This is the BEGINNING of a function to simplify an arbitrary ; logical expression. At the moment, all it does is replace a ; contradiction such as (AND P (NOT P)) with FALSE. (defun simplify (expr) (recursive-apply 'simplify-contradiction expr) ) ; If the argument to "remove-implies" is a list of the form ; (IMPLIES P Q), for arbitrarily complex P and Q, then the ; result is (OR (NOT P) Q). Any other argument is returned ; unchanged. (defun remove-implies (expr) (cond ((atom expr) expr) ((eq (car expr) 'IMPLIES) (list 'OR (list 'NOT (cadr expr)) (caddr expr)) ) (t expr) ) ) ; If the argument to "simplify-contradiction" is a list whose ; CAR is AND and whose CDR contains both P and (NOT P), for ; some P, then the result is FALSE, otherwise the result is ; the same as the input argument. Notice that this function ; compares the two P's for identity, not logical equivalence. (defun simplify-contradiction (expr) (cond ((null expr) ()) ((eq (car expr) 'AND) (cond ((contains-both-x-and-not-x (cdr expr)) 'FALSE) (t expr) ) ) (t expr) ) ) ; Test whether an S-expression occurs in a list. Like MEMBER, but ; the first argument can be anything, not just an atom. (defun contains (x list) (cond ((null list) nil) ((equal x (car list)) t) (t (contains x (cdr list))) ) ) ; Test whether the given expression contains both X and (NOT X), ; for some (unknown) S-expression X. (defun contains-both-x-and-not-x (expr) (cond ((null expr) nil) (t (or ; Test for (P ... (NOT P) ...) (contains (list 'NOT (car expr)) (cdr expr)) ; Test for ((NOT P) ... P ...) (and (not (atom (car expr))) (equal (caar expr) 'NOT) (contains (cadar expr) (cdr expr)) ) ; Car is useless; test cdr (contains-both-x-and-not-x (cdr expr)) )) ) ) ; Recursively apply the function f to every top-level list element ; of expression x, then apply f to the result. (defun recursive-apply (f x) (cond ((atom x) x) (t (apply f (list (cons (recursive-apply f (car x)) (recursive-apply f (cdr x)) )))) ) )