content: -- |
  -- Module:      Math.NumberTheory.SmoothNumbersTests
  -- Copyright:   (c) 2018 Andrew Lelechenko
  -- Licence:     MIT
  -- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>
  --
  -- Tests for Math.NumberTheory.SmoothNumbersTests
  --
  
  {-# LANGUAGE TypeApplications    #-}
  
  {-# OPTIONS_GHC -fno-warn-type-defaults #-}
  
  module Math.NumberTheory.SmoothNumbersTests
    ( testSuite
    ) where
  
  import Prelude hiding (mod, rem)
  import Test.Tasty
  import Test.Tasty.HUnit
  
  import Data.Coerce
  import Data.Euclidean
  import Data.List (nub)
  import Data.List.Infinite (Infinite(..))
  import qualified Data.List.Infinite as Inf
  import Numeric.Natural
  
  import Math.NumberTheory.Primes (Prime (..))
  import qualified Math.NumberTheory.Quadratic.GaussianIntegers as G
  import qualified Math.NumberTheory.Quadratic.EisensteinIntegers as E
  import Math.NumberTheory.SmoothNumbers (SmoothBasis, fromList, isSmooth, smoothOver, smoothOver')
  import Math.NumberTheory.TestUtils
  
  isSmoothPropertyHelper
    :: (Eq a, Num a, Euclidean a)
    => (a -> Integer)
    -> Infinite a
    -> Int
    -> Int
    -> Bool
  isSmoothPropertyHelper norm primes' i1 i2 =
      let primes = Inf.take i1 primes'
          basis  = fromList primes
      in all (isSmooth basis) $ take i2 $ smoothOver' norm basis
  
  isSmoothProperty1 :: Positive Int -> Positive Int -> Bool
  isSmoothProperty1 (Positive i1) (Positive i2) =
      isSmoothPropertyHelper G.norm (fmap unPrime G.primes) i1 i2
  
  isSmoothProperty2 :: Positive Int -> Positive Int -> Bool
  isSmoothProperty2 (Positive i1) (Positive i2) =
      isSmoothPropertyHelper E.norm (fmap unPrime E.primes) i1 i2
  
  smoothOverInRange :: Integral a => SmoothBasis a -> a -> a -> [a]
  smoothOverInRange s lo hi
    = takeWhile (<= hi)
    $ dropWhile (< lo)
    $ smoothOver s
  
  smoothOverInRangeBF
    :: (Eq a, Enum a, GcdDomain a)
    => SmoothBasis a
    -> a
    -> a
    -> [a]
  smoothOverInRangeBF prs lo hi
    = coerce
    $ filter (isSmooth prs)
    $ coerce [lo..hi]
  
  smoothOverInRangeProperty
    :: (Show a, Integral a)
    => (SmoothBasis a, Positive a, Positive a)
    -> ([a], [a])
  smoothOverInRangeProperty (s, Positive lo', Positive diff') =
    (map unwrapIntegral xs, map unwrapIntegral ys)
    where
      lo   = WrapIntegral lo'   `rem` 2^18
      diff = WrapIntegral diff' `rem` 2^18
      hi   = lo + diff
      xs   = smoothOverInRange   (coerce s) lo hi
      ys   = smoothOverInRangeBF (coerce s) lo hi
  
  smoothNumbersAreUniqueProperty
    :: (Show a, Integral a)
    => SmoothBasis a
    -> Positive Int
    -> Bool
  smoothNumbersAreUniqueProperty s (Positive len)
    = nub l == l
    where
      l = take len $ smoothOver s
  
  isSmoothSpecialCase1 :: Assertion
  isSmoothSpecialCase1 = assertBool "should be distinct" $ nub l == l
    where
      b = fromList [1+3*G.ι,6+8*G.ι]
      l = take 10 $ map abs $ smoothOver' G.norm b
  
  isSmoothSpecialCase2 :: Assertion
  isSmoothSpecialCase2 = assertBool "should be smooth" $ isSmooth b 6
    where
      b = fromList [4, 3, 6, 10, 7::Int]
  
  testSuite :: TestTree
  testSuite = testGroup "SmoothNumbers"
    [ testGroup "smoothOverInRange == smoothOverInRangeBF"
      [ testEqualSmallAndQuick "Int"     (smoothOverInRangeProperty @Int)
      , testEqualSmallAndQuick "Word"    (smoothOverInRangeProperty @Word)
      , testEqualSmallAndQuick "Integer" (smoothOverInRangeProperty @Integer)
      , testEqualSmallAndQuick "Natural" (smoothOverInRangeProperty @Natural)
      ]
    , testGroup "smoothOver generates a list without duplicates"
      [ testSmallAndQuick "Integer" (smoothNumbersAreUniqueProperty @Integer)
      , testSmallAndQuick "Natural" (smoothNumbersAreUniqueProperty @Natural)
      ]
    , testGroup "Quadratic rings"
      [ testGroup "smoothOver generates valid smooth numbers"
        [ testSmallAndQuick "Gaussian"   isSmoothProperty1
        , testSmallAndQuick "Eisenstein" isSmoothProperty2
        ]
      , testCase "all distinct for base [1+3*i,6+8*i]" isSmoothSpecialCase1
      , testCase "6 is smooth for base [4,3,6,10,7]" isSmoothSpecialCase2
      ]
    ]
  