{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE GADTs               #-}
{-# LANGUAGE KindSignatures      #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators       #-}

module Numeric.QuadraticIrrational.SquareFree
  ( SquareFree()
  , KnownSquareFree(..)
  , SomeSquareFree(..)
  , someSquareFreeVal
  , sameSquareFree
  ) where

import Data.Proxy
import Data.Type.Equality
import GHC.TypeLits
import Math.NumberTheory.ArithmeticFunctions
import Unsafe.Coerce

data SquareFree = Negative Nat | Positive Nat

class KnownSquareFree (c :: SquareFree) where
  squareFreeVal :: Proxy c -> Integer

instance KnownNat n => KnownSquareFree ('Negative n) where
  squareFreeVal (Proxy :: Proxy ('Negative n)) = negate $ natVal (Proxy :: Proxy n)

instance KnownNat n => KnownSquareFree ('Positive n) where
  squareFreeVal (Proxy :: Proxy ('Positive n)) = natVal (Proxy :: Proxy n)

data SomeSquareFree where
  SomeSquareFree :: KnownSquareFree c => Proxy c -> SomeSquareFree

someSquareFreeVal :: Integer -> Maybe SomeSquareFree
someSquareFreeVal 0 = Nothing
someSquareFreeVal n = case moebius n of
  MoebiusZ -> Nothing
  _ -> case someNatVal n of
    Just (SomeNat (Proxy :: Proxy n)) -> Just (SomeSquareFree (Proxy :: Proxy ('Positive n)))
    Nothing -> case someNatVal (negate n) of
      Just (SomeNat (Proxy :: Proxy n)) -> Just (SomeSquareFree (Proxy :: Proxy ('Negative n)))
      Nothing -> Nothing

sameSquareFree :: (KnownSquareFree a, KnownSquareFree b) => Proxy a -> Proxy b -> Maybe (a :~: b)
sameSquareFree x y
  | squareFreeVal x == squareFreeVal y = Just (unsafeCoerce Refl)
  | otherwise = Nothing