;; Nada Amin (namin@mit.edu)
;; 6.945 Problem Set 7
;; Due: Wed. 2 Apr. 2008

;(load "../test-manager/load.scm")
(load "load.scm")

;; Problem 7.1

(define (solve-yacht-puzzle knowing-mary-ann-moore?)
  (let ((daughter-barnacle 'melissa)
        (daughter-downing (amb 'mary-ann 'gabrielle 'lorna 'rosalind))
        (daughter-hall (amb 'mary-ann 'gabrielle 'lorna 'rosalind))
        (daughter-moore (if knowing-mary-ann-moore? 'mary-ann (amb 'mary-ann 'gabrielle 'lorna 'rosalind)))
        (daughter-parker (amb 'mary-ann 'lorna 'rosalind))
        (boat-barnacle 'gabrielle)
        (boat-downing 'melissa)
        (boat-hall 'rosalind)
        (boat-moore 'lorna)
        (boat-parker 'mary-ann))
    (let ((father-daughter-boat-list 
           (list (list 'barnacle daughter-barnacle boat-barnacle)
                 (list 'downing daughter-downing boat-downing)
                 (list 'hall daughter-hall boat-hall)
                 (list 'moore daughter-moore boat-moore)
                 (list 'parker daughter-parker boat-parker))))
      (define father car)
      (define daughter cadr)
      (define boat caddr)
      (define daughter&boat cdr)
      ; a father cannot name his boat after his daughter
      (for-each
       (lambda (t)
         (require (distinct? (daughter&boat t))))
       father-daughter-boat-list)
      ; all daughters have distinct names
      (require (distinct? (map daughter father-daughter-boat-list)))
      ; all boats have distinct names
      (require (distinct? (map boat father-daughter-boat-list)))
      ; gabrielle's father owns the yacht that is named after dr. parker's daughter
      (for-each
       (lambda (t)
         (if (eq? (daughter t) 'gabrielle)
             (require (eq? (boat t) daughter-parker))))
       father-daughter-boat-list)
      (for-each
       (lambda (t)
         (if (eq? (daughter t) 'lorna)
             (begin
               (display (list "lorna's father is" (father t)))
               (newline))))
       father-daughter-boat-list)
      (for-each
       (lambda (t)
         (display (list (father t) "is the father of" (daughter t) "and the owner of" (boat t)))
         (newline))
       father-daughter-boat-list)
      (newline)
      (amb))))


(define (solve-yacht-puzzle-knowing) (solve-yacht-puzzle #t))
(define (solve-yacht-puzzle-not-knowing) (solve-yacht-puzzle #f))

#|
(init-amb)
;Value: done

(with-depth-first-schedule solve-yacht-puzzle-knowing)
;(lorna's father is downing)
;(barnacle is the father of melissa and the owner of gabrielle)
;(downing is the father of lorna and the owner of melissa)
;(hall is the father of gabrielle and the owner of rosalind)
;(moore is the father of mary-ann and the owner of lorna)
;(parker is the father of rosalind and the owner of mary-ann)
;Value: #f

(with-depth-first-schedule solve-yacht-puzzle-not-knowing)
;(lorna's father is parker)
;(barnacle is the father of melissa and the owner of gabrielle)
;(downing is the father of rosalind and the owner of melissa)
;(hall is the father of mary-ann and the owner of rosalind)
;(moore is the father of gabrielle and the owner of lorna)
;(parker is the father of lorna and the owner of mary-ann)
;
;(lorna's father is downing)
;(barnacle is the father of melissa and the owner of gabrielle)
;(downing is the father of lorna and the owner of melissa)
;(hall is the father of gabrielle and the owner of rosalind)
;(moore is the father of mary-ann and the owner of lorna)
;(parker is the father of rosalind and the owner of mary-ann)
;
;Value: #f
|#

;; Problem 7.2

;; a

(define (snark-hunt tos)
  (call-with-current-continuation
   (lambda (k)
     (define (rec tos) 
       (if (symbol? tos)
           (if (eq? 'snark tos)
               (k #t)
               #f)
           (begin
             (for-each rec tos)
            #f)))
     (rec tos))))


#|
(snark-hunt '((a b c) 'snark))
; Value: #t
(snark-hunt '((a b c) (snark . "oops") snark))
; Value: #t
(snark-hunt '((a b c) (miao . "oops")))
; error
|#

;; b

(define (snark-hunt/instrumented tos)
  (call-with-current-continuation
   (lambda (k)
     (define (rec tos) 
       (pp `(entering (rec ,tos)))
       (if (symbol? tos)
           (if (eq? 'snark tos)
               (k #t)
               #f)
           (begin
             (for-each rec tos)
            #f))
       (pp `(exiting (rec ,tos)))
       #f)
     (rec tos))))

#|
(snark-hunt/instrumented '((a snark b)))
;(entering (rec ((a snark b))))
;(entering (rec (a snark b)))
;(entering (rec a))
;(exiting (rec a))
;(entering (rec snark))
;Value: #t

(snark-hunt/instrumented '((snark . "oops")))
;(entering (rec ((snark . "oops"))))
;(entering (rec (snark . "oops")))
;(entering (rec snark))
;Value: #t

(snark-hunt/instrumented '(a b))
;(entering (rec (a b)))
;(entering (rec a))
;(exiting (rec a))
;(entering (rec b))
;(exiting (rec b))
;(exiting (rec (a b)))
;Value: #f

|#

;; Problem 7.3

#|

To reach (12 16 20), depth-first has to explore all triples i,j,k>=10
s.t. i<12 or (i=12 and j<16). 

Because we're searching up to 20, to reach (12 16 20), breadth-first
has to explore all the triples that depth-first explores plus many
more, as it explores the triples in a "fair" order.

If we had extended the search beyond 20, breadth-first would not
necessarily be slower than depth-first.

The extra calls to fail are coming from the require clause of
an-integer-between.

a-pythagorean-triple-from is not usable under depth-first because
depth-first fixes i and j and explores all possible k's. Since there
are infinitely many k's, depth-first stalls exploring triples of the
form (10 10 n) for all n>=10.

|#

;; Problem 7.5

;; A.

#|

An unordered (random) AMB might be useful if we're creating a
generator as opposed to a solver. For example, we might want to
generate random grammatical sentences. If we use a deterministic AMB,
the result is rather boring, as we always get the same sentences in
the same order.

|#

(define (generate-sentence)
  (let ((sent (parse-sentence)))
    sent))

(define (some lst)
  (if (null? lst)
      (amb)
      (amb (car lst) (some (cdr lst)))))

(define (parse-word word-list)
  (let ((found-word (some (cdr word-list))))
    (list (car word-list) found-word)))

#|
(init-amb)
(generate-sentence)
;Value: (sentence (s-noun-phrase (article the) (noun student)) (verb studies))
|#

;; B.

(define (order-left-to-right-alternation alternatives)
  alternatives)

(define (order-right-to-left-alternation alternatives)
  (reverse alternatives))

(define (order-random-order-alternation alternatives)
  (define (all-but-one lst k)
    (append (list-head lst k)
            (list-tail lst (+ k 1))))
  (define (rec n lst)
    (if (= n 0)
        '()
        (let ((i (random n)))
          (let ((one (list-ref lst i))
                (rest (all-but-one lst i)))
            (cons one
                  (rec (- n 1)
                       rest))))))
  (rec (length alternatives)
       alternatives))

(define order-alternation ;; Default
  order-left-to-right-alternation)

(define (with-left-to-right-alternation thunk)
  (call-with-current-continuation
   (lambda (k)
     (fluid-let ((order-alternation
                  order-left-to-right-alternation))
       (thunk)))))

(define (with-right-to-left-alternation thunk)
  (call-with-current-continuation
   (lambda (k)
     (fluid-let ((order-alternation
                  order-right-to-left-alternation))
       (thunk)))))

(define (with-random-order-alternation thunk)
  (call-with-current-continuation
   (lambda (k)
     (fluid-let ((order-alternation
                  order-random-order-alternation))
       (thunk)))))

(define (amb-list alternatives)
  (if (null? alternatives)
      (set! *number-of-calls-to-fail*
            (+ *number-of-calls-to-fail* 1)))
  (call-with-current-continuation
   (lambda (k)
     (add-to-search-schedule
      (order-alternation
       (map (lambda (alternative)
              (lambda ()
                (within-continuation k alternative)))
            alternatives)))
     (yield))))


(define (just-the-words lst)
  (define (remove-cats lst)
    (cond ((symbol? lst)
           lst)
          ((null? (cdr lst))
           (just-the-words (car lst)))
          (else 
           (map just-the-words (cdr lst)))))
  (define (flatten lst)
    (cond ((symbol? lst)
           (list lst))
          ((null? lst)
           lst)
          (else
           (append (flatten (car lst)) (flatten (cdr lst))))))
  (flatten (remove-cats lst)))
#|
(init-amb)
(just-the-words (with-random-order-alternation generate-sentence))
;Value: (the cat eats)
(just-the-words (with-random-order-alternation generate-sentence))
;;Value: (a professor by the cat for the student sleeps)
(just-the-words (with-random-order-alternation generate-sentence))
;Value: (the class studies to the class in a student by the professor in a student in a class in the student)
(just-the-words (with-random-order-alternation generate-sentence))
;Value: (a professor by a student studies)
(just-the-words (with-left-to-right-alternation generate-sentence))
;Value: (the student studies)
(just-the-words (with-right-to-left-alternation generate-sentence))
; out of memory
|#

;; 7.6

(define moby-brain-twister-test
  (lambda ()
    (let ((x) (y) (z))
      (set! x (amb 1 2 3))
      (pp (list x))
      (set! y (amb 'a 'b))
      (pp (list x y))
      (set! z (amb #t #f))
      (pp (list x y z))
      (amb))))

#|
(with-breadth-first-schedule moby-brain-twister-test2)
(with-depth-first-schedule moby-brain-twister-test2)

The explanation is that we run the thunk ((set! x 1) ...) then ((set!
x 2) ...) then ((set! x 3) ...), and so x gets set to three. Then, we
alternate betweent the other options, when we run the inner thunks.

|#