Skip to content

Commit a17e1a3

Browse files
authored
Kwxm/test/extra integer property tests (#7134)
* Extra property tests for integer builtins * Formatting * New files * WIP * Missing file * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * Remove renamed file * WIP * WIP * WIP * Tidying up * More comments * More comments * More comments * Move file to try to make git see that it's renamed * Move file back * Oops: duplicated code * Address PR comments * Improve generator to include really big numbers * Improve generator to include really big numbers * Improve generator to include really big numbers
1 parent 71afec0 commit a17e1a3

File tree

11 files changed

+789
-234
lines changed

11 files changed

+789
-234
lines changed

plutus-core/plutus-core.cabal

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,12 @@ library untyped-plutus-core-testlib
451451
Evaluation.Builtins.Conversion
452452
Evaluation.Builtins.Costing
453453
Evaluation.Builtins.Definition
454-
Evaluation.Builtins.Integer.ExpModInteger
454+
Evaluation.Builtins.Integer.Common
455+
Evaluation.Builtins.Integer.DivModProperties
456+
Evaluation.Builtins.Integer.ExpModIntegerProperties
457+
Evaluation.Builtins.Integer.OrderProperties
458+
Evaluation.Builtins.Integer.QuotRemProperties
459+
Evaluation.Builtins.Integer.RingProperties
455460
Evaluation.Builtins.MakeRead
456461
Evaluation.Builtins.SignatureVerification
457462
Evaluation.Debug

plutus-core/testlib/PlutusCore/Generators/QuickCheck/Builtin.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ instance ArbitraryBuiltin BLS12_381.Pairing.MlResult where
278278
-- the 'Arbitrary' class and the logic for handling elements from 'ArbitraryBuiltin'.
279279
newtype AsArbitraryBuiltin a = AsArbitraryBuiltin
280280
{ unAsArbitraryBuiltin :: a
281-
} deriving newtype (Show)
281+
} deriving newtype (Show, Eq, Ord, Num)
282282

283283
instance ArbitraryBuiltin a => Arbitrary (AsArbitraryBuiltin a) where
284284
arbitrary = coerce $ arbitraryBuiltin @a

plutus-core/untyped-plutus-core/testlib/Evaluation/Builtins/Common.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module Evaluation.Builtins.Common
1212
, typecheckEvaluateCek
1313
, typecheckEvaluateCekNoEmit
1414
, typecheckReadKnownCek
15+
, PlcType
1516
, PlcTerm
1617
, UplcTerm
1718
, CekResult (..)
@@ -21,6 +22,7 @@ module Evaluation.Builtins.Common
2122
, ok
2223
, fails
2324
, evalOkEq
25+
, evalOkTrue
2426
, integer
2527
, bytestring
2628
, zero
@@ -120,6 +122,7 @@ typecheckReadKnownCek semvar =
120122

121123
-- TPLC/UPLC utilities
122124

125+
type PlcType = TPLC.Type TPLC.TyName TPLC.DefaultUni ()
123126
type PlcTerm = TPLC.Term TPLC.TyName TPLC.Name TPLC.DefaultUni TPLC.DefaultFun ()
124127
type PlcError = TypeErrorPlc TPLC.DefaultUni TPLC.DefaultFun ()
125128
type UplcTerm = UPLC.Term TPLC.Name TPLC.DefaultUni TPLC.DefaultFun ()
@@ -190,5 +193,7 @@ evalOkEq t1 t2 =
190193
r@(CekSuccess _) -> r === evalTerm t2
191194
_ -> property False
192195

196+
evalOkTrue :: PlcTerm -> Property
197+
evalOkTrue t = evalOkEq t true
193198

194199

