{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances, OverlappingInstances #-}
module Math.NumberTheory.QuadraticField where
import Prelude hiding (sqrt, (*>) )
import Data.List as L
import Math.Core.Field
import Math.Core.Utils (powersetdfs)
import Math.Algebras.VectorSpace
import Math.Algebras.TensorProduct
import Math.Algebras.Structures
import Math.NumberTheory.Factor
import Math.Algebra.LinearAlgebra hiding (inverse, (*>) )
import Math.CommutativeAlgebra.Polynomial
data QNFBasis = One | Sqrt Integer deriving (Eq,Ord)
instance Show QNFBasis where
    show One = "1"
    show (Sqrt d) | d == -1 = "i"
                  | otherwise = "sqrt(" ++ show d ++ ")"
type QNF = Vect Q QNFBasis
sqrt :: Integer -> QNF
sqrt d | fr == 1   = fromInteger sq
       | otherwise = fromInteger sq * return (Sqrt fr)
    where (sq,fr) = squaredFree 1 1 (pfactors d)
          squaredFree squared free (d1:d2:ds) =
              if d1 == d2 then squaredFree (d1*squared) free ds else squaredFree squared (d1*free) (d2:ds)
          squaredFree squared free ds = (squared, free * product ds)
sqrt2 = sqrt 2
sqrt3 = sqrt 3
sqrt5 = sqrt 5
sqrt6 = sqrt 6
sqrt7 = sqrt 7
i :: QNF
i = sqrt (-1)
instance (Eq k, Num k) => Algebra k QNFBasis where
    unit x = x *> return One
    mult = linear mult'
         where mult' (One,x) = return x
               mult' (x,One) = return x
               mult' (Sqrt m, Sqrt n) | m == n = unit (fromInteger m)
                                      | otherwise = let (i,d) = interdiff (pfactors m) (pfactors n) 1 1
                                                    in fromInteger i *> return (Sqrt d)
               
               
               
               
               interdiff (p:ps) (q:qs) i d =
                   case compare p q of
                   LT -> interdiff ps (q:qs) i (d*p)
                   EQ -> interdiff ps qs (i*p) d
                   GT -> interdiff (p:ps) qs i (d*q)
               interdiff ps qs i d = (i, d * product (ps ++ qs))
newtype XVar = X Int deriving (Eq, Ord, Show)
instance Fractional QNF where
    recip x@(V ts) =
        let ds = [d | (Sqrt d, _) <- terms x]
            fs = (if any (<0) ds then [-1] else []) ++ pfactors (foldl lcm 1 ds) 
            rs = map (\d -> case d of 1 -> One; d' -> Sqrt d') $
                 map product $ powersetdfs $ fs
            
            n = length rs
            y = V $ zip rs $ map (glexvar . X) [1..n] 
            x' = V $ map (\(s,c) -> (s, unit c)) ts 
            one = x' * y
            m = [ [coeff (mvar (X j)) c | j <- [1..n]] | i <- rs, let c = coeff i one] 
            b = 1 : replicate (n-1) 0
        in case solveLinearSystem m b of 
            Just v -> nf $ V $ zip rs v
            Nothing -> error "QNF.recip 0"
    fromRational q = fromRational q *> 1