forked from jdubray/sam-lib
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dieharder.js
125 lines (109 loc) · 2.85 KB
/
dieharder.js
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
114
115
116
117
118
119
120
121
122
123
124
125
// This code is implementing the dieharder TLA+ specification
// https://github.com/tlaplus/Examples/blob/master/specifications/DieHard/DieHarder.pdf
/* eslint-disable no-unused-expressions */
/* eslint-disable no-undef */
const { api, createInstance } = require('./dist/SAM')
const { utils: { E, or } } = require('./dist/SAM')
const { checker } = require('./dist/SAM')
const dieHarder = createInstance({ hasAsyncActions: false, instanceName: 'dieharder' })
const {
addInitialState, addComponent, setRender
} = api(dieHarder)
let checkerIntents = []
// addTimeTraveler()
addInitialState({
n: 2,
jugs: [0, 0],
capacity: [3, 5],
goal: 4
})
const { intents } = addComponent({
actions: [
(j1, j2) => ({
jug2jug: { j1, j2 }, __name: 'jug2jug'
}),
j => ({ empty: j, __name: 'empty' }),
j => ({ fill: j, __name: 'fill' })
],
acceptors: [
state => ({ fill }) => {
if (E(fill) && fill < state.n && fill >= 0) {
state.jugs[fill] = state.capacity[fill]
}
},
state => ({ empty }) => {
if (E(empty) && empty < state.n && empty >= 0) {
state.jugs[empty] = 0
}
},
state => ({ jug2jug }) => {
if (E(jug2jug)) {
const { j1, j2 } = jug2jug
if (j1 !== j2) {
if (E(j1) && j1 < state.n && j1 >= 0
&& E(j2) && j2 < state.n && j2 >= 0) {
const maxAllowed = state.capacity[j2] - state.jugs[j2]
const transfer = Math.min(maxAllowed, state.jugs[j1])
state.jugs[j1] -= transfer
state.jugs[j2] += transfer
}
}
}
}
]
})
setRender((state) => {
const { goal, jugs = [] } = state
const goalReached = jugs.map(content => content === goal).reduce(or, false)
console.log(`Goal: ${goal} [${jugs.map(content => content).join(', ')}]`)
console.log( goalReached ? 'Goal reached!!!' : '')
})
const [
jug2jug,
empty,
fill
] = intents
// fill(1)
// jug2jug(1, 0)
// empty(0)
// jug2jug(1, 0)
// fill(1)
// jug2jug(1, 0)
checkerIntents = [{
intent: fill,
name: 'fill',
values: [
[0],
[1]
]
}, {
intent: empty,
name: 'empty',
values: [
[0],
[1]
]
}, {
intent: jug2jug,
name: 'jug2jug',
values: [
[0, 1],
[1, 0]
]
}
]
checker({
instance: dieHarder,
intents: checkerIntents,
reset: () => {
empty(0)
empty(1)
},
liveness: ({ goal, jugs = [] }) => jugs.map(content => content === goal).reduce(or, false),
safety: ({ jugs = [], capacity = [] }) => jugs.map((content, index) => content > capacity[index]).reduce(or, false),
options: { depthMax: 6, noDuplicateAction: true, doNotStartWith: ['empty', 'jug2jug'] }
}, (behavior) => {
// console.log(`\nthe model checker found this behavior to reach the liveness condition:\n${behavior.join('\n')}\n`)
}, (err) => {
// console.log('The model checker detected a safety condition: ', err)
})