sbv-11.1: Documentation/SBV/Examples/KnuckleDragger/CaseSplit.hs
-----------------------------------------------------------------------------
-- |
-- Module : Documentation.SBV.Examples.KnuckleDragger.CaseSplit
-- Copyright : (c) Levent Erkok
-- License : BSD3
-- Maintainer: erkokl@gmail.com
-- Stability : experimental
--
-- Use KnuckleDragger to prove @2n^2 + n + 1@ is never divisible by @3@.
-----------------------------------------------------------------------------
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeAbstractions #-}
{-# OPTIONS_GHC -Wall -Werror #-}
module Documentation.SBV.Examples.KnuckleDragger.CaseSplit where
import Prelude hiding (sum, length)
import Data.SBV
import Data.SBV.Tools.KnuckleDragger
-- | Prove that @2n^2 + n + 1@ is not divisible by @3@.
--
-- We have:
--
-- >>> notDiv3
-- Lemma: case_n_mod_3_eq_0
-- Asms : 1 Q.E.D.
-- Step : 1 Q.E.D.
-- Step : 2 Q.E.D.
-- Step : 3 Q.E.D.
-- Step : 4 Q.E.D.
-- Step : 5 Q.E.D.
-- Result: Q.E.D.
-- Lemma: case_n_mod_3_eq_1
-- Asms : 1 Q.E.D.
-- Step : 1 Q.E.D.
-- Step : 2 Q.E.D.
-- Step : 3 Q.E.D.
-- Step : 4 Q.E.D.
-- Step : 5 Q.E.D.
-- Step : 6 Q.E.D.
-- Result: Q.E.D.
-- Lemma: case_n_mod_3_eq_2
-- Asms : 1 Q.E.D.
-- Step : 1 Q.E.D.
-- Step : 2 Q.E.D.
-- Step : 3 Q.E.D.
-- Step : 4 Q.E.D.
-- Step : 5 Q.E.D.
-- Step : 6 Q.E.D.
-- Result: Q.E.D.
-- Lemma: notDiv3 Q.E.D.
-- [Proven] notDiv3
notDiv3 :: IO Proof
notDiv3 = runKD $ do
let s n = 2 * n * n + n + 1
p n = s n `sEMod` 3 ./= 0
-- Do the proof in 3 phases; one each for the possible value of n `mod` 3 being 0, 1, and 2
-- Note that we use the euclidian definition of division/modulus.
let case0 n = n `sEMod` 3 .== 0
case1 n = n `sEMod` 3 .== 1
case2 n = n `sEMod` 3 .== 2
-- Case 0: n = 0 (mod 3)
c0 <- calc "case_n_mod_3_eq_0"
(\(Forall @"n" n) -> n `sEMod` 3 .== 0 .=> p n) $
\n -> [case0 n] |- s n ? case0 n
=: let w = some "witness" $ \k -> n .== 3*k -- Grab the witness for the case
in s (3*w)
=: s (3*w)
=: 2*(3*w)*(3*w) + 3*w + 1
=: 18*w*w + 3*w + 1
=: 3*(6*w*w + w) + 1
=: qed
-- Case 1: n = 1 (mod 3)
c1 <- calc "case_n_mod_3_eq_1"
(\(Forall @"n" n) -> n `sEMod` 3 .== 1 .=> p n) $
\n -> [case1 n] |- s n ? case1 n
=: let w = some "witness" $ \k -> n .== 3*k+1 -- Grab the witness for n being 1 modulo 3
in s (3*w+1)
=: 2*(3*w+1)*(3*w+1) + (3*w+1) + 1
=: 2*(9*w*w + 3*w + 3*w + 1) + (3*w + 1) + 1
=: 18*w*w + 12*w + 2 + 3*w + 2
=: 18*w*w + 15*w + 4
=: 3*(6*w*w + 5*w + 1) + 1
=: qed
-- Case 2: n = 2 (mod 3)
c2 <- calc "case_n_mod_3_eq_2"
(\(Forall @"n" n) -> n `sEMod` 3 .== 2 .=> p n) $
\n -> [case2 n] |- s n ? case2 n
=: let w = some "witness" $ \k -> n .== 3*k+2 -- Grab the witness for n being 2 modulo 3
in s (3*w+2)
=: 2*(3*w+2)*(3*w+2) + (3*w+2) + 1
=: 2*(9*w*w + 6*w + 6*w + 4) + (3*w + 2) + 1
=: 18*w*w + 24*w + 8 + 3*w + 3
=: 18*w*w + 27*w + 11
=: 3*(6*w*w + 9*w + 3) + 2
=: qed
-- Note that z3 is smart enough to figure out the above cases are complete, so
-- no extra completeness helper is needed.
lemma "notDiv3" (\(Forall @"n" n) -> p n) [c0, c1, c2]