#lang racket
(require csc151)
(require csc151/trees)
(require csc151/counters)
(require rackunit)
(require rackunit/text-ui)

;;; File:
;;;   000000.rkt
;;; Authors:
;;;   The student currently referred to as 000000
;;;   Titus Klinge
;;;   Samuel A. Rebelsky
;;; Contents:
;;;   Code and solutions for Exam 4 2018S
;;; Citations:
;;;

;; +---------+--------------------------------------------------------
;; | Grading |
;; +---------+

;; This section is for the grader's use.

;; Problem 1: 
;; Problem 2:
;; Problem 3:
;; Problem 4:
;; Problem 5:
;; Problem 6:
;;           ----
;;     Total:

;;    Scaled:
;;    Errors:
;;     Times:
;;          :
;;          :
;;          :
;;           ----
;;     Total:

;; +----------+-------------------------------------------------------
;; | Prologue |
;; +----------+

; Time Log:
;   Date        Start   Finish  Elapsed Activity

; Time Spent: 

;; +-----------+------------------------------------------------------
;; | Problem 1 |
;; +-----------+

; Time Log:
;   Date        Start   Finish  Elapsed Activity

; Time Spent: 

; Citations:

; Solution:

;;; Procedure:
;;;   
;;; Parameters:
;;;   
;;; Purpose:
;;;   
;;; Produces:
;;;   
;;; Preconditions:
;;;   
;;; Postconditions:
;;;   
(define subvector
  (lambda (vec i j)
    vec)) ; STUB

; Examples/Tests:

;; +-----------+------------------------------------------------------
;; | Problem 2 |
;; +-----------+

; Time Log:
;   Date        Start   Finish  Elapsed Activity

; Time Spent: 

; Citations:

; Solution:

;;; Procedure:
;;;   tree-select
;;; Parameters:
;;;   tree, a tree
;;;   pred?, a unary predicate
;;; Purpose:
;;;   Returns a list of every element in `tree` that satisfies `(pred? ele)`
;;; Produces:
;;;   result, a list
;;; Preconditions:
;;;   * `pred?` is a procedure that takes one parameter and returns a Boolean
;;;   * `pred?` is compatible with every element in `tree`
;;; Postconditions:
;;;   * Every element, ele,  in `tree` that satisfies (pred? ele) is in `result`
;;;   * If ele appears k times in `result`, then ele appears k times in `tree`
(define tree-select
  (lambda (tree pred?)
    null)) ; STUB

; Examples/Tests:


;; +-----------+------------------------------------------------------
;; | Problem 3 |
;; +-----------+

; Time Log:
;   Date        Start   Finish  Elapsed Activity

; Time Spent: 

; Citations:

; Solution:

;;; Procedure:
;;;   file-append
;;; Parameters:
;;;   file1, a string
;;;   file2, a string
;;;   dst-file, a string
;;; Purpose:
;;;   Writes the contents of file1 followed by the contents of file2 to dst-file
;;; Produces:
;;;   [Nothing; called for the side effect]
;;; Preconditions:
;;;   * file1 and file2 are valid paths to existing files
;;;   * dst-file is a valid path to a non-existing file
;;;   * The current user has read/write permissions to each file
;;; Postconditions:
;;;   * A file is created for dst-file
;;;   * If len1 and len2 are the number of characters in the files
;;;     file1 and file2, then
;;;      * dst-file contains exactly len1 + len2 characters
;;;      * The first len1 characters of dst-file are identical to the
;;;        contents of file1
;;;      * The last len2 characters of dst-file are indentical to the
;;;        contents of file2
(define file-append
  (lambda (file1 file2 dst-file)
    (void))) ; STUB

; Examples/Tests:

;; +-----------+------------------------------------------------------
;; | Problem 4 |
;; +-----------+

; Time Log:
;   Date        Start   Finish  Elapsed Activity

; Time Spent: 

; Citations:

