Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Week1 #62

Merged
merged 5 commits into from
Sep 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions cis194/week1/zhansongl/credit_card.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import Test.QuickCheck
import Test.Hspec

toDigitsRev :: Integer -> [Integer]
toDigitsRev n
| n >= 0 = digit n
| otherwise = []
where digit 0 = []
digit n = n `mod` 10 : digit (n `div` 10)
hvariant marked this conversation as resolved.
Show resolved Hide resolved

toDigitsRev_prop n = (reverse . joinDigits $ toDigitsRev n) == (showDigits n)
where types = n::Integer
joinDigits xs = foldr (++) "" $ map show xs
showDigits n
| n <= 0 = "" -- weird restriction, but okay
| otherwise = show n

toDigits :: Integer -> [Integer]
toDigits = reverse . toDigitsRev

toDigits_prop n = (joinDigits $ toDigits n) == (showDigits n)
where types = n::Integer
joinDigits xs = foldr (++) "" $ map show xs
showDigits n
| n <= 0 = "" -- weird restriction, but okay
| otherwise = show n

doubleEveryOtherRev :: [Integer] -> [Integer]
doubleEveryOtherRev [] = []
doubleEveryOtherRev [x] = [x]
doubleEveryOtherRev (x:y:xs) = (x:2*y:doubleEveryOtherRev xs)
hvariant marked this conversation as resolved.
Show resolved Hide resolved

doubleEveryOtherRev_prop1 xs = (2 * (sum xs)) == sum (doubleEveryOtherRev $ padList xs)
where types = xs::[Integer]
padList [] = []
padList [x] = [0,x]
padList (x:xs) = (0:x:padList xs)

doubleEveryOtherRev_prop2 xs = (sum xs) == sum (doubleEveryOtherRev $ padList xs)
where types = xs::[Integer]
padList [] = []
padList [x] = [x,0]
padList (x:xs) = (x:0:padList xs)

doubleEveryOtherRev_prop3 xs = (sum xs) == sum (doubleEveryOtherRev $ padList xs)
where types = xs::[Integer]
padList [] = []
padList [x] = [x]
padList (x:xs) = (x:0:padList xs)

doubleEveryOther :: [Integer] -> [Integer]
doubleEveryOther = reverse . doubleEveryOtherRev . reverse
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given we are going to sum the digits after double every other digits from the right, do we need to reverse the list back?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, if the only goal is to finish validate, then I suppose there is no point in reversing the list back. For the sake of this exercise though, if the list is returned in reverse it doesn't satisfy the requirements.


-- [1,2,3] ==> [0,1,0,2,0,3]
doubleEveryOther_prop1 xs = (sum xs) == sum (doubleEveryOther $ padList xs)
where types = xs::[Integer]
padList [] = []
padList [x] = [0,x]
padList (x:xs) = (0:x:padList xs)

-- [1,2,3] ==> [1,0,2,0,3,0]
doubleEveryOther_prop2 xs = (2 * (sum xs)) == sum (doubleEveryOther $ padList xs)
where types = xs::[Integer]
padList [] = []
padList (x:xs) = (x:0:padList xs)

-- [1,2,3] ==> [1,0,2,0,3]
doubleEveryOther_prop3 xs = (sum xs) == sum (doubleEveryOther $ padList xs)
where types = xs::[Integer]
padList [] = []
padList [x] = [x]
padList (x:xs) = (x:0:padList xs)

sumDigits :: [Integer] -> Integer
sumDigits xs = sum . flatten $ map toDigits xs
where flatten = foldr (++) []

genNonNegativeLists :: Gen [Integer]
genNonNegativeLists = suchThat arbitrary nonNegative
where nonNegative [] = True
nonNegative (x:xs) = (x >= 0) && (nonNegative xs)

sumDigits_prop =
forAll genNonNegativeLists $
\xs -> (sumDigits xs) == (sum . numDigits $ strDigits xs)
where strDigits xs = foldr (++) "" $ map show xs
numDigits s
| s == "" = [0]
| otherwise = toDigits $ read s

validate :: Integer -> Bool
validate n = ((sumDigits . doubleEveryOther $ toDigits n) `mod` 10) == 0

main :: IO ()
hvariant marked this conversation as resolved.
Show resolved Hide resolved
main = hspec $ do
describe "Credit card number verification" $ do
describe "toDigits quickchecks" $ do
it "toDigitsRev" $ property $
\x -> toDigitsRev_prop x
it "toDigits" $ property $
\x -> toDigits_prop x
describe "doubleEveryOther quickchecks" $ do
describe "doubleEveryOtherRev with different paddings" $ do
it "[1,2,3] => [0,1,0,2,0,3]" $ property $
\xs -> doubleEveryOtherRev_prop1 xs
it "[1,2,3] => [1,0,2,0,3,0]" $ property $
\xs -> doubleEveryOtherRev_prop2 xs
it "[1,2,3] => [1,0,2,0,3]" $ property $
\xs -> doubleEveryOtherRev_prop3 xs
describe "doubleEveryOther with different paddings" $ do
it "[1,2,3] => [0,1,0,2,0,3]" $ property $
\xs -> doubleEveryOther_prop1 xs
it "[1,2,3] => [1,0,2,0,3,0]" $ property $
\xs -> doubleEveryOther_prop2 xs
it "[1,2,3] => [1,0,2,0,3]" $ property $
\xs -> doubleEveryOther_prop3 xs
describe "sumDigits quickchecks" $ do
it "sumDigits_prop" $ property $ sumDigits_prop
describe "validate works" $ do
it "should return True for valid numbers" $ do
(validate 4012888888881881) `shouldBe` True
it "should return False for invalid numbers" $ do
(validate 4012888888881882) `shouldBe` False


