dscheck-0.5.0/0000755000175000017500000000000014702442054011614 5ustar kylekyledscheck-0.5.0/gen_traces.sh0000755000175000017500000000070214641522241014263 0ustar kylekyleset -eux pipefail dscheck_trace_file="tests/traces/ms_queue" dune exec tests/test_michael_scott_queue.exe dscheck_trace_file="tests/traces/naive_counter" dune exec tests/test_naive_counter.exe dscheck_trace_file="tests/traces/list" dune exec tests/test_list.exe dscheck_trace_file="tests/traces/conditional_nested" dune exec tests/test_conditional_nested.exe dscheck_trace_file="tests/traces/conditional_ssb" dune exec tests/test_conditional_ssb.exedscheck-0.5.0/src/0000755000175000017500000000000014641522241012402 5ustar kylekyledscheck-0.5.0/src/tracedAtomic.ml0000644000175000017500000005516514641522241015347 0ustar kylekyleopen Effect open Effect.Shallow type 'a t = 'a Atomic.t * int type _ Effect.t += | Make : 'a -> 'a t Effect.t | Get : 'a t -> 'a Effect.t | Set : ('a t * 'a) -> unit Effect.t | Exchange : ('a t * 'a) -> 'a Effect.t | CompareAndSwap : ('a t * 'a * 'a) -> bool Effect.t | FetchAndAdd : (int t * int) -> int Effect.t module IntSet = Set.Make (Int) module IntMap = Map.Make (Int) let _string_of_set s = IntSet.fold (fun y x -> string_of_int y ^ "," ^ x) s "" let tracing = ref false let finished_processes = ref 0 type process_data = { mutable next_op : Atomic_op.t; mutable next_repr : int option; mutable resume_func : (unit, unit) handler -> unit; mutable finished : bool; mutable discontinue_f : unit -> unit; } let every_func = ref (fun () -> ()) let final_func = ref (fun () -> ()) (* Atomics implementation *) let atomics_counter = ref 1 let make v = if !tracing then perform (Make v) else let i = !atomics_counter in atomics_counter := !atomics_counter + 1; (Atomic.make v, i) let make_contended = make let get r = if !tracing then perform (Get r) else match r with v, _ -> Atomic.get v let set r v = if !tracing then perform (Set (r, v)) else match r with x, _ -> Atomic.set x v let exchange r v = if !tracing then perform (Exchange (r, v)) else match r with x, _ -> Atomic.exchange x v let compare_and_set r seen v = if !tracing then perform (CompareAndSwap (r, seen, v)) else match r with x, _ -> Atomic.compare_and_set x seen v let fetch_and_add r n = if !tracing then perform (FetchAndAdd (r, n)) else match r with x, _ -> Atomic.fetch_and_add x n let incr r = ignore (fetch_and_add r 1) let decr r = ignore (fetch_and_add r (-1)) (* Tracing infrastructure *) exception Terminated_early let discontinue k () = discontinue_with k Terminated_early { retc = (fun _ -> ()); exnc = (function Terminated_early -> () | _e -> ()); effc = (fun (type a) (_ : a Effect.t) -> None); } let processes = CCVector.create () let update_process_data process_id f op repr k = let process_rec = CCVector.get processes process_id in process_rec.resume_func <- f; process_rec.next_repr <- repr; process_rec.next_op <- op; process_rec.discontinue_f <- discontinue k let finish_process process_id = let process_rec = CCVector.get processes process_id in process_rec.finished <- true; process_rec.discontinue_f <- (fun () -> ()); finished_processes := !finished_processes + 1 let handler current_process_id runner = { retc = (fun _ -> finish_process current_process_id; runner ()); exnc = (fun s -> raise s); effc = (fun (type a) (e : a Effect.t) -> match e with | Make v -> Some (fun (k : (a, _) continuation) -> let i = !atomics_counter in let m = (Atomic.make v, i) in atomics_counter := !atomics_counter + 1; update_process_data current_process_id (fun h -> continue_with k m h) Make (Some i) k; runner ()) | Get (v, i) -> Some (fun (k : (a, _) continuation) -> update_process_data current_process_id (fun h -> continue_with k (Atomic.get v) h) Get (Some i) k; runner ()) | Set ((r, i), v) -> Some (fun (k : (a, _) continuation) -> update_process_data current_process_id (fun h -> continue_with k (Atomic.set r v) h) Set (Some i) k; runner ()) | Exchange ((a, i), b) -> Some (fun (k : (a, _) continuation) -> update_process_data current_process_id (fun h -> continue_with k (Atomic.exchange a b) h) Exchange (Some i) k; runner ()) | CompareAndSwap ((x, i), s, v) -> Some (fun (k : (a, _) continuation) -> let rec status = ref (`Unknown (fun () -> let result = Atomic.get x == s in status := if result then `Success else `Fail; if result then `Success else `Fail)) in update_process_data current_process_id (fun h -> continue_with k ((match !status with | `Success | `Fail -> failwith "this result has been predicted" | `Unknown _ -> ()); let result = Atomic.compare_and_set x s v in status := if result then `Success else `Fail; result) h) (Atomic_op.CompareAndSwap status) (Some i) k; runner ()) | FetchAndAdd ((v, i), x) -> Some (fun (k : (a, _) continuation) -> update_process_data current_process_id (fun h -> continue_with k (Atomic.fetch_and_add v x) h) FetchAndAdd (Some i) k; runner ()) | _ -> None); } let spawn f = let fiber_f = fiber f in let resume_func = continue_with fiber_f () in CCVector.push processes { next_op = Start; next_repr = None; resume_func; finished = false; discontinue_f = discontinue fiber_f; } let rec last_element l = match l with h :: [] -> h | [] -> assert false | _ :: tl -> last_element tl type proc_rec = { proc_id : int; op : Atomic_op.t; obj_ptr : int option } type state_cell = { procs : proc_rec list; run_proc : int; run_op : Atomic_op.t; run_ptr : int option; enabled : IntSet.t; mutable backtrack : IntSet.t; } let sleep_set_blocked = ref 0 let num_states = ref 0 let num_interleavings = ref 0 (* we stash the current state in case a check fails and we need to log it *) let schedule_for_checks = ref [] let var_name i = match i with | None -> "" | Some i -> let c = Char.chr (i + 96) in Printf.sprintf "%c" c let print_execution_sequence chan = let highest_proc = List.fold_left (fun highest (curr_proc, _, _) -> if curr_proc > highest then curr_proc else highest) (-1) !schedule_for_checks in let bar = List.init ((highest_proc * 20) + 20) (fun _ -> "-") |> String.concat "" in Printf.fprintf chan "\nsequence %d\n" !num_interleavings; Printf.fprintf chan "%s\n" bar; List.init (highest_proc + 1) (fun proc -> Printf.fprintf chan "P%d\t\t\t" proc) |> ignore; Printf.fprintf chan "\n%s\n" bar; List.iter (fun s -> match s with | last_run_proc, last_run_op, last_run_ptr -> let last_run_ptr = var_name last_run_ptr in let tabs = List.init last_run_proc (fun _ -> "\t\t\t") |> String.concat "" in Printf.fprintf chan "%s%s %s\n" tabs (Atomic_op.to_str last_run_op) last_run_ptr) !schedule_for_checks; Printf.fprintf chan "%s\n%!" bar let interleavings_chan = (ref None : out_channel option ref) let record_traces_flag = ref false let do_run init_func init_schedule = init_func (); (*set up run *) tracing := true; schedule_for_checks := init_schedule; (* cache the number of processes in case it's expensive*) let num_processes = CCVector.length processes in (* current number of ops we are through the current run *) finished_processes := 0; let rec run_trace s true_schedule_rev () = tracing := false; !every_func (); tracing := true; match s with | [] -> if !finished_processes == num_processes then ( tracing := false; num_interleavings := !num_interleavings + 1; if !record_traces_flag then Trace_tracker.add_trace (List.rev true_schedule_rev); (match !interleavings_chan with | None -> () | Some chan -> print_execution_sequence chan); !final_func (); tracing := true) | (process_id_to_run, next_op, next_ptr) :: schedule -> ( if !finished_processes == num_processes then (* this should never happen *) failwith "no enabled processes" else let process_to_run = CCVector.get processes process_id_to_run in let at = process_to_run.next_op in assert (Atomic_op.weak_cmp process_to_run.next_op next_op); assert (process_to_run.next_repr = next_ptr); let true_schedule_rev = (process_id_to_run, process_to_run.next_op, process_to_run.next_repr) :: true_schedule_rev in process_to_run.resume_func (handler process_id_to_run (run_trace schedule true_schedule_rev)); match at with | CompareAndSwap cas -> ( match !cas with `Unknown _ -> assert false | _ -> ()) | _ -> ()) in tracing := true; run_trace init_schedule [] (); finished_processes := 0; tracing := false; num_states := !num_states + 1; let procs = CCVector.mapi (fun i p -> { proc_id = i; op = p.next_op; obj_ptr = p.next_repr }) processes |> CCVector.to_list in let current_enabled = CCVector.to_seq processes |> OSeq.zip_index |> Seq.filter (fun (_, proc) -> not proc.finished) |> Seq.map (fun (id, _) -> id) |> IntSet.of_seq in CCVector.iter (fun proc -> proc.discontinue_f ()) processes; CCVector.clear processes; atomics_counter := 1; match last_element init_schedule with | run_proc, run_op, run_ptr -> { procs; enabled = current_enabled; run_proc; run_op; run_ptr; backtrack = IntSet.empty; } let rec explore_random func state = let s = last_element state in let enabled = IntSet.to_seq s.enabled |> List.of_seq in let len = List.length enabled in if len == 0 then () else let random_index = Random.int len in let j = List.nth enabled random_index in let j_proc = List.nth s.procs j in let schedule = List.map (fun s -> (s.run_proc, s.run_op, s.run_ptr)) state @ [ (j, j_proc.op, j_proc.obj_ptr) ] in let statedash = state @ [ do_run func schedule ] in explore_random func statedash let same_proc state_cell1 state_cell2 = state_cell1.run_proc = state_cell2.run_proc module Causality = struct let hb ((proc1 : int), (ptr1 : int option), op1) ((proc2 : int), (ptr2 : int option), op2) = (* assumes the two ops are adjacent *) let same_proc = proc1 = proc2 in let same_var = match (ptr1, ptr2) with | Some ptr1, Some ptr2 -> ptr1 = ptr2 | Some _, None | None, Some _ | None, None -> false in let conflicting = let is_write = Atomic_op.is_write ~allow_unknown:true in Lazy.from_val (is_write op1 || is_write op2) in same_proc || (same_var && Lazy.force conflicting) let happens_before = function | `State (state_cell1, state_cell2) -> hb (state_cell1.run_proc, state_cell1.run_ptr, state_cell1.run_op) (state_cell2.run_proc, state_cell2.run_ptr, state_cell2.run_op) | `Proc (proc1, proc2) -> hb (proc1.proc_id, proc1.obj_ptr, proc1.op) (proc2.proc_id, proc2.obj_ptr, proc2.op) let mark_happen_before operation (sequence : state_cell list) = let sequence = List.map (fun v -> (v, ref false)) sequence in let sequence = (operation, ref true) :: sequence in let mark_intransitive hd_sequence tl_sequence = List.iter (fun (state_cell, hb) -> hb := !hb || happens_before (`State (hd_sequence, state_cell))) tl_sequence in let rec mark_all = function | [] | _ :: [] -> () | (op, hb) :: tl -> if !hb then mark_intransitive op tl; mark_all tl in mark_all sequence; match sequence with | [] -> assert false | (operation', _) :: sequence -> assert (operation == operation'); sequence end let is_reversible_race (op1 : state_cell) (between : state_cell list) (op2 : state_cell) = let hb_intransitively = (* Two ops have to be causally related for us to want to reverse them. *) Causality.happens_before (`State (op1, op2)) in let diff_proc = (* If two ops belong two the same proc, they cannot be reversed. *) not (same_proc op1 op2) in if hb_intransitively && diff_proc then let not_transitively_related = (* If two ops are related transitively, technically not a race (see paper). *) let between = Causality.mark_happen_before op1 between in let between_hb = List.filter_map (fun (op, hb) -> if !hb then Some op else None) between in let op2_not_transitively_related = List.for_all (fun op -> not (Causality.happens_before (`State (op2, op)))) between_hb in op2_not_transitively_related in not_transitively_related else false let filter_out_happen_after operation sequence = Causality.mark_happen_before operation sequence |> List.filter_map (fun (op, hb) -> if !hb then None else Some op) let rec explore_source func state sleep_sets = (* The code here closely follows the Algorithm 1 outlined in [Source Sets: A Foundation for Optimal Dynamic Partial Order Reduction]. Likewise variable names (e.g. reversible race, indep_and_p, initials) etc. reference constructs introduced in the paper. *) let sleep = ref (last_element sleep_sets) in let s = last_element state in let p_maybe = IntSet.min_elt_opt (IntSet.diff s.enabled !sleep) in match p_maybe with | None -> if not (IntSet.is_empty s.enabled) then sleep_set_blocked := !sleep_set_blocked + 1 | Some p -> s.backtrack <- IntSet.singleton p; while IntSet.(cardinal (diff s.backtrack !sleep)) > 0 do let p = IntSet.min_elt (IntSet.diff s.backtrack !sleep) in let proc = List.nth s.procs p in let state_top = let schedule = List.map (fun s -> (s.run_proc, s.run_op, s.run_ptr)) state @ [ (p, proc.op, proc.obj_ptr) ] in do_run func schedule in assert (state_top.run_proc = p); let new_state = state @ [ state_top ] in (* Find the races (transitions dependent directly, without a transitive dependency). *) let reversible_races = List.fold_right (fun op1 ((between, reversible_races, skip_rest) as acc) -> if skip_rest then acc else if is_reversible_race op1 between state_top then ( op1 :: between, op1 :: reversible_races, Atomic_op.is_write op1.run_op ) else (op1 :: between, reversible_races, false)) state ([], [], false) |> function | _, l, _ -> l in List.iter (fun e -> let prefix, suffix = (* We need the last operation before the first operation of the race occured because that's where alternative (reversal) is scheduled. We need the suffix to compute how to schedule the reversal. *) let found_e, prefix_rev, suffix_rev = List.fold_left (fun (seen_e, prefix, suffix) proc' -> if seen_e then (seen_e, prefix, proc' :: suffix) else if proc' == e then (true, prefix, suffix) else (false, proc' :: prefix, suffix)) (false, [], []) state in assert found_e; (* Out first operation is always a spawn, which cannot race with anything. Thus, any race has a prefix. *) assert (List.length prefix_rev > 0); assert ( List.length suffix_rev = List.length state - List.length prefix_rev - 1); (List.rev prefix_rev, List.rev suffix_rev) in (* Filter out operations that are dependent on the first operation of the race (e.g. successive operations of e.run_proc). We definitely don't want to schedule them. *) let indep_and_p = let indep = filter_out_happen_after e suffix in indep @ [ state_top ] in (* Compute the set of operations, that lead to reversal of the race. The main premise here is that there may be a number of independent sequences that lead to reversal. For example, suppose two racing operations t, t' and some sequences E, w, u. Suppose the current sequence is E.t.w.u.t', t' happens after u and w is independent of everything. There's at least two ways to reverse t and t': - E.u.t'.(t,w in any order) - E.w.u.t'.t Thus, initials consist of the first operations of u and w, since these are the operations that lead to exploration of the above sequences from E. *) let initials = let rec loop = function | [] -> [] | initial :: sequence -> initial.run_proc :: loop (filter_out_happen_after initial sequence) in loop indep_and_p in (* Exploring one of the initials guarantees that reversal has been visited. Thus, schedule one of the initials only if none of them is in backtrack. *) let prefix_top = last_element prefix in if IntSet.(cardinal (inter prefix_top.backtrack (of_list initials))) = 0 && let slp = List.nth sleep_sets (List.length prefix - 1) in IntSet.(cardinal (inter slp (of_list initials))) = 0 then (* We can add any initial *) let initial = last_element initials in prefix_top.backtrack <- IntSet.add initial prefix_top.backtrack) reversible_races; let sleep' = (* Keep q that are independent with p only. Must be other thread of execution and act on a different object (or none). The key idea here is as follows. Suppose we have processed some execution sequence E and there are just two enabled transitions left. Namely, t=(read a), t'=(read b). Crucially, they are independent. We begin the exploration from E with E.t and descend into E.t.t' afterwards. Since no more transitions are enabled, we return back to E and execute E.t'. But there's no point in executing E.t'.t. Since t and t' are independent, there's a guarantee that E.t.t' and E.t'.t belong to the same trace. Therefore, at E, t is put into sleep set, and we explore with E.t' with sleep=[t]. Then E.t'.t gets sleep-set blocked and we save an execution sequence. Naturally, if there's some w such that it's dependent with t, then before we explore E.t'.w, we have to "wake" t up. *) IntSet.filter (fun q -> let proc' = List.nth s.procs q in not (Causality.happens_before (`Proc (proc, proc')))) !sleep in explore_source func new_state (sleep_sets @ [ sleep' ]); sleep := IntSet.add p !sleep done let rec explore func state clock last_access = let s = last_element state in List.iter (fun proc -> let j = proc.proc_id in let i = Option.bind proc.obj_ptr (fun ptr -> IntMap.find_opt ptr last_access) |> Option.value ~default:0 in if i != 0 then let pre_s = List.nth state (i - 1) in if IntSet.mem j pre_s.enabled then pre_s.backtrack <- IntSet.add j pre_s.backtrack else pre_s.backtrack <- IntSet.union pre_s.backtrack pre_s.enabled) s.procs; if IntSet.cardinal s.enabled > 0 then ( let p = IntSet.min_elt s.enabled in let dones = ref IntSet.empty in s.backtrack <- IntSet.singleton p; while IntSet.(cardinal (diff s.backtrack !dones)) > 0 do let j = IntSet.min_elt (IntSet.diff s.backtrack !dones) in dones := IntSet.add j !dones; let j_proc = List.nth s.procs j in let schedule = List.map (fun s -> (s.run_proc, s.run_op, s.run_ptr)) state @ [ (j, j_proc.op, j_proc.obj_ptr) ] in let statedash = state @ [ do_run func schedule ] in let state_time = List.length statedash - 1 in let new_last_access = match j_proc.obj_ptr with | Some ptr -> IntMap.add ptr state_time last_access | None -> last_access in let new_clock = IntMap.add j state_time clock in explore func statedash new_clock new_last_access done) let every f = every_func := f let final f = final_func := f let check f = let tracing_at_start = !tracing in tracing := false; if not (f ()) then ( Printf.printf "Found assertion violation at run %d:\n" !num_interleavings; print_execution_sequence stdout; assert false); tracing := tracing_at_start let reset_state () = finished_processes := 0; atomics_counter := 1; num_states := 0; sleep_set_blocked := 0; num_interleavings := 0; schedule_for_checks := []; Trace_tracker.clear_traces (); CCVector.clear processes let dscheck_trace_file_env = Sys.getenv_opt "dscheck_trace_file" let random func iters = reset_state (); let empty_state = do_run func [ (0, Start, None) ] :: [] in for _ = 1 to iters do explore_random func empty_state done let dpor func = reset_state (); let empty_state = do_run func [ (0, Start, None) ] :: [] in let empty_clock = IntMap.empty in let empty_last_access = IntMap.empty in explore func empty_state empty_clock empty_last_access let dpor_source func = reset_state (); let empty_state = do_run func [ (0, Start, None) ] in explore_source func [ empty_state ] [ IntSet.empty ] let trace ?(impl = `Dpor_source) ?interleavings ?(record_traces = false) func = record_traces_flag := record_traces || Option.is_some dscheck_trace_file_env; interleavings_chan := interleavings; (match impl with | `Dpor -> dpor func | `Random iters -> random func iters | `Dpor_source -> dpor_source func); (* print reports *) if record_traces && Option.is_none !interleavings_chan then interleavings_chan := Some stdout; (match !interleavings_chan with | None -> () | Some chan -> Printf.fprintf chan "\nexplored %d interleavings and %d states\n" !num_interleavings !num_states); match dscheck_trace_file_env with | None -> () | Some path -> let chan = open_out path in Trace_tracker.print_traces chan; close_out chan dscheck-0.5.0/src/tracedAtomic.mli0000644000175000017500000000725114641522241015511 0ustar kylekyle(**************************************************************************) (* *) (* OCaml *) (* *) (* Stephen Dolan, University of Cambridge *) (* Gabriel Scherer, projet Partout, INRIA Paris-Saclay *) (* *) (* Copyright 2020 Institut National de Recherche en Informatique et *) (* en Automatique. *) (* *) (* All rights reserved. This file is distributed under the terms of *) (* the GNU Lesser General Public License version 2.1, with the *) (* special exception on linking described in the file LICENSE. *) (* *) (**************************************************************************) type !'a t (** An atomic (mutable) reference to a value of type ['a]. *) val make : 'a -> 'a t (** Create an atomic reference. *) val make_contended : 'a -> 'a t (** Create an atomic reference that is alone on a cache line. It occupies 4-16x the memory of one allocated with make v. The primary purpose is to prevent false-sharing and the resulting performance degradation. When a CPU performs an atomic operation, it temporarily takes ownership of an entire cache line that contains the atomic reference. If multiple atomic references share the same cache line, modifying these disjoint memory regions simultaneously becomes impossible, which can create a bottleneck. Hence, as a general guideline, if an atomic reference is experiencing contention, assigning it its own cache line may enhance performance. *) val get : 'a t -> 'a (** Get the current value of the atomic reference. *) val set : 'a t -> 'a -> unit (** Set a new value for the atomic reference. *) val exchange : 'a t -> 'a -> 'a (** Set a new value for the atomic reference, and return the current value. *) val compare_and_set : 'a t -> 'a -> 'a -> bool (** [compare_and_set r seen v] sets the new value of [r] to [v] only if its current value is physically equal to [seen] -- the comparison and the set occur atomically. Returns [true] if the comparison succeeded (so the set happened) and [false] otherwise. *) val fetch_and_add : int t -> int -> int (** [fetch_and_add r n] atomically increments the value of [r] by [n], and returns the current value (before the increment). *) val incr : int t -> unit (** [incr r] atomically increments the value of [r] by [1]. *) val decr : int t -> unit (** [decr r] atomically decrements the value of [r] by [1]. *) val trace : ?impl:[ `Random of int | `Dpor | `Dpor_source ] -> ?interleavings:out_channel -> ?record_traces:bool -> (unit -> unit) -> unit (** [trace ?interleavings ?record_traces f] starts the simulation trace. [impl] lets user choose the underlying exploration algorithm. If [interleavings] output channel is provided, DSCheck will continously print the visited interleavings there. [record_traces] enables [Trace_tracker], which is typically used for testing DSCheck itself. *) val spawn : (unit -> unit) -> unit (** spawn [f] as a new 'thread' *) val check : (unit -> bool) -> unit (** if [f] returns false then an assertion has failed *) val final : (unit -> unit) -> unit (** run [f] after all processes complete *) val every : (unit -> unit) -> unit (** run [f] between every possible interleaving *) dscheck-0.5.0/src/atomic_op.ml0000644000175000017500000000261614641522241014713 0ustar kylekyletype t = | Start | Make | Get | Set | Exchange | CompareAndSwap of [ `Success | `Fail | `Unknown of unit -> [ `Success | `Fail ] ] ref | FetchAndAdd let to_str x = match x with | Start -> "start" | Make -> "make" | Get -> "get" | Set -> "set" | Exchange -> "exchange" | CompareAndSwap _ -> "compare_and_swap" | FetchAndAdd -> "fetch_and_add" let is_write ?(allow_unknown = false) op = (* [allow_unknown] is a switch that lets enabled evaluation of undetermined operations. We sometimes need to predict the outcome while DSCheck is running, since it stops right before the operation executes. Elsewhere, e.g. trace_tracker, we operatore on the true history of terminated execution and should never need this. *) match op with | Get | Make -> false | CompareAndSwap outcome -> ( match !outcome with | `Success -> true | `Fail -> false | `Unknown currently_f -> ( assert allow_unknown; match currently_f () with `Success -> true | `Fail -> false)) | _ -> true let weak_cmp t1 t2 = let to_int = function | Start -> 0 | Make -> 1 | Get -> 2 | Set -> 3 | Exchange -> 4 | FetchAndAdd -> 5 | CompareAndSwap _ -> assert false in match (t1, t2) with | CompareAndSwap _, CompareAndSwap _ -> true | CompareAndSwap _, _ | _, CompareAndSwap _ -> false | _, _ -> to_int t1 = to_int t2 dscheck-0.5.0/src/trace_tracker.ml0000644000175000017500000000740414641522241015552 0ustar kylekylemodule Op = struct type t = { proc : int; variable : int; step : int; atomic_op : Atomic_op.t } let is_dependent t1 t2 = t1.variable == t2.variable && (Atomic_op.is_write t1.atomic_op || Atomic_op.is_write t2.atomic_op) let compare_proc_step t1 t2 = let c1 = Int.compare t1.proc t2.proc in if c1 <> 0 then c1 else Int.compare t1.step t2.step let to_str t = let rw = if Atomic_op.is_write t.atomic_op then "w" else "r" in Printf.sprintf "(%d,%c,%s)" t.proc (Char.chr (t.variable + 96)) rw end module OpSet = struct include Set.Make (struct include Op let compare = compare_proc_step end) let to_str t = to_seq t |> List.of_seq |> List.map Op.to_str |> String.concat ", " |> Printf.sprintf "(%s)" end module Trace = struct module Key = struct type t = (Op.t * OpSet.t) list let compare (t1 : t) t2 = List.compare (fun (op1, dep1) (op2, dep2) -> let c1 = Op.compare_proc_step op1 op2 in if c1 <> 0 then c1 else OpSet.compare dep1 dep2) t1 t2 end type t = Op.t List.t let of_schedule_for_checks schedule_for_checks : t = let steps = Hashtbl.create 10 in List.map (fun (proc, atomic_op, variable) -> Option.map (fun variable : Op.t -> let current = Hashtbl.find_opt steps proc |> Option.value ~default:0 in Hashtbl.replace steps proc (current + 1); let step = Hashtbl.find steps proc in { proc; variable; step; atomic_op }) variable) schedule_for_checks |> List.filter_map Fun.id let to_string t = List.map Op.to_str t |> String.concat "," let tag_with_deps (t : t) : Key.t = let next_dep op tl = let _, deps = List.fold_left (fun (seen_transitive, deps) curr_op -> if seen_transitive then (true, deps) else if Option.is_some (OpSet.find_first_opt (Op.is_dependent curr_op) deps) then (true, deps) else if Op.is_dependent op curr_op then (false, OpSet.add curr_op deps) else (false, deps)) (false, OpSet.empty) tl in deps in let rec attach_deps t = match t with | [] -> [] | hd :: [] -> [ (hd, OpSet.empty) ] | hd :: tl -> (hd, next_dep hd tl) :: attach_deps tl in let tagged = attach_deps t in List.sort (fun (op1, _) (op2, _) -> Op.compare_proc_step op1 op2) tagged let deps_to_str (key : Key.t) : string = List.map (fun (op, deps) -> Op.to_str op ^ "-" ^ OpSet.to_str deps) key |> String.concat "," end module TraceMap = Map.Make (Trace.Key) type t = Trace.t TraceMap.t let traces = ref TraceMap.empty let add_trace trace = let trace = Trace.of_schedule_for_checks trace in let key = Trace.tag_with_deps trace in traces := TraceMap.update key (function Some v -> Some v | None -> Some trace) !traces let print traces channel = Printf.fprintf channel "----\n"; TraceMap.iter (fun _ trace -> Printf.fprintf channel "%s\n" (Trace.to_string trace)) traces; Printf.fprintf channel "----\n"; flush channel let print_traces chan = print !traces chan let clear_traces () = traces := TraceMap.empty let get_traces () = !traces let get_deps_str traces = TraceMap.to_seq traces |> List.of_seq |> List.map (fun (_, value) -> value) |> List.map Trace.tag_with_deps |> List.map Trace.deps_to_str |> String.concat "\n" let equal t1 t2 = TraceMap.compare (fun _ _ -> 0 (* any values under the same key are known to be equivalent, even if the exact sequence is not identical *)) t1 t2 == 0 let subset t1 t2 = TraceMap.fold (fun key _ seen_all -> TraceMap.mem key t2 && seen_all) t1 true let count = TraceMap.cardinal dscheck-0.5.0/src/trace_tracker.mli0000644000175000017500000000047014641522241015717 0ustar kylekyletype t val add_trace : (int * Atomic_op.t * int option) list -> unit val clear_traces : unit -> unit val get_traces : unit -> t val print_traces : out_channel -> unit val print : t -> out_channel -> unit val equal : t -> t -> bool val get_deps_str : t -> string val subset : t -> t -> bool val count : t -> int dscheck-0.5.0/src/dune0000644000175000017500000000014614641522241013261 0ustar kylekyle(library (public_name dscheck) (enabled_if (>= %{ocaml_version} 5)) (libraries containers oseq)) dscheck-0.5.0/README.md0000644000175000017500000004206314641522241013077 0ustar kylekyle# DSCheck — tool for testing concurrent OCaml programs Experimental model checker for testing concurrent programs. DSCheck explores interleavings of a user-provided program and helps ensure that its invariants are maintained regardless of scheduling decisions. # Contents 1. [Motivation](#motivation) 2. [Get DSCheck](#get-dscheck) 3. [Usage](#usage) 4. [Development](#development) 5. [Contributions](#contributions) 6. [References](#references) # Motivation As experience shows, fine-grained concurrency is notoriously challenging to get right. - As the program grows, the number of possible interleavings increases exponentially and quickly becomes too large for a human to reasonably validate. That's exacerbated by the fact that different interleavings often lead to the right outcome for different reasons. - Certain concurrency bugs manifest rarely and are borderline impossible to reproduce. They may occur under specific system conditions only and disappear when a debugging system is attached. DSCheck helps manage this complexity by letting us instrument a multicore test to explore relevant interleavings. Thus ensuring that all terminal states are valid and no edge cases have been missed. # Get DSCheck Dscheck can be installed from `opam`: `opam install dscheck`. # Usage Sample usage on [naive counter](tests/test_naive_counter.ml) is shown below. ```ocaml module Atomic = Dscheck.TracedAtomic (* the test needs to use DSCheck's atomic module *) let test_counter () = let counter = Atomic.make 0 in let incr () = Atomic.set counter (Atomic.get counter + 1) in Atomic.spawn incr; Atomic.spawn incr; Atomic.final (fun () -> Atomic.check (fun () -> Atomic.get counter == 2)) ``` The test spawns two domains (`Atomic.spawn`), each trying to increase the counter. The assertion at the end validates that counter has the expected value (`Atomic.final`). This is a classic example of a race condition with two threads trying to perform read-modify-write operation without synchronisation. In effect, there is a risk of losing one of the updates. DSCheck finds and reports the offending interleaving to the user: ``` Found assertion violation at run 2: sequence 2 ---------------------------------------- P0 P1 ---------------------------------------- start get a start get a set a set a ---------------------------------------- ``` ## Validation Soundness For model-checking to be sound, tested program must meet the following conditions: - Determinism. Otherwise DSCheck may encounter errors or (more dangerously) terminate successfuly without exploring all traces. - Tested programs cannot have races between non-atomic variables. DSCheck does not explore different _behaviours_, (e.g. a non-atomic read may see the most recently written value or a number of stale ones). - Domains can communicate through atomic variables only. Validation including higher-level synchronisation primitives is possible but constitutes future work. - Tested programs have to be at least lock-free. If any thread cannot finish on its own, DSCheck will explore its transitions ad infinitum. As some remedy, the space of traces can be covered partially by forcing the test to be lock-free. For example, spinlock can be modified to fail explicitely once some artifical limit is reached. ## Validation Logic As highlighted in the [Motivation](#motivation), the number of interleavings tends to grow exponentialy with size of the program and the number of threads. It follows that the interleavings of even small programs are not just impossible for humans to walk through but also incomputable in reasonable time. The key advance that made DSCheck-like model-checkers possible is the emergence of dynamic partial-order reduction (DPOR) methods. The application to model-checking stems from the observation that in real-world programs many interleavings are equivalent and if at least one is covered, so is the entire equivalence class. More formally, a particular interleaving is a total order induced by a causal relation between events of different domains (partial-order). DSCheck aims to cover exactly one interleaving per trace. While the DPOR algorithms and formalism tend to be quite involved, the emergent behavior is intuitive. Consider the following program: ```ocaml let a = Atomic.make 0 in let b = Atomic.make 0 in (* Domain P *) Domain.spawn (fun () -> Atomic.set a 1; Atomic.set b 1; ) |> ignore; (* Domain Q *) Domain.spawn (fun () -> Atomic.set a 2; ) |> ignore ``` There are three possible interleavings: `P.P.Q`, `P.Q.P`, `Q.P.P`. Clearly, the ordering between _Q_ and the second step of _P_ does not matter. Thus the execution sequences `P.P.Q` and `P.Q.P` are different realizations of the same trace. DPOR skips the redundant execution sequences and provides an exponential improvement over the naive search. That in turn significantly expands the universe of checkable programs and makes this enumeration useful. ### Reads The leading example showcases reduction of search space based on accesses to disjoint locations. A similar approach can be taken for accesses on overlapping locations that do not conflict. If _P_ and _Q_ had only read memory, there would have been no race between them, in turn requiring DSCheck to explore only a single interleaving. ```ocaml let a = Atomic.make 0 in (* P *) Atomic.spawn (fun () -> ignore (Atomic.compare_and_set a 1 2)); (* Q *) Atomic.spawn (fun () -> ignore (Atomic.compare_and_set a 2 3)); ``` Compare and set is a read-write operation. In this particular case, however, both CASes fail and leave the initial value untouched. DSCheck recognizes such special cases and avoids exploration of redundant interleavings. ### Causal Ordering ```ocaml let a = Atomic.make 0 in let b = Atomic.make 0 in Atomic.spawn (fun () -> Atomic.set a 1; (* P *) Atomic.set b 1; (* Q *)); Atomic.spawn (fun () -> Atomic.set a 2; Atomic.set b 2); ``` In more general sense, DSCheck tracks causal order between events of different domains and tries to schedule sequences reversing it, where possible. At times, it may be counterintuitive. In the example above DSCheck explores 4 interleavings. What if we swap the lines `P` and `Q`? # Development ## Design Notes DSCheck sees test as a graph, where edge is a step in execution of one of the active domains. The validation involves traversing the graph in a depth-first fashion and running user assertions in the leaf states. For example, the graph for [Example Reads](#reads) looks as follows: ``` Start (a=0) ---> P: CompareAndSet a 1 2 ---> Q: CompareAndSet a 2 3 | | \/ \/ Q: CompareAndSet a 2 3 ---> P: CompareAndSet a 1 2 ---> Termination (a=0) ``` DSCheck begins by running the main function of the test, which registers domains P and Q. Then, the exploration starts by taking a step into execution of either domains and follows with one step of the other, thus arriving at the terminal state. In the case above both paths are equivalent (hence shared leaf node), but it naturally does not have to be the case, e.g. variable `a` could be initialized with `1` making both paths unique traces. Consider the following. ``` Start (a=1) --> P: CompareAndSet a 1 2 --> Q: CompareAndSet a 2 3 | | | \/ | Termination (a=3) \/ Q: CompareAndSet a 2 3 ---> P: CompareAndSet a 1 2 ---> Termination (a=2) ``` _P_ and _Q_ are dependent operations and the two interleavings lead to different outcomes. Thus DSCheck has to explore both. Skimming over the details, DSCheck begins by exploring the first branch, `P.Q`, to the end and schedules new transitions on the way there. Here, it will notice that _P_ and _Q_ are potentially racing operations (since it's a read-write pair on the same location) and schedule transition _Q_ after start. We call that a _race reversal_. The DFS exploration may look tricky at first. The key idea to realize is that at any step in the sequence, model-checker aims to explore all the traces produced by remaining events. For some events _c_-_z_ and execution sequence `x.c.w`, DSCheck has to explore all traces produced by the remaining events. If _g_ and _h_ are dependent and _A_, _B_ some sequences, it has to explore at least `x.c.w.A.g.h.B` and `x.c.w.A.h.g.B` (or equivalent). It does so by choosing a random path and continuously scheduling sequences reversing encountered races. The key optimization techniques identify transitions leading to sequences, which are equal to some already explored ones. - Persistent/source sets. A DPOR algorithm has to execute at least all the transition in the source set at a particular state to ensure that all revelant interleavings are explored. Once such a set has been explored, there's no need to explore any other transitions from that state. See [Comparing Source Sets and Persistent Sets for Partial Order Reduction](https://user.it.uu.se/~bengt/Papers/Full/kimfest17.pdf). - Sleep sets. At times, we can suspend exploration of a transition until a relevant event occurs. For example, if _x_ and _c_ are independent and we have explored `x.c`, there's no need to explore `c.x` unless some other event (dependent with _x_) occurs. ## Testing The formalism underpinning DPOR leads to a fairly straightforward testing setup. For any two execution sentences, they belong to the same trace if reordering of commutative operations leads from one to the other. For example, operations `P:(read a), Q:(read b)` clearly commute (since their reordering leads to the same outcome), while `P:(write a), Q:(write a)` may not. Thus, since one sequence can be transformed into another, they are realizations of the same trace. Whenever DSCheck explores multiple sequences for a single trace, it constitutes an inefficiency. Conversely, if a change to the DPOR logic leaves some traces without unexplored, it is incorrect. Note, the assignment of execution sequences to traces uses the definition of dependency relation. If the change improves dependency relation rather than DPOR, we would expect to see new pairs of equivalent execution sequences and thus groups of multiple traces collapsing into one. To facilitate the testing, DSCheck includes a random test generator and a trace deduplication mechanism. For any proposed change, we can generate a large number of tests and ensure that the same traces have been explored. Furthermore, if reference implementation is suspicious itself or too inefficient, the proposed change can be asserted to explore a superset of traces explored by a random search. The trace deduplication mechanism took a few iterations to get right. Generally, the approach involving extracting traces (happens-before) from sequences and comparing those turned out to be more robust than the attempts to bring the execution sequences into some normal form and compare directly. ## Literature Glossary Literature defines a lot of new term. While the rigour is important for implementation, here's brief explanation in the context of DSCheck. - Event. Modification of shared state or communication between threads. - Transition. One step forward into execution of a particular domain. That includes the atomic operation it suspended on and all non-atomic operation precedent the next atomic call. In the case of DSCheck one transition is a single event. - Execution sequence. A particular interleaving of a program. - Trace. A class of equivalent execution sequences. An optimal DPOR explores exactly one execution sequence per trace. - Dependency relationship. A binary relation from two transitions to boolean indicating whether events are dependent. If two adjacent events are independent, then they commute, i.e. swapping two adjacent independent events produces a new execution sequence that constitutes the same trace. Thus, DPOR focuses on reordering pairs of dependent events. - Happens-before relationship. A superset of dependency relationship, which includes program order. - Reversible race. Two events executed by different domains, which are hb-related directly and not transitively. The latter lets us avoid some redundant exploration. - Maximal trace. Trace that terminates all domains. - Enabling/disabling transitions. Some transitions may enable or disable transitions in other domains, e.g. domain A taking a lock renders any other acquisition attempts disabled. Currently not implemented but worth mentioning as it is often used in the literature. ## Future Work - DSCheck was written with validation of lock-free structures in mind and handles single-word atomic operations only. There is a wealth of other thread-safe communication and synchroniation methods and, in principle, we should be able to validate all of them. - Non-atomic accesses. OCaml's memory model gives a precise semantics to concurrent non-atomic accesses. These could be verified with DSCheck as well. The key part seems to be the possibility of reading a stale value. Thus, DSCheck should maintain the list of values that may be read from any non-atomic location and ensure that program works in all cases. See [CDSCHECKER: Checking Concurrent Data Structures Written with C/C++ Atomics](http://plrg.eecs.uci.edu/publications/c11modelcheck.pdf) for more details. - High-level primitives, e.g. lock, channel, join. Currently, DSCheck cannot terminate on any program weaker than lock-free. Blocking primitives need explicit support. Section 5 [Source Sets: A Foundation for Optimal Dynamic Partial Order Reduction](https://user.it.uu.se/~parosh/publications/papers/jacm17.pdf) includes a modification of Source- and Optimal-DPOR allowing blocking operations. - Kcas, to validate programs using [kcas](https://github.com/ocaml-multicore/kcas) efficiently. That fits into Source-DPOR and the existing implementation quite naturally as operations with multiple happens-before and happens-after dependencies. - Further performance improvements. In particular implementation of wake-up trees to eliminate sleep-set blocking or a leap towards [TruST](https://plv.mpi-sws.org/genmc/popl2022-trust.pdf). - Support nested domain spawns and concurrent logic in the main test function. DSCheck lets us spawn n domains in the main test function and validate their interleavings, which is enough to test lock-free algorithms, but many real-world programs are more complicated. ## Reference Implementations - [CDSChecker](https://github.com/computersforpeace/model-checker) for the original DPOR implementation. - [Nidhugg](https://github.com/nidhugg/nidhugg/) for Source-DPOR. ### Nidhugg Nidhugg may come helpful for troubleshooting DSCheck. It's based on the same publication and, although aimed at C/C++ programs, it does have sequential consistency mode as well. Install it as per instructions in the repository. Consider the following program. It spawns two threads, each trying to increment `a` with CAS. Note making the variable `zero` local reduces accesses to shared memory and lowers the amount of noise. ```C #include #include atomic_int a; static void *t(void *arg) { int zero = 0; atomic_compare_exchange_strong(&a, &zero, 1); return NULL; } int main() { pthread_t tid[2]; atomic_init(&a, 0); pthread_create(&tid[0], NULL, t, (void *)(uintptr_t)0); pthread_create(&tid[1], NULL, t, (void *)(uintptr_t)0); pthread_join(tid[0], NULL); pthread_join(tid[1], NULL); return 0; } ``` Save the program as `test.c` and run using the following command: `nidhuggc -- --debug-print-on-reset --sc --source test.c 2>&1 | rg "(Cmp|=)"`. ``` === TSOTraceBuilder reset === (<0.0>,1-4) CmpXhg(Global(1)(4),0x0,0x1) SLP:{} - (<0.1>,1) (<0.1>,1-5) CmpXhgFail(Global(1)(4),0x0,0x1) SLP:{} ============================= === TSOTraceBuilder reset === (<0.1>,1) CmpXhg(Global(1)(4),0x0,0x1) SLP:{<0.0>} (<0.0>,1-5) CmpXhgFail(Global(1)(4),0x0,0x1) SLP:{} ============================= ``` The output shows visited interleavings. It also displays contents of the `sleep` and `backtrack` sets at any stage. To get a better understanding of how nidhugg takes particular decision consider adding log statements to `TSOTraceBuilder::race_detect`. # Contributions Contributions are appreciated! Please create issues/PRs to this repo. # References - [Source Sets: A Foundation for Optimal Dynamic Partial Order Reduction](https://user.it.uu.se/~parosh/publications/papers/jacm17.pdf) - [CDSCHECKER: Checking Concurrent Data Structures Written with C/C++ Atomics](http://plrg.eecs.uci.edu/publications/c11modelcheck.pdf) - [Dynamic Partial-Order Reduction for Model Checking Software](https://users.soe.ucsc.edu/~cormac/papers/popl05.pdf) - [Comparing Source Sets and Persistent Sets for Partial Order Reduction](https://user.it.uu.se/~bengt/Papers/Full/kimfest17.pdf) - [Truly Stateless, Optimal Dynamic Partial Order Reduction](https://plv.mpi-sws.org/genmc/popl2022-trust.pdf) dscheck-0.5.0/dune-project0000644000175000017500000000061414641522241014136 0ustar kylekyle(lang dune 3.9) (generate_opam_files true) (name dscheck) (version 0.5.0) (source (github ocaml-multicore/dscheck)) (license ISC) (authors "Sadiq Jaffer") (maintainers "Sadiq Jaffer") (package (name dscheck) (synopsis "Traced Atomics") (allow_empty) (depends (ocaml (>= 4.12.0)) dune containers tsort oseq (alcotest (and (>= 1.6.0) :with-test)) cmdliner)) dscheck-0.5.0/.ocamlformat0000644000175000017500000000004214641522241014114 0ustar kylekyleprofile = default version = 0.26.2dscheck-0.5.0/LICENSE.md0000644000175000017500000000131414641522241013216 0ustar kylekyleCopyright (c) 2022 Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.dscheck-0.5.0/.prettierrc0000644000175000017500000000023214641522241013774 0ustar kylekyle{ "arrowParens": "avoid", "bracketSpacing": false, "printWidth": 80, "semi": false, "singleQuote": true, "proseWrap": "always" }dscheck-0.5.0/tests/0000755000175000017500000000000014641522241012755 5ustar kylekyledscheck-0.5.0/tests/traces/0000755000175000017500000000000014641522241014236 5ustar kylekyledscheck-0.5.0/tests/traces/conditional10000644000175000017500000000051214641522241016543 0ustar kylekyle---- (2,b,r),(1,b,w),(2,a,r),(1,a,w),(0,a,w),(2,c,w) (2,b,r),(1,b,w),(1,a,w),(2,a,r),(0,a,w) (1,b,w),(1,a,w),(0,a,w),(2,b,r) (2,b,r),(1,b,w),(2,a,r),(0,a,w),(1,a,w),(2,c,w) (0,a,w),(2,b,r),(1,b,w),(1,a,w),(2,a,r) (0,a,w),(1,b,w),(1,a,w),(2,b,r) (0,a,w),(2,b,r),(1,b,w),(2,a,r),(1,a,w) (2,b,r),(1,b,w),(1,a,w),(0,a,w),(2,a,r) ---- dscheck-0.5.0/tests/traces/list0000644000175000017500000010341214641522241015135 0ustar kylekyle---- (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) ---- dscheck-0.5.0/tests/traces/conditional_nested0000644000175000017500000000051214641522241020024 0ustar kylekyle---- (2,b,r),(1,b,w),(2,a,r),(1,a,w),(0,a,w),(2,c,w) (2,b,r),(1,b,w),(1,a,w),(2,a,r),(0,a,w) (1,b,w),(1,a,w),(0,a,w),(2,b,r) (2,b,r),(1,b,w),(2,a,r),(0,a,w),(1,a,w),(2,c,w) (0,a,w),(2,b,r),(1,b,w),(1,a,w),(2,a,r) (0,a,w),(1,b,w),(1,a,w),(2,b,r) (0,a,w),(2,b,r),(1,b,w),(2,a,r),(1,a,w) (2,b,r),(1,b,w),(1,a,w),(0,a,w),(2,a,r) ---- dscheck-0.5.0/tests/traces/conditional20000644000175000017500000000053214641522241016546 0ustar kylekyle---- (0,a,r),(2,b,r),(3,c,r),(2,c,w),(3,b,r),(1,b,w) (2,b,r),(2,c,w),(3,c,r),(3,b,r),(1,b,w),(3,a,w),(0,a,r) (0,a,r),(3,c,r),(3,b,r),(1,b,w),(2,b,r) (0,a,r),(1,b,w),(2,b,r),(3,c,r),(3,b,r) (0,a,r),(2,b,r),(1,b,w),(3,c,r),(2,c,w),(3,b,r) (0,a,r),(2,b,r),(1,b,w),(2,c,w),(3,c,r),(3,b,r) (0,a,r),(2,b,r),(2,c,w),(3,c,r),(3,b,r),(1,b,w),(3,a,w) ---- dscheck-0.5.0/tests/traces/conditional_ssb0000644000175000017500000000053214641522241017333 0ustar kylekyle---- (0,a,r),(2,b,r),(3,c,r),(2,c,w),(3,b,r),(1,b,w) (2,b,r),(2,c,w),(3,c,r),(3,b,r),(1,b,w),(3,a,w),(0,a,r) (0,a,r),(3,c,r),(3,b,r),(1,b,w),(2,b,r) (0,a,r),(1,b,w),(2,b,r),(3,c,r),(3,b,r) (0,a,r),(2,b,r),(1,b,w),(3,c,r),(2,c,w),(3,b,r) (0,a,r),(2,b,r),(1,b,w),(2,c,w),(3,c,r),(3,b,r) (0,a,r),(2,b,r),(2,c,w),(3,c,r),(3,b,r),(1,b,w),(3,a,w) ---- dscheck-0.5.0/tests/traces/naive_counter0000644000175000017500000000005214641522241017017 0ustar kylekyle---- (1,a,w),(0,a,w) (0,a,w),(1,a,w) ---- dscheck-0.5.0/tests/traces/ms_queue0000644000175000017500000000641214641522241016007 0ustar kylekyle---- (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) (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) ---- dscheck-0.5.0/tests/michael_scott_queue.ml0000644000175000017500000000434714641522241017341 0ustar kylekyle(* * Copyright (c) 2015, Théo Laurent * Copyright (c) 2015, KC Sivaramakrishnan * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) (* Michael-Scott queue copied over from [saturn](github.com/ocaml-multicore/saturn) *) module Atomic = Dscheck.TracedAtomic type 'a node = Nil | Next of 'a * 'a node Atomic.t type 'a t = { head : 'a node Atomic.t; tail : 'a node Atomic.t } let create () = let head = Next (Obj.magic (), Atomic.make Nil) in { head = Atomic.make_contended head; tail = Atomic.make_contended head } let is_empty q = match Atomic.get q.head with | Nil -> failwith "MSQueue.is_empty: impossible" | Next (_, x) -> ( match Atomic.get x with Nil -> true | _ -> false) let pop q = let rec loop () = let s = Atomic.get q.head in let nhead = match s with | Nil -> failwith "MSQueue.pop: impossible" | Next (_, x) -> Atomic.get x in match nhead with | Nil -> None | Next (v, _) when Atomic.compare_and_set q.head s nhead -> Some v | _ -> loop () in loop () let push q v = let rec find_tail_and_enq curr_end node = if Atomic.compare_and_set curr_end Nil node then () else match Atomic.get curr_end with | Nil -> find_tail_and_enq curr_end node | Next (_, n) -> find_tail_and_enq n node in let newnode = Next (v, Atomic.make Nil) in let tail = Atomic.get q.tail in match tail with | Nil -> failwith "HW_MSQueue.push: impossible" | Next (_, n) -> find_tail_and_enq n newnode; ignore (Atomic.compare_and_set q.tail tail newnode) dscheck-0.5.0/tests/test_hb.ml0000644000175000017500000000071414641522241014741 0ustar kylekylemodule Atomic = Dscheck.TracedAtomic (* get + get *) let test1 () = let a = Atomic.make 0 in Atomic.spawn (fun () -> Atomic.compare_and_set a 0 1 |> ignore); Atomic.spawn (fun () -> Atomic.compare_and_set a 1 2 |> ignore); Atomic.spawn (fun () -> Atomic.compare_and_set a 1 2 |> ignore); () let () = Atomic.trace ~record_traces:true test1; Printf.printf "traces: %d\n%!" (Dscheck.Trace_tracker.get_traces () |> Dscheck.Trace_tracker.count) dscheck-0.5.0/tests/test_commutative.ml0000644000175000017500000000231614641522241016705 0ustar kylekylemodule Atomic = Dscheck.TracedAtomic (* get + get *) let test1 () = let a = Atomic.make 0 in Atomic.spawn (fun () -> Atomic.get a |> ignore); Atomic.spawn (fun () -> Atomic.get a |> ignore) (* get + cas fail *) let test2 () = let a = Atomic.make 0 in let b = Atomic.make 0 in Atomic.spawn (fun () -> Atomic.get a |> ignore); Atomic.spawn (fun () -> Atomic.compare_and_set a 1 2 |> ignore; Atomic.get b |> ignore) (* get + cas success *) let test3 () = let a = Atomic.make 0 in Atomic.spawn (fun () -> Atomic.compare_and_set a 0 1 |> ignore); Atomic.spawn (fun () -> Atomic.compare_and_set a 0 1 |> ignore) (* get + cas fail + set *) let test4 () = let a = Atomic.make 0 in Atomic.spawn (fun () -> Atomic.compare_and_set a 2 3 |> ignore); Atomic.spawn (fun () -> Atomic.get a |> ignore); Atomic.spawn (fun () -> Atomic.set a 1 |> ignore) let test5 () = let a = Atomic.make 0 in Atomic.spawn (fun () -> Atomic.get a |> ignore); Atomic.spawn (fun () -> Atomic.set a 2 |> ignore); Atomic.spawn (fun () -> Atomic.compare_and_set a 2 3 |> ignore) let () = let tests = [ test1; test2; test3; test4; test5 ] in List.iter (fun test -> Atomic.trace ~impl:`Dpor_source test) tests dscheck-0.5.0/tests/test_conditional_nested.ml0000644000175000017500000000114214641522241020211 0ustar kylekylemodule Atomic = Dscheck.TracedAtomic let test () = let b = Atomic.make 0 in let c = Atomic.make 0 in let ok = Atomic.make false in let seen_b = ref (-1) in Atomic.spawn (fun () -> Atomic.set b 1); Atomic.spawn (fun () -> Atomic.set c 1; Atomic.set b 2); Atomic.spawn (fun () -> if Atomic.get c = 0 then ( seen_b := Atomic.get b; if !seen_b = 0 then Atomic.set ok true)) (* Atomic.final (fun () -> Format.printf "seen_b=%i b=%i c=%i ok=%b@." (!seen_b) (Atomic.get b) (Atomic.get c) (Atomic.get ok)) *) let () = Atomic.trace ~record_traces:true test dscheck-0.5.0/tests/report_trace.expected0000644000175000017500000000102314641522241017165 0ustar kylekyle sequence 1 ---------------------------------------- P0 P1 ---------------------------------------- start fetch_and_add a start fetch_and_add a fetch_and_add b ---------------------------------------- sequence 2 ---------------------------------------- P0 P1 ---------------------------------------- start start fetch_and_add a fetch_and_add a fetch_and_add b ---------------------------------------- explored 2 interleavings and 9 states ---- (1,a,w),(0,a,w),(1,b,w) (0,a,w),(1,a,w),(1,b,w) ---- dscheck-0.5.0/tests/gen_program.ml0000644000175000017500000002734314641522241015620 0ustar kylekylemodule Atomic = Dscheck.TracedAtomic module IntSet = Set.Make (Int) type config = { global_count : int; value_limit : int; operation_count : int; domain_count : int; generate_conditionals : bool; print_tests : bool; seed : int; } let print_config t = Printf.printf "CONFIG\n"; Printf.printf "global_count: %d\n" t.global_count; Printf.printf "value_limit: %d\n" t.value_limit; Printf.printf "operations_count: %d\n" t.operation_count; Printf.printf "domain_count: %d\n" t.domain_count; Printf.printf "generate_conditionals: %b\n%!" t.generate_conditionals; Printf.printf "seed: %d\n%!" t.seed let var_name i = Char.chr (i + 97) module Function = struct type t = | Get of { true_on : IntSet.t } | CompareAndSet of { old_value : int; new_value : int } | FetchAndAdd of { delta : int; true_on : IntSet.t } let gen value_limit = let rec set_f () = let set = List.init value_limit (fun i -> if Random.bool () then Some i else None) |> List.filter_map Fun.id |> IntSet.of_list in let size = IntSet.cardinal set in if 0 < size && size < value_limit then set else set_f () in match Random.int 6 with | 0 | 1 | 2 -> Get { true_on = set_f () } | 3 | 4 -> let old_value = Random.int value_limit in let new_value = Random.int value_limit in CompareAndSet { old_value; new_value } | 5 -> let delta = Random.int value_limit in FetchAndAdd { delta; true_on = set_f () } | _ -> assert false let eval variable = function | Get { true_on } -> IntSet.mem (Atomic.get variable) true_on | FetchAndAdd { delta; true_on } -> IntSet.mem (Atomic.fetch_and_add variable delta) true_on | CompareAndSet { old_value; new_value } -> Atomic.compare_and_set variable old_value new_value let to_string var_id = function | Get { true_on } -> Printf.sprintf "IntSet.mem (Atomic.get %c) (IntSet.of_list [%s])" (var_name var_id) (IntSet.to_seq true_on |> List.of_seq |> List.map Int.to_string |> String.concat "; ") | FetchAndAdd { delta; true_on } -> Printf.sprintf "IntSet.mem (Atomic.fetch_and_add %c %d) (IntSet.of_list [%s])" (var_name var_id) delta (IntSet.to_seq true_on |> List.of_seq |> List.map Int.to_string |> String.concat "; ") | CompareAndSet { old_value; new_value } -> Printf.sprintf "Atomic.compare_and_set %c %d %d" (var_name var_id) old_value new_value end module Conditional = struct type t = { functions : Function.t list; operator : [ `And | `Or ] } let gen function_count ~value_limit = let functions = List.init function_count (fun _ -> Function.gen value_limit) in let operator = if Random.bool () then `And else `Or in { functions; operator } let eval t variables = let functions = t.functions in let rec f operator vars funcs = match (vars, funcs) with | [], [] -> ( match operator with `And -> true | `Or -> false) | var :: vars_tl, func :: funcs_tl -> ( let output = Function.eval var func in match (operator, output) with | `Or, true -> true | `And, false -> false | `Or, false | `And, true -> f operator vars_tl funcs_tl) | _, [] | [], _ -> failwith "gen_program: lists of variables and functions have different \ lengths" in f t.operator variables functions let to_string t ~var_ids = let operator_str = match t.operator with `Or -> " || " | `And -> " && " in List.combine t.functions var_ids |> List.map (fun (func, var_id) -> Function.to_string var_id func) |> String.concat operator_str end module Step = struct type t = | Write of { var_id : int; new_value : int; next : t } | Read of { var_id : int; next : t } | CompareAndSet of { var_id : int; old_value : int; new_value : int; next : t; } | FetchAndAdd of { var_id : int; delta : int; next : t } | Conditional of { var_ids : int list; conditional : Conditional.t; on_true : t; next : t; } | Noop let rec run ~globals = function | Write { var_id; new_value; next } -> Atomic.set (CCVector.get globals var_id) new_value; run ~globals next | Read { var_id; next } -> ignore (Atomic.get (CCVector.get globals var_id)); run ~globals next | CompareAndSet { var_id; old_value; new_value; next } -> ignore (Atomic.compare_and_set (CCVector.get globals var_id) old_value new_value); run ~globals next | FetchAndAdd { var_id; delta; next } -> ignore (Atomic.fetch_and_add (CCVector.get globals var_id) delta); run ~globals next | Conditional { var_ids; conditional; on_true; next } -> let variables = List.map (fun var_id -> CCVector.get globals var_id) var_ids in if Conditional.eval conditional variables then run ~globals on_true; run ~globals next | Noop -> () let rec print t ~depth = let indent = List.init depth (fun _ -> "\t") |> String.concat "" in match t with | Write { var_id; new_value; next } -> Printf.printf "%sAtomic.set %c %d;\n" indent (var_name var_id) new_value; print ~depth next | Read { var_id; next } -> Printf.printf "%sAtomic.get %c |> ignore;\n" indent (var_name var_id); print ~depth next | CompareAndSet { var_id; old_value; new_value; next } -> Printf.printf "%sAtomic.compare_and_set %c %d %d |> ignore;\n" indent (var_name var_id) old_value new_value; print ~depth next | FetchAndAdd { var_id; delta; next } -> Printf.printf "%sAtomic.fetch_and_add %c %d |> ignore;\n" indent (var_name var_id) delta; print ~depth next | Conditional { var_ids; conditional; on_true; next } -> let s = Conditional.to_string conditional ~var_ids in Printf.printf "%sif (%s) then (\n" indent s; print ~depth:(depth + 1) on_true; Printf.printf "%s);\n" indent; print ~depth next | Noop -> () let rec gen ~config ~fuel () = let var_id = Random.int config.global_count in let next fuel = if fuel > 1 then gen ~config ~fuel:(fuel - 1) () else Noop in let maybe_conditionals = if config.generate_conditionals then 1 else 0 in match Random.int (6 + maybe_conditionals) with | 0 | 1 -> let new_value = Random.int config.value_limit in Write { var_id; new_value; next = next fuel } | 2 | 3 -> Read { var_id; next = next fuel } | 4 -> let old_value = Random.int config.value_limit in let new_value = Random.int config.value_limit in CompareAndSet { var_id; old_value; new_value; next = next fuel } | 5 -> let delta = Random.int config.value_limit - (config.value_limit / 2) in FetchAndAdd { var_id; delta; next = next fuel } | 6 -> let func_count = min (max 1 fuel) (min config.global_count (1 + Random.int 2)) in let var_ids = List.init func_count (fun _ -> Random.int config.global_count) in let conditional = Conditional.gen func_count ~value_limit:config.value_limit in let fuel_a, fuel_b = let tmp = Random.int (max (fuel - func_count) 1) in (tmp / 2, tmp / 2) in let on_true = gen ~config ~fuel:fuel_a () in Conditional { var_ids; conditional; on_true; next = next fuel_b } | _ -> failwith "drew number without corresponding instruction" end module Program = struct type thread = Step.t type t = { globals : (int, CCVector.ro) CCVector.t; threads : thread list } let run ~impl { globals; threads } = Atomic.trace ~impl ~record_traces:true (fun () -> let globals = CCVector.map Atomic.make globals |> CCVector.freeze in List.iter (fun thread -> Atomic.spawn (fun () -> Step.run ~globals thread)) threads; ()); Dscheck.Trace_tracker.get_traces () let print { globals; threads } = CCVector.iteri (fun var_id value -> Printf.printf "let %c = Atomic.make %d in\n" (var_name var_id) value) globals; List.iter (fun thread -> Printf.printf "\nDomain.spawn (fun () -> \n"; Step.print thread ~depth:1; Printf.printf ")\n%!") threads end let run_random config () = Random.init config.seed; let globals = CCVector.of_list (List.init config.global_count Fun.id) in let thread_f = Step.gen ~config ~fuel:config.operation_count in let threads = List.init config.domain_count (fun _ -> thread_f ()) in let program = ({ globals; threads } : Program.t) in if config.print_tests then Program.print program; let dpor_source = Program.run ~impl:`Dpor_source program in let dpor = Program.run ~impl:`Dpor program in if not (Dscheck.Trace_tracker.equal dpor_source dpor) then ( Printf.printf "found mismatch\n\n%!"; Program.print program; Dscheck.Trace_tracker.print dpor stdout; Dscheck.Trace_tracker.print dpor_source stdout; assert false) let run config test_count = Printf.printf "\n\n"; for i = 1 to test_count do Printf.printf "----run: %d/%d\r%!" i test_count; run_random config () done; Printf.printf "\nall generated programs passed\n%!" (* Command line interface *) open Cmdliner let test_count = let default = 100 in let info = Arg.info [ "t"; "test-count" ] ~docv:"INT" ~doc:"Number of programs to generate and test." in Arg.value (Arg.opt Arg.int default info) let global_count = let default = 3 in let info = Arg.info [ "g"; "global-count" ] ~docv:"INT" ~doc:"Number of global atomic variables in generated programs." in Arg.value (Arg.opt Arg.int default info) let print_tests = let info = Arg.info [ "p"; "print-tests" ] ~doc:"Print generated tests." in Arg.value (Arg.flag info) let value_limit = let default = 3 in let info = Arg.info [ "l"; "value-limit" ] ~docv:"INT" ~doc: "Values of atomic operations stay (mostly) between zero and this value." in Arg.value (Arg.opt Arg.int default info) let operation_count = let default = 3 in let info = Arg.info [ "o"; "operation-count" ] ~docv:"INT" ~doc:"Number of operations generated for every domain." in Arg.value (Arg.opt Arg.int default info) let domain_count = let default = 3 in let info = Arg.info [ "d"; "domain-count" ] ~docv:"INT" ~doc:"Number of domains in generated tests." in Arg.value (Arg.opt Arg.int default info) let generate_conditionals = let info = Arg.info [ "c"; "generate-conditionals" ] ~doc:"Generate tests with conditional statements." in Arg.value (Arg.flag info) let seed_opt = let info = Arg.info [ "s"; "random-seed" ] ~docv:"INT" ~doc:"Random seed" in Arg.value (Arg.opt (Arg.some Arg.int) None info) let cmd = let open Term in const (fun test_count global_count print_tests value_limit operation_count domain_count generate_conditionals seed_opt -> let seed = match seed_opt with | Some seed -> seed | None -> Random.self_init (); Random.bits () in let config = ({ global_count; value_limit; operation_count; domain_count; generate_conditionals; print_tests; seed; } : config) in print_config config; run config test_count) $ test_count $ global_count $ print_tests $ value_limit $ operation_count $ domain_count $ generate_conditionals $ seed_opt let () = exit @@ Cmd.eval @@ Cmd.v (Cmd.info ~doc:"Test generator for DSCheck" "gen_program") cmd dscheck-0.5.0/tests/test_conditional_ssb.ml0000644000175000017500000000157214641522241017525 0ustar kylekylemodule Atomic = Dscheck.TracedAtomic (* This test shows sleep-set blocking with source sets and constitutes an interesting conditional in its own right. Taken from Fig 2 in [Source Sets: A Foundation for Optimal Dynamic Partial Order Reduction]. *) let test () = let x = Atomic.make 0 in let y = Atomic.make 0 in let z = Atomic.make 0 in let tmp = ref (-1) in Atomic.spawn (fun () -> tmp := Atomic.get x); Atomic.spawn (fun () -> Atomic.set y 1); Atomic.spawn (fun () -> let m = Atomic.get y in if m = 0 then Atomic.set z 1); Atomic.spawn (fun () -> let n = Atomic.get z in let l = Atomic.get y in if n = 1 then if l = 0 then Atomic.set x 1) (* Atomic.final (fun () -> Format.printf "tmp=%d x=%d y=%d z=%d\n%!" !tmp (Atomic.get x) (Atomic.get y) (Atomic.get z)) *) let () = Atomic.trace ~record_traces:true test dscheck-0.5.0/tests/test_trace.ml0000644000175000017500000000076514641522241015454 0ustar kylekylemodule Atomic = Dscheck.TracedAtomic let counter incr () = let c1 = Atomic.make 0 in let c2 = Atomic.make_contended 0 in Atomic.spawn (fun () -> incr c1); Atomic.spawn (fun () -> incr c1; incr c2); Atomic.final (fun () -> Atomic.check (fun () -> Atomic.get c1 == 2 && Atomic.get c2 == 1)) let test_safe_counter () = Atomic.trace ~interleavings:stdout ~record_traces:true (counter Atomic.incr); Dscheck.Trace_tracker.print_traces stdout let _ = test_safe_counter () dscheck-0.5.0/tests/test_list.ml0000644000175000017500000000370514641522241015326 0ustar kylekylemodule Atomic = Dscheck.TracedAtomic (* a simple concurrent list *) type conc_list = { value : int; next : conc_list option } let rec add_node_naive list_head n = (* try to add a new node to head *) let old_head = Atomic.get list_head in let new_node = { value = n; next = Some old_head } in (* introduce bug *) if Atomic.get list_head = old_head then ( Atomic.set list_head new_node; true) else add_node_naive list_head n let rec add_node_safe list_head n = let old_head = Atomic.get list_head in let new_node = { value = n; next = Some old_head } in if Atomic.compare_and_set list_head old_head new_node then true else add_node_safe list_head n let check_node list_head n = let rec check_from_node node = match (node.value, node.next) with | v, _ when v = n -> true | _, None -> false | _, Some next_node -> check_from_node next_node in (* try to find the node *) check_from_node (Atomic.get list_head) let create_test add_node_f upto () = let list_head = Atomic.make { value = 0; next = None } in for x = 1 to upto do Atomic.spawn (fun () -> assert (add_node_f list_head x); assert (check_node list_head x)) done; Atomic.final (fun () -> for x = 1 to upto do Atomic.check (fun () -> check_node list_head x) done) let test_list_naive_single_domain () = Atomic.trace (create_test add_node_naive 1) let test_list_naive domains () = match Atomic.trace (create_test add_node_naive domains) with | exception _ -> () | _ -> failwith "expected failure" let test_list_safe () = Atomic.trace (create_test add_node_safe 3) let () = let open Alcotest in run "dscheck" [ ( "list", [ test_case "naive-1-domain" `Quick test_list_naive_single_domain; test_case "naive-2-domains" `Quick (test_list_naive 2); test_case "naive-8-domains" `Quick (test_list_naive 8); test_case "safe" `Quick test_list_safe; ] ); ] dscheck-0.5.0/tests/test_michael_scott_queue.ml0000644000175000017500000000214214641522241020367 0ustar kylekylemodule Atomic = Dscheck.TracedAtomic let drain queue = let remaining = ref 0 in while not (Michael_scott_queue.is_empty queue) do remaining := !remaining + 1; assert (Option.is_some (Michael_scott_queue.pop queue)) done; !remaining let producer_consumer () = Atomic.trace ~record_traces:true (fun () -> let queue = Michael_scott_queue.create () in let items_total = 4 in Atomic.spawn (fun () -> for _ = 1 to items_total do Michael_scott_queue.push queue 0 done); (* consumer *) let popped = ref 0 in Atomic.spawn (fun () -> for _ = 1 to items_total do match Michael_scott_queue.pop queue with | None -> () | Some _ -> popped := !popped + 1 done); (* checks*) Atomic.final (fun () -> Atomic.check (fun () -> let remaining = drain queue in !popped + remaining = items_total))) let () = let open Alcotest in run "michael_scott_queue_dscheck" [ ("basic", [ test_case "1-producer-1-consumer" `Slow producer_consumer ]) ] dscheck-0.5.0/tests/dune0000644000175000017500000000262414641522241013637 0ustar kylekyle(test (name test_list) (libraries dscheck alcotest) (build_if (>= %{ocaml_version} 5)) (modules test_list)) (test (name test_naive_counter) (libraries dscheck alcotest) (build_if (>= %{ocaml_version} 5)) (modules test_naive_counter)) (test (name test_michael_scott_queue) (libraries dscheck alcotest) (build_if (>= %{ocaml_version} 5)) (modules test_michael_scott_queue michael_scott_queue)) (test (name test_trace) (libraries dscheck alcotest) (build_if (>= %{ocaml_version} 5)) (modules test_trace)) (rule (action (with-stdout-to report_trace.output (run ./test_trace.exe))) (enabled_if (>= %{ocaml_version} 5))) (rule (alias runtest) (action (diff report_trace.expected report_trace.output)) (enabled_if (>= %{ocaml_version} 5))) (test (name test_conditional_nested) (libraries dscheck alcotest) (build_if (>= %{ocaml_version} 5)) (modules test_conditional_nested)) (test (name test_conditional_ssb) (libraries dscheck alcotest) (build_if (>= %{ocaml_version} 5)) (modules test_conditional_ssb)) (test (name test_commutative) (libraries dscheck alcotest) (build_if (>= %{ocaml_version} 5)) (modules test_commutative)) (test (name test_hb) (libraries dscheck alcotest) (build_if (>= %{ocaml_version} 5)) (modules test_hb)) (executable (name gen_program) (libraries dscheck cmdliner) (enabled_if (>= %{ocaml_version} 5)) (modules gen_program)) dscheck-0.5.0/tests/test_naive_counter.ml0000644000175000017500000000133614641522241017212 0ustar kylekylemodule Atomic = Dscheck.TracedAtomic let counter incr () = let counter = Atomic.make 0 in Atomic.spawn (fun () -> incr counter); Atomic.spawn (fun () -> incr counter); Atomic.final (fun () -> Atomic.check (fun () -> Atomic.get counter == 2)) let test_naive_counter () = let naive_incr counter = Atomic.set counter (Atomic.get counter + 1) in match Atomic.trace (counter naive_incr) with | exception _ -> () | _ -> failwith "expected failure" let test_safe_counter () = Atomic.trace (counter Atomic.incr) let () = let open Alcotest in run "dscheck" [ ( "counter", [ test_case "naive" `Quick test_naive_counter; test_case "safe" `Quick test_safe_counter; ] ); ] dscheck-0.5.0/.gitignore0000644000175000017500000000000714641522241013600 0ustar kylekyle_build dscheck-0.5.0/dscheck.opam0000644000175000017500000000130114641522241014070 0ustar kylekyleversion: "0.5.0" # This file is generated by dune, edit dune-project instead opam-version: "2.0" synopsis: "Traced Atomics" maintainer: ["Sadiq Jaffer"] authors: ["Sadiq Jaffer"] license: "ISC" homepage: "https://github.com/ocaml-multicore/dscheck" bug-reports: "https://github.com/ocaml-multicore/dscheck/issues" depends: [ "ocaml" {>= "4.12.0"} "dune" {>= "3.9"} "containers" "tsort" "oseq" "alcotest" {>= "1.6.0" & with-test} "cmdliner" "odoc" {with-doc} ] build: [ ["dune" "subst"] {dev} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@doc" {with-doc} ] ] dev-repo: "git+https://github.com/ocaml-multicore/dscheck.git"dscheck-0.5.0/CHANGES.md0000644000175000017500000000110314641522241013200 0ustar kylekyle## 0.5.0 - Add Atomic.make_contended (@lyrm, review : @polytypic) ## 0.4.0 - Made (empty) package available on OCaml 4 (@polytypic, review: @lyrm) ## 0.3.0 - Granular dependency relation (@bartoszmodelski, review: @lyrm) ## 0.2.0 - Source sets (@bartoszmodelski, review: @lyrm, @art-w) - Better traces (@bartoszmodelski, review: @lyrm, @art-w) - Test generation (@bartoszmodelski, review: @lyrm) - New README (@bartoszmodelski, review: @lyrm, @Sudha247) ## 0.1.1 - Fix continuations leak (@bartoszmodelski, review: @polytypic) ## 0.1.0 - Initial experimental release.