#lang scheme/base
  (require
   scheme/list       htdp/image
   lang/error
   )
  
  (provide
   image-above image-above-align-right image-above-align-left image-above-align-center
   image-beside image-beside-align-top image-beside-align-bottom image-beside-align-center
   reflect-vert reflect-horiz reflect-main-diag reflect-other-diag
   rotate-cw rotate-ccw rotate-180
   show-pinhole
   crop-top crop-bottom crop-left crop-right)
  (provide (all-from-out htdp/image))
  
  
    
    (define (image-translate image dx dy)
    (overlay/xy (rectangle (+ (image-width image) dx)
                           (+ (image-height image) dy)
                           'solid
                           'white)
                dx
                dy 
                image))
  
  
    (define (pinhole-to-bottom img)
    (- (image-height img) (pinhole-y img)))
    (define (pinhole-to-top img)
    (pinhole-y img))
    (define (pinhole-to-left img)
    (pinhole-x  img))
    (define (pinhole-to-right img)
    (- (image-width img) (pinhole-x img)))
  
  (define (concat-vert under over)
    (let [[dy (+ (pinhole-to-bottom over) (pinhole-to-top under))]]
      (move-pinhole
       (overlay/xy over 0 (round (+ (pinhole-to-bottom over) (pinhole-to-top under))) under)
       0 (round (/ dy 2)))))
  (define (check-image tag i rank . other-message)
  (if (and (pair? other-message) (string? (car other-message)))
      (check-arg tag (image? i) (car other-message) rank i)
      (check-arg tag (image? i) "image" rank i)))
(define numbered-arg-error "expected <~a> as argument number ~a, given: ~e")
(define (check-numbered-arg pname condition expected arg-posn given)
  (unless condition (error pname numbered-arg-error expected arg-posn given)))
(define (check-all tag test? expected args)
  (check-all0 tag test? expected args 1))
(define (check-all0 tag test? expected args count)
  (cond [(null? args) (void)]
        [(cons? args)
         (check-numbered-arg tag (test? (car args)) expected count (car args))
         (check-all0 tag test? expected (cdr args) (add1 count))]))
(define (check-all-images tag args)
  (check-all tag image? "image" args))
    
  (define (image-above . images)
    (check-all-images 'image-above images)
    (cond [(null? images) (error 'image-above "Expected two or more images; given 0")]
          [(null? (cdr images)) (error 'image-above "Expected two or more images; given 1")]
          [else (foldl concat-vert (car images) (cdr images))]))
  (define (image-above-align-right . images)
    (check-all-images 'image-above-align-right images)
    (apply image-above
           (map (lambda (img)
                  (move-pinhole img (pinhole-to-right img) 0))
                images)))
  
  (define (image-above-align-left . images)
    (check-all-images 'image-above-align-left images)
    (apply image-above
           (map (lambda (img)
                  (move-pinhole img (- (pinhole-to-left img)) 0))
                images)))
  
  
    (define (concat-horiz right left)
    (let [[dx (+ (pinhole-to-right left) (pinhole-to-left right))]]
      (move-pinhole
       (overlay/xy left dx 0 right)
       (round (/ dx 2)) 0)))
  (define  (image-beside . images)
    (check-all-images 'image-beside images)
    (cond [(null? images) (error 'image-beside "Expected two or more images; given 0")]
          [(null? (cdr images)) (error 'image-beside "Expected two or more images; given 1")]
          [else (foldl concat-horiz (car images) (cdr images))]))
  (define (image-beside-align-top . images)
    (check-all-images 'image-beside-align-top images)
    (apply image-beside
           (map (lambda (img)
                  (move-pinhole img 0 (- (pinhole-to-top img))))
                images)))
  
  (define (image-beside-align-bottom . images)
    (check-all-images 'image-beside-align-bottom images)
    (apply image-beside
           (map (lambda (img)
                  (move-pinhole img 0 (pinhole-to-bottom img)))
                images)))
    (define (center-pinhole img)
    (put-pinhole img
                 (quotient (image-width img) 2)
                 (quotient (image-height img) 2)))
  
    (define (image-beside-align-center . images)
    (check-all-images 'image-beside-align-center images)
    (apply image-beside
           (map center-pinhole images)))
  
    (define (image-above-align-center . images)
    (check-all-images 'image-above-align-center images)
    (apply image-above
           (map center-pinhole images)))
  
      (define (show-pinhole img)
    (check-image 'show-pinhole img "first")
    (overlay img (circle 2 "solid" "black")))
    (define (first-n L n)
    (cond [(null? L) '()]
          [(<= n 0) '()]
          [else (cons (car L)
                      (first-n (cdr L) (- n 1)))]))
          
    (define (rest-n L n)
    (cond [(null? L) '()]
          [(<= n 0) L]
          [else (rest-n (cdr L) (- n 1))]))
          
      (define (slice L width)
    (cond [(null? L) '()]
          [else (cons (first-n L width)
                      (slice (rest-n L width) width))]))
  
      
    (define (slice-pic picture)
    (slice (image->alpha-color-list picture)
           (image-width picture)))
  
    (define (unslice lists)
    (apply append lists))
      
    (define (unslice-pic pixels width height phx phy)
    (cond [(or (zero? width) (zero? height))
           (put-pinhole (rectangle width height 'solid 'white) phx phy)]
          [else (alpha-color-list->image
                 (unslice pixels)
                 width height phx phy)
                ]))
  
    (define (reflect-horiz picture)
    (check-image 'reflect-horiz picture "first")
    (unslice-pic
     (map reverse
          (slice-pic picture))
     (image-width picture)
     (image-height picture)
     (pinhole-to-right picture)
     (pinhole-to-top picture)))
  
    (define (natural-number? x)
    (and (integer? x) (>= x 0)))
  
      (define (crop-left picture pixels)
    (check-image 'crop-left picture "first")
    (check-arg 'crop-left (natural-number? pixels) 'natural-number "second" pixels)
    (let* ((new-width (max 0 (- (image-width picture) pixels)))
           (new-phx (max 0 (- (pinhole-x picture) pixels)))
           (real-pixels (- (image-width picture) new-width)))
      (unslice-pic
       (map
        (lambda (row) (rest-n row real-pixels))
        (slice-pic picture))
       new-width
       (image-height picture)
       new-phx
       (pinhole-y picture))))
  
      (define (crop-top picture pixels)
    (check-image 'crop-top picture "first")
    (check-arg 'crop-top (natural-number? pixels) 'natural-number "second" pixels)
    (let* ((new-height (max 0 (- (image-height picture) pixels)))
           (new-phy (max 0 (- (pinhole-y picture) pixels)))
           (real-pixels (- (image-height picture) new-height)))
      (unslice-pic
       (rest-n
        (slice-pic picture)
        real-pixels)
       (image-width picture)
       new-height
       (pinhole-x picture)
       new-phy)))
  
      (define (crop-right picture pixels)
    (check-image 'crop-right picture "first")
    (check-arg 'crop-right (natural-number? pixels) 'natural-number "second" pixels)
    (let* ((new-width (max 0 (- (image-width picture) pixels)))
           (new-phx (min (pinhole-x picture) new-width)))
      (unslice-pic
       (map
        (lambda (row) (first-n row new-width))
        (slice-pic picture))
       new-width
       (image-height picture)
       new-phx
       (pinhole-y picture))))
  
      (define (crop-bottom picture pixels)
    (check-image 'crop-bottom picture "first")
    (check-arg 'crop-bottom (natural-number? pixels) 'natural-number "second" pixels)
    (let* ((new-height (max 0 (- (image-height picture) pixels)))
           (new-phy (min (pinhole-y picture) new-height)))
      (unslice-pic
       (first-n
        (slice-pic picture)
        new-height)
       (image-width picture)
       new-height
       (pinhole-x picture)
       new-phy)))
  
    (define (reflect-vert picture)
    (check-image 'reflect-vert picture "first")
    (unslice-pic
     (reverse 
      (slice-pic picture))
     (image-width picture)
     (image-height picture)
     (pinhole-to-left picture)
     (pinhole-to-bottom picture)))
  
    (define (ncons-each L)
    (map list L))
          
        (define (transpose rows)
    (apply map (cons list rows)))
            
    (define (reflect-main-diag picture)
    (check-image 'reflect-main-diag picture "first")
    (unslice-pic
     (transpose
      (slice-pic picture))
     (image-height picture)
     (image-width picture)
     (pinhole-y picture)
     (pinhole-x picture)))
  
    (define (reflect-other-diag picture)
    (check-image 'reflect-other-diag picture "first")
                (unslice-pic
     (reverse
      (transpose
       (reverse
        (slice-pic picture))))
     (image-height picture)
     (image-width picture)
     (pinhole-to-bottom picture)
     (pinhole-to-right picture)))
  
      (define (rotate-cw picture)
    (check-image 'rotate-cw picture "first")
        (unslice-pic
     (transpose
      (reverse
       (slice-pic picture)))
     (image-height picture)
     (image-width picture)
     (pinhole-to-bottom picture)
     (pinhole-to-left picture)))
  
      (define (rotate-ccw picture)
    (check-image 'rotate-ccw picture "first")
        (unslice-pic
     (reverse
      (transpose
       (slice-pic picture)))
     (image-height picture)
     (image-width picture)
     (pinhole-to-top picture)
     (pinhole-to-right picture)))
  
    (define (rotate-180 picture)
    (check-image 'rotate-180 picture "first")
            (unslice-pic
     (reverse
      (map reverse
           (slice-pic picture)))
     (image-width picture)
     (image-height picture)
     (pinhole-to-right picture)
     (pinhole-to-bottom picture)))