#lang racket

;; 2.1
(define (normalize n d)
  (if (equal? (< n 0) (< d 0))
      (cons (abs n) (abs d))
      (cons (- (abs n)) (abs d))))
(define (make-rat n d)
  (let ([g (gcd n d)])
    ; if they have the same sign, just take absolutes
    (normalize (/ n g) (/ d g))))

(define (print-rat rat)
  (display (car rat))
  (display "/")
  (display (cdr rat))
  (newline))

(map print-rat
     (map (λ (x) (apply make-rat x))
          '((2 3)
            (1 2)
            (-3 4)
            (-100 -4)
            (100 -4))))

;; 2.2
;; we are using racket, so we could also define a structure.
;; (struct point (x y))
;; instead we'll keep to the book and use cons cells.
(define point-x car)
(define point-y cdr)
(define point cons)
;; the above could be replaced with
; (struct point (x y))
(define line-p1 car)
(define line-p2 cdr)
(define line cons)
; (struct line (p1 p2))

(define (average a b) (/ (+ a b) 2))
(define (midpoint-segment ls)
  (point (average (point-x (line-p1 ls))
                  (point-x (line-p2 ls)))
         (average (point-y (line-p1 ls))
                  (point-y (line-p2 ls)))))

;; 2.3
;; not sure what the book means here. But I guess we can implement
;; rectangles as pairs, too, since we only need two corners really.
;; as "another representation", I guess we could store two line segments?
;; but either way the rect-p1 and rect-p2
;; functions can be replaced very easily.
;; the area and perimeter functions do not care about their implementation.
(define rect-p1 car)
(define rect-p2 cdr)
(define rect cons)

;; further abstraction, actually: rectangle side 1.
;; returns the length of rect's one side
(define (rect-side-helper rect fun)
  (abs (- (fun (rect-p1 rect))
          (fun (rect-p2 rect)))))
(define (rect-s1 rect)
  (rect-side-helper rect point-x))
(define (rect-s2 rect)
  (rect-side-helper rect point-y))

(define (area rect)
  (* (rect-s1 rect) (rect-s2 rect)))
(define (perimeter rect)
  (* 2 (+ (rect-s1 rect) (rect-s2 rect))))

; 2.6
(define zero (lambda (f) (lambda (x) x)))
(define (add-1 n)
  (lambda (f) (lambda (x) (f ((n f) x)))))
(define one (λ (f) (λ (x) (f x)))) ;; applies f once.
(define two (λ (f) (λ (x) (f (f x)))))
;(add-1 zero) ; is the same as
;(lambda (f) (lambda (x) (f ((zero f) x))))
;(lambda (f) (lambda (x) ((add-1 zero) (((add-1 zero) f) x))))

(define (addition n1 n2)
  "Returns a function that takes a function f, returns:
    A function that takes a parameter x, applies f to x n1 + n2 times
    (n1 and n2 being church numerals, i.e. λfλx forms.)"
  (lambda (f) (lambda (x)
                ((n2 f) ((n1 f) x))
                )))

(define (de-churchify n)
  ((n (λ (x) (+ x 1))) 0))