92 changes: 92 additions & 0 deletions cis194/week1/zhansongl/hanoi.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import Test.Hspec

type Peg = String
type Move = (Peg, Peg)

hanoi :: Integer -> Peg -> Peg -> Peg -> [Move]
hanoi 0 _ _ _ = [] -- base case, 0
hanoi 1 a b _ = [(a,b)] -- base case, 1
hanoi n a b c = (hanoi (n-1) a c b) ++ [(a,b)] ++ (hanoi (n-1) c b a)


{-
- Concrete Math Exercise 1.17
- (1) If Wn is the minimum number of moves needed to transfer a tower of n
- disks from one peg to another when there are four pegs instead of three,
- show that
-
- W_{n(n+1)/2} <= 2 * W_{n(n-1)/2} + T_n, n > 0
-
- Proof:
- To move n(n+1)/2 disks from peg a to peg b, we can move n(n-1)/2 disks from a
- to c using b and d as temporary pegs, move the remaining n disks on peg a to
- peg b using peg d as temporary peg (can't use c because of illegal moves), then
- move all disks on peg c to peg b using peg a and d as temporary pegs. This yield
- exactly the above inequality. <>
-
- (2) Use this to find a closed form f(n) such that
-
- W_{n(n+1)/2} <= f(n), n >= 0
-
- Proof:
- Define f as follows:
-
- f(n) = 2*f(n-1) + T_n, n > 0
- f(0) = 0
-
- base case:
- f(0) = 0
- W_0 = 0 <= f(0) = 0
- induction case:
- f(n) = 2*f(n-1) + T_n >= 2*W{n(n-1)/2} + T_n >= W{n(n+1)/2}
-
- closed form of f(n) (left as exercise to the reader) should be:
-
- f(0) = 0
- f(n) = 2^n * (n - 1) + 1, n > 0
-
- <>
-
-
- For arbitrary W_n (n > 2), find the maximum p such that p*(p+1)/2 <= n,
- then
- W_n <= W_{p(p-1)/2} + T_{n-p(p-1)/2}
-
- The frame-stewart conjecture suggests that W_n = W_{p(p-1)/2} + T_{n-p(p-1)/2},
- this was proven in 2014 by this paper:
-
- https://scholar.google.com/scholar?cluster=7454289741749105922
-
-}

hanoi4 :: Integer -> Peg -> Peg -> Peg -> Peg -> [Move]
hanoi4 0 _ _ _ _ = []
hanoi4 n a b c d = (hanoi4 p' a c b d) ++ (hanoi (n - p') a b d) ++ (hanoi4 p' c b a d)
where p' = p * (p - 1) `div` 2
p = floor ((sqrt (fromIntegral (1 + 8 * n)) - 1) / 2)

main :: IO ()
main = hspec $ do
describe "hanoi-3" $ do
it "returns correct moves with 0 disc" $ do
(hanoi 0 "a" "b" "c") `shouldBe` []
it "returns correct moves with 1 disc" $ do
(hanoi 1 "a" "b" "c") `shouldBe` [("a", "b")]
it "returns correct moves with 2 disc" $ do
(hanoi 2 "a" "b" "c") `shouldBe` [("a","c"), ("a","b"), ("c","b")]
it "returns correct moves with 3 disc" $ do
(hanoi 3 "a" "b" "c") `shouldBe` [("a", "b"), ("a", "c"), ("b", "c"),
("a", "b"), ("c", "a"), ("c", "b"),
("a", "b")]
describe "hanoi-4" $ do
it "returns correct moves with 0 disc" $ do
(hanoi4 0 "a" "b" "c" "d") `shouldBe` []
it "returns correct moves with 1 disc" $ do
(hanoi4 1 "a" "b" "c" "d") `shouldBe` [("a", "b")]
it "returns correct number of moves with 2 disc" $ do
(length (hanoi4 2 "a" "b" "c" "d")) `shouldBe` 3
it "returns correct number of moves with 3 disc" $ do
(length (hanoi4 3 "a" "b" "c" "d")) `shouldBe` 5
it "returns correct number of moves with 15 disc" $ do
(length (hanoi4 15 "a" "b" "c" "d")) `shouldBe` 129