;; Kathi Fisler
;; insertion sort : code and notes

#| 
Task: write a function sort that consumes a list of numbers and sorts 
it into increasing order.  Don't think about which algorithm to use.  
Just follow the template and see where it leads you.

If you follow the template, you get the following code

;; sort : list-of-number -> list-of-number[sorted]
;; sort list of numbers into increasing order
(define (sort alon)
  (cond [(empty? alon) ...]
        [(cons? alon)
         ... (first alon) 
         ... (sort (rest alon))]))

How do you work from here to a solution?  The examples show you how to 
complete the empty case.  When you have to fill in the holes in a more 
complicated case, always ask yourself two questions:

  1. What do the pieces give me? (what type, and what do they mean)
  2. How do I combine the pieces into the answer I want?

For this problem, (first alon) gives you a number, and (sort (rest alon))
"sorts the rest of the list into increasing order" (quotes because I read
this off the purpose statement, replacing "list" with "rest of list").  How
do we combine a number with a sorted list to get a sorted list?  Find the 
right place for the number and drop it in; in other words, insert it.  This
suggests that we need a helper function called insert to do this task for us.

How do you know you need another function, as opposed to writing the code
in place?  If what you need to write is more than a simple call (to something
like + or cons, just make a helper function.  You won't go wrong that way.  And
in this case you need the helper function because you have to walk down the 
sorted list to find the right place to insert the number; each time you walk
the list you need a new function.
                                 
This reasoning lets you use the operator name "insert" in the sort code, and write
a contract and purpose for insert.  Finish sort, then go back and finish insert.

|#

;; A list-of-numbers is either
;;  - empty, or
;;  - (cons number list-of-numbers)

;; sort : list-of-number -> list-of-number
;; sort list of numbers into increasing order
(define (sort alon)
  (cond [(empty? alon) empty]
        [(cons? alon)
         (insert (first alon)
                 (sort (rest alon)))]))

;; insert : number list-of-number[sorted] -> list-of-number[sorted]
;; inserts number into list in increasing order
(define (insert num alon)
  (cond [(empty? alon) (cons num alon)]
        [(cons? alon)
         (cond [(<= num (first alon)) (cons num alon)]
               [else (cons (first alon) 
                           (insert num (rest alon)))])]))

;; test cases
(check-expect (insert 5 empty) (cons 5 empty))
(check-expect (insert 5 (cons 9 empty)) (cons 5 (cons 9 empty)))
(check-expect (insert 5 (cons -1 (cons 3 empty))) 
              (cons -1 (cons 3 (cons 5 empty))))

(check-expect (sort empty) empty)
(check-expect (sort (list 5 2 0 6 1)) 
	      (cons 0 (cons 1 (cons 2 (cons 5 (cons 6 empty))))))
(check-expect (sort (list 3 2 1)) 
	      (cons 1 (cons 2 (cons 3 empty)))
(check-expect (sort (list 1 2 3)) 
	      (cons 1 (cons 2 (cons 3 empty))))

#|
Note that doing the test cases before you write insert is particularly 
helpful because insert is the first function you've done where the empty
case doesn't just return empty.  I've rarely seen people who write the
test cases first get the empty? case wrong in insert, while those who
do not often forget the cons and just return empty.
|#