#lang racket
(require csc151)
(provide (all-defined-out))

;;; File:
;;;   analysis-lab.rkt
;;; Authors:
;;;   Janet Davis
;;;   Titus Klinge
;;;   Samuel A. Rebelsky
;;;   Jerod Weinman
;;;   YOUR NAME HERE
;;; Summary:
;;;   Procedures for the lab on analyzing procedures

; +--------------------------------+----------------------------------
; | Generalized Counter Procedures |
; +--------------------------------+

;;; Procedure:
;;;   counter-new
;;; Parameters:
;;;   name, a string
;;; Purpose:
;;;   Create a counter associated with the given name.
;;; Produces:
;;;   counter, a counter
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   counter can be used as a parameter to the various counter
;;;   procedures.
;;; Process:
;;;   Counters are two element vectors.  Element 0 is the name, and
;;;   should not change.  Element 1 is the count, and should change.
(define counter-new
  (lambda (name)
    (vector name 0)))

;;; Procedure:
;;;   counter-increment!
;;; Parameters:
;;;   counter, a counter 
;;; Purpose:
;;;   count the counter
;;; Produces:
;;;   counter, the same counter, now mutated
;;; Preconditions:
;;;   counter was created by counter-new (or something similar) and
;;;   has only been modified by the counter procedures.
;;; Postconditions:
;;;   (counter-get counter) gives a number one higher than it 
;;;   did before.
(define counter-increment!
  (lambda (counter)
    (vector-set! counter 1 (+ 1 (vector-ref counter 1)))
    counter))

;;; Procedure:
;;;   counter-get
;;; Parameters:
;;;   counter, a counter
;;; Purpose:
;;;   Get the number of times that counter-count has been called
;;;   on this counter.
;;; Produces:
;;;   count, a non-negative integer
;;; Preconditions:
;;;   counter was created by counter-new and has only been modified
;;;   by the counter procedures.
;;; Postconditions:
;;;   count is the number of calls to counter-new on this counter since
;;;   the last call to counter-reset! on this counter, or since the
;;;   counter was created, if there have been no calls to counter-reset!
(define counter-get
  (lambda (counter)
    (vector-ref counter 1)))

;;; Procedure:
;;;   counter-reset!
;;; Parameters:
;;;   counter, a counter 
;;; Purpose:
;;;   reset the counter
;;; Produces:
;;;   counter, the same counter, now set to 0
;;; Preconditions:
;;;   counter was created by counter-new (or something similar) and
;;;   has only been modified by the other counter procedures.
;;; Postconditions:
;;;   (counter-get counter) gives 0.
(define counter-reset!
  (lambda (counter)
    (vector-set! counter 1 0)
    counter))

;;; Procedure:
;;;   counter-print
;;; Parameters:
;;;   counter, a counter
;;; Purpose:
;;;   Print out the information associated with the counter.
;;; Produces:
;;;   counter, the same counter
;;; Preconditions:
;;;   counter was created by counter-new and has only been modified
;;;   by the various counter procedures.
;;; Postconditions:
;;;   counter is unchanged.
;;;   The output port now contains information on counter.
;;; Ponderings:
;;;   Why does counter-print have a bang, given that it doesn't mutate
;;;   it's parameter?  Because it mutates the broader environment - we
;;;   call counter-print not to compute a value, but to print something.
(define counter-print
  (lambda (counter)
    (display (vector-ref counter 0))
    (display ": ")
    (display (vector-ref counter 1))
    (newline)))

; +-------------------+-----------------------------------------------
; | Specific Counters |
; +-------------------+

(define alphabetically-first-1-counter (counter-new "alphabetically-first-1"))
(define alphabetically-first-2-counter (counter-new "alphabetically-first-2"))
(define alphabetically-first-counters 
  (list alphabetically-first-1-counter alphabetically-first-2-counter))

; +--------------------------+----------------------------------------
; | Finding the first string |
; +--------------------------+

;;; Procedure:
;;;   first-of-two
;;; Parameters:
;;;   str1, a string
;;;   str2, a string
;;; Purpose:
;;;   Find the alphabetically first of two strings.
;;; Produces:
;;;   first, a string
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   * first is either str1 or str2
;;;   * (string-ci<= first str1)
;;;   * (string-ci<= first str2)
(define first-of-two
  (lambda (str1 str2)
    (if (string-ci<=? str1 str2)
        str1
        str2)))

;;; Procedure:
;;;   alphabetically-first
;;; Parameters:
;;;   strings, a list of strings
;;; Purpose:
;;;   Finds the alphabetically first string in the list.
;;; Produces:
;;;   first, a string
;;; Preconditions:
;;;   strings contains at least one element
;;; Postconditions:
;;;   * first is an element of strings
;;;   * For any string, str, in strings

(define alphabetically-first-1
  (lambda (strings)
    (counter-increment! alphabetically-first-1-counter)
    (cond
      [(null? (cdr strings))
       (car strings)]
      [(string-ci<=? (car strings) (alphabetically-first-1 (cdr strings)))
       (car strings)]
      [else
       (alphabetically-first-1 (cdr strings))])))

(define alphabetically-first-2
  (lambda (strings)
    (counter-increment! alphabetically-first-2-counter)
    (if (null? (cdr strings))
        (car strings)
        (first-of-two (car strings)
                      (alphabetically-first-2 (cdr strings))))))

(define alphabetically-first-3
  (lambda (strings)
    (let kernel ([first-so-far (car strings)]
                 [remaining (cdr strings)])
      (if (null? remaining)
          first-so-far
          (kernel (first-of-two first-so-far (car remaining))
                  (cdr remaining))))))

(define alphabetically-first-4
  (lambda (strings)
    (when (not (all-string? strings))
      (error "alphabetically-first: expects a list strings; received" strings))
    (if (null? (cdr strings))
        (car strings)
        (first-of-two (car strings)
                      (alphabetically-first-4 (cdr strings))))))

;;; Procedure:
;;;   all-string?
;;; Parameters:
;;;   lst, a list
;;; Purpose:
;;;   Determine if all elements of lst are strings.
;;; Produces:
;;;   ok?, a Boolean
(define all-string?
  (lambda (lst)
    (or (null? lst)
        (and (string? (car lst))
             (all-string? (cdr lst))))))

; +-----------------+-------------------------------------------------
; | List Procedures |
; +-----------------+

;;; 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)))))

(define list-reverse-1
  (lambda  (lst)
    (if (null? lst)
        null
        (list-append (list-reverse-1 (cdr lst)) (list (car lst))))))

(define list-reverse-2
  (lambda (lst)
    (let kernel ([reversed null]
                 [remaining lst])
      (if (null? remaining)
          reversed
          (kernel (cons (car remaining) reversed)
                  (cdr remaining))))))

