-- |
  Module :       Math .  . Primes . LinearAlgebraTests
  Copyright :    ( c ) 2020  
  Licence :      MIT
  Maintainer :     <  >
--
  Tests for Math .  . Primes . Factorisation . LinearAlgebra
--

# LANGUAGE ScopedTypeVariables #

module Math.NumberTheory.Primes.LinearAlgebraTests
  ( testSuite
  ) where

import qualified Data.List as L
import qualified Data.Vector.Sized as SV
import qualified Data.Vector.Unboxed as U
import Test.Tasty
import Math.NumberTheory.TestUtils
import Math.NumberTheory.Primes.Factorisation.LinearAlgebra
import GHC.TypeNats
import Data.Proxy
import Data.Maybe
import System.Random
-- import qualified Data.Set as S
-- import Debug.Trace

-- This test checks if the vector computed in @linearSolve@ is non-empty and
-- is a solution of the matrix.
testLinear :: Positive Int -> Int ->  Bool
testLinear (Positive dim') seedMat = let dim = dim' + 6 in case someNatVal (fromIntegral dim) of
  SomeNat (Proxy :: Proxy dim) -> mat `mult` solution == mempty && solution /= mempty
    where
      -- 0 is the random seed.
      solution = linearSolve 0 mat
      -- The floating point number is the density of the matrix.
      mat :: SBMatrix dim = getRandomMatrix dim (mkStdGen seedMat) 0.3

-- This test checks whether @linearSolve@ finds different solutions when inputting
-- different random seeds. This is desirable property when using @linearSolve@
-- in the context of integer factorisation. As it stands, the test fails. More
  precisely , in 10 % of the cases , it only finds one solution in 25 attempts .
  To test , uncomment the two lines importing files at the top , uncomment
  the test below and uncomment the line at the bottom in the @testSuite@.

-- testVariation :: Positive Int -> Int -> Bool
-- testVariation (Positive dim') seedMat = let dim = dim' + 6 in case someNatVal (fromIntegral dim) of
     ( Proxy : : Proxy dim ) - > numberOfSols > 1
--     where
--       numberOfSols = trace ("Matrix: " ++ show mat) $ (S.size . S.fromList) solutions
        solutions = map ( ` linearSolve ` mat ) $ take 25 $ [ 0 .. ]
        mat : :  dim = getRandomMatrix dim ( mkStdGen seedMat ) 0.3

-- Generates a random matrix.
getRandomMatrix :: KnownNat k => Int -> StdGen -> Double -> SBMatrix k
getRandomMatrix dim seedMat density = SBMatrix $ fromJust . SV.fromList $ listOfColumns
    where
  Choosing @(dim - 5)@ implies that the number of rows is five less than
      -- the number of columns. This ensures the matrix is singular.
      listOfColumns = L.take dim $ getRandomSBVectors (dim - 5) density seedMat

  Infinite lists of random SBVectors to be used as columns of the random matrix .
getRandomSBVectors :: KnownNat k => Int -> Double -> StdGen -> [SBVector k]
getRandomSBVectors numberOfRows density gen = go randomEntries
  where
    randomEntries = map (< density) $ randomRs (0, 1) gen
    go :: KnownNat k => [Bool] -> [SBVector k]
    go list = newVector `seq` (newVector : go backOfList)
      where
        newVector = SBVector (U.fromList listOfEntries)
        listOfEntries = map fst $ filter snd $ zip [minBound..maxBound] frontOfList
        (frontOfList, backOfList) = L.splitAt numberOfRows list

testSuite :: TestTree
testSuite = testGroup "Linear Algebra"
  [ testSmallAndQuick "LinearSolver" testLinear
, testSmallAndQuick " Variation of Solutions " testVariation
  ]

 |


 import qualified Data.Set as S
 import Debug.Trace
 This test checks if the vector computed in @linearSolve@ is non-empty and
 is a solution of the matrix.
 0 is the random seed.
 The floating point number is the density of the matrix.
 This test checks whether @linearSolve@ finds different solutions when inputting
 different random seeds. This is desirable property when using @linearSolve@
 in the context of integer factorisation. As it stands, the test fails. More
 testVariation :: Positive Int -> Int -> Bool
 testVariation (Positive dim') seedMat = let dim = dim' + 6 in case someNatVal (fromIntegral dim) of
     where
       numberOfSols = trace ("Matrix: " ++ show mat) $ (S.size . S.fromList) solutions
 Generates a random matrix.
 the number of columns. This ensures the matrix is singular.
  Module :       Math .  . Primes . LinearAlgebraTests
  Copyright :    ( c ) 2020  
  Licence :      MIT
  Maintainer :     <  >
  Tests for Math .  . Primes . Factorisation . LinearAlgebra

# LANGUAGE ScopedTypeVariables #

module Math.NumberTheory.Primes.LinearAlgebraTests
  ( testSuite
  ) where

import qualified Data.List as L
import qualified Data.Vector.Sized as SV
import qualified Data.Vector.Unboxed as U
import Test.Tasty
import Math.NumberTheory.TestUtils
import Math.NumberTheory.Primes.Factorisation.LinearAlgebra
import GHC.TypeNats
import Data.Proxy
import Data.Maybe
import System.Random

testLinear :: Positive Int -> Int ->  Bool
testLinear (Positive dim') seedMat = let dim = dim' + 6 in case someNatVal (fromIntegral dim) of
  SomeNat (Proxy :: Proxy dim) -> mat `mult` solution == mempty && solution /= mempty
    where
      solution = linearSolve 0 mat
      mat :: SBMatrix dim = getRandomMatrix dim (mkStdGen seedMat) 0.3

  precisely , in 10 % of the cases , it only finds one solution in 25 attempts .
  To test , uncomment the two lines importing files at the top , uncomment
  the test below and uncomment the line at the bottom in the @testSuite@.

     ( Proxy : : Proxy dim ) - > numberOfSols > 1
        solutions = map ( ` linearSolve ` mat ) $ take 25 $ [ 0 .. ]
        mat : :  dim = getRandomMatrix dim ( mkStdGen seedMat ) 0.3

getRandomMatrix :: KnownNat k => Int -> StdGen -> Double -> SBMatrix k
getRandomMatrix dim seedMat density = SBMatrix $ fromJust . SV.fromList $ listOfColumns
    where
  Choosing @(dim - 5)@ implies that the number of rows is five less than
      listOfColumns = L.take dim $ getRandomSBVectors (dim - 5) density seedMat

  Infinite lists of random SBVectors to be used as columns of the random matrix .
getRandomSBVectors :: KnownNat k => Int -> Double -> StdGen -> [SBVector k]
getRandomSBVectors numberOfRows density gen = go randomEntries
  where
    randomEntries = map (< density) $ randomRs (0, 1) gen
    go :: KnownNat k => [Bool] -> [SBVector k]
    go list = newVector `seq` (newVector : go backOfList)
      where
        newVector = SBVector (U.fromList listOfEntries)
        listOfEntries = map fst $ filter snd $ zip [minBound..maxBound] frontOfList
        (frontOfList, backOfList) = L.splitAt numberOfRows list

testSuite :: TestTree
testSuite = testGroup "Linear Algebra"
  [ testSmallAndQuick "LinearSolver" testLinear
, testSmallAndQuick " Variation of Solutions " testVariation
  ]
