#lang at-exp scheme/base
  (require "teachprims.rkt"
           mzlib/etc
           mzlib/list
           mzlib/pretty
           syntax/docprovide
           scheme/promise
           scheme/port
           "../posn.rkt"
           (for-syntax scheme/base))

(define advanced-list? list?)

;; Documents the procedures:
(require "provide-and-scribble.rkt")

(define pp (let ([pretty-print (lambda (v) (pretty-write v))]) pretty-print))

(provide-and-scribble
   procedures

   (begin
     (require scribble/manual scribble/eval "sl-eval.rkt")
     (define (asl)
       (define *bsl
         (asl-eval
	   [(define c1 (circle 10 "solid" "green"))

          (define zero 0)

          (define one (list 1))

          ;; b 69, c 42, e 61
          (define list-for-hash '((b 69) (c 42) (e 61) (r 999)))

          (define hsh (make-hash list-for-hash))
          (define heq (make-hasheq list-for-hash))
          (define heqv (make-hasheqv list-for-hash))
          (define ish (make-immutable-hash list-for-hash))
          (define ieq (make-immutable-hasheq list-for-hash))
          (define ieqv (make-immutable-hasheqv list-for-hash))

          (define q (make-posn "bye" 2))
          (define p (make-posn 2 -3))
          
          (define v (vector "a" "b" "c" "d" "e"))
          
          (define b (box 33))

          (define s "hello world")
          (define x (list 2 "hello" #true))]))
       (set! asl (lambda () *bsl))
       *bsl))
   
   ("Numbers: Integers, Rationals, Reals, Complex, Exacts, Inexacts"
    @defproc[(random [x natural]) natural]{
    Generates a random number. If given one argument @racket[random] returns a natural number
    less than the given natural. In ASL, if given no arguments, @racket[random] generates a
    random inexact number between 0.0 and 1.0 exclusive.
    @interaction[#:eval (asl) (random)]
    @interaction[#:eval (asl) (random)]
    @interaction[#:eval (asl) (random 42)]
    @interaction[#:eval (asl) (random 42)]
    } 
    )

   ("Reading and Printing"
    @defproc[(with-input-from-file [f string] [p (-> any)]) any]{
    Opens the named input file @racket[f] and allows @racket[p] to read from it.
    } 
    @defproc[(with-output-to-file [f string] [p (-> any)]) any]{
    Opens the named input file @racket[f] and allows @racket[p] to write to it.
    } 
    @defproc[(with-input-from-string [s string] [p (-> any)]) any]{
    Turns @racket[s] into input for @racket[read] operations in @racket[p].
    @interaction[#:eval (asl)
      (with-input-from-string "hello" read)
      (string-length (symbol->string (with-input-from-string "hello" read)))]
    } 
    @defproc[(with-output-to-string [p (-> any)]) any]{
    Produces a string from all write/display/print operations in @racket[p].
    @interaction[#:eval (asl)
      (with-output-to-string (lambda () (display 10)))]
    } 
    
    @defproc[(print [x any]) void]{
    Prints the argument as a value.
    @interaction[#:eval (asl)
      (print 10)
      (print "hello")
      (print 'hello)]
    } 
    @defproc[(display [x any]) void]{
    Prints the argument to stdout (without quotes on symbols and strings, etc.).
    @interaction[#:eval (asl)
      (display 10)
      (display "hello")
      (display 'hello)]
    } 
    @defproc[(write [x any]) void]{
    Prints the argument to stdout (in a traditional style that is somewhere 
    between @racket[print] and @racket[display]).
    @interaction[#:eval (asl)
      (write 10)
      (write "hello")
      (write 'hello)]
    } 
    @defproc[((pp pretty-print) [x any]) void]{
    Pretty prints S-expressions (like @racket[write]). 
    @interaction[
 #:eval (asl)
 (pretty-print '((1 2 3) ((a) ("hello world" #true) (((false "good bye"))))))
 (pretty-print (build-list 10 (lambda (i) (build-list 10 (lambda (j) (= i j))))))
 ]
    }

    @defproc[(printf [f string] [x any] ...) void]{
    Formats the rest of the arguments according to the first argument and print it. } 

    @defproc[(newline) void]{
    Prints a newline.} 

    @defproc[(read) sexp]{
    Reads input from the user.}) 
   
   ("Lists"
;    @defproc[((advanced-list? list?) [x any]) boolean]{
;    Determines whether some value is a list. 
;    In ASL, @racket[list?] also deals with cyclic lists. 
;    } 
    @defproc[((advanced-list* list*) [x any] ... [l (listof any)]) (listof any)]{
    Constructs a list by adding multiple items to a list.
    In ASL, @racket[list*] also deals with cyclic lists. 
    } 
    @defproc[((advanced-cons cons) [x X] [l (listof X)]) (listof X)]{
    Constructs a list.
    In ASL, @racket[cons] creates a mutable list. 
    } 
    @defproc[((advanced-append append) [l (listof any)] ...) (listof any)]{
    Creates a single list from several.
    In ASL, @racket[list*] also deals with cyclic lists. 
    } 
)
   
   ("Misc"
    @defproc[(gensym) symbol?]{
    Generates a new symbol, different from all symbols in the program.
    @interaction[#:eval (asl) (gensym)]
    } 
    @defproc[(sleep [sec positive-num]) void]{
    Causes the program to sleep for the given number of seconds.
    } 
    @defproc[(current-milliseconds) exact-integer]{
    Returns the current “time” in fixnum milliseconds (possibly negative).
    @interaction[#:eval (asl) (current-milliseconds)]
    } 
    @defproc[(force [v any]) any]{
    Finds the delayed value; see also delay.
    } 
    @defproc[(promise? [x any]) boolean?]{
    Determines if a value is delayed.
    } 
    @defproc[(void) void?]{
    Produces a void value.
    @interaction[#:eval (asl) (void)]
    } 
    @defproc[(void? [x any]) boolean?]{
    Determines if a value is void.
    @interaction[#:eval (asl) (void? (void)) (void? 42)]
    }) 
   
   ("Posns"
    @defproc[(set-posn-x! [p posn] [x any]) void?]{
    Updates the x component of a posn.
    @interaction[#:eval (asl) p (set-posn-x! p 678) p]
    } 
    @defproc[(set-posn-y! [p posn] [x any]) void]{
    Updates the y component of a posn.
    @interaction[#:eval (asl) q (set-posn-y! q 678) q]
    }) 
   
   ("Vectors"
    @defproc[(vector [x X] ...) (vector X ...)]{
    Constructs a vector from the given values.
    @interaction[#:eval (asl) (vector 1 2 3 -1 -2 -3)]
    } 
    @defproc[(make-vector [n number] [x X]) (vectorof X)]{
    Constructs a vector of @racket[n] copies of @racket[x].
    @interaction[#:eval (asl) (make-vector 5 0)]
    } 
    @defproc[(build-vector [n nat] [f (nat -> X)]) (vectorof X)]{
    Constructs a vector by applying @racket[f] to the numbers @racket[0] through
    @racket[(- n 1)].
    @interaction[#:eval (asl) (build-vector 5 add1)]
    }
    @defproc[(vector-ref [v (vector X)] [n nat]) X]{
    Extracts the @racket[n]th element from @racket[v].
    @interaction[#:eval (asl) v (vector-ref v 3)]
    } 
    @defproc[(vector-length [v (vector X)]) nat]{
    Determines the length of @racket[v].
    @interaction[#:eval (asl) v (vector-length v)]
    } 
    @defproc[(vector-set! [v (vectorof X)][n nat][x X]) void]{
    Updates @racket[v] at position @racket[n] to be @racket[x].
    @interaction[#:eval (asl) v (vector-set! v 3 77) v]
    } 
    @defproc[(vector->list [v (vectorof X)]) (listof X)]{
    Transforms @racket[v] into a list. 
    @interaction[#:eval (asl) (vector->list (vector 'a 'b 'c))]
    } 
    @defproc[(list->vector [l (listof X)]) (vectorof X)]{
    Transforms @racket[l] into a vector. 
    @interaction[#:eval (asl) (list->vector (list "hello" "world" "good" "bye"))]
    } 
    @defproc[(vector? [x any]) boolean]{
    Determines if a value is a vector.
    @interaction[#:eval (asl) v (vector? v) (vector? 42)]
    }) 
   
   ("Boxes"
    @defproc[(box [x any/c]) box?]{
    Constructs a box.
    @interaction[#:eval (asl) (box 42)]
    } 
    @defproc[(unbox [b box?]) any]{
    Extracts the boxed value.
    @interaction[#:eval (asl) b (unbox b)]
    } 
    @defproc[(set-box! [b box?][x any/c]) void]{
    Updates a box.
    @interaction[#:eval (asl) b (set-box! b 31) b]
    } 
    @defproc[(box? [x any/c]) boolean?]{
    Determines if a value is a box.
    @interaction[#:eval (asl) b (box? b) (box? 42)]
    }) 
   
   ("Hash Tables"
    @defproc[((advanced-make-hash make-hash)) (hash X Y)]{
    Constructs a mutable hash table from an optional list of mappings that
    uses equal? for comparisons.
    @interaction[#:eval (asl)
      (make-hash)
      (make-hash '((b 69) (e 61) (i 999)))
      ]
    } 
    @defproc[((advanced-make-hasheq make-hasheq)) (hash X Y)]{
    Constructs a mutable hash table from an optional list of mappings that
    uses eq? for comparisons.
    @interaction[#:eval (asl)
      (make-hasheq)
      (make-hasheq '((b 69) (e 61) (i 999)))
      ] 
    } 
    @defproc[((advanced-make-hasheqv make-hasheqv)) (hash X Y)]{
    Constructs a mutable hash table from an optional list of mappings that
    uses eqv? for comparisons.
    @interaction[#:eval (asl)
      (make-hasheqv)
      (make-hasheqv '((b 69) (e 61) (i 999)))
      ]
    } 
    @defproc[((advanced-make-immutable-hash make-immutable-hash)) (hash X Y)]{
    Constructs an immutable hash table from an optional list of mappings
    that uses equal? for comparisons.
    @interaction[#:eval (asl)
      (make-immutable-hash)
      (make-immutable-hash '((b 69) (e 61) (i 999)))
      ]
    } 
    @defproc[((advanced-make-immutable-hasheq make-immutable-hasheq)) (hash X Y)]{
    Constructs an immutable hash table from an optional list of mappings
    that uses eq? for comparisons.
    @interaction[#:eval (asl)
      (make-immutable-hasheq)
      (make-immutable-hasheq '((b 69) (e 61) (i 999)))
      ]
    } 
    @defproc[((advanced-make-immutable-hasheqv make-immutable-hasheqv)) (hash X Y)]{
    Constructs an immutable hash table from an optional list of mappings
    that uses eqv? for comparisons.
    @interaction[#:eval (asl)
      (make-immutable-hasheqv)
      (make-immutable-hasheqv '((b 69) (e 61) (i 999)))
      ]
    } 
    @defproc[(hash-set! [h (hash X Y)] [k X] [v Y]) void?]{
    Updates a mutable hash table with a new mapping.
    @interaction[#:eval (asl) hsh (hash-set! hsh 'a 23) hsh]
    } 
    @defproc[(hash-set  [h (hash X Y)] [k X] [v Y]) (hash X Y)]{
    Constructs an immutable hash table with one new mapping from an
    existing immutable hash table.
    @interaction[#:eval (asl) (hash-set ish 'a 23)]
    } 
    @defproc[(hash-ref  [h (hash X Y)] [k X]) Y]{
    Extracts the value associated with a key from a hash table; the three
    ; argument case allows a default value or default value computation.
    @interaction[#:eval (asl) hsh (hash-ref hsh 'b)]
    } 
    @defproc[(hash-ref! [h (hash X Y)] [k X] [v Y]) Y]{
    Extracts the value associated with a key from a mutable hash table; if
    ; the key does not have an mapping, the third argument is used as the
    ; value (or used to compute the value) and is added to the hash table
    ; associated with the key.
    @interaction[#:eval (asl) hsh (hash-ref! hsh 'd 99) hsh]
    } 
    @defproc[(hash-update! [h (hash X Y)] [k X] [f (Y -> Y)]) void?]{
    Composes hash-ref and hash-set! to update an existing mapping; the
    ; third argument is used to compute the new mapping value; the fourth
    ; argument is used as the third argument to hash-ref.
    @interaction[#:eval (asl) hsh (hash-update! hsh 'b (lambda (old-b) (+ old-b 1))) hsh]    
    } 
    @defproc[(hash-update  [h (hash X Y)] [k X] [f (Y -> Y)]) (hash X Y)]{
    Composes hash-ref and hash-set to update an existing mapping; the third
    ; argument is used to compute the new mapping value; the fourth
    ; argument is used as the third argument to hash-ref.
    @interaction[#:eval (asl) (hash-update ish 'b (lambda (old-b) (+ old-b 1)))]        
    } 
    @defproc[(hash-has-key? [h (hash X Y)] [x X]) boolean]{
    Determines if a key is associated with a value in a hash table.
    @interaction[#:eval (asl)
      ish
      (hash-has-key? ish 'b)
      hsh
      (hash-has-key? hsh 'd)]
    } 
    @defproc[(hash-remove! [h (hash X Y)] [x X]) void]{
    Removes an mapping from a mutable hash table.
    @interaction[#:eval (asl)
      hsh
      (hash-remove! hsh 'r)
      hsh]
    } 
    @defproc[(hash-remove [h (hash X Y)] [k X]) (hash X Y)]{
    Constructs an immutable hash table with one less mapping than an
    existing immutable hash table.
    @interaction[#:eval (asl)
      ish
      (hash-remove ish 'b)]
    } 
    @defproc[(hash-map [h (hash X Y)] [f (X Y -> Z)]) (listof Z)]{
    Constructs a new list by applying a function to each mapping of a hash
    table.
    @interaction[#:eval (asl)
      ish
      (hash-map ish list)]
    } 
    @defproc[(hash-for-each [h (hash X Y)] [f (X Y -> any)]) void?]{
    Applies a function to each mapping of a hash table for effect only.
    @interaction[#:eval (asl)
      hsh
      (hash-for-each hsh (lambda (ky vl) (hash-set! hsh ky (+ vl 1))))
      hsh]
    } 
    @defproc[(hash-count [h hash]) integer]{
    Determines the number of keys mapped by a hash table.
    @interaction[#:eval (asl)
      ish
      (hash-count ish)]
    } 
    @defproc[(hash-copy [h hash]) hash]{
    Copies a hash table.
    } 
    @defproc[(hash? [x any]) boolean]{
    Determines if a value is a hash table.
    @interaction[#:eval (asl)
      ish
      (hash? ish)
      (hash? 42)]
    } 
    @defproc[(hash-equal? [h hash?]) boolean]{
    Determines if a hash table uses equal? for comparisons.
    @interaction[#:eval (asl)
      ish
      (hash-equal? ish)
      ieq
      (hash-equal? ieq)
      ]
    } 
    @defproc[(hash-eq? [h hash]) boolean]{
    Determines if a hash table uses eq? for comparisons.
    @interaction[#:eval (asl)
      hsh
      (hash-eq? hsh)
      heq
      (hash-eq? heq)
      ]
    } 
    @defproc[(hash-eqv? [h hash]) boolean]{
    Determines if a hash table uses eqv? for comparisons.
    @interaction[#:eval (asl)
      heq
      (hash-eqv? heq)
      heqv
      (hash-eqv? heqv)
      ]
    }))
