module Math.NumberTheory.Pell.Test.Reduced
    ( prop_reduced
    ) where

import Math.NumberTheory.Pell.PQa       (reduced)
import Math.NumberTheory.Powers.Squares (isSquare, integerSquareRoot)
import Test.QuickCheck

data Triple = Triple Integer Integer Integer deriving (Show, Eq)

isProperTriple :: Triple -> Bool
isProperTriple (Triple _ q d) = (q /= 0) && (d > 0) && not (isSquare d)

toDouble :: Triple -> (Double, Double)
toDouble (Triple p q d) =
    let
        pp = fromIntegral p
        qq = fromIntegral q
        dd = sqrt $ fromIntegral d
        x  = (pp + dd) / qq
        y  = (pp - dd) / qq
    in
        (x, y)

isReduced :: Triple -> Bool
isReduced t = let (x, y) = toDouble t in (x > 1) && (-1 < y) && (y < 0)

genTriple :: Gen Triple
genTriple = flip suchThat isProperTriple $ do
        p <- scale (* 2) arbitrary
        q <- scale (* 2) arbitrary
        d <- scale (* 3) arbitrary
        return $ Triple p q d

instance Arbitrary Triple where
    arbitrary = oneof $ map (suchThat genTriple) [isReduced, not . isReduced]
    shrink (Triple p q d) = filter isProperTriple $
        [Triple p' q  d  | p' <- shrink p] ++
        [Triple p  q' d  | q' <- shrink q] ++
        [Triple p  q  d' | d' <- shrink d]

reduced' :: Triple -> Bool
reduced' (Triple p q d) = reduced p q (integerSquareRoot d)

prop_reduced :: Triple -> Property
prop_reduced t@(Triple _ _ d) =
    counterexample (show $ toDouble t)        $
    classify (reduced' t)       "reduced"     $
    classify (not $ reduced' t) "not reduced" $
    classify (d <= 100)         "d <= 100"    $
    classify (d >  100)         "d >  100"    $
    reduced' t === isReduced t