(define compose
  (lambda (f g)
    (lambda (x) (f (g x)))))

(define neither
  (lambda (pred)
    (lambda (arg1 arg2)
      (not (or (pred arg1) (pred arg2))))))

;; straightforward
(define both
  (lambda (pred)
    (lambda (arg1 arg2)
      ((neither (lambda (x) (not (pred x)))) arg1 arg2))))

;; (lambda (x) (not (pred x))) -> (compose not pred)
(define both
  (lambda (pred)
    (lambda (arg1 arg2)
      ((neither (compose not pred)) arg1 arg2))))

;; (lambda (arg1 arg2)
;;   ((neither (compose not pred)) arg1 arg2))
;;   -> (neither (compose not pred))
(define both
  (lambda (pred)
    (neither (compose not pred))))

(define at-least-one
  (lambda (pred)
    (lambda (arg1 arg2)
      (not ((neither pred) arg1 arg2)))))

;; from book
(define make-set
  (lambda args
   (letrec
    ((list-make-set
      (lambda (args-list)
	(if (null? arg-list)
	    the-empty-set
	    (adjoin
	     (car args-list)
	     (list-make-set (cdr args-list)))))))
    (list-make-set args))))

;; better
(define make-set
  (lambda args
    (if (null? args)
	the-empty-set
	(adjoin
	 (car args)
	 (apply make-set (cdr args))))))

(define cardinal
  (lambda (s)
    (if (empty-set? s)
	0
        (add1 (cardinal ((residue (pick s)) s))))))

(define print-set
  (lambda (s)
    (letrec
     ((singleton?
       (lambda (s)
	 (empty-set? ((residue (pick s)) s))))
      (printer
       (lambda (s)
	 (if (empty-set? s)
	     (display "}")
	     (let ((elem (pick s)))
	       (display elem)
	       (if (not (singleton? s))
		   (display ","))
	       (printer ((residue elem) s)))))))
     (display "{")
     (printer s)
     (newline))))

(define none
  (lambda (pred)
    (letrec
     ((test
       (lambda (s)
	 (or (empty-set? s)
	     (let ((elem (pick s)))
	       (and (not (pred elem))
		    (test ((residue elem) s))))))))
     test)))

;; straightforward
(define there-exists
  (lambda (pred)
    (lambda (s)
      (not ((none pred) s)))))

;; (lambda (x) (f (g x))) -> (compose f g)
(define there-exists
  (lambda (pred)
    (compose not (none pred))))

;; straightforward
(define for-all
  (lambda (pred)
    (lambda (s)
      ((none (lambda (x) (not (pred x)))) s))))

;; (lambda (x) (not (pred x))) -> (compose not pred)
;; (lambda (s) (none ((compose not pred) s))) ->
;;   (none (compose not pred))
(define for-all
  (lambda (pred)
    (none (compose not pred))))

(define set-equal
  (lambda (obj1)
    (lambda (obj2)
      (or (and ((neither set?) obj1 obj2)
	       (equal? obj1 obj2))
	  (and ((both set?) obj1 obj2)
	       ((subset obj1) obj2)
	       ((subset obj2) obj1))))))

;; straightforward
(define element
  (lambda (obj)
    (lambda (s)
      ((there-exists (set-equal obj)) s))))

;; (lambda (s) ((there-exists (set-equal obj)) s)) ->
;;   (there-exists (set-equal obj))
(define element
  (lambda (obj)
    (there-exists (set-equal obj))))

;; (lambda (obj) (there-exists (set-equal obj))) ->
;;   (compose there-exists set-equal))
(define element
  (compose there-exists set-equal))


;; what is the difference between element and contains?
(define contains
  (lambda (s)
    (lambda (obj)
      ((element obj) s))))

(define superset
  (lambda (s1)
    (lambda (s2)
      ((for-all (contains s1)) s2))))

(define subset
  (lambda (s1)
    (lambda (s2)
      ((superset s2) s1))))