#lang racket
(require (planet "main.rkt" ("samsergey" "rewrite.plt" 1 0))
rackunit)
(define-formal && || !)
(define infix->prefix
(replace-all-repeated
`(,a __1 <=> ,b __1) --> (&& `(,a => ,b) `(,b => ,a))
`(,a __1 => ,b __1) --> (|| b (! a))
`(,a __1 || ,b __1) --> (|| a b)
`(,a __1 && ,b __1) --> (&& a b)
`(,a) --> a))
(define ordered?
(replace
(? symbol? x) (? symbol? y) --> (string<=? (symbol->string x) (symbol->string y))
(? symbol?) (? pair?) --> #t
(cons a x) (cons a y) --> (ordered? x y)
(cons x _) (cons y _) --> (ordered? x y)
_ _ --> #f ))
(define simplify
(replace-all-repeated
`(,a) --> a
(|| a) --> a
(||) --> 'F
(&& a) --> a
(&&) --> 'T
(! (! a)) --> a
(! 'F) --> 'T
(! 'T) --> 'F
(&& _ ___ 'F _ ___) --> 'F
(&& x ___ 'T y ___) --> `(&& ,@x ,@y)
(&& x ___ A y ___ A z ___) --> `(&& ,@x ,A ,@y ,@z)
(&& _ ___ A _ ___ (! A) _ ___) --> 'F
(|| _ ___ 'T _ ___) --> 'T
(|| x ___ 'F y ___) --> `(|| ,@x ,@y)
(|| x ___ A y ___ A z ___) --> `(|| ,@x ,A ,@y ,@z)
(|| _ ___ A _ ___ (! A) _ ___) --> 'T
(&& a ___ (&& b __1) c ___) --> `(&& ,@a ,@b ,@c)
(|| a ___ (|| b __1) c ___) --> `(|| ,@a ,@b ,@c)
(|| x ___ A y ___ (&& _ ___ A _ ___) z ___) --> `(|| ,@x ,A ,@y ,@z)
(|| x ___ A y ___ (&& p ___ (! A) q ___) z ___) --> `(|| ,@x ,A ,@y (&& ,@p ,@q) ,@z)
(|| x ___ (! A) y ___ (&& p ___ A q ___) z ___) --> `(|| ,@x (! ,A) ,@y (&& ,@p ,@q) ,@z)
(&& x ___ A y ___ (|| _ ___ A _ ___) z ___) --> `(&& ,@x ,A ,@y ,@z)
(&& x ___ A y ___ (|| p ___ (! A) q ___) z ___) --> `(&& ,@x ,A ,@y (|| ,@p ,@q) ,@z)
(&& x ___ (! A) y ___ (|| p ___ A q ___) z ___) --> `(&& ,@x (! ,A) ,@y (|| ,@p ,@q) ,@z)
(cons op x) --> (cons op (sort (map simplify x) ordered?))))
(define DNF
(compose simplify
(replace-all-repeated
(! (|| a b __1)) --> (&& (! a) (! `(|| ,@b)))
(! (&& a b __1)) --> (|| (! a) (! `(&& ,@b)))
(&& a ___ (|| b bb __1) c ___) --> `(|| (&& ,@a ,b ,@c)
(&& ,@a (|| ,@bb) ,@c))
(cons op x) --> (cons op (map DNF x)))))
(define CNF
(compose simplify
(replace-all-repeated
(! (|| a b __1)) --> (&& (! a) (! `(|| ,@b)))
(! (&& a b __1)) --> (|| (! a) (! `(&& ,@b)))
(|| a ___ (&& b bb __1) c ___) --> `(&& (|| ,@a ,b ,@c)
(|| ,@a (&& ,@bb) ,@c))
(cons op x) --> (cons op (map CNF x)))))
(define expand (compose DNF CNF infix->prefix))
(check-equal? (expand '((A || B) && C)) '(|| (&& A C) (&& B C)))
(check-equal? (expand '((B || A) && (A || ! B))) 'A)
(check-equal? (expand '((A || B) && B)) 'B)
(check-equal? (expand '((A => B) && A)) '(&& A B))
(check-equal? (expand '(A => ! A)) '(! A))
(check-eq?
(expand '(A || ! A))
'T)
(check-eq?
(expand '((A => B) <=> (! B => ! A)))
'T)
(check-eq?
(expand '((! A => B) && (! A => ! B) => A))
'T)
(check-eq?
(expand '((human => mortal) && (Socrates => human) => (Socrates => mortal)))
'T)
(check-eq?
(expand '((A || B) && (A => C) && (B => C) => C))
'T)
(check-equal?
(expand '(((P => Q) => P) => P))
'T)
(check-equal?
(expand '((p || ! r) && s => (r && s => (p && q) || p)))
'T)
(check-equal?
(expand '((A && B => C) <=> (A => (B => C))||(B => (A => C))))
'T)
(check-equal? (expand '((A && B) => (A || B))) 'T)
(check-equal?
(expand '(((A <=> B) <=> C) <=> (A <=> (B <=> C))))
'T)
(check-equal?
(expand '(((A || B => A || C) <=> (A || (B => C)))))
'T)