posn.ss
#lang typed-scheme
(require scheme/list
         scheme/math)

(define-struct: cartesian-posn
  ([x : Number]
   [y : Number]))
(define-struct: polar-posn
  ([r : Number]
   [theta : Number]))

(define-type-alias posn (U cartesian-posn polar-posn))

(: cartesian-posn+2 (cartesian-posn cartesian-posn -> cartesian-posn))
(define (cartesian-posn+2 p1 p2)
  (make-cartesian-posn (+ (cartesian-posn-x p1) (cartesian-posn-x p2))
                       (+ (cartesian-posn-y p1) (cartesian-posn-y p2))))

(: cartesian-posn-scale (cartesian-posn Number -> cartesian-posn))
(define (cartesian-posn-scale p c)
  (make-cartesian-posn (* c (cartesian-posn-x p))
                       (* c (cartesian-posn-y p))))

(: polar-posn+2 (polar-posn polar-posn -> polar-posn))
(define (polar-posn+2 p1 p2)
  (make-polar-posn (+ (polar-posn-r p1) (polar-posn-r p2))
                   (+ (polar-posn-theta p1) (polar-posn-theta p2))))

(: polar-posn-scale (polar-posn Number -> polar-posn))
(define (polar-posn-scale p c)
  (make-polar-posn (* c (polar-posn-r p))
                   (* c (polar-posn-theta p))))

(: polar->cartesian (polar-posn -> cartesian-posn))
(define (polar->cartesian p)
  (make-cartesian-posn (* (polar-posn-r p) (cos (polar-posn-theta p)))
                       (* (polar-posn-r p) (sin (polar-posn-theta p)))))

(: cartesian->polar (cartesian-posn -> polar-posn))
(define (cartesian->polar p)
  (define x (cartesian-posn-x p))
  (define y (cartesian-posn-y p))
  (define r (sqrt (+ (expt x 2) (expt y 2))))
  (define theta
    (cond
      [(and (= x 0) (= y 0))
       0]
      [(x . >= . 0)
       (asin (/ y r))]
      [else
       (- pi (asin (/ y r)))]))       
  (make-polar-posn r theta))

(: posn->cartesian-posn (posn -> cartesian-posn))
(define (posn->cartesian-posn p)
  (if (cartesian-posn? p) p
      (polar->cartesian p)))

(: posn->polar-posn (posn -> polar-posn))
(define (posn->polar-posn p)
  (if (polar-posn? p) p
      (cartesian->polar p)))

(: posn+2 (posn posn -> posn))
(define (posn+2 p1 p2)
  (if (cartesian-posn? p1)
      (if (cartesian-posn? p2)
          (cartesian-posn+2 p1 p2)
          (cartesian-posn+2 p1 (polar->cartesian p2)))
      (if (cartesian-posn? p2)
          (cartesian-posn+2 (polar->cartesian p1) p2)
          (polar-posn+2 p1 p2))))

(: posn-scale : (posn Number -> posn))
(define (posn-scale p c)
  (if (cartesian-posn? p)
      (cartesian-posn-scale p c)
      (polar-posn-scale p c)))

(: posn+ (posn * -> posn))
(define (posn+ . ps)
  (cond
    [(empty? ps)
     (make-cartesian-posn 0 0)]
    [(= (length ps) 1)
     (first ps)]
    [else
     (posn+2 (first ps) (apply posn+ (rest ps)))]))

(: degrees->radians (Number -> Number))
(define (degrees->radians d)
  (* d (/ pi 180)))

(provide (all-defined-out))