summaryrefslogtreecommitdiff
path: root/assignment-12/cashModel.icl
blob: d76b35709ccd72d3b15c0c4ca799d9960c5d9f3b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
implementation module cashModel
/*
	Pieter Koopman, Radboud University, 2017
	pieter@cs.ru.nl
	Advanced programming

	A simple state model for an automated cash register
*/

import StdEnv, GenEq
import gastje
from Data.Func import mapSt
import Data.Tuple

class euro a :: a -> Euro
instance euro Product
where
	euro Pizza = euro (4,99)
	euro Beer  = euro (0,65)
	euro _     = euro 1

instance euro Int where euro e = {euro = e, cent = 0}
instance euro (Int, Int) where euro (e,c) = {euro = e, cent = c}
instance euro [e] | euro e where euro l = sum (map euro l)
instance euro Euro where euro e = e

instance + Euro
where
	+ x y = {euro = c / 100, cent = (abs c) rem 100}
	where
		c = (x.euro + y.euro) * 100 + sign x.euro * x.cent + sign y.euro * y.cent

instance - Euro
where
	- x y = {euro = c / 100, cent = (abs c) rem 100}
	where
		c = (x.euro - y.euro) * 100 + sign x.euro * x.cent - sign y.euro * y.cent

instance zero Euro where zero = {euro = 0, cent = 0}
derive gEq Euro, Product
instance == Product where (==) p q = p === q
instance == Euro where (==) p q = p === q

instance ~ Euro where ~ e = {e & euro = ~e.euro}

model :: [Product] Action -> ([Product],[Euro])
model list (Add p) = ([p:list],[euro p])
model list (Rem p)
| isMember p list = (removeMember p list,[~ (euro p)])
| otherwise       = (list,[])
model list Pay = ([],[euro list])

derive gEq Action
derive gen Euro, Action, Product, []
derive string Euro, Action, Product, []
derive bimap []

/**
 * plus_zero_is_identity:          Fail for: {Euro|euro=0, cent=1} ; {Euro|euro=0, cent=1} <> {Euro|euro=0, cent=0}
 * plus_is_commutative:            Passed
 * minus_zero_is_identity:         Fail for: {Euro|euro=0, cent=1} ; {Euro|euro=0, cent=1} <> {Euro|euro=0, cent=0}
 * prop_double_neg_is_identity:    Passed
 * prop_neg_is_not_identity:       Fail for: {Euro|euro=0, cent=1}
 * prop_minus_is_plus_after_neg:   Fail for: {Euro|euro=1, cent=0} {Euro|euro=-9223372036854775808, cent=1} ; {Euro|euro=0, cent=99} <> {Euro|euro=1, cent=1}
 * prop_neg_distributes_over_plus: Fail for: {Euro|euro=1, cent=0} {Euro|euro=-9223372036854775808, cent=1} ; {Euro|euro=-1, cent=1} <> {Euro|euro=0, cent=99}
 * prop_plus_minus_is_identity:    Fail for: {Euro|euro=0, cent=1} {Euro|euro=0, cent=0} ; {Euro|euro=0, cent=0} <> {Euro|euro=0, cent=1}
 */
Start =
	[ ["plus_zero_is_identity:          ": test prop_plus_zero_is_identity]
	, ["plus_is_commutative:            ": test prop_plus_is_commutative]
	, ["minus_zero_is_identity:         ": test prop_minus_zero_is_identity]
	, ["prop_double_neg_is_identity:    ": test prop_double_neg_is_identity]
	, ["prop_neg_is_not_identity:       ": test prop_neg_is_not_identity]
	, ["prop_minus_is_plus_after_neg:   ": test prop_minus_is_plus_after_neg]
	, ["prop_neg_distributes_over_plus: ": test prop_neg_distributes_over_plus]
	, ["prop_plus_minus_is_identity:    ": test prop_plus_minus_is_identity]
	]
where
	prop_plus_zero_is_identity :: Euro -> Property
	prop_plus_zero_is_identity e = e + zero =.= e

	prop_plus_is_commutative :: Euro Euro -> Property
	prop_plus_is_commutative a b = a + b =.= b + a

	prop_minus_zero_is_identity :: Euro -> Property
	prop_minus_zero_is_identity e = e - zero =.= e

	prop_plus_minus_is_identity :: Euro Euro -> Property
	prop_plus_minus_is_identity a b = a =.= a - b + b

	prop_double_neg_is_identity :: Euro -> Property
	prop_double_neg_is_identity e = ~(~e) =.= e

	prop_neg_is_not_identity :: Euro -> Property
	prop_neg_is_not_identity e = e <> zero ==> e <> ~e

	prop_minus_is_plus_after_neg :: Euro Euro -> Property
	prop_minus_is_plus_after_neg a b = a - b =.= a + ~b

	prop_neg_distributes_over_plus :: Euro Euro -> Property
	prop_neg_distributes_over_plus a b = ~(a + b) =.= ~a + ~b

/**
 * Passed
 *
 * NB: this only checks Rem (as requested). The same property holds for Add.
 * For Pay, the output of the model should be negated.
 */
Start = test fairness
where
	fairness :: [Product] Product -> Property
	fairness ps p = euro newps =.= euro ps + euro out
	where (newps, out) = model ps (Rem p)