├── .gitignore ├── .ocamlformat ├── .prettierrc ├── CHANGES.md ├── LICENSE.md ├── README.md ├── dscheck.opam ├── dune-project ├── gen_traces.sh ├── src ├── atomic_op.ml ├── dune ├── trace_tracker.ml ├── trace_tracker.mli ├── tracedAtomic.ml └── tracedAtomic.mli └── tests ├── dune ├── gen_program.ml ├── michael_scott_queue.ml ├── report_trace.expected ├── test_commutative.ml ├── test_conditional_nested.ml ├── test_conditional_ssb.ml ├── test_hb.ml ├── test_list.ml ├── test_michael_scott_queue.ml ├── test_naive_counter.ml ├── test_trace.ml └── traces ├── conditional1 ├── conditional2 ├── conditional_nested ├── conditional_ssb ├── list ├── ms_queue └── naive_counter /.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | -------------------------------------------------------------------------------- /.ocamlformat: -------------------------------------------------------------------------------- 1 | profile = default 2 | version = 0.27.0 3 | 4 | exp-grouping=preserve 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "bracketSpacing": false, 4 | "printWidth": 80, 5 | "semi": false, 6 | "singleQuote": true, 7 | "proseWrap": "always" 8 | } -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | ## 0.5.0 2 | 3 | - Add Atomic.make_contended (@lyrm, review : @polytypic) 4 | 5 | ## 0.4.0 6 | 7 | - Made (empty) package available on OCaml 4 (@polytypic, review: @lyrm) 8 | 9 | ## 0.3.0 10 | 11 | - Granular dependency relation (@bartoszmodelski, review: @lyrm) 12 | 13 | ## 0.2.0 14 | 15 | - Source sets (@bartoszmodelski, review: @lyrm, @art-w) 16 | - Better traces (@bartoszmodelski, review: @lyrm, @art-w) 17 | - Test generation (@bartoszmodelski, review: @lyrm) 18 | - New README (@bartoszmodelski, review: @lyrm, @Sudha247) 19 | 20 | ## 0.1.1 21 | 22 | - Fix continuations leak (@bartoszmodelski, review: @polytypic) 23 | 24 | ## 0.1.0 25 | 26 | - Initial experimental release. 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DSCheck — tool for testing concurrent OCaml programs 2 | 3 | Experimental model checker for testing concurrent programs. DSCheck explores 4 | interleavings of a user-provided program and helps ensure that its invariants 5 | are maintained regardless of scheduling decisions. 6 | 7 | # Contents 8 | 9 | 1. [Motivation](#motivation) 10 | 2. [Get DSCheck](#get-dscheck) 11 | 3. [Usage](#usage) 12 | 4. [Development](#development) 13 | 5. [Contributions](#contributions) 14 | 6. [References](#references) 15 | 16 | # Motivation 17 | 18 | As experience shows, fine-grained concurrency is notoriously challenging to get 19 | right. 20 | 21 | - As the program grows, the number of possible interleavings increases 22 | exponentially and quickly becomes too large for a human to reasonably 23 | validate. That's exacerbated by the fact that different interleavings often 24 | lead to the right outcome for different reasons. 25 | 26 | - Certain concurrency bugs manifest rarely and are borderline impossible to 27 | reproduce. They may occur under specific system conditions only and disappear 28 | when a debugging system is attached. 29 | 30 | DSCheck helps manage this complexity by letting us instrument a multicore test 31 | to explore relevant interleavings. Thus ensuring that all terminal states are 32 | valid and no edge cases have been missed. 33 | 34 | # Get DSCheck 35 | 36 | Dscheck can be installed from `opam`: `opam install dscheck`. 37 | 38 | # Usage 39 | 40 | Sample usage on [naive counter](tests/test_naive_counter.ml) is shown below. 41 | 42 | ```ocaml 43 | module Atomic = Dscheck.TracedAtomic 44 | (* the test needs to use DSCheck's atomic module *) 45 | 46 | let test_counter () = 47 | let counter = Atomic.make 0 in 48 | let incr () = Atomic.set counter (Atomic.get counter + 1) in 49 | Atomic.spawn incr; 50 | Atomic.spawn incr; 51 | Atomic.final (fun () -> Atomic.check (fun () -> Atomic.get counter == 2)) 52 | ``` 53 | 54 | The test spawns two domains (`Atomic.spawn`), each trying to increase the 55 | counter. The assertion at the end validates that counter has the expected value 56 | (`Atomic.final`). This is a classic example of a race condition with two threads 57 | trying to perform read-modify-write operation without synchronisation. In 58 | effect, there is a risk of losing one of the updates. DSCheck finds and reports 59 | the offending interleaving to the user: 60 | 61 | ``` 62 | Found assertion violation at run 2: 63 | 64 | sequence 2 65 | ---------------------------------------- 66 | P0 P1 67 | ---------------------------------------- 68 | start 69 | get a 70 | start 71 | get a 72 | set a 73 | set a 74 | ---------------------------------------- 75 | ``` 76 | 77 | ## Validation Soundness 78 | 79 | For model-checking to be sound, tested program must meet the following 80 | conditions: 81 | 82 | - Determinism. Otherwise DSCheck may encounter errors or (more dangerously) 83 | terminate successfuly without exploring all traces. 84 | - Tested programs cannot have races between non-atomic variables. DSCheck does 85 | not explore different _behaviours_, (e.g. a non-atomic read may see the most 86 | recently written value or a number of stale ones). 87 | - Domains can communicate through atomic variables only. Validation including 88 | higher-level synchronisation primitives is possible but constitutes future 89 | work. 90 | - Tested programs have to be at least lock-free. If any thread cannot finish on 91 | its own, DSCheck will explore its transitions ad infinitum. As some remedy, 92 | the space of traces can be covered partially by forcing the test to be 93 | lock-free. For example, spinlock can be modified to fail explicitely once some 94 | artifical limit is reached. 95 | 96 | ## Validation Logic 97 | 98 | As highlighted in the [Motivation](#motivation), the number of interleavings 99 | tends to grow exponentialy with size of the program and the number of threads. 100 | It follows that the interleavings of even small programs are not just impossible 101 | for humans to walk through but also incomputable in reasonable time. 102 | 103 | The key advance that made DSCheck-like model-checkers possible is the emergence 104 | of dynamic partial-order reduction (DPOR) methods. The application to 105 | model-checking stems from the observation that in real-world programs many 106 | interleavings are equivalent and if at least one is covered, so is the entire 107 | equivalence class. More formally, a particular interleaving is a total order 108 | induced by a causal relation between events of different domains 109 | (partial-order). DSCheck aims to cover exactly one interleaving per trace. 110 | 111 | While the DPOR algorithms and formalism tend to be quite involved, the emergent 112 | behavior is intuitive. Consider the following program: 113 | 114 | ```ocaml 115 | let a = Atomic.make 0 in 116 | let b = Atomic.make 0 in 117 | 118 | (* Domain P *) 119 | Domain.spawn (fun () -> 120 | Atomic.set a 1; 121 | Atomic.set b 1; 122 | ) |> ignore; 123 | 124 | (* Domain Q *) 125 | Domain.spawn (fun () -> 126 | Atomic.set a 2; 127 | ) |> ignore 128 | ``` 129 | 130 | There are three possible interleavings: `P.P.Q`, `P.Q.P`, `Q.P.P`. Clearly, the 131 | ordering between _Q_ and the second step of _P_ does not matter. Thus the 132 | execution sequences `P.P.Q` and `P.Q.P` are different realizations of the same 133 | trace. 134 | 135 | DPOR skips the redundant execution sequences and provides an exponential 136 | improvement over the naive search. That in turn significantly expands the 137 | universe of checkable programs and makes this enumeration useful. 138 | 139 | ### Reads 140 | 141 | The leading example showcases reduction of search space based on accesses to 142 | disjoint locations. A similar approach can be taken for accesses on overlapping 143 | locations that do not conflict. If _P_ and _Q_ had only read memory, there would 144 | have been no race between them, in turn requiring DSCheck to explore only a 145 | single interleaving. 146 | 147 | ```ocaml 148 | let a = Atomic.make 0 in 149 | 150 | (* P *) 151 | Atomic.spawn (fun () -> 152 | ignore (Atomic.compare_and_set a 1 2)); 153 | 154 | (* Q *) 155 | Atomic.spawn (fun () -> 156 | ignore (Atomic.compare_and_set a 2 3)); 157 | ``` 158 | 159 | Compare and set is a read-write operation. In this particular case, however, 160 | both CASes fail and leave the initial value untouched. DSCheck recognizes such 161 | special cases and avoids exploration of redundant interleavings. 162 | 163 | ### Causal Ordering 164 | 165 | ```ocaml 166 | let a = Atomic.make 0 in 167 | let b = Atomic.make 0 in 168 | 169 | Atomic.spawn (fun () -> 170 | Atomic.set a 1; (* P *) 171 | Atomic.set b 1; (* Q *)); 172 | 173 | Atomic.spawn (fun () -> 174 | Atomic.set a 2; 175 | Atomic.set b 2); 176 | ``` 177 | 178 | In more general sense, DSCheck tracks causal order between events of different 179 | domains and tries to schedule sequences reversing it, where possible. At times, 180 | it may be counterintuitive. In the example above DSCheck explores 4 181 | interleavings. What if we swap the lines `P` and `Q`? 182 | 183 | # Development 184 | 185 | ## Design Notes 186 | 187 | DSCheck sees test as a graph, where edge is a step in execution of one of the 188 | active domains. The validation involves traversing the graph in a depth-first 189 | fashion and running user assertions in the leaf states. For example, the graph 190 | for [Example Reads](#reads) looks as follows: 191 | 192 | ``` 193 | Start (a=0) ---> P: CompareAndSet a 1 2 ---> Q: CompareAndSet a 2 3 194 | | | 195 | \/ \/ 196 | Q: CompareAndSet a 2 3 ---> P: CompareAndSet a 1 2 ---> Termination (a=0) 197 | ``` 198 | 199 | DSCheck begins by running the main function of the test, which registers domains 200 | P and Q. Then, the exploration starts by taking a step into execution of either 201 | domains and follows with one step of the other, thus arriving at the terminal 202 | state. In the case above both paths are equivalent (hence shared leaf node), but 203 | it naturally does not have to be the case, e.g. variable `a` could be 204 | initialized with `1` making both paths unique traces. Consider the following. 205 | 206 | ``` 207 | Start (a=1) --> P: CompareAndSet a 1 2 --> Q: CompareAndSet a 2 3 208 | | | 209 | | \/ 210 | | Termination (a=3) 211 | \/ 212 | Q: CompareAndSet a 2 3 ---> P: CompareAndSet a 1 2 ---> Termination (a=2) 213 | ``` 214 | 215 | _P_ and _Q_ are dependent operations and the two interleavings lead to different 216 | outcomes. Thus DSCheck has to explore both. Skimming over the details, DSCheck 217 | begins by exploring the first branch, `P.Q`, to the end and schedules new 218 | transitions on the way there. Here, it will notice that _P_ and _Q_ are 219 | potentially racing operations (since it's a read-write pair on the same 220 | location) and schedule transition _Q_ after start. We call that a _race 221 | reversal_. 222 | 223 | The DFS exploration may look tricky at first. The key idea to realize is that at 224 | any step in the sequence, model-checker aims to explore all the traces produced 225 | by remaining events. For some events _c_-_z_ and execution sequence `x.c.w`, 226 | DSCheck has to explore all traces produced by the remaining events. If _g_ and 227 | _h_ are dependent and _A_, _B_ some sequences, it has to explore at least 228 | `x.c.w.A.g.h.B` and `x.c.w.A.h.g.B` (or equivalent). It does so by choosing a 229 | random path and continuously scheduling sequences reversing encountered races. 230 | 231 | The key optimization techniques identify transitions leading to sequences, which 232 | are equal to some already explored ones. 233 | 234 | - Persistent/source sets. A DPOR algorithm has to execute at least all the 235 | transition in the source set at a particular state to ensure that all revelant 236 | interleavings are explored. Once such a set has been explored, there's no need 237 | to explore any other transitions from that state. See 238 | [Comparing Source Sets and Persistent Sets for Partial Order Reduction](https://user.it.uu.se/~bengt/Papers/Full/kimfest17.pdf). 239 | - Sleep sets. At times, we can suspend exploration of a transition until a 240 | relevant event occurs. For example, if _x_ and _c_ are independent and we have 241 | explored `x.c`, there's no need to explore `c.x` unless some other event 242 | (dependent with _x_) occurs. 243 | 244 | ## Testing 245 | 246 | The formalism underpinning DPOR leads to a fairly straightforward testing setup. 247 | For any two execution sentences, they belong to the same trace if reordering of 248 | commutative operations leads from one to the other. For example, operations 249 | `P:(read a), Q:(read b)` clearly commute (since their reordering leads to the 250 | same outcome), while `P:(write a), Q:(write a)` may not. Thus, since one 251 | sequence can be transformed into another, they are realizations of the same 252 | trace. 253 | 254 | Whenever DSCheck explores multiple sequences for a single trace, it constitutes 255 | an inefficiency. Conversely, if a change to the DPOR logic leaves some traces 256 | without unexplored, it is incorrect. Note, the assignment of execution sequences 257 | to traces uses the definition of dependency relation. If the change improves 258 | dependency relation rather than DPOR, we would expect to see new pairs of 259 | equivalent execution sequences and thus groups of multiple traces collapsing 260 | into one. 261 | 262 | To facilitate the testing, DSCheck includes a random test generator and a trace 263 | deduplication mechanism. For any proposed change, we can generate a large number 264 | of tests and ensure that the same traces have been explored. Furthermore, if 265 | reference implementation is suspicious itself or too inefficient, the proposed 266 | change can be asserted to explore a superset of traces explored by a random 267 | search. 268 | 269 | The trace deduplication mechanism took a few iterations to get right. Generally, 270 | the approach involving extracting traces (happens-before) from sequences and 271 | comparing those turned out to be more robust than the attempts to bring the 272 | execution sequences into some normal form and compare directly. 273 | 274 | ## Literature Glossary 275 | 276 | Literature defines a lot of new term. While the rigour is important for 277 | implementation, here's brief explanation in the context of DSCheck. 278 | 279 | - Event. Modification of shared state or communication between threads. 280 | - Transition. One step forward into execution of a particular domain. That 281 | includes the atomic operation it suspended on and all non-atomic operation 282 | precedent the next atomic call. In the case of DSCheck one transition is a 283 | single event. 284 | - Execution sequence. A particular interleaving of a program. 285 | - Trace. A class of equivalent execution sequences. An optimal DPOR explores 286 | exactly one execution sequence per trace. 287 | - Dependency relationship. A binary relation from two transitions to boolean 288 | indicating whether events are dependent. If two adjacent events are 289 | independent, then they commute, i.e. swapping two adjacent independent events 290 | produces a new execution sequence that constitutes the same trace. Thus, DPOR 291 | focuses on reordering pairs of dependent events. 292 | - Happens-before relationship. A superset of dependency relationship, which 293 | includes program order. 294 | - Reversible race. Two events executed by different domains, which are 295 | hb-related directly and not transitively. The latter lets us avoid some 296 | redundant exploration. 297 | - Maximal trace. Trace that terminates all domains. 298 | - Enabling/disabling transitions. Some transitions may enable or disable 299 | transitions in other domains, e.g. domain A taking a lock renders any other 300 | acquisition attempts disabled. Currently not implemented but worth mentioning 301 | as it is often used in the literature. 302 | 303 | ## Future Work 304 | 305 | - DSCheck was written with validation of lock-free structures in mind and 306 | handles single-word atomic operations only. There is a wealth of other 307 | thread-safe communication and synchroniation methods and, in principle, we 308 | should be able to validate all of them. 309 | - Non-atomic accesses. OCaml's memory model gives a precise semantics to 310 | concurrent non-atomic accesses. These could be verified with DSCheck as 311 | well. The key part seems to be the possibility of reading a stale value. 312 | Thus, DSCheck should maintain the list of values that may be read from any 313 | non-atomic location and ensure that program works in all cases. See 314 | [CDSCHECKER: Checking Concurrent Data Structures Written with C/C++ Atomics](http://plrg.eecs.uci.edu/publications/c11modelcheck.pdf) 315 | for more details. 316 | - High-level primitives, e.g. lock, channel, join. Currently, DSCheck cannot 317 | terminate on any program weaker than lock-free. Blocking primitives need 318 | explicit support. Section 5 319 | [Source Sets: A Foundation for Optimal Dynamic Partial Order Reduction](https://user.it.uu.se/~parosh/publications/papers/jacm17.pdf) 320 | includes a modification of Source- and Optimal-DPOR allowing blocking 321 | operations. 322 | - Kcas, to validate programs using 323 | [kcas](https://github.com/ocaml-multicore/kcas) efficiently. That fits into 324 | Source-DPOR and the existing implementation quite naturally as operations 325 | with multiple happens-before and happens-after dependencies. 326 | - Further performance improvements. In particular implementation of wake-up 327 | trees to eliminate sleep-set blocking or a leap towards 328 | [TruST](https://plv.mpi-sws.org/genmc/popl2022-trust.pdf). 329 | - Support nested domain spawns and concurrent logic in the main test function. 330 | DSCheck lets us spawn n domains in the main test function and validate their 331 | interleavings, which is enough to test lock-free algorithms, but many 332 | real-world programs are more complicated. 333 | 334 | ## Reference Implementations 335 | 336 | - [CDSChecker](https://github.com/computersforpeace/model-checker) for the 337 | original DPOR implementation. 338 | - [Nidhugg](https://github.com/nidhugg/nidhugg/) for Source-DPOR. 339 | 340 | ### Nidhugg 341 | 342 | Nidhugg may come helpful for troubleshooting DSCheck. It's based on the same 343 | publication and, although aimed at C/C++ programs, it does have sequential 344 | consistency mode as well. Install it as per instructions in the repository. 345 | 346 | Consider the following program. It spawns two threads, each trying to increment 347 | `a` with CAS. Note making the variable `zero` local reduces accesses to shared 348 | memory and lowers the amount of noise. 349 | 350 | ```C 351 | #include 352 | #include 353 | 354 | atomic_int a; 355 | 356 | static void *t(void *arg) 357 | { 358 | int zero = 0; 359 | atomic_compare_exchange_strong(&a, &zero, 1); 360 | return NULL; 361 | } 362 | 363 | int main() 364 | { 365 | pthread_t tid[2]; 366 | atomic_init(&a, 0); 367 | 368 | pthread_create(&tid[0], NULL, t, (void *)(uintptr_t)0); 369 | pthread_create(&tid[1], NULL, t, (void *)(uintptr_t)0); 370 | 371 | pthread_join(tid[0], NULL); 372 | pthread_join(tid[1], NULL); 373 | 374 | return 0; 375 | } 376 | ``` 377 | 378 | Save the program as `test.c` and run using the following command: 379 | `nidhuggc -- --debug-print-on-reset --sc --source test.c 2>&1 | rg "(Cmp|=)"`. 380 | 381 | ``` 382 | === TSOTraceBuilder reset === 383 | (<0.0>,1-4) CmpXhg(Global(1)(4),0x0,0x1) SLP:{} - (<0.1>,1) 384 | (<0.1>,1-5) CmpXhgFail(Global(1)(4),0x0,0x1) SLP:{} 385 | ============================= 386 | === TSOTraceBuilder reset === 387 | (<0.1>,1) CmpXhg(Global(1)(4),0x0,0x1) SLP:{<0.0>} 388 | (<0.0>,1-5) CmpXhgFail(Global(1)(4),0x0,0x1) SLP:{} 389 | ============================= 390 | ``` 391 | 392 | The output shows visited interleavings. It also displays contents of the `sleep` 393 | and `backtrack` sets at any stage. To get a better understanding of how nidhugg 394 | takes particular decision consider adding log statements to 395 | `TSOTraceBuilder::race_detect`. 396 | 397 | # Contributions 398 | 399 | Contributions are appreciated! Please create issues/PRs to this repo. 400 | 401 | # References 402 | 403 | - [Source Sets: A Foundation for Optimal Dynamic Partial Order Reduction](https://user.it.uu.se/~parosh/publications/papers/jacm17.pdf) 404 | - [CDSCHECKER: Checking Concurrent Data Structures Written with C/C++ Atomics](http://plrg.eecs.uci.edu/publications/c11modelcheck.pdf) 405 | - [Dynamic Partial-Order Reduction for Model Checking Software](https://users.soe.ucsc.edu/~cormac/papers/popl05.pdf) 406 | - [Comparing Source Sets and Persistent Sets for Partial Order Reduction](https://user.it.uu.se/~bengt/Papers/Full/kimfest17.pdf) 407 | - [Truly Stateless, Optimal Dynamic Partial Order Reduction](https://plv.mpi-sws.org/genmc/popl2022-trust.pdf) 408 | -------------------------------------------------------------------------------- /dscheck.opam: -------------------------------------------------------------------------------- 1 | # This file is generated by dune, edit dune-project instead 2 | opam-version: "2.0" 3 | synopsis: "Traced Atomics" 4 | maintainer: ["Sadiq Jaffer"] 5 | authors: ["Sadiq Jaffer"] 6 | license: "ISC" 7 | homepage: "https://github.com/ocaml-multicore/dscheck" 8 | bug-reports: "https://github.com/ocaml-multicore/dscheck/issues" 9 | depends: [ 10 | "ocaml" {>= "4.12.0"} 11 | "dune" {>= "3.9"} 12 | "containers" 13 | "tsort" 14 | "oseq" 15 | "alcotest" {>= "1.6.0" & with-test} 16 | "cmdliner" 17 | "odoc" {with-doc} 18 | ] 19 | build: [ 20 | ["dune" "subst"] {dev} 21 | [ 22 | "dune" 23 | "build" 24 | "-p" 25 | name 26 | "-j" 27 | jobs 28 | "@install" 29 | "@runtest" {with-test} 30 | "@doc" {with-doc} 31 | ] 32 | ] 33 | dev-repo: "git+https://github.com/ocaml-multicore/dscheck.git" 34 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 3.9) 2 | 3 | (generate_opam_files true) 4 | 5 | (name dscheck) 6 | 7 | (source 8 | (github ocaml-multicore/dscheck)) 9 | 10 | (license ISC) 11 | 12 | (authors "Sadiq Jaffer") 13 | 14 | (maintainers "Sadiq Jaffer") 15 | 16 | (package 17 | (name dscheck) 18 | (synopsis "Traced Atomics") 19 | (allow_empty) 20 | (depends 21 | (ocaml 22 | (>= 4.12.0)) 23 | dune 24 | containers 25 | tsort 26 | oseq 27 | (alcotest 28 | (and 29 | (>= 1.6.0) 30 | :with-test)) 31 | cmdliner)) 32 | -------------------------------------------------------------------------------- /gen_traces.sh: -------------------------------------------------------------------------------- 1 | set -eux pipefail 2 | 3 | dscheck_trace_file="tests/traces/ms_queue" dune exec tests/test_michael_scott_queue.exe 4 | dscheck_trace_file="tests/traces/naive_counter" dune exec tests/test_naive_counter.exe 5 | dscheck_trace_file="tests/traces/list" dune exec tests/test_list.exe 6 | dscheck_trace_file="tests/traces/conditional_nested" dune exec tests/test_conditional_nested.exe 7 | dscheck_trace_file="tests/traces/conditional_ssb" dune exec tests/test_conditional_ssb.exe -------------------------------------------------------------------------------- /src/atomic_op.ml: -------------------------------------------------------------------------------- 1 | type t = 2 | | Start 3 | | Make 4 | | Get 5 | | Set 6 | | Exchange 7 | | CompareAndSwap of 8 | [ `Success | `Fail | `Unknown of unit -> [ `Success | `Fail ] ] ref 9 | | FetchAndAdd 10 | 11 | let to_str x = 12 | match x with 13 | | Start -> "start" 14 | | Make -> "make" 15 | | Get -> "get" 16 | | Set -> "set" 17 | | Exchange -> "exchange" 18 | | CompareAndSwap _ -> "compare_and_swap" 19 | | FetchAndAdd -> "fetch_and_add" 20 | 21 | let is_write ?(allow_unknown = false) op = 22 | (* [allow_unknown] is a switch that lets enabled evaluation of undetermined operations. 23 | 24 | We sometimes need to predict the outcome while DSCheck is running, since it stops right 25 | before the operation executes. Elsewhere, e.g. trace_tracker, we operatore on the true 26 | history of terminated execution and should never need this. 27 | *) 28 | match op with 29 | | Get | Make -> false 30 | | CompareAndSwap outcome -> begin 31 | match !outcome with 32 | | `Success -> true 33 | | `Fail -> false 34 | | `Unknown currently_f -> begin 35 | assert allow_unknown; 36 | match currently_f () with `Success -> true | `Fail -> false 37 | end 38 | end 39 | | _ -> true 40 | 41 | let weak_cmp t1 t2 = 42 | let to_int = function 43 | | Start -> 0 44 | | Make -> 1 45 | | Get -> 2 46 | | Set -> 3 47 | | Exchange -> 4 48 | | FetchAndAdd -> 5 49 | | CompareAndSwap _ -> assert false 50 | in 51 | 52 | match (t1, t2) with 53 | | CompareAndSwap _, CompareAndSwap _ -> true 54 | | CompareAndSwap _, _ | _, CompareAndSwap _ -> false 55 | | _, _ -> to_int t1 = to_int t2 56 | -------------------------------------------------------------------------------- /src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (public_name dscheck) 3 | (enabled_if 4 | (>= %{ocaml_version} 5)) 5 | (libraries containers oseq)) 6 | -------------------------------------------------------------------------------- /src/trace_tracker.ml: -------------------------------------------------------------------------------- 1 | module Op = struct 2 | type t = { proc : int; variable : int; step : int; atomic_op : Atomic_op.t } 3 | 4 | let is_dependent t1 t2 = 5 | t1.variable == t2.variable 6 | && (Atomic_op.is_write t1.atomic_op || Atomic_op.is_write t2.atomic_op) 7 | 8 | let compare_proc_step t1 t2 = 9 | let c1 = Int.compare t1.proc t2.proc in 10 | if c1 <> 0 then c1 else Int.compare t1.step t2.step 11 | 12 | let to_str t = 13 | let rw = if Atomic_op.is_write t.atomic_op then "w" else "r" in 14 | Printf.sprintf "(%d,%c,%s)" t.proc (Char.chr (t.variable + 96)) rw 15 | end 16 | 17 | module OpSet = struct 18 | include Set.Make (struct 19 | include Op 20 | 21 | let compare = compare_proc_step 22 | end) 23 | 24 | let to_str t = 25 | to_seq t |> List.of_seq |> List.map Op.to_str |> String.concat ", " 26 | |> Printf.sprintf "(%s)" 27 | end 28 | 29 | module Trace = struct 30 | module Key = struct 31 | type t = (Op.t * OpSet.t) list 32 | 33 | let compare (t1 : t) t2 = 34 | List.compare 35 | (fun (op1, dep1) (op2, dep2) -> 36 | let c1 = Op.compare_proc_step op1 op2 in 37 | if c1 <> 0 then c1 else OpSet.compare dep1 dep2) 38 | t1 t2 39 | end 40 | 41 | type t = Op.t List.t 42 | 43 | let of_schedule_for_checks schedule_for_checks : t = 44 | let steps = Hashtbl.create 10 in 45 | List.map 46 | (fun (proc, atomic_op, variable) -> 47 | Option.map 48 | (fun variable : Op.t -> 49 | let current = 50 | Hashtbl.find_opt steps proc |> Option.value ~default:0 51 | in 52 | Hashtbl.replace steps proc (current + 1); 53 | let step = Hashtbl.find steps proc in 54 | 55 | { proc; variable; step; atomic_op }) 56 | variable) 57 | schedule_for_checks 58 | |> List.filter_map Fun.id 59 | 60 | let to_string t = List.map Op.to_str t |> String.concat "," 61 | 62 | let tag_with_deps (t : t) : Key.t = 63 | let next_dep op tl = 64 | let _, deps = 65 | List.fold_left 66 | (fun (seen_transitive, deps) curr_op -> 67 | if seen_transitive then (true, deps) 68 | else if 69 | Option.is_some 70 | (OpSet.find_first_opt (Op.is_dependent curr_op) deps) 71 | then (true, deps) 72 | else if Op.is_dependent op curr_op then 73 | (false, OpSet.add curr_op deps) 74 | else (false, deps)) 75 | (false, OpSet.empty) tl 76 | in 77 | deps 78 | in 79 | let rec attach_deps t = 80 | match t with 81 | | [] -> [] 82 | | hd :: [] -> [ (hd, OpSet.empty) ] 83 | | hd :: tl -> (hd, next_dep hd tl) :: attach_deps tl 84 | in 85 | let tagged = attach_deps t in 86 | List.sort (fun (op1, _) (op2, _) -> Op.compare_proc_step op1 op2) tagged 87 | 88 | let deps_to_str (key : Key.t) : string = 89 | List.map (fun (op, deps) -> Op.to_str op ^ "-" ^ OpSet.to_str deps) key 90 | |> String.concat "," 91 | end 92 | 93 | module TraceMap = Map.Make (Trace.Key) 94 | 95 | type t = Trace.t TraceMap.t 96 | 97 | let traces = ref TraceMap.empty 98 | 99 | let add_trace trace = 100 | let trace = Trace.of_schedule_for_checks trace in 101 | let key = Trace.tag_with_deps trace in 102 | traces := 103 | TraceMap.update key 104 | (function Some v -> Some v | None -> Some trace) 105 | !traces 106 | 107 | let print traces channel = 108 | Printf.fprintf channel "----\n"; 109 | TraceMap.iter 110 | (fun _ trace -> Printf.fprintf channel "%s\n" (Trace.to_string trace)) 111 | traces; 112 | Printf.fprintf channel "----\n"; 113 | flush channel 114 | 115 | let print_traces chan = print !traces chan 116 | let clear_traces () = traces := TraceMap.empty 117 | let get_traces () = !traces 118 | 119 | let get_deps_str traces = 120 | TraceMap.to_seq traces |> List.of_seq 121 | |> List.map (fun (_, value) -> value) 122 | |> List.map Trace.tag_with_deps 123 | |> List.map Trace.deps_to_str |> String.concat "\n" 124 | 125 | let equal t1 t2 = 126 | TraceMap.compare 127 | (fun _ _ -> 128 | 0 129 | (* any values under the same key are known to be equivalent, even if the exact sequence is not identical *)) 130 | t1 t2 131 | == 0 132 | 133 | let subset t1 t2 = 134 | TraceMap.fold (fun key _ seen_all -> TraceMap.mem key t2 && seen_all) t1 true 135 | 136 | let count = TraceMap.cardinal 137 | -------------------------------------------------------------------------------- /src/trace_tracker.mli: -------------------------------------------------------------------------------- 1 | type t 2 | 3 | val add_trace : (int * Atomic_op.t * int option) list -> unit 4 | val clear_traces : unit -> unit 5 | val get_traces : unit -> t 6 | val print_traces : out_channel -> unit 7 | val print : t -> out_channel -> unit 8 | val equal : t -> t -> bool 9 | val get_deps_str : t -> string 10 | val subset : t -> t -> bool 11 | val count : t -> int 12 | -------------------------------------------------------------------------------- /src/tracedAtomic.ml: -------------------------------------------------------------------------------- 1 | open Effect 2 | open Effect.Shallow 3 | 4 | type 'a t = 'a Atomic.t * int 5 | 6 | type _ Effect.t += 7 | | Make : 'a -> 'a t Effect.t 8 | | Get : 'a t -> 'a Effect.t 9 | | Set : ('a t * 'a) -> unit Effect.t 10 | | Exchange : ('a t * 'a) -> 'a Effect.t 11 | | CompareAndSwap : ('a t * 'a * 'a) -> bool Effect.t 12 | | FetchAndAdd : (int t * int) -> int Effect.t 13 | 14 | module IntSet = Set.Make (Int) 15 | module IntMap = Map.Make (Int) 16 | 17 | let _string_of_set s = IntSet.fold (fun y x -> string_of_int y ^ "," ^ x) s "" 18 | let tracing = ref false 19 | let finished_processes = ref 0 20 | 21 | type process_data = { 22 | mutable next_op : Atomic_op.t; 23 | mutable next_repr : int option; 24 | mutable resume_func : (unit, unit) handler -> unit; 25 | mutable finished : bool; 26 | mutable discontinue_f : unit -> unit; 27 | } 28 | 29 | let every_func = ref (fun () -> ()) 30 | let final_func = ref (fun () -> ()) 31 | 32 | (* Atomics implementation *) 33 | let atomics_counter = ref 1 34 | 35 | let make v = 36 | if !tracing then perform (Make v) 37 | else 38 | let i = !atomics_counter in 39 | atomics_counter := !atomics_counter + 1; 40 | (Atomic.make v, i) 41 | 42 | let make_contended = make 43 | 44 | let get r = 45 | if !tracing then perform (Get r) else match r with v, _ -> Atomic.get v 46 | 47 | let set r v = 48 | if !tracing then perform (Set (r, v)) 49 | else match r with x, _ -> Atomic.set x v 50 | 51 | let exchange r v = 52 | if !tracing then perform (Exchange (r, v)) 53 | else match r with x, _ -> Atomic.exchange x v 54 | 55 | let compare_and_set r seen v = 56 | if !tracing then perform (CompareAndSwap (r, seen, v)) 57 | else match r with x, _ -> Atomic.compare_and_set x seen v 58 | 59 | let fetch_and_add r n = 60 | if !tracing then perform (FetchAndAdd (r, n)) 61 | else match r with x, _ -> Atomic.fetch_and_add x n 62 | 63 | let incr r = ignore (fetch_and_add r 1) 64 | let decr r = ignore (fetch_and_add r (-1)) 65 | 66 | (* Tracing infrastructure *) 67 | 68 | exception Terminated_early 69 | 70 | let discontinue k () = 71 | discontinue_with k Terminated_early 72 | { 73 | retc = (fun _ -> ()); 74 | exnc = (function Terminated_early -> () | _e -> ()); 75 | effc = (fun (type a) (_ : a Effect.t) -> None); 76 | } 77 | 78 | let processes = CCVector.create () 79 | 80 | let update_process_data process_id f op repr k = 81 | let process_rec = CCVector.get processes process_id in 82 | process_rec.resume_func <- f; 83 | process_rec.next_repr <- repr; 84 | process_rec.next_op <- op; 85 | process_rec.discontinue_f <- discontinue k 86 | 87 | let finish_process process_id = 88 | let process_rec = CCVector.get processes process_id in 89 | process_rec.finished <- true; 90 | process_rec.discontinue_f <- (fun () -> ()); 91 | finished_processes := !finished_processes + 1 92 | 93 | let handler current_process_id runner = 94 | { 95 | retc = 96 | (fun _ -> 97 | finish_process current_process_id; 98 | runner ()); 99 | exnc = (fun s -> raise s); 100 | effc = 101 | (fun (type a) (e : a Effect.t) -> 102 | match e with 103 | | Make v -> 104 | Some 105 | (fun (k : (a, _) continuation) -> 106 | let i = !atomics_counter in 107 | let m = (Atomic.make v, i) in 108 | atomics_counter := !atomics_counter + 1; 109 | update_process_data current_process_id 110 | (fun h -> continue_with k m h) 111 | Make (Some i) k; 112 | runner ()) 113 | | Get (v, i) -> 114 | Some 115 | (fun (k : (a, _) continuation) -> 116 | update_process_data current_process_id 117 | (fun h -> continue_with k (Atomic.get v) h) 118 | Get (Some i) k; 119 | runner ()) 120 | | Set ((r, i), v) -> 121 | Some 122 | (fun (k : (a, _) continuation) -> 123 | update_process_data current_process_id 124 | (fun h -> continue_with k (Atomic.set r v) h) 125 | Set (Some i) k; 126 | runner ()) 127 | | Exchange ((a, i), b) -> 128 | Some 129 | (fun (k : (a, _) continuation) -> 130 | update_process_data current_process_id 131 | (fun h -> continue_with k (Atomic.exchange a b) h) 132 | Exchange (Some i) k; 133 | runner ()) 134 | | CompareAndSwap ((x, i), s, v) -> 135 | Some 136 | (fun (k : (a, _) continuation) -> 137 | let rec status = 138 | ref 139 | (`Unknown 140 | (fun () -> 141 | let result = Atomic.get x == s in 142 | status := if result then `Success else `Fail; 143 | if result then `Success else `Fail)) 144 | in 145 | update_process_data current_process_id 146 | (fun h -> 147 | continue_with k 148 | ((match !status with 149 | | `Success | `Fail -> 150 | failwith "this result has been predicted" 151 | | `Unknown _ -> ()); 152 | let result = Atomic.compare_and_set x s v in 153 | status := if result then `Success else `Fail; 154 | result) 155 | h) 156 | (Atomic_op.CompareAndSwap status) (Some i) k; 157 | runner ()) 158 | | FetchAndAdd ((v, i), x) -> 159 | Some 160 | (fun (k : (a, _) continuation) -> 161 | update_process_data current_process_id 162 | (fun h -> continue_with k (Atomic.fetch_and_add v x) h) 163 | FetchAndAdd (Some i) k; 164 | runner ()) 165 | | _ -> None); 166 | } 167 | 168 | let spawn f = 169 | let fiber_f = fiber f in 170 | let resume_func = continue_with fiber_f () in 171 | CCVector.push processes 172 | { 173 | next_op = Start; 174 | next_repr = None; 175 | resume_func; 176 | finished = false; 177 | discontinue_f = discontinue fiber_f; 178 | } 179 | 180 | let rec last_element l = 181 | match l with h :: [] -> h | [] -> assert false | _ :: tl -> last_element tl 182 | 183 | type proc_rec = { proc_id : int; op : Atomic_op.t; obj_ptr : int option } 184 | 185 | type state_cell = { 186 | procs : proc_rec list; 187 | run_proc : int; 188 | run_op : Atomic_op.t; 189 | run_ptr : int option; 190 | enabled : IntSet.t; 191 | mutable backtrack : IntSet.t; 192 | } 193 | 194 | let sleep_set_blocked = ref 0 195 | let num_states = ref 0 196 | let num_interleavings = ref 0 197 | 198 | (* we stash the current state in case a check fails and we need to log it *) 199 | let schedule_for_checks = ref [] 200 | 201 | let var_name i = 202 | match i with 203 | | None -> "" 204 | | Some i -> 205 | let c = Char.chr (i + 96) in 206 | Printf.sprintf "%c" c 207 | 208 | let print_execution_sequence chan = 209 | let highest_proc = 210 | List.fold_left 211 | (fun highest (curr_proc, _, _) -> 212 | if curr_proc > highest then curr_proc else highest) 213 | (-1) !schedule_for_checks 214 | in 215 | 216 | let bar = 217 | List.init ((highest_proc * 20) + 20) (fun _ -> "-") |> String.concat "" 218 | in 219 | Printf.fprintf chan "\nsequence %d\n" !num_interleavings; 220 | Printf.fprintf chan "%s\n" bar; 221 | List.init (highest_proc + 1) (fun proc -> 222 | Printf.fprintf chan "P%d\t\t\t" proc) 223 | |> ignore; 224 | Printf.fprintf chan "\n%s\n" bar; 225 | 226 | List.iter 227 | (fun s -> 228 | match s with 229 | | last_run_proc, last_run_op, last_run_ptr -> 230 | let last_run_ptr = var_name last_run_ptr in 231 | let tabs = 232 | List.init last_run_proc (fun _ -> "\t\t\t") |> String.concat "" 233 | in 234 | Printf.fprintf chan "%s%s %s\n" tabs 235 | (Atomic_op.to_str last_run_op) 236 | last_run_ptr) 237 | !schedule_for_checks; 238 | Printf.fprintf chan "%s\n%!" bar 239 | 240 | let interleavings_chan = (ref None : out_channel option ref) 241 | let record_traces_flag = ref false 242 | 243 | let do_run init_func init_schedule = 244 | init_func (); 245 | (*set up run *) 246 | tracing := true; 247 | schedule_for_checks := init_schedule; 248 | (* cache the number of processes in case it's expensive*) 249 | let num_processes = CCVector.length processes in 250 | (* current number of ops we are through the current run *) 251 | finished_processes := 0; 252 | let rec run_trace s true_schedule_rev () = 253 | tracing := false; 254 | !every_func (); 255 | tracing := true; 256 | match s with 257 | | [] -> 258 | if !finished_processes == num_processes then begin 259 | tracing := false; 260 | 261 | num_interleavings := !num_interleavings + 1; 262 | 263 | if !record_traces_flag then 264 | Trace_tracker.add_trace (List.rev true_schedule_rev); 265 | 266 | (match !interleavings_chan with 267 | | None -> () 268 | | Some chan -> print_execution_sequence chan); 269 | 270 | !final_func (); 271 | tracing := true 272 | end 273 | | (process_id_to_run, next_op, next_ptr) :: schedule -> begin 274 | if !finished_processes == num_processes then 275 | (* this should never happen *) 276 | failwith "no enabled processes" 277 | else 278 | let process_to_run = CCVector.get processes process_id_to_run in 279 | let at = process_to_run.next_op in 280 | assert (Atomic_op.weak_cmp process_to_run.next_op next_op); 281 | assert (process_to_run.next_repr = next_ptr); 282 | 283 | let true_schedule_rev = 284 | (process_id_to_run, process_to_run.next_op, process_to_run.next_repr) 285 | :: true_schedule_rev 286 | in 287 | 288 | process_to_run.resume_func 289 | (handler process_id_to_run (run_trace schedule true_schedule_rev)); 290 | match at with 291 | | CompareAndSwap cas -> begin 292 | match !cas with `Unknown _ -> assert false | _ -> () 293 | end 294 | | _ -> () 295 | end 296 | in 297 | tracing := true; 298 | run_trace init_schedule [] (); 299 | 300 | finished_processes := 0; 301 | tracing := false; 302 | num_states := !num_states + 1; 303 | let procs = 304 | CCVector.mapi 305 | (fun i p -> { proc_id = i; op = p.next_op; obj_ptr = p.next_repr }) 306 | processes 307 | |> CCVector.to_list 308 | in 309 | let current_enabled = 310 | CCVector.to_seq processes |> OSeq.zip_index 311 | |> Seq.filter (fun (_, proc) -> not proc.finished) 312 | |> Seq.map (fun (id, _) -> id) 313 | |> IntSet.of_seq 314 | in 315 | CCVector.iter (fun proc -> proc.discontinue_f ()) processes; 316 | CCVector.clear processes; 317 | atomics_counter := 1; 318 | match last_element init_schedule with 319 | | run_proc, run_op, run_ptr -> 320 | { 321 | procs; 322 | enabled = current_enabled; 323 | run_proc; 324 | run_op; 325 | run_ptr; 326 | backtrack = IntSet.empty; 327 | } 328 | 329 | let rec explore_random func state = 330 | let s = last_element state in 331 | let enabled = IntSet.to_seq s.enabled |> List.of_seq in 332 | let len = List.length enabled in 333 | if len == 0 then () 334 | else 335 | let random_index = Random.int len in 336 | let j = List.nth enabled random_index in 337 | let j_proc = List.nth s.procs j in 338 | let schedule = 339 | List.map (fun s -> (s.run_proc, s.run_op, s.run_ptr)) state 340 | @ [ (j, j_proc.op, j_proc.obj_ptr) ] 341 | in 342 | let statedash = state @ [ do_run func schedule ] in 343 | explore_random func statedash 344 | 345 | let same_proc state_cell1 state_cell2 = 346 | state_cell1.run_proc = state_cell2.run_proc 347 | 348 | module Causality = struct 349 | let hb ((proc1 : int), (ptr1 : int option), op1) 350 | ((proc2 : int), (ptr2 : int option), op2) = 351 | (* assumes the two ops are adjacent *) 352 | let same_proc = proc1 = proc2 in 353 | let same_var = 354 | match (ptr1, ptr2) with 355 | | Some ptr1, Some ptr2 -> ptr1 = ptr2 356 | | Some _, None | None, Some _ | None, None -> false 357 | in 358 | let conflicting = 359 | let is_write = Atomic_op.is_write ~allow_unknown:true in 360 | Lazy.from_val (is_write op1 || is_write op2) 361 | in 362 | same_proc || (same_var && Lazy.force conflicting) 363 | 364 | let happens_before = function 365 | | `State (state_cell1, state_cell2) -> 366 | hb 367 | (state_cell1.run_proc, state_cell1.run_ptr, state_cell1.run_op) 368 | (state_cell2.run_proc, state_cell2.run_ptr, state_cell2.run_op) 369 | | `Proc (proc1, proc2) -> 370 | hb 371 | (proc1.proc_id, proc1.obj_ptr, proc1.op) 372 | (proc2.proc_id, proc2.obj_ptr, proc2.op) 373 | 374 | let mark_happen_before operation (sequence : state_cell list) = 375 | let sequence = List.map (fun v -> (v, ref false)) sequence in 376 | let sequence = (operation, ref true) :: sequence in 377 | let mark_intransitive hd_sequence tl_sequence = 378 | List.iter 379 | (fun (state_cell, hb) -> 380 | hb := !hb || happens_before (`State (hd_sequence, state_cell))) 381 | tl_sequence 382 | in 383 | let rec mark_all = function 384 | | [] | _ :: [] -> () 385 | | (op, hb) :: tl -> 386 | if !hb then mark_intransitive op tl; 387 | mark_all tl 388 | in 389 | mark_all sequence; 390 | match sequence with 391 | | [] -> assert false 392 | | (operation', _) :: sequence -> 393 | assert (operation == operation'); 394 | sequence 395 | end 396 | 397 | let is_reversible_race (op1 : state_cell) (between : state_cell list) 398 | (op2 : state_cell) = 399 | let hb_intransitively = 400 | (* Two ops have to be causally related for us to want to reverse them. *) 401 | Causality.happens_before (`State (op1, op2)) 402 | in 403 | let diff_proc = 404 | (* If two ops belong two the same proc, they cannot be reversed. *) 405 | not (same_proc op1 op2) 406 | in 407 | if hb_intransitively && diff_proc then 408 | let not_transitively_related = 409 | (* If two ops are related transitively, technically not a race (see paper). *) 410 | let between = Causality.mark_happen_before op1 between in 411 | let between_hb = 412 | List.filter_map (fun (op, hb) -> if !hb then Some op else None) between 413 | in 414 | let op2_not_transitively_related = 415 | List.for_all 416 | (fun op -> not (Causality.happens_before (`State (op2, op)))) 417 | between_hb 418 | in 419 | op2_not_transitively_related 420 | in 421 | not_transitively_related 422 | else false 423 | 424 | let filter_out_happen_after operation sequence = 425 | Causality.mark_happen_before operation sequence 426 | |> List.filter_map (fun (op, hb) -> if !hb then None else Some op) 427 | 428 | let rec explore_source func state sleep_sets = 429 | (* The code here closely follows the Algorithm 1 outlined in [Source Sets: 430 | A Foundation for Optimal Dynamic Partial Order Reduction]. Likewise 431 | variable names (e.g. reversible race, indep_and_p, initials) etc. 432 | reference constructs introduced in the paper. 433 | *) 434 | let sleep = ref (last_element sleep_sets) in 435 | let s = last_element state in 436 | let p_maybe = IntSet.min_elt_opt (IntSet.diff s.enabled !sleep) in 437 | match p_maybe with 438 | | None -> 439 | if not (IntSet.is_empty s.enabled) then 440 | sleep_set_blocked := !sleep_set_blocked + 1 441 | | Some p -> 442 | s.backtrack <- IntSet.singleton p; 443 | 444 | while IntSet.(cardinal (diff s.backtrack !sleep)) > 0 do 445 | let p = IntSet.min_elt (IntSet.diff s.backtrack !sleep) in 446 | let proc = List.nth s.procs p in 447 | 448 | let state_top = 449 | let schedule = 450 | List.map (fun s -> (s.run_proc, s.run_op, s.run_ptr)) state 451 | @ [ (p, proc.op, proc.obj_ptr) ] 452 | in 453 | do_run func schedule 454 | in 455 | assert (state_top.run_proc = p); 456 | let new_state = state @ [ state_top ] in 457 | 458 | (* Find the races (transitions dependent directly, without a transitive dependency). 459 | *) 460 | let reversible_races = 461 | List.fold_right 462 | (fun op1 ((between, reversible_races, skip_rest) as acc) -> 463 | if skip_rest then acc 464 | else if is_reversible_race op1 between state_top then 465 | ( op1 :: between, 466 | op1 :: reversible_races, 467 | Atomic_op.is_write op1.run_op ) 468 | else (op1 :: between, reversible_races, false)) 469 | state ([], [], false) 470 | |> function 471 | | _, l, _ -> l 472 | in 473 | List.iter 474 | (fun e -> 475 | let prefix, suffix = 476 | (* We need the last operation before the first operation of the race 477 | occured because that's where alternative (reversal) is scheduled. 478 | We need the suffix to compute how to schedule the reversal. *) 479 | let found_e, prefix_rev, suffix_rev = 480 | List.fold_left 481 | (fun (seen_e, prefix, suffix) proc' -> 482 | if seen_e then (seen_e, prefix, proc' :: suffix) 483 | else if proc' == e then (true, prefix, suffix) 484 | else (false, proc' :: prefix, suffix)) 485 | (false, [], []) state 486 | in 487 | 488 | assert found_e; 489 | (* Out first operation is always a spawn, which cannot 490 | race with anything. Thus, any race has a prefix. 491 | *) 492 | assert (List.length prefix_rev > 0); 493 | assert ( 494 | List.length suffix_rev 495 | = List.length state - List.length prefix_rev - 1); 496 | (List.rev prefix_rev, List.rev suffix_rev) 497 | in 498 | 499 | (* Filter out operations that are dependent on the first operation 500 | of the race (e.g. successive operations of e.run_proc). We definitely 501 | don't want to schedule them. 502 | *) 503 | let indep_and_p = 504 | let indep = filter_out_happen_after e suffix in 505 | indep @ [ state_top ] 506 | in 507 | (* Compute the set of operations, that lead to reversal of the race. 508 | The main premise here is that there may be a number of independent 509 | sequences that lead to reversal. 510 | 511 | For example, suppose two racing operations t, t' and some sequences 512 | E, w, u. Suppose the current sequence is E.t.w.u.t', t' happens 513 | after u and w is independent of everything. 514 | 515 | There's at least two ways to reverse t and t': 516 | - E.u.t'.(t,w in any order) 517 | - E.w.u.t'.t 518 | 519 | Thus, initials consist of the first operations of u and w, since 520 | these are the operations that lead to exploration of the above 521 | sequences from E. 522 | *) 523 | let initials = 524 | let rec loop = function 525 | | [] -> [] 526 | | initial :: sequence -> 527 | initial.run_proc 528 | :: loop (filter_out_happen_after initial sequence) 529 | in 530 | loop indep_and_p 531 | in 532 | 533 | (* Exploring one of the initials guarantees that reversal has been 534 | visited. Thus, schedule one of the initials only if none of them 535 | is in backtrack. *) 536 | let prefix_top = last_element prefix in 537 | if 538 | IntSet.(cardinal (inter prefix_top.backtrack (of_list initials))) 539 | = 0 540 | && 541 | let slp = List.nth sleep_sets (List.length prefix - 1) in 542 | IntSet.(cardinal (inter slp (of_list initials))) = 0 543 | then 544 | (* We can add any initial *) 545 | let initial = last_element initials in 546 | prefix_top.backtrack <- IntSet.add initial prefix_top.backtrack) 547 | reversible_races; 548 | 549 | let sleep' = 550 | (* Keep q that are independent with p only. Must be other thread of execution and act on a different object (or none). 551 | 552 | The key idea here is as follows. Suppose we have processed some execution sequence E and there are 553 | just two enabled transitions left. Namely, t=(read a), t'=(read b). Crucially, they are independent. 554 | We begin the exploration from E with E.t and descend into E.t.t' afterwards. Since no more transitions 555 | are enabled, we return back to E and execute E.t'. But there's no point in executing E.t'.t. Since t and 556 | t' are independent, there's a guarantee that E.t.t' and E.t'.t belong to the same trace. 557 | 558 | Therefore, at E, t is put into sleep set, and we explore with E.t' with sleep=[t]. Then E.t'.t gets 559 | sleep-set blocked and we save an execution sequence. Naturally, if there's some w such that it's dependent 560 | with t, then before we explore E.t'.w, we have to "wake" t up. 561 | *) 562 | IntSet.filter 563 | (fun q -> 564 | let proc' = List.nth s.procs q in 565 | not (Causality.happens_before (`Proc (proc, proc')))) 566 | !sleep 567 | in 568 | explore_source func new_state (sleep_sets @ [ sleep' ]); 569 | sleep := IntSet.add p !sleep 570 | done 571 | 572 | let rec explore func state clock last_access = 573 | let s = last_element state in 574 | List.iter 575 | (fun proc -> 576 | let j = proc.proc_id in 577 | let i = 578 | Option.bind proc.obj_ptr (fun ptr -> IntMap.find_opt ptr last_access) 579 | |> Option.value ~default:0 580 | in 581 | if i != 0 then 582 | let pre_s = List.nth state (i - 1) in 583 | if IntSet.mem j pre_s.enabled then 584 | pre_s.backtrack <- IntSet.add j pre_s.backtrack 585 | else pre_s.backtrack <- IntSet.union pre_s.backtrack pre_s.enabled) 586 | s.procs; 587 | if IntSet.cardinal s.enabled > 0 then begin 588 | let p = IntSet.min_elt s.enabled in 589 | let dones = ref IntSet.empty in 590 | s.backtrack <- IntSet.singleton p; 591 | while IntSet.(cardinal (diff s.backtrack !dones)) > 0 do 592 | let j = IntSet.min_elt (IntSet.diff s.backtrack !dones) in 593 | dones := IntSet.add j !dones; 594 | let j_proc = List.nth s.procs j in 595 | let schedule = 596 | List.map (fun s -> (s.run_proc, s.run_op, s.run_ptr)) state 597 | @ [ (j, j_proc.op, j_proc.obj_ptr) ] 598 | in 599 | let statedash = state @ [ do_run func schedule ] in 600 | let state_time = List.length statedash - 1 in 601 | let new_last_access = 602 | match j_proc.obj_ptr with 603 | | Some ptr -> IntMap.add ptr state_time last_access 604 | | None -> last_access 605 | in 606 | let new_clock = IntMap.add j state_time clock in 607 | explore func statedash new_clock new_last_access 608 | done 609 | end 610 | 611 | let every f = every_func := f 612 | let final f = final_func := f 613 | 614 | let check f = 615 | let tracing_at_start = !tracing in 616 | tracing := false; 617 | if not (f ()) then begin 618 | Printf.printf "Found assertion violation at run %d:\n" !num_interleavings; 619 | print_execution_sequence stdout; 620 | assert false 621 | end; 622 | tracing := tracing_at_start 623 | 624 | let reset_state () = 625 | finished_processes := 0; 626 | atomics_counter := 1; 627 | num_states := 0; 628 | sleep_set_blocked := 0; 629 | num_interleavings := 0; 630 | schedule_for_checks := []; 631 | Trace_tracker.clear_traces (); 632 | CCVector.clear processes 633 | 634 | let dscheck_trace_file_env = Sys.getenv_opt "dscheck_trace_file" 635 | 636 | let random func iters = 637 | reset_state (); 638 | let empty_state = do_run func [ (0, Start, None) ] :: [] in 639 | for _ = 1 to iters do 640 | explore_random func empty_state 641 | done 642 | 643 | let dpor func = 644 | reset_state (); 645 | let empty_state = do_run func [ (0, Start, None) ] :: [] in 646 | let empty_clock = IntMap.empty in 647 | let empty_last_access = IntMap.empty in 648 | explore func empty_state empty_clock empty_last_access 649 | 650 | let dpor_source func = 651 | reset_state (); 652 | let empty_state = do_run func [ (0, Start, None) ] in 653 | explore_source func [ empty_state ] [ IntSet.empty ] 654 | 655 | let trace ?(impl = `Dpor_source) ?interleavings ?(record_traces = false) func = 656 | record_traces_flag := record_traces || Option.is_some dscheck_trace_file_env; 657 | interleavings_chan := interleavings; 658 | 659 | (match impl with 660 | | `Dpor -> dpor func 661 | | `Random iters -> random func iters 662 | | `Dpor_source -> dpor_source func); 663 | 664 | (* print reports *) 665 | if record_traces && Option.is_none !interleavings_chan then 666 | interleavings_chan := Some stdout; 667 | 668 | (match !interleavings_chan with 669 | | None -> () 670 | | Some chan -> 671 | Printf.fprintf chan "\nexplored %d interleavings and %d states\n" 672 | !num_interleavings !num_states); 673 | 674 | match dscheck_trace_file_env with 675 | | None -> () 676 | | Some path -> 677 | let chan = open_out path in 678 | Trace_tracker.print_traces chan; 679 | close_out chan 680 | -------------------------------------------------------------------------------- /src/tracedAtomic.mli: -------------------------------------------------------------------------------- 1 | (**************************************************************************) 2 | (* *) 3 | (* OCaml *) 4 | (* *) 5 | (* Stephen Dolan, University of Cambridge *) 6 | (* Gabriel Scherer, projet Partout, INRIA Paris-Saclay *) 7 | (* *) 8 | (* Copyright 2020 Institut National de Recherche en Informatique et *) 9 | (* en Automatique. *) 10 | (* *) 11 | (* All rights reserved. This file is distributed under the terms of *) 12 | (* the GNU Lesser General Public License version 2.1, with the *) 13 | (* special exception on linking described in the file LICENSE. *) 14 | (* *) 15 | (**************************************************************************) 16 | 17 | type !'a t 18 | (** An atomic (mutable) reference to a value of type ['a]. *) 19 | 20 | val make : 'a -> 'a t 21 | (** Create an atomic reference. *) 22 | 23 | val make_contended : 'a -> 'a t 24 | (** Create an atomic reference that is alone on a cache line. It occupies 4-16x 25 | the memory of one allocated with make v. 26 | 27 | The primary purpose is to prevent false-sharing and the resulting 28 | performance degradation. When a CPU performs an atomic operation, it 29 | temporarily takes ownership of an entire cache line that contains the atomic 30 | reference. If multiple atomic references share the same cache line, 31 | modifying these disjoint memory regions simultaneously becomes impossible, 32 | which can create a bottleneck. Hence, as a general guideline, if an atomic 33 | reference is experiencing contention, assigning it its own cache line may 34 | enhance performance. *) 35 | 36 | val get : 'a t -> 'a 37 | (** Get the current value of the atomic reference. *) 38 | 39 | val set : 'a t -> 'a -> unit 40 | (** Set a new value for the atomic reference. *) 41 | 42 | val exchange : 'a t -> 'a -> 'a 43 | (** Set a new value for the atomic reference, and return the current value. *) 44 | 45 | val compare_and_set : 'a t -> 'a -> 'a -> bool 46 | (** [compare_and_set r seen v] sets the new value of [r] to [v] only if its 47 | current value is physically equal to [seen] -- the comparison and the set 48 | occur atomically. Returns [true] if the comparison succeeded (so the set 49 | happened) and [false] otherwise. *) 50 | 51 | val fetch_and_add : int t -> int -> int 52 | (** [fetch_and_add r n] atomically increments the value of [r] by [n], and 53 | returns the current value (before the increment). *) 54 | 55 | val incr : int t -> unit 56 | (** [incr r] atomically increments the value of [r] by [1]. *) 57 | 58 | val decr : int t -> unit 59 | (** [decr r] atomically decrements the value of [r] by [1]. *) 60 | 61 | val trace : 62 | ?impl:[ `Random of int | `Dpor | `Dpor_source ] -> 63 | ?interleavings:out_channel -> 64 | ?record_traces:bool -> 65 | (unit -> unit) -> 66 | unit 67 | (** [trace ?interleavings ?record_traces f] starts the simulation trace. 68 | 69 | [impl] lets user choose the underlying exploration algorithm. 70 | 71 | If [interleavings] output channel is provided, DSCheck will continously 72 | print the visited interleavings there. 73 | 74 | [record_traces] enables [Trace_tracker], which is typically used for testing 75 | DSCheck itself. *) 76 | 77 | val spawn : (unit -> unit) -> unit 78 | (** spawn [f] as a new 'thread' *) 79 | 80 | val check : (unit -> bool) -> unit 81 | (** if [f] returns false then an assertion has failed *) 82 | 83 | val final : (unit -> unit) -> unit 84 | (** run [f] after all processes complete *) 85 | 86 | val every : (unit -> unit) -> unit 87 | (** run [f] between every possible interleaving *) 88 | -------------------------------------------------------------------------------- /tests/dune: -------------------------------------------------------------------------------- 1 | (test 2 | (name test_list) 3 | (libraries dscheck alcotest) 4 | (build_if 5 | (>= %{ocaml_version} 5)) 6 | (modules test_list)) 7 | 8 | (test 9 | (name test_naive_counter) 10 | (libraries dscheck alcotest) 11 | (build_if 12 | (>= %{ocaml_version} 5)) 13 | (modules test_naive_counter)) 14 | 15 | (test 16 | (name test_michael_scott_queue) 17 | (libraries dscheck alcotest) 18 | (build_if 19 | (>= %{ocaml_version} 5)) 20 | (modules test_michael_scott_queue michael_scott_queue)) 21 | 22 | (test 23 | (name test_trace) 24 | (libraries dscheck alcotest) 25 | (build_if 26 | (>= %{ocaml_version} 5)) 27 | (modules test_trace)) 28 | 29 | (rule 30 | (action 31 | (with-stdout-to 32 | report_trace.output 33 | (run ./test_trace.exe))) 34 | (enabled_if 35 | (>= %{ocaml_version} 5))) 36 | 37 | (rule 38 | (alias runtest) 39 | (action 40 | (diff report_trace.expected report_trace.output)) 41 | (enabled_if 42 | (>= %{ocaml_version} 5))) 43 | 44 | (test 45 | (name test_conditional_nested) 46 | (libraries dscheck alcotest) 47 | (build_if 48 | (>= %{ocaml_version} 5)) 49 | (modules test_conditional_nested)) 50 | 51 | (test 52 | (name test_conditional_ssb) 53 | (libraries dscheck alcotest) 54 | (build_if 55 | (>= %{ocaml_version} 5)) 56 | (modules test_conditional_ssb)) 57 | 58 | (test 59 | (name test_commutative) 60 | (libraries dscheck alcotest) 61 | (build_if 62 | (>= %{ocaml_version} 5)) 63 | (modules test_commutative)) 64 | 65 | (test 66 | (name test_hb) 67 | (libraries dscheck alcotest) 68 | (build_if 69 | (>= %{ocaml_version} 5)) 70 | (modules test_hb)) 71 | 72 | (executable 73 | (name gen_program) 74 | (libraries dscheck cmdliner) 75 | (enabled_if 76 | (>= %{ocaml_version} 5)) 77 | (modules gen_program)) 78 | -------------------------------------------------------------------------------- /tests/gen_program.ml: -------------------------------------------------------------------------------- 1 | module Atomic = Dscheck.TracedAtomic 2 | module IntSet = Set.Make (Int) 3 | 4 | type config = { 5 | global_count : int; 6 | value_limit : int; 7 | operation_count : int; 8 | domain_count : int; 9 | generate_conditionals : bool; 10 | print_tests : bool; 11 | seed : int; 12 | } 13 | 14 | let print_config t = 15 | Printf.printf "CONFIG\n"; 16 | Printf.printf "global_count: %d\n" t.global_count; 17 | Printf.printf "value_limit: %d\n" t.value_limit; 18 | Printf.printf "operations_count: %d\n" t.operation_count; 19 | Printf.printf "domain_count: %d\n" t.domain_count; 20 | Printf.printf "generate_conditionals: %b\n%!" t.generate_conditionals; 21 | Printf.printf "seed: %d\n%!" t.seed 22 | 23 | let var_name i = Char.chr (i + 97) 24 | 25 | module Function = struct 26 | type t = 27 | | Get of { true_on : IntSet.t } 28 | | CompareAndSet of { old_value : int; new_value : int } 29 | | FetchAndAdd of { delta : int; true_on : IntSet.t } 30 | 31 | let gen value_limit = 32 | let rec set_f () = 33 | let set = 34 | List.init value_limit (fun i -> if Random.bool () then Some i else None) 35 | |> List.filter_map Fun.id |> IntSet.of_list 36 | in 37 | let size = IntSet.cardinal set in 38 | if 0 < size && size < value_limit then set else set_f () 39 | in 40 | match Random.int 6 with 41 | | 0 | 1 | 2 -> Get { true_on = set_f () } 42 | | 3 | 4 -> 43 | let old_value = Random.int value_limit in 44 | let new_value = Random.int value_limit in 45 | CompareAndSet { old_value; new_value } 46 | | 5 -> 47 | let delta = Random.int value_limit in 48 | FetchAndAdd { delta; true_on = set_f () } 49 | | _ -> assert false 50 | 51 | let eval variable = function 52 | | Get { true_on } -> IntSet.mem (Atomic.get variable) true_on 53 | | FetchAndAdd { delta; true_on } -> 54 | IntSet.mem (Atomic.fetch_and_add variable delta) true_on 55 | | CompareAndSet { old_value; new_value } -> 56 | Atomic.compare_and_set variable old_value new_value 57 | 58 | let to_string var_id = function 59 | | Get { true_on } -> 60 | Printf.sprintf "IntSet.mem (Atomic.get %c) (IntSet.of_list [%s])" 61 | (var_name var_id) 62 | (IntSet.to_seq true_on |> List.of_seq |> List.map Int.to_string 63 | |> String.concat "; ") 64 | | FetchAndAdd { delta; true_on } -> 65 | Printf.sprintf 66 | "IntSet.mem (Atomic.fetch_and_add %c %d) (IntSet.of_list [%s])" 67 | (var_name var_id) delta 68 | (IntSet.to_seq true_on |> List.of_seq |> List.map Int.to_string 69 | |> String.concat "; ") 70 | | CompareAndSet { old_value; new_value } -> 71 | Printf.sprintf "Atomic.compare_and_set %c %d %d" (var_name var_id) 72 | old_value new_value 73 | end 74 | 75 | module Conditional = struct 76 | type t = { functions : Function.t list; operator : [ `And | `Or ] } 77 | 78 | let gen function_count ~value_limit = 79 | let functions = 80 | List.init function_count (fun _ -> Function.gen value_limit) 81 | in 82 | let operator = if Random.bool () then `And else `Or in 83 | { functions; operator } 84 | 85 | let eval t variables = 86 | let functions = t.functions in 87 | let rec f operator vars funcs = 88 | match (vars, funcs) with 89 | | [], [] -> ( match operator with `And -> true | `Or -> false) 90 | | var :: vars_tl, func :: funcs_tl -> ( 91 | let output = Function.eval var func in 92 | match (operator, output) with 93 | | `Or, true -> true 94 | | `And, false -> false 95 | | `Or, false | `And, true -> f operator vars_tl funcs_tl) 96 | | _, [] | [], _ -> 97 | failwith 98 | "gen_program: lists of variables and functions have different \ 99 | lengths" 100 | in 101 | f t.operator variables functions 102 | 103 | let to_string t ~var_ids = 104 | let operator_str = match t.operator with `Or -> " || " | `And -> " && " in 105 | List.combine t.functions var_ids 106 | |> List.map (fun (func, var_id) -> Function.to_string var_id func) 107 | |> String.concat operator_str 108 | end 109 | 110 | module Step = struct 111 | type t = 112 | | Write of { var_id : int; new_value : int; next : t } 113 | | Read of { var_id : int; next : t } 114 | | CompareAndSet of { 115 | var_id : int; 116 | old_value : int; 117 | new_value : int; 118 | next : t; 119 | } 120 | | FetchAndAdd of { var_id : int; delta : int; next : t } 121 | | Conditional of { 122 | var_ids : int list; 123 | conditional : Conditional.t; 124 | on_true : t; 125 | next : t; 126 | } 127 | | Noop 128 | 129 | let rec run ~globals = function 130 | | Write { var_id; new_value; next } -> 131 | Atomic.set (CCVector.get globals var_id) new_value; 132 | run ~globals next 133 | | Read { var_id; next } -> 134 | ignore (Atomic.get (CCVector.get globals var_id)); 135 | run ~globals next 136 | | CompareAndSet { var_id; old_value; new_value; next } -> 137 | ignore 138 | (Atomic.compare_and_set 139 | (CCVector.get globals var_id) 140 | old_value new_value); 141 | run ~globals next 142 | | FetchAndAdd { var_id; delta; next } -> 143 | ignore (Atomic.fetch_and_add (CCVector.get globals var_id) delta); 144 | run ~globals next 145 | | Conditional { var_ids; conditional; on_true; next } -> 146 | let variables = 147 | List.map (fun var_id -> CCVector.get globals var_id) var_ids 148 | in 149 | if Conditional.eval conditional variables then run ~globals on_true; 150 | run ~globals next 151 | | Noop -> () 152 | 153 | let rec print t ~depth = 154 | let indent = List.init depth (fun _ -> "\t") |> String.concat "" in 155 | match t with 156 | | Write { var_id; new_value; next } -> 157 | Printf.printf "%sAtomic.set %c %d;\n" indent (var_name var_id) new_value; 158 | print ~depth next 159 | | Read { var_id; next } -> 160 | Printf.printf "%sAtomic.get %c |> ignore;\n" indent (var_name var_id); 161 | print ~depth next 162 | | CompareAndSet { var_id; old_value; new_value; next } -> 163 | Printf.printf "%sAtomic.compare_and_set %c %d %d |> ignore;\n" indent 164 | (var_name var_id) old_value new_value; 165 | print ~depth next 166 | | FetchAndAdd { var_id; delta; next } -> 167 | Printf.printf "%sAtomic.fetch_and_add %c %d |> ignore;\n" indent 168 | (var_name var_id) delta; 169 | print ~depth next 170 | | Conditional { var_ids; conditional; on_true; next } -> 171 | let s = Conditional.to_string conditional ~var_ids in 172 | Printf.printf "%sif (%s) then (\n" indent s; 173 | print ~depth:(depth + 1) on_true; 174 | Printf.printf "%s);\n" indent; 175 | print ~depth next 176 | | Noop -> () 177 | 178 | let rec gen ~config ~fuel () = 179 | let var_id = Random.int config.global_count in 180 | let next fuel = 181 | if fuel > 1 then gen ~config ~fuel:(fuel - 1) () else Noop 182 | in 183 | let maybe_conditionals = if config.generate_conditionals then 1 else 0 in 184 | match Random.int (6 + maybe_conditionals) with 185 | | 0 | 1 -> 186 | let new_value = Random.int config.value_limit in 187 | Write { var_id; new_value; next = next fuel } 188 | | 2 | 3 -> Read { var_id; next = next fuel } 189 | | 4 -> 190 | let old_value = Random.int config.value_limit in 191 | let new_value = Random.int config.value_limit in 192 | CompareAndSet { var_id; old_value; new_value; next = next fuel } 193 | | 5 -> 194 | let delta = Random.int config.value_limit - (config.value_limit / 2) in 195 | FetchAndAdd { var_id; delta; next = next fuel } 196 | | 6 -> 197 | let func_count = 198 | min (max 1 fuel) (min config.global_count (1 + Random.int 2)) 199 | in 200 | let var_ids = 201 | List.init func_count (fun _ -> Random.int config.global_count) 202 | in 203 | let conditional = 204 | Conditional.gen func_count ~value_limit:config.value_limit 205 | in 206 | let fuel_a, fuel_b = 207 | let tmp = Random.int (max (fuel - func_count) 1) in 208 | (tmp / 2, tmp / 2) 209 | in 210 | 211 | let on_true = gen ~config ~fuel:fuel_a () in 212 | Conditional { var_ids; conditional; on_true; next = next fuel_b } 213 | | _ -> failwith "drew number without corresponding instruction" 214 | end 215 | 216 | module Program = struct 217 | type thread = Step.t 218 | type t = { globals : (int, CCVector.ro) CCVector.t; threads : thread list } 219 | 220 | let run ~impl { globals; threads } = 221 | Atomic.trace ~impl ~record_traces:true (fun () -> 222 | let globals = CCVector.map Atomic.make globals |> CCVector.freeze in 223 | List.iter 224 | (fun thread -> Atomic.spawn (fun () -> Step.run ~globals thread)) 225 | threads; 226 | ()); 227 | Dscheck.Trace_tracker.get_traces () 228 | 229 | let print { globals; threads } = 230 | CCVector.iteri 231 | (fun var_id value -> 232 | Printf.printf "let %c = Atomic.make %d in\n" (var_name var_id) value) 233 | globals; 234 | List.iter 235 | (fun thread -> 236 | Printf.printf "\nDomain.spawn (fun () -> \n"; 237 | Step.print thread ~depth:1; 238 | Printf.printf ")\n%!") 239 | threads 240 | end 241 | 242 | let run_random config () = 243 | Random.init config.seed; 244 | let globals = CCVector.of_list (List.init config.global_count Fun.id) in 245 | let thread_f = Step.gen ~config ~fuel:config.operation_count in 246 | let threads = List.init config.domain_count (fun _ -> thread_f ()) in 247 | let program = ({ globals; threads } : Program.t) in 248 | if config.print_tests then Program.print program; 249 | 250 | let dpor_source = Program.run ~impl:`Dpor_source program in 251 | let dpor = Program.run ~impl:`Dpor program in 252 | if not (Dscheck.Trace_tracker.equal dpor_source dpor) then ( 253 | Printf.printf "found mismatch\n\n%!"; 254 | Program.print program; 255 | Dscheck.Trace_tracker.print dpor stdout; 256 | Dscheck.Trace_tracker.print dpor_source stdout; 257 | assert false) 258 | 259 | let run config test_count = 260 | Printf.printf "\n\n"; 261 | for i = 1 to test_count do 262 | Printf.printf "----run: %d/%d\r%!" i test_count; 263 | run_random config () 264 | done; 265 | Printf.printf "\nall generated programs passed\n%!" 266 | 267 | (* Command line interface *) 268 | open Cmdliner 269 | 270 | let test_count = 271 | let default = 100 in 272 | let info = 273 | Arg.info [ "t"; "test-count" ] ~docv:"INT" 274 | ~doc:"Number of programs to generate and test." 275 | in 276 | Arg.value (Arg.opt Arg.int default info) 277 | 278 | let global_count = 279 | let default = 3 in 280 | let info = 281 | Arg.info [ "g"; "global-count" ] ~docv:"INT" 282 | ~doc:"Number of global atomic variables in generated programs." 283 | in 284 | Arg.value (Arg.opt Arg.int default info) 285 | 286 | let print_tests = 287 | let info = Arg.info [ "p"; "print-tests" ] ~doc:"Print generated tests." in 288 | Arg.value (Arg.flag info) 289 | 290 | let value_limit = 291 | let default = 3 in 292 | let info = 293 | Arg.info [ "l"; "value-limit" ] ~docv:"INT" 294 | ~doc: 295 | "Values of atomic operations stay (mostly) between zero and this value." 296 | in 297 | Arg.value (Arg.opt Arg.int default info) 298 | 299 | let operation_count = 300 | let default = 3 in 301 | let info = 302 | Arg.info [ "o"; "operation-count" ] ~docv:"INT" 303 | ~doc:"Number of operations generated for every domain." 304 | in 305 | Arg.value (Arg.opt Arg.int default info) 306 | 307 | let domain_count = 308 | let default = 3 in 309 | let info = 310 | Arg.info [ "d"; "domain-count" ] ~docv:"INT" 311 | ~doc:"Number of domains in generated tests." 312 | in 313 | Arg.value (Arg.opt Arg.int default info) 314 | 315 | let generate_conditionals = 316 | let info = 317 | Arg.info 318 | [ "c"; "generate-conditionals" ] 319 | ~doc:"Generate tests with conditional statements." 320 | in 321 | Arg.value (Arg.flag info) 322 | 323 | let seed_opt = 324 | let info = Arg.info [ "s"; "random-seed" ] ~docv:"INT" ~doc:"Random seed" in 325 | Arg.value (Arg.opt (Arg.some Arg.int) None info) 326 | 327 | let cmd = 328 | let open Term in 329 | const 330 | (fun 331 | test_count 332 | global_count 333 | print_tests 334 | value_limit 335 | operation_count 336 | domain_count 337 | generate_conditionals 338 | seed_opt 339 | -> 340 | let seed = 341 | match seed_opt with 342 | | Some seed -> seed 343 | | None -> 344 | Random.self_init (); 345 | Random.bits () 346 | in 347 | let config = 348 | ({ 349 | global_count; 350 | value_limit; 351 | operation_count; 352 | domain_count; 353 | generate_conditionals; 354 | print_tests; 355 | seed; 356 | } 357 | : config) 358 | in 359 | print_config config; 360 | run config test_count) 361 | $ test_count $ global_count $ print_tests $ value_limit $ operation_count 362 | $ domain_count $ generate_conditionals $ seed_opt 363 | 364 | let () = 365 | exit @@ Cmd.eval 366 | @@ Cmd.v (Cmd.info ~doc:"Test generator for DSCheck" "gen_program") cmd 367 | -------------------------------------------------------------------------------- /tests/michael_scott_queue.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Copyright (c) 2015, Théo Laurent 3 | * Copyright (c) 2015, KC Sivaramakrishnan 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | *) 17 | 18 | (* Michael-Scott queue copied over from [saturn](github.com/ocaml-multicore/saturn) *) 19 | module Atomic = Dscheck.TracedAtomic 20 | 21 | type 'a node = Nil | Next of 'a * 'a node Atomic.t 22 | type 'a t = { head : 'a node Atomic.t; tail : 'a node Atomic.t } 23 | 24 | let create () = 25 | let head = Next (Obj.magic (), Atomic.make Nil) in 26 | { head = Atomic.make_contended head; tail = Atomic.make_contended head } 27 | 28 | let is_empty q = 29 | match Atomic.get q.head with 30 | | Nil -> failwith "MSQueue.is_empty: impossible" 31 | | Next (_, x) -> ( match Atomic.get x with Nil -> true | _ -> false) 32 | 33 | let pop q = 34 | let rec loop () = 35 | let s = Atomic.get q.head in 36 | let nhead = 37 | match s with 38 | | Nil -> failwith "MSQueue.pop: impossible" 39 | | Next (_, x) -> Atomic.get x 40 | in 41 | match nhead with 42 | | Nil -> None 43 | | Next (v, _) when Atomic.compare_and_set q.head s nhead -> Some v 44 | | _ -> loop () 45 | in 46 | loop () 47 | 48 | let push q v = 49 | let rec find_tail_and_enq curr_end node = 50 | if Atomic.compare_and_set curr_end Nil node then () 51 | else 52 | match Atomic.get curr_end with 53 | | Nil -> find_tail_and_enq curr_end node 54 | | Next (_, n) -> find_tail_and_enq n node 55 | in 56 | let newnode = Next (v, Atomic.make Nil) in 57 | let tail = Atomic.get q.tail in 58 | match tail with 59 | | Nil -> failwith "HW_MSQueue.push: impossible" 60 | | Next (_, n) -> 61 | find_tail_and_enq n newnode; 62 | ignore (Atomic.compare_and_set q.tail tail newnode) 63 | -------------------------------------------------------------------------------- /tests/report_trace.expected: -------------------------------------------------------------------------------- 1 | 2 | sequence 1 3 | ---------------------------------------- 4 | P0 P1 5 | ---------------------------------------- 6 | start 7 | fetch_and_add a 8 | start 9 | fetch_and_add a 10 | fetch_and_add b 11 | ---------------------------------------- 12 | 13 | sequence 2 14 | ---------------------------------------- 15 | P0 P1 16 | ---------------------------------------- 17 | start 18 | start 19 | fetch_and_add a 20 | fetch_and_add a 21 | fetch_and_add b 22 | ---------------------------------------- 23 | 24 | explored 2 interleavings and 9 states 25 | ---- 26 | (1,a,w),(0,a,w),(1,b,w) 27 | (0,a,w),(1,a,w),(1,b,w) 28 | ---- 29 | -------------------------------------------------------------------------------- /tests/test_commutative.ml: -------------------------------------------------------------------------------- 1 | module Atomic = Dscheck.TracedAtomic 2 | 3 | (* get + get *) 4 | let test1 () = 5 | let a = Atomic.make 0 in 6 | Atomic.spawn (fun () -> Atomic.get a |> ignore); 7 | Atomic.spawn (fun () -> Atomic.get a |> ignore) 8 | 9 | (* get + cas fail *) 10 | let test2 () = 11 | let a = Atomic.make 0 in 12 | let b = Atomic.make 0 in 13 | Atomic.spawn (fun () -> Atomic.get a |> ignore); 14 | 15 | Atomic.spawn (fun () -> 16 | Atomic.compare_and_set a 1 2 |> ignore; 17 | Atomic.get b |> ignore) 18 | 19 | (* get + cas success *) 20 | let test3 () = 21 | let a = Atomic.make 0 in 22 | Atomic.spawn (fun () -> Atomic.compare_and_set a 0 1 |> ignore); 23 | Atomic.spawn (fun () -> Atomic.compare_and_set a 0 1 |> ignore) 24 | 25 | (* get + cas fail + set *) 26 | let test4 () = 27 | let a = Atomic.make 0 in 28 | Atomic.spawn (fun () -> Atomic.compare_and_set a 2 3 |> ignore); 29 | Atomic.spawn (fun () -> Atomic.get a |> ignore); 30 | Atomic.spawn (fun () -> Atomic.set a 1 |> ignore) 31 | 32 | let test5 () = 33 | let a = Atomic.make 0 in 34 | Atomic.spawn (fun () -> Atomic.get a |> ignore); 35 | Atomic.spawn (fun () -> Atomic.set a 2 |> ignore); 36 | Atomic.spawn (fun () -> Atomic.compare_and_set a 2 3 |> ignore) 37 | 38 | let () = 39 | let tests = [ test1; test2; test3; test4; test5 ] in 40 | List.iter (fun test -> Atomic.trace ~impl:`Dpor_source test) tests 41 | -------------------------------------------------------------------------------- /tests/test_conditional_nested.ml: -------------------------------------------------------------------------------- 1 | module Atomic = Dscheck.TracedAtomic 2 | 3 | let test () = 4 | let b = Atomic.make 0 in 5 | let c = Atomic.make 0 in 6 | let ok = Atomic.make false in 7 | let seen_b = ref (-1) in 8 | 9 | Atomic.spawn (fun () -> Atomic.set b 1); 10 | Atomic.spawn (fun () -> 11 | Atomic.set c 1; 12 | Atomic.set b 2); 13 | Atomic.spawn (fun () -> 14 | if Atomic.get c = 0 then ( 15 | seen_b := Atomic.get b; 16 | if !seen_b = 0 then Atomic.set ok true)) 17 | 18 | (* Atomic.final (fun () -> 19 | Format.printf "seen_b=%i b=%i c=%i ok=%b@." (!seen_b) (Atomic.get b) (Atomic.get c) 20 | (Atomic.get ok)) *) 21 | 22 | let () = Atomic.trace ~record_traces:true test 23 | -------------------------------------------------------------------------------- /tests/test_conditional_ssb.ml: -------------------------------------------------------------------------------- 1 | module Atomic = Dscheck.TracedAtomic 2 | 3 | (* This test shows sleep-set blocking with source sets and constitutes an 4 | interesting conditional in its own right. Taken from Fig 2 in 5 | [Source Sets: A Foundation for Optimal Dynamic Partial Order Reduction]. 6 | *) 7 | 8 | let test () = 9 | let x = Atomic.make 0 in 10 | let y = Atomic.make 0 in 11 | let z = Atomic.make 0 in 12 | 13 | let tmp = ref (-1) in 14 | Atomic.spawn (fun () -> tmp := Atomic.get x); 15 | Atomic.spawn (fun () -> Atomic.set y 1); 16 | 17 | Atomic.spawn (fun () -> 18 | let m = Atomic.get y in 19 | if m = 0 then Atomic.set z 1); 20 | 21 | Atomic.spawn (fun () -> 22 | let n = Atomic.get z in 23 | let l = Atomic.get y in 24 | if n = 1 then if l = 0 then Atomic.set x 1) 25 | 26 | (* 27 | Atomic.final (fun () -> 28 | Format.printf "tmp=%d x=%d y=%d z=%d\n%!" !tmp (Atomic.get x) 29 | (Atomic.get y) (Atomic.get z)) *) 30 | 31 | let () = Atomic.trace ~record_traces:true test 32 | -------------------------------------------------------------------------------- /tests/test_hb.ml: -------------------------------------------------------------------------------- 1 | module Atomic = Dscheck.TracedAtomic 2 | 3 | (* get + get *) 4 | let test1 () = 5 | let a = Atomic.make 0 in 6 | Atomic.spawn (fun () -> Atomic.compare_and_set a 0 1 |> ignore); 7 | Atomic.spawn (fun () -> Atomic.compare_and_set a 1 2 |> ignore); 8 | Atomic.spawn (fun () -> Atomic.compare_and_set a 1 2 |> ignore); 9 | () 10 | 11 | let () = 12 | Atomic.trace ~record_traces:true test1; 13 | Printf.printf "traces: %d\n%!" 14 | (Dscheck.Trace_tracker.get_traces () |> Dscheck.Trace_tracker.count) 15 | -------------------------------------------------------------------------------- /tests/test_list.ml: -------------------------------------------------------------------------------- 1 | module Atomic = Dscheck.TracedAtomic 2 | 3 | (* a simple concurrent list *) 4 | 5 | type conc_list = { value : int; next : conc_list option } 6 | 7 | let rec add_node_naive list_head n = 8 | (* try to add a new node to head *) 9 | let old_head = Atomic.get list_head in 10 | let new_node = { value = n; next = Some old_head } in 11 | (* introduce bug *) 12 | if Atomic.get list_head = old_head then ( 13 | Atomic.set list_head new_node; 14 | true) 15 | else add_node_naive list_head n 16 | 17 | let rec add_node_safe list_head n = 18 | let old_head = Atomic.get list_head in 19 | let new_node = { value = n; next = Some old_head } in 20 | if Atomic.compare_and_set list_head old_head new_node then true 21 | else add_node_safe list_head n 22 | 23 | let check_node list_head n = 24 | let rec check_from_node node = 25 | match (node.value, node.next) with 26 | | v, _ when v = n -> true 27 | | _, None -> false 28 | | _, Some next_node -> check_from_node next_node 29 | in 30 | (* try to find the node *) 31 | check_from_node (Atomic.get list_head) 32 | 33 | let create_test add_node_f upto () = 34 | let list_head = Atomic.make { value = 0; next = None } in 35 | for x = 1 to upto do 36 | Atomic.spawn (fun () -> 37 | assert (add_node_f list_head x); 38 | assert (check_node list_head x)) 39 | done; 40 | Atomic.final (fun () -> 41 | for x = 1 to upto do 42 | Atomic.check (fun () -> check_node list_head x) 43 | done) 44 | 45 | let test_list_naive_single_domain () = 46 | Atomic.trace (create_test add_node_naive 1) 47 | 48 | let test_list_naive domains () = 49 | match Atomic.trace (create_test add_node_naive domains) with 50 | | exception _ -> () 51 | | _ -> failwith "expected failure" 52 | 53 | let test_list_safe () = Atomic.trace (create_test add_node_safe 3) 54 | 55 | let () = 56 | let open Alcotest in 57 | run "dscheck" 58 | [ 59 | ( "list", 60 | [ 61 | test_case "naive-1-domain" `Quick test_list_naive_single_domain; 62 | test_case "naive-2-domains" `Quick (test_list_naive 2); 63 | test_case "naive-8-domains" `Quick (test_list_naive 8); 64 | test_case "safe" `Quick test_list_safe; 65 | ] ); 66 | ] 67 | -------------------------------------------------------------------------------- /tests/test_michael_scott_queue.ml: -------------------------------------------------------------------------------- 1 | module Atomic = Dscheck.TracedAtomic 2 | 3 | let drain queue = 4 | let remaining = ref 0 in 5 | while not (Michael_scott_queue.is_empty queue) do 6 | remaining := !remaining + 1; 7 | assert (Option.is_some (Michael_scott_queue.pop queue)) 8 | done; 9 | !remaining 10 | 11 | let producer_consumer () = 12 | Atomic.trace ~record_traces:true (fun () -> 13 | let queue = Michael_scott_queue.create () in 14 | let items_total = 4 in 15 | 16 | Atomic.spawn (fun () -> 17 | for _ = 1 to items_total do 18 | Michael_scott_queue.push queue 0 19 | done); 20 | 21 | (* consumer *) 22 | let popped = ref 0 in 23 | Atomic.spawn (fun () -> 24 | for _ = 1 to items_total do 25 | match Michael_scott_queue.pop queue with 26 | | None -> () 27 | | Some _ -> popped := !popped + 1 28 | done); 29 | 30 | (* checks*) 31 | Atomic.final (fun () -> 32 | Atomic.check (fun () -> 33 | let remaining = drain queue in 34 | !popped + remaining = items_total))) 35 | 36 | let () = 37 | let open Alcotest in 38 | run "michael_scott_queue_dscheck" 39 | [ ("basic", [ test_case "1-producer-1-consumer" `Slow producer_consumer ]) ] 40 | -------------------------------------------------------------------------------- /tests/test_naive_counter.ml: -------------------------------------------------------------------------------- 1 | module Atomic = Dscheck.TracedAtomic 2 | 3 | let counter incr () = 4 | let counter = Atomic.make 0 in 5 | Atomic.spawn (fun () -> incr counter); 6 | Atomic.spawn (fun () -> incr counter); 7 | Atomic.final (fun () -> Atomic.check (fun () -> Atomic.get counter == 2)) 8 | 9 | let test_naive_counter () = 10 | let naive_incr counter = Atomic.set counter (Atomic.get counter + 1) in 11 | match Atomic.trace (counter naive_incr) with 12 | | exception _ -> () 13 | | _ -> failwith "expected failure" 14 | 15 | let test_safe_counter () = Atomic.trace (counter Atomic.incr) 16 | 17 | let () = 18 | let open Alcotest in 19 | run "dscheck" 20 | [ 21 | ( "counter", 22 | [ 23 | test_case "naive" `Quick test_naive_counter; 24 | test_case "safe" `Quick test_safe_counter; 25 | ] ); 26 | ] 27 | -------------------------------------------------------------------------------- /tests/test_trace.ml: -------------------------------------------------------------------------------- 1 | module Atomic = Dscheck.TracedAtomic 2 | 3 | let counter incr () = 4 | let c1 = Atomic.make 0 in 5 | let c2 = Atomic.make_contended 0 in 6 | Atomic.spawn (fun () -> incr c1); 7 | Atomic.spawn (fun () -> 8 | incr c1; 9 | incr c2); 10 | 11 | Atomic.final (fun () -> 12 | Atomic.check (fun () -> Atomic.get c1 == 2 && Atomic.get c2 == 1)) 13 | 14 | let test_safe_counter () = 15 | Atomic.trace ~interleavings:stdout ~record_traces:true (counter Atomic.incr); 16 | Dscheck.Trace_tracker.print_traces stdout 17 | 18 | let _ = test_safe_counter () 19 | -------------------------------------------------------------------------------- /tests/traces/conditional1: -------------------------------------------------------------------------------- 1 | ---- 2 | (2,b,r),(1,b,w),(2,a,r),(1,a,w),(0,a,w),(2,c,w) 3 | (2,b,r),(1,b,w),(1,a,w),(2,a,r),(0,a,w) 4 | (1,b,w),(1,a,w),(0,a,w),(2,b,r) 5 | (2,b,r),(1,b,w),(2,a,r),(0,a,w),(1,a,w),(2,c,w) 6 | (0,a,w),(2,b,r),(1,b,w),(1,a,w),(2,a,r) 7 | (0,a,w),(1,b,w),(1,a,w),(2,b,r) 8 | (0,a,w),(2,b,r),(1,b,w),(2,a,r),(1,a,w) 9 | (2,b,r),(1,b,w),(1,a,w),(0,a,w),(2,a,r) 10 | ---- 11 | -------------------------------------------------------------------------------- /tests/traces/conditional2: -------------------------------------------------------------------------------- 1 | ---- 2 | (0,a,r),(2,b,r),(3,c,r),(2,c,w),(3,b,r),(1,b,w) 3 | (2,b,r),(2,c,w),(3,c,r),(3,b,r),(1,b,w),(3,a,w),(0,a,r) 4 | (0,a,r),(3,c,r),(3,b,r),(1,b,w),(2,b,r) 5 | (0,a,r),(1,b,w),(2,b,r),(3,c,r),(3,b,r) 6 | (0,a,r),(2,b,r),(1,b,w),(3,c,r),(2,c,w),(3,b,r) 7 | (0,a,r),(2,b,r),(1,b,w),(2,c,w),(3,c,r),(3,b,r) 8 | (0,a,r),(2,b,r),(2,c,w),(3,c,r),(3,b,r),(1,b,w),(3,a,w) 9 | ---- 10 | -------------------------------------------------------------------------------- /tests/traces/conditional_nested: -------------------------------------------------------------------------------- 1 | ---- 2 | (2,b,r),(1,b,w),(2,a,r),(1,a,w),(0,a,w),(2,c,w) 3 | (2,b,r),(1,b,w),(1,a,w),(2,a,r),(0,a,w) 4 | (1,b,w),(1,a,w),(0,a,w),(2,b,r) 5 | (2,b,r),(1,b,w),(2,a,r),(0,a,w),(1,a,w),(2,c,w) 6 | (0,a,w),(2,b,r),(1,b,w),(1,a,w),(2,a,r) 7 | (0,a,w),(1,b,w),(1,a,w),(2,b,r) 8 | (0,a,w),(2,b,r),(1,b,w),(2,a,r),(1,a,w) 9 | (2,b,r),(1,b,w),(1,a,w),(0,a,w),(2,a,r) 10 | ---- 11 | -------------------------------------------------------------------------------- /tests/traces/conditional_ssb: -------------------------------------------------------------------------------- 1 | ---- 2 | (0,a,r),(2,b,r),(3,c,r),(2,c,w),(3,b,r),(1,b,w) 3 | (2,b,r),(2,c,w),(3,c,r),(3,b,r),(1,b,w),(3,a,w),(0,a,r) 4 | (0,a,r),(3,c,r),(3,b,r),(1,b,w),(2,b,r) 5 | (0,a,r),(1,b,w),(2,b,r),(3,c,r),(3,b,r) 6 | (0,a,r),(2,b,r),(1,b,w),(3,c,r),(2,c,w),(3,b,r) 7 | (0,a,r),(2,b,r),(1,b,w),(2,c,w),(3,c,r),(3,b,r) 8 | (0,a,r),(2,b,r),(2,c,w),(3,c,r),(3,b,r),(1,b,w),(3,a,w) 9 | ---- 10 | -------------------------------------------------------------------------------- /tests/traces/list: -------------------------------------------------------------------------------- 1 | ---- 2 | (2,a,r),(2,a,w),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r) 3 | (2,a,r),(2,a,w),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 4 | (1,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r) 5 | (1,a,r),(2,a,r),(1,a,w),(2,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r) 6 | (1,a,r),(1,a,w),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 7 | (1,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 8 | (1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r) 9 | (1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 10 | (2,a,r),(2,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,w),(1,a,r) 11 | (0,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,w),(2,a,r) 12 | (0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 13 | (2,a,r),(2,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 14 | (0,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,w),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 15 | (0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 16 | (0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 17 | (0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 18 | (0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 19 | (0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 20 | (0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,w),(2,a,r) 21 | (0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 22 | (0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 23 | (0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 24 | (0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 25 | (0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 26 | (2,a,r),(2,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 27 | (2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r) 28 | (0,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,w),(2,a,r) 29 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 30 | (2,a,r),(2,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 31 | (0,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 32 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 33 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r) 34 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 35 | (0,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 36 | (0,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 37 | (0,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 38 | (0,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 39 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,w),(2,a,r) 40 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 41 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(2,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 42 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 43 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 44 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 45 | (2,a,r),(2,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 46 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 47 | (0,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 48 | (0,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 49 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 50 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 51 | (2,a,r),(2,a,w),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r) 52 | (2,a,r),(2,a,w),(1,a,r),(1,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 53 | (1,a,r),(1,a,w),(2,a,r),(2,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 54 | (1,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 55 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,w),(1,a,r) 56 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 57 | (1,a,r),(1,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(2,a,r) 58 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 59 | (1,a,r),(1,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 60 | (2,a,r),(2,a,w),(1,a,r),(1,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 61 | (1,a,r),(1,a,w),(2,a,r),(2,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 62 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 63 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(2,a,r) 64 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 65 | (1,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 66 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r) 67 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 68 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 69 | (1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r) 70 | (1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 71 | (1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 72 | (0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 73 | (0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 74 | (1,a,r),(1,a,w),(0,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 75 | (1,a,r),(1,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,w),(2,a,r) 76 | (0,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 77 | (0,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 78 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 79 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 80 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 81 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 82 | (1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 83 | (1,a,r),(1,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 84 | (0,a,r),(2,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 85 | (0,a,r),(2,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 86 | (2,a,r),(2,a,w),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 87 | (1,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,w),(0,a,r),(2,a,r) 88 | (1,a,r),(1,a,w),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 89 | (1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 90 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,w),(2,a,r) 91 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 92 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 93 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 94 | (1,a,r),(2,a,r),(1,a,w),(2,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,w),(0,a,r),(2,a,r) 95 | (1,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 96 | (2,a,r),(2,a,w),(0,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,w),(0,a,r),(1,a,r) 97 | (2,a,r),(2,a,w),(0,a,r),(0,a,w),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 98 | (0,a,r),(0,a,w),(1,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 99 | (0,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,w),(2,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 100 | (0,a,r),(0,a,w),(1,a,r),(1,a,w),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 101 | (0,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 102 | (0,a,r),(0,a,w),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(2,a,r) 103 | (0,a,r),(0,a,w),(1,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 104 | (0,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 105 | (0,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 106 | (0,a,r),(0,a,w),(1,a,r),(2,a,r),(1,a,w),(2,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 107 | (0,a,r),(0,a,w),(1,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 108 | (0,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 109 | (0,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 110 | (0,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 111 | (0,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 112 | (0,a,r),(0,a,w),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 113 | (0,a,r),(0,a,w),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 114 | (0,a,r),(2,a,r),(0,a,w),(1,a,r),(2,a,r),(1,a,w),(2,a,r),(1,a,r),(2,a,w),(0,a,r),(2,a,r) 115 | (0,a,r),(2,a,r),(0,a,w),(1,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 116 | (0,a,r),(2,a,r),(0,a,w),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(2,a,r) 117 | (0,a,r),(2,a,r),(0,a,w),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 118 | (0,a,r),(2,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,r),(1,a,w),(2,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 119 | (0,a,r),(2,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 120 | (0,a,r),(2,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 121 | (0,a,r),(2,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 122 | (0,a,r),(2,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 123 | (0,a,r),(2,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 124 | (0,a,r),(2,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 125 | (0,a,r),(2,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 126 | (2,a,r),(2,a,w),(0,a,r),(0,a,w),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 127 | (2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r) 128 | (2,a,r),(2,a,w),(0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 129 | (0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 130 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(2,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 131 | (0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 132 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 133 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r) 134 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 135 | (0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(2,a,r) 136 | (0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 137 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 138 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 139 | (0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(2,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 140 | (0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 141 | (0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 142 | (0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 143 | (0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 144 | (0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 145 | (0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 146 | (0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 147 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(2,a,r),(1,a,r),(2,a,w),(0,a,r),(2,a,r) 148 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 149 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(2,a,r) 150 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 151 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(2,a,r),(1,a,w),(2,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 152 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 153 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 154 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 155 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 156 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 157 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 158 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 159 | (2,a,r),(2,a,w),(0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 160 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 161 | (0,a,r),(1,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 162 | (0,a,r),(1,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 163 | (0,a,r),(1,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 164 | (0,a,r),(1,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 165 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 166 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 167 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 168 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 169 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,w),(0,a,r),(1,a,r) 170 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 171 | (1,a,r),(1,a,w),(0,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 172 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,w),(2,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 173 | (1,a,r),(1,a,w),(0,a,r),(2,a,r),(0,a,w),(2,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 174 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(0,a,w),(2,a,r),(1,a,r),(2,a,w),(0,a,r),(2,a,r) 175 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(0,a,w),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 176 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(0,a,w),(2,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 177 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r) 178 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 179 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 180 | (0,a,r),(1,a,r),(0,a,w),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 181 | (0,a,r),(1,a,r),(0,a,w),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 182 | (1,a,r),(1,a,w),(0,a,r),(0,a,w),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 183 | (1,a,r),(1,a,w),(0,a,r),(1,a,r),(0,a,w),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 184 | (0,a,r),(0,a,w),(2,a,r),(2,a,w),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 185 | (0,a,r),(0,a,w),(2,a,r),(2,a,w),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 186 | (0,a,r),(0,a,w),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 187 | (0,a,r),(0,a,w),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 188 | (0,a,r),(1,a,r),(0,a,w),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 189 | (0,a,r),(1,a,r),(0,a,w),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 190 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 191 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 192 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 193 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 194 | (1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 195 | (1,a,r),(1,a,w),(0,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 196 | (0,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 197 | (0,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 198 | (0,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 199 | (0,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 200 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 201 | (0,a,r),(1,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 202 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 203 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 204 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 205 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 206 | (2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r) 207 | (2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 208 | (0,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r) 209 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r) 210 | (0,a,r),(1,a,r),(1,a,w),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 211 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 212 | (2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r) 213 | (2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 214 | (0,a,r),(1,a,r),(1,a,w),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 215 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 216 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(2,a,r) 217 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 218 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 219 | (2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 220 | (0,a,r),(1,a,r),(1,a,w),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 221 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(2,a,r) 222 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 223 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 224 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 225 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,w),(2,a,r) 226 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 227 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 228 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 229 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 230 | (2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 231 | (0,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(2,a,r) 232 | (0,a,r),(1,a,r),(1,a,w),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 233 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,w),(2,a,r) 234 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 235 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(2,a,r) 236 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(2,a,r),(0,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r) 237 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(2,a,r) 238 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 239 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 240 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(2,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 241 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(2,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 242 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(2,a,r),(1,a,r),(2,a,w),(0,a,r),(2,a,r) 243 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(2,a,r),(0,a,w),(2,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 244 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 245 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 246 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 247 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 248 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 249 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 250 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 251 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 252 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r) 253 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(2,a,r),(0,a,w),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r) 254 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r) 255 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 256 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 257 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 258 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,w),(0,a,r),(2,a,r) 259 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 260 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r) 261 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 262 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 263 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 264 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(2,a,r) 265 | (0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 266 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r) 267 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 268 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 269 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 270 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,w),(0,a,r),(2,a,r) 271 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 272 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r) 273 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 274 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 275 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 276 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(2,a,r) 277 | (0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 278 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r) 279 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 280 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r) 281 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 282 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 283 | (1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 284 | (1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r) 285 | (1,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 286 | (0,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r) 287 | (0,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 288 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r) 289 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 290 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,w),(1,a,r) 291 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 292 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 293 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r) 294 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 295 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r) 296 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 297 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 298 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 299 | (1,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 300 | (0,a,r),(2,a,r),(2,a,w),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r) 301 | (0,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 302 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,w),(1,a,r) 303 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 304 | (1,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 305 | (0,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 306 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 307 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r) 308 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(1,a,r),(2,a,r) 309 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(1,a,r),(0,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(1,a,r) 310 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r) 311 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 312 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 313 | (1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(2,a,r) 314 | (1,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 315 | (0,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 316 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 317 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,w),(0,a,r),(1,a,r) 318 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 319 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 320 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r) 321 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 322 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r) 323 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 324 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 325 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 326 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,w),(0,a,r),(1,a,r) 327 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 328 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 329 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r) 330 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r) 331 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(1,a,r),(0,a,w),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r) 332 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r) 333 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 334 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r) 335 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 336 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 337 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 338 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r) 339 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 340 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r) 341 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 342 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 343 | (0,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 344 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r) 345 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 346 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r) 347 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 348 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 349 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 350 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r) 351 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 352 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(1,a,r),(2,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r) 353 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 354 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 355 | (0,a,r),(1,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(1,a,r),(1,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 356 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r) 357 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(2,a,r),(0,a,w),(0,a,r) 358 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(2,a,r),(0,a,w),(0,a,r),(1,a,r) 359 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(1,a,r),(2,a,r) 360 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(1,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(0,a,w),(0,a,r),(2,a,r) 361 | (1,a,r),(2,a,r),(1,a,w),(0,a,r),(2,a,r),(2,a,r),(2,a,w),(0,a,r),(0,a,r),(1,a,r),(0,a,w),(0,a,r),(2,a,r) 362 | ---- 363 | -------------------------------------------------------------------------------- /tests/traces/ms_queue: -------------------------------------------------------------------------------- 1 | ---- 2 | (0,d,r),(0,b,r),(1,c,r),(1,a,r),(1,c,r),(1,a,r),(1,c,r),(1,a,r),(1,c,r),(1,a,r),(0,a,w),(0,b,w),(0,e,r),(0,b,r),(0,d,w),(0,b,w),(0,f,r),(0,b,r),(0,e,w),(0,b,w),(0,g,r),(0,b,r),(0,f,w),(0,b,w) 3 | (0,d,r),(0,b,r),(0,a,w),(0,b,w),(0,e,r),(0,b,r),(1,c,r),(1,a,r),(1,c,w),(1,c,r),(1,d,r),(1,c,r),(1,d,r),(1,c,r),(1,d,r),(0,d,w),(0,b,w),(0,f,r),(0,b,r),(0,e,w),(0,b,w),(0,g,r),(0,b,r),(0,f,w),(0,b,w) 4 | (0,d,r),(0,b,r),(0,a,w),(0,b,w),(0,e,r),(0,b,r),(0,d,w),(0,b,w),(0,f,r),(0,b,r),(1,c,r),(1,a,r),(1,c,w),(1,c,r),(1,d,r),(1,c,w),(1,c,r),(1,e,r),(1,c,r),(1,e,r),(0,e,w),(0,b,w),(0,g,r),(0,b,r),(0,f,w),(0,b,w) 5 | (0,d,r),(0,b,r),(0,a,w),(0,b,w),(0,e,r),(0,b,r),(0,d,w),(0,b,w),(0,f,r),(0,b,r),(0,e,w),(0,b,w),(0,g,r),(0,b,r),(1,c,r),(1,a,r),(1,c,w),(1,c,r),(1,d,r),(1,c,w),(1,c,r),(1,e,r),(1,c,w),(1,c,r),(1,f,r),(0,f,w),(0,b,w) 6 | (0,d,r),(0,b,r),(0,a,w),(0,b,w),(0,e,r),(0,b,r),(0,d,w),(0,b,w),(0,f,r),(0,b,r),(0,e,w),(0,b,w),(0,g,r),(0,b,r),(0,f,w),(0,b,w),(1,c,r),(1,a,r),(1,c,w),(1,c,r),(1,d,r),(1,c,w),(1,c,r),(1,e,r),(1,c,w),(1,c,r),(1,f,r),(1,c,w) 7 | (0,d,r),(0,b,r),(0,a,w),(0,b,w),(0,e,r),(0,b,r),(0,d,w),(0,b,w),(0,f,r),(0,b,r),(1,c,r),(1,a,r),(1,c,w),(1,c,r),(1,d,r),(1,c,w),(1,c,r),(1,e,r),(0,e,w),(0,b,w),(0,g,r),(0,b,r),(0,f,w),(0,b,w),(1,c,r),(1,e,r),(1,c,w) 8 | (0,d,r),(0,b,r),(0,a,w),(0,b,w),(0,e,r),(0,b,r),(1,c,r),(1,a,r),(1,c,w),(1,c,r),(1,d,r),(0,d,w),(0,b,w),(0,f,r),(0,b,r),(1,c,r),(1,d,r),(1,c,w),(1,c,r),(1,e,r),(0,e,w),(0,b,w),(0,g,r),(0,b,r),(0,f,w),(0,b,w) 9 | (0,d,r),(0,b,r),(0,a,w),(0,b,w),(0,e,r),(0,b,r),(1,c,r),(1,a,r),(1,c,w),(1,c,r),(1,d,r),(0,d,w),(0,b,w),(0,f,r),(0,b,r),(0,e,w),(0,b,w),(0,g,r),(0,b,r),(0,f,w),(0,b,w),(1,c,r),(1,d,r),(1,c,w),(1,c,r),(1,e,r),(1,c,w) 10 | (0,d,r),(0,b,r),(0,a,w),(0,b,w),(0,e,r),(0,b,r),(1,c,r),(1,a,r),(1,c,w),(1,c,r),(1,d,r),(1,c,r),(1,d,r),(0,d,w),(0,b,w),(0,f,r),(0,b,r),(0,e,w),(0,b,w),(0,g,r),(0,b,r),(0,f,w),(0,b,w),(1,c,r),(1,d,r),(1,c,w) 11 | (0,d,r),(0,b,r),(1,c,r),(1,a,r),(0,a,w),(0,b,w),(0,e,r),(0,b,r),(1,c,r),(1,a,r),(1,c,w),(1,c,r),(1,d,r),(1,c,r),(1,d,r),(0,d,w),(0,b,w),(0,f,r),(0,b,r),(0,e,w),(0,b,w),(0,g,r),(0,b,r),(0,f,w),(0,b,w) 12 | (0,d,r),(0,b,r),(1,c,r),(1,a,r),(0,a,w),(0,b,w),(0,e,r),(0,b,r),(0,d,w),(0,b,w),(0,f,r),(0,b,r),(1,c,r),(1,a,r),(1,c,w),(1,c,r),(1,d,r),(1,c,w),(1,c,r),(1,e,r),(0,e,w),(0,b,w),(0,g,r),(0,b,r),(0,f,w),(0,b,w) 13 | (0,d,r),(0,b,r),(1,c,r),(1,a,r),(0,a,w),(0,b,w),(0,e,r),(0,b,r),(0,d,w),(0,b,w),(0,f,r),(0,b,r),(0,e,w),(0,b,w),(0,g,r),(0,b,r),(0,f,w),(0,b,w),(1,c,r),(1,a,r),(1,c,w),(1,c,r),(1,d,r),(1,c,w),(1,c,r),(1,e,r),(1,c,w) 14 | (0,d,r),(0,b,r),(1,c,r),(1,a,r),(0,a,w),(0,b,w),(0,e,r),(0,b,r),(1,c,r),(1,a,r),(1,c,w),(1,c,r),(1,d,r),(0,d,w),(0,b,w),(0,f,r),(0,b,r),(0,e,w),(0,b,w),(0,g,r),(0,b,r),(0,f,w),(0,b,w),(1,c,r),(1,d,r),(1,c,w) 15 | (0,d,r),(0,b,r),(1,c,r),(1,a,r),(1,c,r),(1,a,r),(0,a,w),(0,b,w),(0,e,r),(0,b,r),(1,c,r),(1,a,r),(1,c,w),(1,c,r),(1,d,r),(0,d,w),(0,b,w),(0,f,r),(0,b,r),(0,e,w),(0,b,w),(0,g,r),(0,b,r),(0,f,w),(0,b,w) 16 | (0,d,r),(0,b,r),(1,c,r),(1,a,r),(1,c,r),(1,a,r),(0,a,w),(0,b,w),(0,e,r),(0,b,r),(0,d,w),(0,b,w),(0,f,r),(0,b,r),(0,e,w),(0,b,w),(0,g,r),(0,b,r),(0,f,w),(0,b,w),(1,c,r),(1,a,r),(1,c,w),(1,c,r),(1,d,r),(1,c,w) 17 | (0,d,r),(0,b,r),(1,c,r),(1,a,r),(1,c,r),(1,a,r),(1,c,r),(1,a,r),(0,a,w),(0,b,w),(0,e,r),(0,b,r),(0,d,w),(0,b,w),(0,f,r),(0,b,r),(0,e,w),(0,b,w),(0,g,r),(0,b,r),(0,f,w),(0,b,w),(1,c,r),(1,a,r),(1,c,w) 18 | ---- 19 | -------------------------------------------------------------------------------- /tests/traces/naive_counter: -------------------------------------------------------------------------------- 1 | ---- 2 | (1,a,w),(0,a,w) 3 | (0,a,w),(1,a,w) 4 | ---- 5 | --------------------------------------------------------------------------------