plutus-core/untyped-plutus-core/testlib/Evaluation/Builtins/Definition.hs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ import Evaluation.Builtins.BLS12_381 (test_BLS12_381)
2424
import Evaluation.Builtins.Common (typecheckAnd, typecheckEvaluateCek, typecheckEvaluateCekNoEmit,
2525
typecheckReadKnownCek)
2626
import Evaluation.Builtins.Conversion qualified as Conversion
27-
import Evaluation.Builtins.Integer.ExpModInteger (test_expModInteger_properties)
27+
import Evaluation.Builtins.Integer.DivModProperties (test_integer_div_mod_properties)
28+
import Evaluation.Builtins.Integer.ExpModIntegerProperties (test_integer_exp_mod_properties)
29+
import Evaluation.Builtins.Integer.OrderProperties (test_integer_order_properties)
30+
import Evaluation.Builtins.Integer.QuotRemProperties (test_integer_quot_rem_properties)
31+
import Evaluation.Builtins.Integer.RingProperties (test_integer_ring_properties)
2832
import Evaluation.Builtins.SignatureVerification (ecdsaSecp256k1Prop, ed25519_VariantAProp,
2933
ed25519_VariantBProp, ed25519_VariantCProp,
3034
schnorrSecp256k1Prop)
@@ -1217,6 +1221,17 @@ test_Bitwise_CIP0123 =
12171221
]
12181222
]
12191223

