{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE KindSignatures      #-}
{-# LANGUAGE PolyKinds           #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Numeric.QuadraticIrrational.Ring
  ( QuadExt(..)
  ) where

import Data.Proxy
import Numeric.QuadraticIrrational.SquareFree

data QuadExt (c :: SquareFree) t = QuadExt t t
  deriving (Eq)

instance (KnownSquareFree c, Show t) => Show (QuadExt c t) where
  showsPrec p (QuadExt a b) = showParen (p > 10)
    $ showString "QuadExt " . showsPrec 11 a . showChar '+' . showsPrec 11 b . showChar '√' . showsPrec 11 c
    where
      c = squareFreeVal (Proxy :: Proxy c)

instance (KnownSquareFree c, Num t) => Num (QuadExt c t) where
  QuadExt a1 b1 + QuadExt a2 b2 = QuadExt (a1 + a2) (b1 + b2)
  QuadExt a1 b1 - QuadExt a2 b2 = QuadExt (a1 - a2) (b1 - b2)
  negate (QuadExt a b) = QuadExt (negate a) (negate b)
  abs q = q
  signum _ = 1
  fromInteger n = QuadExt (fromInteger n) 0

  QuadExt a1 b1 * QuadExt a2 b2 = QuadExt a b
    where
      a = a1 * a2 + fromInteger c * b1 * b2
      b = a1 * b2 + a2 * b1
      c = squareFreeVal (Proxy :: Proxy c)

instance (KnownSquareFree c, Fractional t) => Fractional (QuadExt c t) where
  fromRational r = QuadExt (fromRational r) 0

  recip (QuadExt a b) = QuadExt (a / d) (negate b / d)
    where
      d = a * a - fromInteger c * b * b
      c = squareFreeVal (Proxy :: Proxy c)

  QuadExt a1 b1 / QuadExt a2 b2 = QuadExt (a / d) (b / d)
    where
      QuadExt a b = QuadExt a1 b1 * QuadExt a2 (negate b2) :: QuadExt c t
      d = a2 * a2 - fromInteger c * b2 * b2
      c = squareFreeVal (Proxy :: Proxy c)