{-@ LIQUID "--short-names"    @-}

module Point where

-- | Non-negative numbers:

{-@ type NonNeg  = {v:Double | v >= 0.0 } @-}

-- | Non-negative, and zero iff X is zero:

{-@ type PosZ X  = {v:NonNeg | X == 0.0 <=> v == 0.0 } @-}
 
-- | The distance function, STATICALLY guaranteed to satisfy "pre/post"
--   or type spec, assuming no run-time checks fail

{-@ dist                 :: p1:Point -> p2:Point -> {v:NonNeg | v == 0.0 <=> EqPoint p1 p2} @-}
dist (P x1 y1) (P x2 y2) = d
  where 
    d                    = root   (dx + dy)
    dx                   = square (x1 - x2)
    dy                   = square (y1 - y2)

-- | A Data Type for Points

data Point = P { px :: Double, py::Double }

{-@ data Point = P { px :: Double, py :: Double } @-}

-- | When are two points "equal" ?

{-@ predicate EqPoint P1 P2 = (px P1 == px P2 && py P1 == py P2) @-}
                
-- | Squaring numbers: DYNAMIC checks establish numerical properties

{-@ square :: x:Double -> PosZ x @-}
square   :: Double -> Double
square 0 = 0
square x = assume (xx > 0) xx 
  where
    xx   = x * x

{-@ root :: x:NonNeg -> PosZ x  @-}
root :: Double -> Double
root 0  = 0
root x  = assume (rx > 0) rx
  where
    rx  = sqrt x

-- | Run-time Checks

{-@ assume     :: b:_ -> a -> {v:a | b} @-}
assume True  x = x
assume False _ = undefined -- error "Failed Dynamic Check!"