; Solution:
;;; Procedure:
;;;   
;;; Parameters:
;;;   
;;;   
;;; Purpose:
;;;   
;;; Produces:
;;;   
;;; Preconditions:
;;;   
;;; Postconditions:
;;;   
(define dead-boys-song
  (lambda
    (*)
  (lambda
    (/)                                          (letrec ([+
  (lambda
    (list)                                       (or (and (=
      list
      (- (vector-length /) 1)) (vector-ref / list))
      (* (vector-ref / list)
      (+ (increment list)))))]) (+ 0)))))

; c.

#|
|#

; Examples/Tests:

#|
|#

;; +-----------+------------------------------------------------------
;; | Problem 5 |
;; +-----------+

; Time Log:
;   Date        Start   Finish  Elapsed Activity

; Time Spent: 

; Citations:

; Solution:

; a.

#|
|#

; b.

;;; Procedure:
;;;   indices-of-largest
;;; Parameters:
;;;   lst, a nonempty list of real numbers
;;; Purpose:
;;;   Find the indices of the largest values in lst
;;; Produces:
;;;   indices, a list of integers
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   * indices is nonempty
;;;   * Every value in indices is between 0 and (- (length lst) 1), inclusive.
;;;   * For any i in indices, j not in indices, where j is a valid index of lst,
;;;     (> (list-ref lst i) (list-ref lst j)).
(define indices-of-largest
  (lambda (lst)
    ; The kernel builds up the list of indices.
    ; pos represents the next position we have to
    ; look at
    (let kernel ([indices (list 0)]
                 [pos 1])
      (cond
        ; If we're out of elements, just use the list we've built
        [(>= pos (length lst))
         indices]
        ; If the position is already in the list of indices, skip it
        [(member? pos indices)
         (kernel indices
                 (+ pos 1))]
        ; If the element at the given position is bigger than
        ; the biggest we've seen, start again with just that
        ; position.
        [(> (list-ref lst pos)
            (list-ref lst (car indices)))
         (kernel (list pos)
                 0)]
        ; If the element at the given position is equal to
        ; the biggest we've seen, add it to the list of
        ; indices.
        [(= (list-ref lst pos)
            (list-ref lst (car indices)))
         (kernel (append indices (list pos))
                 (+ pos 1))]
        ; Otherwise, the element at the given position
        ; is smaller than something we've seen, so
        ; ignore it
        [else
         (kernel indices
                 (+ pos 1))]))))

; c.

#|
|#

; Examples/Tests

; These checks are included to make sure that you do not break
; the code.  You may, but need not, add other checks.