1224+
test_integer_properties :: TestTree
1225+
test_integer_properties =
1226+
testGroup "test_integer_properties"
1227+
[ test_integer_ring_properties
1228+
, test_integer_div_mod_properties
1229+
, test_integer_quot_rem_properties
1230+
, test_integer_order_properties
1231+
, test_integer_ring_properties
1232+
, test_integer_exp_mod_properties
1233+
]
1234+
12201235
test_Case :: TestTree
12211236
test_Case =
12221237
testGroup "Case on constants"
@@ -1303,7 +1318,7 @@ test_definition =
13031318
, test_HashSizes
13041319
, test_SignatureVerification
13051320
, test_BLS12_381
1306-
, test_expModInteger_properties
1321+
, test_integer_properties
13071322
, test_Other
13081323
, test_Version
13091324
, test_ConsByteString
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
{-# LANGUAGE AllowAmbiguousTypes #-}
2+
{-# LANGUAGE TypeApplications #-}
3+
{-# LANGUAGE TypeOperators #-}
4+
module Evaluation.Builtins.Integer.Common
5+
where
6+
7+
import Prelude hiding (and, not, or)
8+
9+
import PlutusCore qualified as PLC
10+
import PlutusCore.Generators.QuickCheck.Builtin (AsArbitraryBuiltin (..))
11+
import PlutusCore.MkPlc (builtin, mkIterAppNoAnn, mkTyBuiltin, tyInst)
12+
13+
import Evaluation.Builtins.Common
14+
15+
import Test.QuickCheck (Arbitrary, Gen, arbitrary, choose, oneof)
16+
17+
{- We don't want to use the standard QuickCheck generator for Integers in these
18+
property tests because that only produces values in [-99..99]. Instead we
19+
mix the better generator for AsArbitraryBuiltin Integer and one which
20+
produces Integers up to 400 bits. The name `BigInteger` is maybe slightly
21+
misleading but it has the merit of being relatively short.
22+
-}
23+
arbitraryBigInteger :: Gen Integer
24+
arbitraryBigInteger =
25+
oneof [ unAsArbitraryBuiltin <$> arbitrary
26+
, choose (-b, b)
27+
]
28+
where b = (2::Integer)^(400::Integer)
29+
30+
newtype BigInteger = BigInteger Integer
31+
deriving newtype (Show, Eq, Ord, Num)
32+
33+
instance Arbitrary BigInteger where
34+
arbitrary = BigInteger <$> arbitraryBigInteger
35+
36+
biginteger :: BigInteger -> PlcTerm
37+
biginteger (BigInteger n) = integer n
38+
39+
-- Functions creating terms that are applications of various builtins, for
40+
-- convenience.
41+
42+
addInteger :: PlcTerm -> PlcTerm -> PlcTerm
43+
addInteger a b = mkIterAppNoAnn (builtin () PLC.AddInteger) [a, b]
44+
45+
subtractInteger :: PlcTerm -> PlcTerm -> PlcTerm
46+
subtractInteger a b = mkIterAppNoAnn (builtin () PLC.SubtractInteger) [a, b]
47+
48+
divideInteger :: PlcTerm -> PlcTerm -> PlcTerm
49+
divideInteger a b = mkIterAppNoAnn (builtin () PLC.DivideInteger) [a, b]
50+
51+
modInteger :: PlcTerm -> PlcTerm -> PlcTerm
52+
modInteger a b = mkIterAppNoAnn (builtin () PLC.ModInteger) [a, b]
53+
54+
multiplyInteger :: PlcTerm -> PlcTerm -> PlcTerm
55+
multiplyInteger a b = mkIterAppNoAnn (builtin () PLC.MultiplyInteger) [a, b]
56+
57+
quotientInteger :: PlcTerm -> PlcTerm -> PlcTerm
58+
quotientInteger a b = mkIterAppNoAnn (builtin () PLC.QuotientInteger) [a, b]
59+
60+
remainderInteger :: PlcTerm -> PlcTerm -> PlcTerm
61+
remainderInteger a b = mkIterAppNoAnn (builtin () PLC.RemainderInteger) [a, b]
62+
63+
equalsInteger :: PlcTerm -> PlcTerm -> PlcTerm
64+
equalsInteger a b = mkIterAppNoAnn (builtin () PLC.EqualsInteger) [a, b]
65+
66+
lessThanInteger :: PlcTerm -> PlcTerm -> PlcTerm
67+
lessThanInteger a b = mkIterAppNoAnn (builtin () PLC.LessThanInteger) [a, b]
68+
69+
lessThanEqualsInteger :: PlcTerm -> PlcTerm -> PlcTerm
70+
lessThanEqualsInteger a b = mkIterAppNoAnn (builtin () PLC.LessThanEqualsInteger) [a, b]
71+
72+
le0 :: PlcTerm -> PlcTerm
73+
le0 t = lessThanEqualsInteger t zero
74+
75+
ge0 :: PlcTerm -> PlcTerm
76+
ge0 t = lessThanEqualsInteger zero t
77+
78+
ite
79+
:: forall a
80+
. PLC.DefaultUni `PLC.HasTypeLevel` a
81+
=> PlcTerm -> PlcTerm -> PlcTerm -> PlcTerm
82+
ite b t f =
83+
let ty = mkTyBuiltin @_ @a ()
84+
iteInst = tyInst () (builtin () PLC.IfThenElse) ty
85+
in mkIterAppNoAnn iteInst [b, t, f]
86+
87+
-- Various logical combinations of boolean terms.
88+
89+
abs :: PlcTerm -> PlcTerm
90+
abs t = ite @Integer (lessThanEqualsInteger zero t) t (subtractInteger zero t)
91+
92+
not :: PlcTerm -> PlcTerm
93+
not t = ite @Bool t false true
94+
95+
and :: PlcTerm -> PlcTerm -> PlcTerm
96+
and t1 t2 = ite @Bool t1 (ite @Bool t2 true false) false
97+
98+
or :: PlcTerm -> PlcTerm -> PlcTerm
99+
or t1 t2 = ite @Bool t1 true (ite @Bool t2 true false)
100+
101+
xor :: PlcTerm -> PlcTerm -> PlcTerm
102+
xor t1 t2 = ite @Bool t1 (ite @Bool t2 false true) t2
103+
104+
implies :: PlcTerm -> PlcTerm -> PlcTerm
105+
implies t1 t2 = (not t1) `or` t2
106+
107+
iff :: PlcTerm -> PlcTerm -> PlcTerm
108+
iff t1 t2 = (t1 `implies` t2) `and` (t2 `implies` t1)
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
-- editorconfig-checker-disable
2+
{-# LANGUAGE OverloadedStrings #-}
3+
{-# LANGUAGE ViewPatterns #-}
4+
5+
{- | Property tests for the `divideInteger` and `modInteger` builtins -}
6+
module Evaluation.Builtins.Integer.DivModProperties (test_integer_div_mod_properties)
7+
where
8+
9+
import Evaluation.Builtins.Common
10+
import Evaluation.Builtins.Integer.Common
11+
12+
import Test.Tasty (TestName, TestTree, testGroup)
13+
import Test.Tasty.QuickCheck
14+
15+
numberOfTests :: Int
16+
numberOfTests = 200
17+
18+
testProp :: Testable prop => TestName -> prop -> TestTree
19+
testProp s p = testProperty s $ withMaxSuccess numberOfTests p
20+
21+
-- `divideInteger _ 0` always fails.
22+
prop_div_0_fails :: BigInteger -> Property
23+
prop_div_0_fails (biginteger -> a) =
24+
fails $ divideInteger a zero
25+
26+
-- `modInteger _ 0` always fails.
27+
prop_mod_0_fails :: BigInteger -> Property
28+
prop_mod_0_fails (biginteger -> a) =
29+
fails $ modInteger a zero
30+
31+
-- b /= 0 => a = b * (a `div` b) + (a `mod` b)
32+
-- This is the crucial property relating `divideInteger` and `modInteger`.
33+
prop_div_mod_compatible :: BigInteger -> NonZero BigInteger -> Property
34+
prop_div_mod_compatible (biginteger -> a) (NonZero (biginteger -> b)) =
35+
let t = addInteger (multiplyInteger b (divideInteger a b) ) (modInteger a b)
36+
in evalOkEq t a
37+
38+
-- (k*b) `div` b = b and (k*b) `mod` b = 0 for all k
39+
prop_div_mod_multiple :: BigInteger -> NonZero BigInteger -> Property
40+
prop_div_mod_multiple (biginteger -> k) (NonZero (biginteger -> b)) =
41+
let t1 = divideInteger (multiplyInteger k b) b
42+
t2 = modInteger (multiplyInteger k b) b
43+
in evalOkEq t1 k .&&. evalOkEq t2 zero
44+
45+
-- For fixed b, `modInteger _ b` is an additive homomorphism:
46+
-- (a+a') `mod` b = ((a `mod` b) + (a' `mod` b)) `mod` b
47+
-- Together with prop_div_mod_multiple this means that `mod _ b` is
48+
-- periodic: (a+k*b) `mod` b = a mod b` for all k.
49+
prop_mod_additive :: BigInteger -> BigInteger -> NonZero BigInteger -> Property
50+
prop_mod_additive (biginteger -> a) (biginteger -> a') (NonZero (biginteger -> b)) =
51+
let t1 = modInteger (addInteger a a') b
52+
t2 = modInteger (addInteger (modInteger a b) (modInteger a' b)) b
53+
in evalOkEq t1 t2
54+
55+
-- For fixed b, `modInteger _ b` is a multiplicative homomorphism:
56+
-- (a*a') `mod` b = ((a `mod` b) * (a' `mod` b)) `mod` b
57+
prop_mod_multiplicative :: BigInteger -> BigInteger -> NonZero BigInteger -> Property
58+
prop_mod_multiplicative (biginteger -> a) (biginteger -> a') (NonZero (biginteger -> b)) =
59+
let t1 = modInteger (multiplyInteger a a') b
60+
t2 = modInteger (multiplyInteger (modInteger a b) (modInteger a' b)) b
61+
in evalOkEq t1 t2
62+
63+
-- For b > 0, 0 <= a `mod` b < b;
64+
prop_mod_size_pos :: BigInteger -> Positive BigInteger -> Property
65+
prop_mod_size_pos (biginteger -> a) (Positive (biginteger -> b)) =
66+
let t1 = lessThanEqualsInteger zero (modInteger a b)
67+
t2 = lessThanInteger (modInteger a b) b
68+
in evalOkTrue t1 .&&. evalOkTrue t2
69+
70+
-- For b < 0, b < a `mod` b <= 0
71+
prop_mod_size_neg :: BigInteger -> Negative BigInteger -> Property
72+
prop_mod_size_neg (biginteger -> a) (Negative (biginteger -> b)) =
73+
let t1 = lessThanEqualsInteger (modInteger a b) zero
74+
t2 = lessThanInteger b (modInteger a b)
75+
in evalOkTrue t1 .&&. evalOkTrue t2
76+
77+
-- a >= 0 && b > 0 => a `div` b >= 0 and a `mod` b >= 0
78+
-- a <= 0 && b > 0 => a `div` b <= 0 and a `mod` b >= 0
79+
-- a >= 0 && b < 0 => a `div` b <= 0 and a `mod` b <= 0
80+
-- a < 0 && b < 0 => a `div` b >= 0 and a `mod` b <= 0
81+
82+
prop_div_pos_pos :: (NonNegative BigInteger) -> (Positive BigInteger) -> Property
83+
prop_div_pos_pos (NonNegative (biginteger -> a)) (Positive (biginteger -> b)) =
84+
evalOkTrue $ ge0 (divideInteger a b)
85+
86+
prop_div_neg_pos :: (NonPositive BigInteger) -> (Positive BigInteger) -> Property
87+
prop_div_neg_pos (NonPositive (biginteger -> a)) (Positive (biginteger -> b)) =
88+
evalOkTrue $ le0 (divideInteger a b)
89+
90+
prop_div_pos_neg :: (NonNegative BigInteger) -> (Negative BigInteger) -> Property
91+
prop_div_pos_neg (NonNegative (biginteger -> a)) (Negative (biginteger -> b)) =
92+
evalOkTrue $ le0 (divideInteger a b)
93+
94+
prop_div_neg_neg :: (NonPositive BigInteger) -> (Negative BigInteger) -> Property
95+
prop_div_neg_neg (NonPositive (biginteger -> a)) (Negative (biginteger -> b)) =
96+
evalOkTrue $ ge0 (divideInteger a b)
97+
98+
prop_mod_pos_pos :: (NonNegative BigInteger) -> (Positive BigInteger) -> Property
99+
prop_mod_pos_pos (NonNegative (biginteger -> a)) (Positive (biginteger -> b)) =
100+
evalOkTrue $ ge0 (modInteger a b)
101+
102+
prop_mod_neg_pos :: (NonPositive BigInteger) -> (Positive BigInteger) -> Property
103+
prop_mod_neg_pos (NonPositive (biginteger -> a)) (Positive (biginteger -> b)) =
104+
evalOkTrue $ ge0 (modInteger a b)
105+
106+
prop_mod_pos_neg :: (NonNegative BigInteger) -> (Negative BigInteger) -> Property
107+
prop_mod_pos_neg (NonNegative (biginteger -> a)) (Negative (biginteger -> b)) =
108+
evalOkTrue $ le0 (modInteger a b)
109+
110+
prop_mod_neg_neg :: (NonPositive BigInteger) -> (Negative BigInteger) -> Property
111+
prop_mod_neg_neg (NonPositive (biginteger -> a)) (Negative (biginteger -> b)) =
112+
evalOkTrue $ le0 (modInteger a b)
113+
114+
test_integer_div_mod_properties :: TestTree
115+
test_integer_div_mod_properties =
116+
testGroup "Property tests for divideInteger and modInteger"
117+
[ testProp "divideInteger _ 0 always fails" prop_div_0_fails
118+
, testProp "modInteger _ 0 always fails" prop_mod_0_fails
119+
, testProp "divideInteger and modInteger are compatible" prop_div_mod_compatible
120+
, testProp "divideInteger and modInteger behave sensibly on multiples of the divisor" prop_div_mod_multiple
121+
, testProp "mod is an additive homomorphism" prop_mod_additive
122+
, testProp "mod is a multiplicative homomorphism" prop_mod_multiplicative
123+
, testProp "modInteger size is correct for positive modulus" prop_mod_size_pos
124+
, testProp "modInteger size is correct for negative modulus" prop_mod_size_neg
125+
, testProp "divideInteger (>= 0) (> 0) >= 0" prop_div_pos_pos
126+
, testProp "divideInteger (<= 0) (> 0) <= 0" prop_div_neg_pos
127+
, testProp "divideInteger (>= 0) (< 0) <= 0" prop_div_pos_neg
128+
, testProp "divideInteger (<= 0) (< 0) >= 0" prop_div_neg_neg
129+
, testProp "modInteger (>= 0) (> 0) >= 0 " prop_mod_pos_pos
130+
, testProp "modInteger (>= 0) (< 0) >= 0" prop_mod_neg_pos
131+
, testProp "modInteger (<= 0) (> 0) <= 0" prop_mod_pos_neg
132+
, testProp "modInteger (<= 0) (< 0) <= 0" prop_mod_neg_neg
133+
]
134+

0 commit comments

Comments
 (0)