module Test.Circuit.Affine where
import Circuit.Affine
import qualified Data.Map as Map
import Protolude
import Test.Tasty.QuickCheck
-------------------------------------------------------------------------------
-- Generators
-------------------------------------------------------------------------------
arbAffineCircuit ::
Arbitrary f =>
Int ->
Int ->
Gen (AffineCircuit Int f)
arbAffineCircuit numVars size
| size <= 0 =
oneof $
[ ConstGate <$> arbitrary
]
++ if numVars > 0
then [Var <$> choose (0, numVars - 1)]
else []
| size > 0 =
oneof
[ ScalarMul <$> arbitrary <*> arbAffineCircuit numVars (size - 1),
Add <$> arbAffineCircuit numVars (size - 1)
<*> arbAffineCircuit numVars (size - 1)
]
arbInputVector :: Arbitrary f => Int -> Gen (Map Int f)
arbInputVector numVars = Map.fromList . zip [0 ..] <$> vector numVars
-- | The input vector has to have the correct length, so we want to
-- generate the program and the test input simultaneously.
data AffineCircuitWithInputs f = AffineCircuitWithInputs (AffineCircuit Int f) [Map Int f]
deriving (Show)
instance Arbitrary f => Arbitrary (AffineCircuitWithInputs f) where
arbitrary = do
numVars <- abs <$> arbitrary
program <- scale (`div` 7) $ sized (arbAffineCircuit numVars)
inputs <- vectorOf 10 $ arbInputVector numVars
pure $ AffineCircuitWithInputs program inputs
-------------------------------------------------------------------------------
-- Tests
-------------------------------------------------------------------------------
-- | Check that evaluating the vector representation of the circuit
-- yields the same results as evaluating the circuit "directly". Field
-- is instantiated as being the rationals for testing. It later should
-- probably be something like Pairing.Fr.Fr.
prop_affineCircuitToAffineMap ::
AffineCircuitWithInputs Rational -> Bool
prop_affineCircuitToAffineMap (AffineCircuitWithInputs program inputs) =
all testInput inputs
where
testInput input =
evalAffineCircuit Map.lookup input program
== evalAffineMap (affineCircuitToAffineMap program) input