(check-equal? (indices-of-largest (list 5 4 3 2 1))
              '(0))
(check-equal? (indices-of-largest (list 2 5 4 3 1))
              '(1))
(check-equal? (indices-of-largest (list 1 2 5 4 3))
              '(2))
(check-equal? (indices-of-largest (list 1 2 4 5 3))
              '(3))
(check-equal? (indices-of-largest (list 4 3 2 1 5))
              '(4))
(check-equal? (indices-of-largest (list 1))
              '(0))
(check-equal? (indices-of-largest (list 2 0 -1 2 1 2))
              '(0 3 5))
(check-equal? (indices-of-largest (list -1 -1 -1 -1))
              '(0 1 2 3))
(check-equal? (indices-of-largest (list 0 1 2 3 2 1 3 2 1))
              '(3 6))


;; +-----------+------------------------------------------------------
;; | Problem 6 |
;; +-----------+

; Time Log:
;   Date        Start   Finish  Elapsed Activity

; Time Spent: 

; Citations:

; Supplied code

;;; Counter
;;;   car-counter
(define car-counter (counter-new "car"))

;;; Counter
;;;   cdr-counter
(define cdr-counter (counter-new "cdr"))

;;; Counter
;;;   cons-counter
(define cons-counter (counter-new "cons"))

;;; Counter
;;;   null?-counter
(define null?-counter (counter-new "null?"))

;;; Procedure:
;;;   $cons
;;; Parameters:
;;;   left, a Scheme value
;;;   right, a Scheme value
;;; Purpose:
;;;   Make a pair from left and right and log the operation.
;;; Produces:
;;;   pair, a pair
;;; Preconditions:
;;;   cons-counter is defined.
;;; Postconditions:
;;;    (car pair) = left
;;;    (cdr pair) = right
;;;    cons-counter has been incremented.
(define $cons
  (lambda (val lst)
    (counter-increment! cons-counter)
    (cons val lst)))

;;; Procedure:
;;;   list-append
;;; Parameters:
;;;   front, a list of size n
;;;   back, a list of size m
;;; Purpose:
;;;   Put front and back together into a single list.
;;; Produces:
;;;   appended, a list of size n+m.
;;; Preconditions:
;;;   front is a list [Unverified]
;;;   back is a list [Unverified]
;;; Postconditions:
;;;   For all i, 0 <= i < n,
;;;    (list-ref appended i) is (list-ref front i)
;;;   For all i, n <= i < n+m
;;;;   (list-ref appended i) is (list-ref back (- i n))
(define list-append
  (lambda (front back)
    (if (null? front)
        back
        (cons (car front) 
              (list-append (cdr front) back)))))

;;; Procedure:
;;;   partition
;;; Parameters:
;;;   lst, a list of real numbers
;;;   num, a real number
;;; Purpose:
;;;   Separate lst into three lists: things less than num, things equal
;;;   to num, and things greater than num.
;;; Produces:
;;;   vol, a vector of lists of real numbers of the form
;;;     #(smaller equal greater).
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   * (append smaller equal greater) is a permutation of lst.
;;;   * All values in smaller are less than num.
;;;   * All values in equal are equal to num.
;;;   * All values in greater are larger than num.
(define partition
  (lambda (lst num)
    (let kernel ([remaining lst]
                 [smaller null]
                 [equal null]
                 [greater null])
      (if (null? remaining)
          (vector smaller equal greater)
          (let [(val (car remaining))]
            (kernel (cdr remaining)
                    (if (< val num) (cons val smaller) smaller)
                    (if (= val num) (cons val equal) equal)
                    (if (> val num) (cons val greater) greater)))))))

;;; Procedure:
;;;   quicksort
;;; Parameters:
;;;   lst, a list of real numbers
;;; Purpose:
;;;   Sort the list using the legendary quicksort algorithm.
;;; Produces:
;;;   sorted, a list
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   * sorted is a permutation of lst.  No elements are added or removed.
;;;   * sorted is sorted.  For all reasonable i, (list-ref sorted i) <=
;;;     (list-ref sorted (+ i 1)).
(define quicksort
  (lambda (lst)
    (if (or (null? lst) (null? (cdr lst)))
        lst
        (let [(parts (partition lst (random-element lst)))]
          (append (quicksort (vector-ref parts 0))
                  (append (vector-ref parts 1)
                          (quicksort (vector-ref parts 2))))))))

;;; Procedure:
;;;   random-list
;;; Parameters:
;;;   len, a non-negative integer
;;;   lb, an integer
;;;   ub, an integer
;;; Purpose:
;;;   Make a list of `len` random numbers, each in the range lb..ub, inclusive.
;;; Produces:
;;;   lst, a list of numbers
;;; Preconditions:
;;;   lb <= ub.
;;; Postconditions:
;;;   * (length lst) = len.
;;;   * All values in lst are between lb and ub.
;;;   * It is difficult to predict the values in lst.
(define random-list
  (lambda (len lb ub)
    (if (zero? len)
        null
        (cons (+ lb (random (+ 1 (- ub lb))))
              (random-list (- len 1) lb ub)))))

;;; Procedure:
;;;   random-element
;;; Parameters:
;;;   lst, a nonempty list
;;; Purpose:
;;;   Unpredictably select an element of lst.
;;; Produces:
;;;   val, an element of list
(define random-element
  (lambda (lst)
    (list-ref lst (random (length lst)))))

;; Solution;

; a. Code updated above.

; b.

#|
Note: Please include log of experiments.

Summary of Data
---------------
                        sorted, average                 random, average
n     nlogn     n*n     car   cdr   cons  null?         car   cdr   cons  null?
---   -----   -----     ---   ---   ----  -----         ---   ---   ----  -----
100     664   10000
200    1529   40000
400    3458  160000
800    7715  640000
|#

; c.

#|
|#
