Termination.hs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. {-# LANGUAGE ImplicitParams #-}
  2. module Internal.Termination.Termination ( tests ) where
  3. import Agda.Termination.CutOff
  4. import Agda.Termination.CallGraph
  5. import Agda.Termination.CallMatrix -- hiding (toList)
  6. import Agda.Termination.Order
  7. import Agda.Termination.SparseMatrix
  8. import Agda.Termination.Termination
  9. import Agda.Utils.Either
  10. import Internal.Helpers -- hiding (idempotent)
  11. ------------------------------------------------------------------------
  12. -- Some examples
  13. {- Convention (see TermCheck):
  14. Guardedness flag is in position (0,0) of the matrix,
  15. it is always present even if the functions are all recursive.
  16. The examples below do not include the guardedness flag, though.
  17. -}
  18. -- | The call graph instantiation used by the examples below.
  19. type CG = CallGraph ()
  20. -- | Constructs a call graph. No meta info.
  21. buildCallGraph :: [Call ()] -> CG
  22. buildCallGraph = fromList
  23. -- | The example from the JFP'02 paper.
  24. example1 :: CG
  25. example1 = buildCallGraph [c1, c2, c3]
  26. where
  27. flat = 1
  28. aux = 2
  29. c1 = mkCall' flat aux $ CallMatrix $ fromLists (Size 2 1) [ [lt]
  30. , [lt]]
  31. c2 = mkCall' aux aux $ CallMatrix $ fromLists (Size 2 2) [ [lt, unknown]
  32. , [unknown, le]]
  33. c3 = mkCall' aux flat $ CallMatrix $ fromLists (Size 1 2) [ [unknown, le]]
  34. prop_terminates_example1 :: (?cutoff :: CutOff) => Bool
  35. prop_terminates_example1 = isRight $ terminates example1
  36. -- | An example which is now handled by this algorithm: argument
  37. -- swapping addition.
  38. --
  39. -- @S x + y = S (y + x)@
  40. --
  41. -- @Z + y = y@
  42. example2 :: CG
  43. example2 = buildCallGraph [c]
  44. where
  45. plus = 1
  46. c = mkCall' plus plus $ CallMatrix $ fromLists (Size 2 2) [ [unknown, le]
  47. , [lt, unknown] ]
  48. prop_terminates_example2 :: (?cutoff :: CutOff) => Bool
  49. prop_terminates_example2 = isRight $ terminates example2
  50. -- | A related example which is anyway handled: argument swapping addition
  51. -- using two alternating functions.
  52. --
  53. -- @S x + y = S (y +' x)@
  54. --
  55. -- @Z + y = y@
  56. --
  57. -- @S x +' y = S (y + x)@
  58. --
  59. -- @Z +' y = y@
  60. example3 :: CG
  61. example3 = buildCallGraph [c plus plus', c plus' plus]
  62. where
  63. plus = 1
  64. plus' = 2
  65. c f g = mkCall' f g $ CallMatrix $ fromLists (Size 2 2) [ [unknown, le]
  66. , [lt, unknown] ]
  67. prop_terminates_example3 :: (?cutoff :: CutOff) => Bool
  68. prop_terminates_example3 = isRight $ terminates example3
  69. -- | A contrived example.
  70. --
  71. -- @f (S x) y = f (S x) y + g x y@
  72. --
  73. -- @f Z y = y@
  74. --
  75. -- @g x y = f x y@
  76. --
  77. -- TODO: This example checks that the meta information is reported properly
  78. -- when an error is encountered.
  79. example4 :: CG
  80. example4 = buildCallGraph [c1, c2, c3]
  81. where
  82. f = 1
  83. g = 2
  84. c1 = mkCall' f f $ CallMatrix $ fromLists (Size 2 2) $
  85. [ [le, unknown]
  86. , [unknown, le] ]
  87. c2 = mkCall' f g $ CallMatrix $ fromLists (Size 2 2) $
  88. [ [lt, unknown]
  89. , [unknown, le] ]
  90. c3 = mkCall' g f $ CallMatrix $ fromLists (Size 2 2) $
  91. [ [le, unknown]
  92. , [unknown, le] ]
  93. prop_terminates_example4 :: (?cutoff :: CutOff) => Bool
  94. prop_terminates_example4 = isLeft $ terminates example4
  95. -- | This should terminate.
  96. --
  97. -- @f (S x) (S y) = g x (S y) + f (S (S x)) y@
  98. --
  99. -- @g (S x) (S y) = f (S x) (S y) + g x (S y)@
  100. example5 :: CG
  101. example5 = buildCallGraph [c1, c2, c3, c4]
  102. where
  103. f = 1
  104. g = 2
  105. c1 = mkCall' f g $ CallMatrix $ fromLists (Size 2 2) [ [lt, unknown]
  106. , [unknown, le] ]
  107. c2 = mkCall' f f $ CallMatrix $ fromLists (Size 2 2) [ [unknown, unknown]
  108. , [unknown, lt] ]
  109. c3 = mkCall' g f $ CallMatrix $ fromLists (Size 2 2) [ [le, unknown]
  110. , [unknown, le] ]
  111. c4 = mkCall' g g $ CallMatrix $ fromLists (Size 2 2) [ [lt, unknown]
  112. , [unknown, le] ]
  113. prop_terminates_example5 :: (?cutoff :: CutOff) => Bool
  114. prop_terminates_example5 = isRight $ terminates example5
  115. -- | Another example which should fail.
  116. --
  117. -- @f (S x) = f x + f (S x)@
  118. --
  119. -- @f x = f x@
  120. --
  121. -- TODO: This example checks that the meta information is reported properly
  122. -- when an error is encountered.
  123. example6 :: CG
  124. example6 = buildCallGraph [c1, c2, c3]
  125. where
  126. f = 1
  127. c1 = mkCall' f f $ CallMatrix $ fromLists (Size 1 1) [ [lt] ]
  128. c2 = mkCall' f f $ CallMatrix $ fromLists (Size 1 1) [ [le] ]
  129. c3 = mkCall' f f $ CallMatrix $ fromLists (Size 1 1) [ [le] ]
  130. prop_terminates_example6 :: (?cutoff :: CutOff) => Bool
  131. prop_terminates_example6 = isLeft $ terminates example6
  132. -- See issue 1055.
  133. -- (The following function was adapted from Lee, Jones, and Ben-Amram,
  134. -- POPL '01).
  135. --
  136. -- p : ℕ → ℕ → ℕ → ℕ
  137. -- p m n (succ r) = p m r n
  138. -- p m (succ n) zero = p zero n m
  139. -- p m zero zero = m
  140. example7 :: CG
  141. example7 = buildCallGraph [call1, call2]
  142. where
  143. call1 = mkCall' 1 1 $ CallMatrix $ fromLists (Size 3 3)
  144. [ [le, le, le]
  145. , [un, lt, un]
  146. , [le, un, un]
  147. ]
  148. call2 = mkCall' 1 1 $ CallMatrix $ fromLists (Size 3 3)
  149. [ [le, un, un]
  150. , [un, un, lt]
  151. , [un, le, un]
  152. ]
  153. un = unknown
  154. prop_terminates_example7 :: (?cutoff :: CutOff) => Bool
  155. prop_terminates_example7 = isRight $ terminates example7
  156. ------------------------------------------------------------------------
  157. -- * All tests
  158. ------------------------------------------------------------------------
  159. -- (ASR 2018-01-06) Since some properties use implicit parameters we
  160. -- cannot use 'allProperties' for collecting all the tests (see
  161. -- https://github.com/nick8325/quickcheck/issues/193 ).
  162. tests :: TestTree
  163. tests = testGroup "Internal.Termination.Termination"
  164. [ testProperty "prop_terminates_example1" prop_terminates_example1
  165. , testProperty "prop_terminates_example2" prop_terminates_example2
  166. , testProperty "prop_terminates_example3" prop_terminates_example3
  167. , testProperty "prop_terminates_example4" prop_terminates_example4
  168. , testProperty "prop_terminates_example5" prop_terminates_example5
  169. , testProperty "prop_terminates_example6" prop_terminates_example6
  170. , testProperty "prop_terminates_example7" prop_terminates_example7
  171. ]
  172. where ?cutoff = CutOff 0 -- all these examples are with just lt,le,unknown