From 45fea70c1841aedbb32377d88c0280c7a83e6208 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 7 Nov 2025 02:18:43 -0800 Subject: [PATCH] add ExternModuleSimulationState::fork_join_scope --- crates/fayalite/src/sim.rs | 243 ++- crates/fayalite/tests/sim.rs | 93 ++ .../sim/expected/sim_fork_join_scope.txt | 523 ++++++ .../sim/expected/sim_fork_join_scope.vcd | 1467 +++++++++++++++++ 4 files changed, 2325 insertions(+), 1 deletion(-) create mode 100644 crates/fayalite/tests/sim/expected/sim_fork_join_scope.txt create mode 100644 crates/fayalite/tests/sim/expected/sim_fork_join_scope.vcd diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index b19eeb04..01233cb2 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -49,7 +49,7 @@ use std::{ any::Any, borrow::Cow, cell::{Cell, RefCell}, - collections::BTreeMap, + collections::{BTreeMap, BTreeSet}, fmt, future::{Future, IntoFuture}, hash::Hash, @@ -3347,6 +3347,128 @@ impl ExternModuleSimulationState { module_index, } } + pub async fn fork_join_scope<'env, F, Fut>( + &mut self, + in_scope: F, + ) -> ::Output + where + F: FnOnce(ForkJoinScope<'env>, ExternModuleSimulationState) -> Fut, + Fut: IntoFuture>, + { + let scope = ForkJoinScope { + new_tasks: Rc::new(RefCell::new(vec![])), + sim: self.forked_state(), + }; + let join_handle = scope.spawn(in_scope); + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] + struct TaskId(u64); + struct TasksStateInner { + next_task_id: u64, + ready_tasks: BTreeSet, + not_ready_tasks: BTreeSet, + base_waker: std::task::Waker, + } + impl Default for TasksStateInner { + fn default() -> Self { + Self { + next_task_id: Default::default(), + ready_tasks: Default::default(), + not_ready_tasks: Default::default(), + base_waker: std::task::Waker::noop().clone(), + } + } + } + #[derive(Default)] + struct TasksState { + inner: Mutex, + } + #[derive(Clone)] + struct TaskWaker { + state: std::sync::Weak, + task: TaskId, + } + impl std::task::Wake for TaskWaker { + fn wake(self: Arc) { + self.wake_by_ref(); + } + fn wake_by_ref(self: &Arc) { + let Some(state) = self.state.upgrade() else { + return; + }; + let mut inner = state.inner.lock().expect("not poisoned"); + if inner.not_ready_tasks.remove(&self.task) { + inner.ready_tasks.insert(self.task); + inner.base_waker.wake_by_ref(); + } + } + } + struct Task<'env> { + task: Pin + 'env>>, + waker: std::task::Waker, + } + let mut tasks: BTreeMap = BTreeMap::new(); + let tasks_state = Arc::new(TasksState::default()); + std::future::poll_fn(move |cx: &mut std::task::Context<'_>| { + let mut state_inner = tasks_state.inner.lock().expect("not poisoned"); + state_inner.base_waker.clone_from(cx.waker()); + loop { + for new_task in scope.new_tasks.borrow_mut().drain(..) { + let task_id = TaskId(state_inner.next_task_id); + let Some(next_task_id) = state_inner.next_task_id.checked_add(1) else { + drop(state_inner); + panic!("spawned too many tasks"); + }; + state_inner.next_task_id = next_task_id; + state_inner.ready_tasks.insert(task_id); + tasks.insert( + task_id, + Task { + task: new_task, + waker: Arc::new(TaskWaker { + state: Arc::downgrade(&tasks_state), + task: task_id, + }) + .into(), + }, + ); + } + let Some(task_id) = state_inner.ready_tasks.pop_first() else { + if state_inner.not_ready_tasks.is_empty() { + return Poll::Ready(()); + } else { + return Poll::Pending; + }; + }; + state_inner.not_ready_tasks.insert(task_id); // task can be woken while we're running poll + drop(state_inner); + let std::collections::btree_map::Entry::Occupied(mut entry) = tasks.entry(task_id) + else { + unreachable!(); + }; + let Task { task, waker } = entry.get_mut(); + match task.as_mut().poll(&mut std::task::Context::from_waker( + &std::task::Waker::from(waker.clone()), + )) { + Poll::Pending => { + state_inner = tasks_state.inner.lock().expect("not poisoned"); + continue; + } + Poll::Ready(()) => {} + } + drop(entry.remove()); // drop outside lock + state_inner = tasks_state.inner.lock().expect("not poisoned"); + state_inner.not_ready_tasks.remove(&task_id); + state_inner.ready_tasks.remove(&task_id); + } + }) + .await; + match &mut *join_handle.state.borrow_mut() { + JoinHandleState::Running(_) => unreachable!(), + JoinHandleState::Finished(state) => state + .take() + .expect("filled by running all futures to completion"), + } + } impl_simulation_methods!( async_await = (async, await), track_caller = (), @@ -3354,6 +3476,125 @@ impl ExternModuleSimulationState { ); } +pub struct ForkJoinScope<'env> { + new_tasks: Rc + 'env>>>>>, + sim: ExternModuleSimulationState, +} + +impl<'env> Clone for ForkJoinScope<'env> { + fn clone(&self) -> Self { + Self { + new_tasks: self.new_tasks.clone(), + sim: self.sim.forked_state(), + } + } +} + +impl<'env> ForkJoinScope<'env> { + fn spawn_inner(&self, fut: Pin + 'env>>) { + self.new_tasks.borrow_mut().push(fut); + } + pub fn spawn_detached_future( + &self, + fut: impl IntoFuture>, + ) { + self.spawn_inner(Box::pin(fut.into_future())); + } + pub fn spawn_detached(&self, f: F) + where + F: FnOnce(ForkJoinScope<'env>, ExternModuleSimulationState) -> Fut, + Fut: IntoFuture>, + { + self.spawn_detached_future(f(self.clone(), self.sim.forked_state())); + } + pub fn spawn(&self, f: F) -> JoinHandle + where + F: FnOnce(ForkJoinScope<'env>, ExternModuleSimulationState) -> Fut, + Fut: IntoFuture>, + { + let join_handle = JoinHandle { + state: Default::default(), + }; + let state = Rc::downgrade(&join_handle.state); + let fut = f(self.clone(), self.sim.forked_state()).into_future(); + self.spawn_detached_future(async move { + let result = fut.await; + let Some(state) = state.upgrade() else { return }; + let mut state = state.borrow_mut(); + let waker = match &mut *state { + JoinHandleState::Running(waker) => waker.take(), + JoinHandleState::Finished(_) => unreachable!(), + }; + *state = JoinHandleState::Finished(Some(result)); + drop(state); + let Some(waker) = waker else { return }; + waker.wake(); + }); + join_handle + } +} + +enum JoinHandleState { + Running(Option), + Finished(Option), +} + +impl Default for JoinHandleState { + fn default() -> Self { + Self::Running(None) + } +} + +pub struct JoinHandle { + state: Rc>>, +} + +impl JoinHandle { + pub fn is_finished(&self) -> bool { + matches!(*self.state.borrow(), JoinHandleState::Finished(_)) + } + pub fn try_join(self) -> Result { + let mut state = self.state.borrow_mut(); + match &mut *state { + JoinHandleState::Running(_) => { + drop(state); + Err(self) + } + JoinHandleState::Finished(retval) => { + let Some(retval) = retval.take() else { + panic!("already returned the value in poll"); + }; + Ok(retval) + } + } + } + pub async fn join(self) -> T { + self.await + } +} + +impl Future for JoinHandle { + type Output = T; + + fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { + match &mut *self.state.borrow_mut() { + JoinHandleState::Running(waker) => { + match waker { + None => *waker = Some(cx.waker().clone()), + Some(waker) => waker.clone_from(cx.waker()), + } + Poll::Pending + } + JoinHandleState::Finished(retval) => { + let Some(retval) = retval.take() else { + panic!("already returned Poll::Ready"); + }; + Poll::Ready(retval) + } + } + } +} + struct ForkJoinImpl<'a> { futures: Vec + 'a>>>, } diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index 75f7cef4..6d0380ba 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -2109,6 +2109,99 @@ fn test_sim_fork_join() { } } +#[hdl_module(outline_generated, extern)] +pub fn sim_fork_join_scope() +where + ConstUsize: KnownSize, +{ + #[hdl] + let clocks: Array = m.input(); + #[hdl] + let outputs: Array, N> = m.output(); + m.extern_module_simulation_fn((clocks, outputs), |(clocks, outputs), mut sim| async move { + sim.write(outputs, [0u8; N]).await; + loop { + let written = vec![std::cell::Cell::new(false); N]; // test shared scope + let written = &written; // work around move in async move + sim.fork_join_scope(|scope, _| async move { + let mut spawned = vec![]; + for i in 0..N { + let join_handle = + scope.spawn(move |_, mut sim: ExternModuleSimulationState| async move { + sim.wait_for_clock_edge(clocks[i]).await; + let v = sim + .read_bool_or_int(outputs[i]) + .await + .to_bigint() + .try_into() + .expect("known to be in range"); + sim.write(outputs[i], 1u8.wrapping_add(v)).await; + written[i].set(true); + i + }); + if i % 2 == 0 && i < N - 1 { + spawned.push((i, join_handle)); + } + } + for (i, join_handle) in spawned { + assert_eq!(i, join_handle.join().await); + } + }) + .await; + for written in written { + assert!(written.get()); + } + } + }); +} + +#[test] +fn test_sim_fork_join_scope() { + let _n = SourceLocation::normalize_files_for_tests(); + const N: usize = 3; + let mut sim = Simulation::new(sim_fork_join_scope::()); + let mut writer = RcWriter::default(); + sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); + sim.write(sim.io().clocks, [false; N]); + let mut clocks_triggered = [false; N]; + let mut expected = [0u8; N]; + for i0 in 0..N { + for i1 in 0..N { + for i2 in 0..N { + for i3 in 0..N { + let indexes = [i0, i1, i2, i3]; + for i in indexes { + sim.advance_time(SimDuration::from_micros(1)); + sim.write(sim.io().clocks[i], true); + sim.advance_time(SimDuration::from_micros(1)); + sim.write(sim.io().clocks[i], false); + if !clocks_triggered[i] { + expected[i] = expected[i].wrapping_add(1); + } + clocks_triggered[i] = true; + if clocks_triggered == [true; N] { + clocks_triggered = [false; N]; + } + let output = sim.read(sim.io().outputs); + assert_eq!(output, expected.to_sim_value(), "indexes={indexes:?} i={i}"); + } + } + } + } + } + sim.flush_traces().unwrap(); + let vcd = String::from_utf8(writer.take()).unwrap(); + println!("####### VCD:\n{vcd}\n#######"); + if vcd != include_str!("sim/expected/sim_fork_join_scope.vcd") { + panic!(); + } + let sim_debug = format!("{sim:#?}"); + println!("#######\n{sim_debug}\n#######"); + if sim_debug != include_str!("sim/expected/sim_fork_join_scope.txt") { + panic!(); + } +} + #[hdl_module(outline_generated, extern)] pub fn sim_resettable_counter() { #[hdl] diff --git a/crates/fayalite/tests/sim/expected/sim_fork_join_scope.txt b/crates/fayalite/tests/sim/expected/sim_fork_join_scope.txt new file mode 100644 index 00000000..ba5577d4 --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_fork_join_scope.txt @@ -0,0 +1,523 @@ +Simulation { + state: State { + insns: Insns { + state_layout: StateLayout { + ty: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 6, + debug_data: [ + SlotDebugData { + name: "InstantiatedModule(sim_fork_join_scope: sim_fork_join_scope).sim_fork_join_scope::clocks[0]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_fork_join_scope: sim_fork_join_scope).sim_fork_join_scope::clocks[1]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_fork_join_scope: sim_fork_join_scope).sim_fork_join_scope::clocks[2]", + ty: Clock, + }, + SlotDebugData { + name: "InstantiatedModule(sim_fork_join_scope: sim_fork_join_scope).sim_fork_join_scope::outputs[0]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_fork_join_scope: sim_fork_join_scope).sim_fork_join_scope::outputs[1]", + ty: UInt<8>, + }, + SlotDebugData { + name: "InstantiatedModule(sim_fork_join_scope: sim_fork_join_scope).sim_fork_join_scope::outputs[2]", + ty: UInt<8>, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + memories: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + insns: [ + // at: module-XXXXXXXXXX.rs:1:1 + 0: Return, + ], + .. + }, + pc: 0, + memory_write_log: [], + memories: StatePart { + value: [], + }, + small_slots: StatePart { + value: [], + }, + big_slots: StatePart { + value: [ + 0, + 0, + 0, + 49, + 50, + 50, + ], + }, + sim_only_slots: StatePart { + value: [], + }, + }, + io: Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }, + main_module: SimulationModuleState { + base_targets: [ + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.clocks, + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.outputs, + ], + uninitialized_ios: {}, + io_targets: { + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.clocks, + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.clocks[0], + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.clocks[1], + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.clocks[2], + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.outputs, + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.outputs[0], + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.outputs[1], + Instance { + name: ::sim_fork_join_scope, + instantiated: Module { + name: sim_fork_join_scope, + .. + }, + }.outputs[2], + }, + did_initial_settle: true, + }, + extern_modules: [ + SimulationExternModuleState { + module_state: SimulationModuleState { + base_targets: [ + ModuleIO { + name: sim_fork_join_scope::clocks, + is_input: true, + ty: Array, + .. + }, + ModuleIO { + name: sim_fork_join_scope::outputs, + is_input: false, + ty: Array, 3>, + .. + }, + ], + uninitialized_ios: {}, + io_targets: { + ModuleIO { + name: sim_fork_join_scope::clocks, + is_input: true, + ty: Array, + .. + }, + ModuleIO { + name: sim_fork_join_scope::clocks, + is_input: true, + ty: Array, + .. + }[0], + ModuleIO { + name: sim_fork_join_scope::clocks, + is_input: true, + ty: Array, + .. + }[1], + ModuleIO { + name: sim_fork_join_scope::clocks, + is_input: true, + ty: Array, + .. + }[2], + ModuleIO { + name: sim_fork_join_scope::outputs, + is_input: false, + ty: Array, 3>, + .. + }, + ModuleIO { + name: sim_fork_join_scope::outputs, + is_input: false, + ty: Array, 3>, + .. + }[0], + ModuleIO { + name: sim_fork_join_scope::outputs, + is_input: false, + ty: Array, 3>, + .. + }[1], + ModuleIO { + name: sim_fork_join_scope::outputs, + is_input: false, + ty: Array, 3>, + .. + }[2], + }, + did_initial_settle: true, + }, + sim: ExternModuleSimulation { + generator: SimGeneratorFn { + args: ( + ModuleIO { + name: sim_fork_join_scope::clocks, + is_input: true, + ty: Array, + .. + }, + ModuleIO { + name: sim_fork_join_scope::outputs, + is_input: false, + ty: Array, 3>, + .. + }, + ), + f: ..., + }, + sim_io_to_generator_map: { + ModuleIO { + name: sim_fork_join_scope::clocks, + is_input: true, + ty: Array, + .. + }: ModuleIO { + name: sim_fork_join_scope::clocks, + is_input: true, + ty: Array, + .. + }, + ModuleIO { + name: sim_fork_join_scope::outputs, + is_input: false, + ty: Array, 3>, + .. + }: ModuleIO { + name: sim_fork_join_scope::outputs, + is_input: false, + ty: Array, 3>, + .. + }, + }, + source_location: SourceLocation( + module-XXXXXXXXXX.rs:4:1, + ), + }, + running_generator: Some( + ..., + ), + }, + ], + trace_decls: TraceModule { + name: "sim_fork_join_scope", + children: [ + TraceModuleIO { + name: "clocks", + child: TraceArray { + name: "clocks", + elements: [ + TraceClock { + location: TraceScalarId(0), + name: "[0]", + flow: Source, + }, + TraceClock { + location: TraceScalarId(1), + name: "[1]", + flow: Source, + }, + TraceClock { + location: TraceScalarId(2), + name: "[2]", + flow: Source, + }, + ], + ty: Array, + flow: Source, + }, + ty: Array, + flow: Source, + }, + TraceModuleIO { + name: "outputs", + child: TraceArray { + name: "outputs", + elements: [ + TraceUInt { + location: TraceScalarId(3), + name: "[0]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(4), + name: "[1]", + ty: UInt<8>, + flow: Sink, + }, + TraceUInt { + location: TraceScalarId(5), + name: "[2]", + ty: UInt<8>, + flow: Sink, + }, + ], + ty: Array, 3>, + flow: Sink, + }, + ty: Array, 3>, + flow: Sink, + }, + ], + }, + traces: [ + SimTrace { + id: TraceScalarId(0), + kind: BigClock { + index: StatePartIndex(0), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(1), + kind: BigClock { + index: StatePartIndex(1), + }, + state: 0x0, + last_state: 0x0, + }, + SimTrace { + id: TraceScalarId(2), + kind: BigClock { + index: StatePartIndex(2), + }, + state: 0x0, + last_state: 0x1, + }, + SimTrace { + id: TraceScalarId(3), + kind: BigUInt { + index: StatePartIndex(3), + ty: UInt<8>, + }, + state: 0x31, + last_state: 0x31, + }, + SimTrace { + id: TraceScalarId(4), + kind: BigUInt { + index: StatePartIndex(4), + ty: UInt<8>, + }, + state: 0x32, + last_state: 0x32, + }, + SimTrace { + id: TraceScalarId(5), + kind: BigUInt { + index: StatePartIndex(5), + ty: UInt<8>, + }, + state: 0x32, + last_state: 0x32, + }, + ], + trace_memories: {}, + trace_writers: [ + Running( + VcdWriter { + finished_init: true, + timescale: 1 ps, + .. + }, + ), + ], + clocks_triggered: [], + event_queue: EventQueue(EventQueueData { + instant: 648 μs, + events: {}, + }), + waiting_sensitivity_sets_by_address: { + SensitivitySet { + id: 198, + values: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + }, + changed: Cell { + value: false, + }, + .. + }, + }, + waiting_sensitivity_sets_by_compiled_value: { + CompiledValue { + layout: CompiledTypeLayout { + ty: Clock, + layout: TypeLayout { + small_slots: StatePartLayout { + len: 0, + debug_data: [], + .. + }, + big_slots: StatePartLayout { + len: 1, + debug_data: [ + SlotDebugData { + name: "", + ty: Clock, + }, + ], + .. + }, + sim_only_slots: StatePartLayout { + len: 0, + debug_data: [], + layout_data: [], + .. + }, + }, + body: Scalar, + }, + range: TypeIndexRange { + small_slots: StatePartIndexRange { start: 0, len: 0 }, + big_slots: StatePartIndexRange { start: 0, len: 1 }, + sim_only_slots: StatePartIndexRange { start: 0, len: 0 }, + }, + write: None, + }: ( + SimValue { + ty: Clock, + value: OpaqueSimValue { + bits: 0x0_u1, + sim_only_values: [], + }, + }, + { + SensitivitySet { + id: 198, + .. + }, + }, + ), + }, + .. +} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/sim_fork_join_scope.vcd b/crates/fayalite/tests/sim/expected/sim_fork_join_scope.vcd new file mode 100644 index 00000000..555e83ed --- /dev/null +++ b/crates/fayalite/tests/sim/expected/sim_fork_join_scope.vcd @@ -0,0 +1,1467 @@ +$timescale 1 ps $end +$scope module sim_fork_join_scope $end +$scope struct clocks $end +$var wire 1 ! \[0] $end +$var wire 1 " \[1] $end +$var wire 1 # \[2] $end +$upscope $end +$scope struct outputs $end +$var wire 8 $ \[0] $end +$var wire 8 % \[1] $end +$var wire 8 & \[2] $end +$upscope $end +$upscope $end +$enddefinitions $end +$dumpvars +0! +0" +0# +b0 $ +b0 % +b0 & +$end +#1000000 +1! +b1 $ +#2000000 +0! +#3000000 +1! +#4000000 +0! +#5000000 +1! +#6000000 +0! +#7000000 +1! +#8000000 +0! +#9000000 +1! +#10000000 +0! +#11000000 +1! +#12000000 +0! +#13000000 +1! +#14000000 +0! +#15000000 +1" +b1 % +#16000000 +0" +#17000000 +1! +#18000000 +0! +#19000000 +1! +#20000000 +0! +#21000000 +1! +#22000000 +0! +#23000000 +1# +b1 & +#24000000 +0# +#25000000 +1! +b10 $ +#26000000 +0! +#27000000 +1! +#28000000 +0! +#29000000 +1" +b10 % +#30000000 +0" +#31000000 +1! +#32000000 +0! +#33000000 +1! +#34000000 +0! +#35000000 +1! +#36000000 +0! +#37000000 +1" +#38000000 +0" +#39000000 +1" +#40000000 +0" +#41000000 +1! +#42000000 +0! +#43000000 +1! +#44000000 +0! +#45000000 +1" +#46000000 +0" +#47000000 +1# +b10 & +#48000000 +0# +#49000000 +1! +b11 $ +#50000000 +0! +#51000000 +1! +#52000000 +0! +#53000000 +1# +b11 & +#54000000 +0# +#55000000 +1! +#56000000 +0! +#57000000 +1! +#58000000 +0! +#59000000 +1! +#60000000 +0! +#61000000 +1# +#62000000 +0# +#63000000 +1" +b11 % +#64000000 +0" +#65000000 +1! +b100 $ +#66000000 +0! +#67000000 +1! +#68000000 +0! +#69000000 +1# +b100 & +#70000000 +0# +#71000000 +1# +#72000000 +0# +#73000000 +1! +#74000000 +0! +#75000000 +1" +b100 % +#76000000 +0" +#77000000 +1! +b101 $ +#78000000 +0! +#79000000 +1! +#80000000 +0! +#81000000 +1! +#82000000 +0! +#83000000 +1" +b101 % +#84000000 +0" +#85000000 +1! +#86000000 +0! +#87000000 +1" +#88000000 +0" +#89000000 +1! +#90000000 +0! +#91000000 +1" +#92000000 +0" +#93000000 +1! +#94000000 +0! +#95000000 +1# +b101 & +#96000000 +0# +#97000000 +1! +b110 $ +#98000000 +0! +#99000000 +1" +b110 % +#100000000 +0" +#101000000 +1" +#102000000 +0" +#103000000 +1! +#104000000 +0! +#105000000 +1! +#106000000 +0! +#107000000 +1" +#108000000 +0" +#109000000 +1" +#110000000 +0" +#111000000 +1" +#112000000 +0" +#113000000 +1! +#114000000 +0! +#115000000 +1" +#116000000 +0" +#117000000 +1" +#118000000 +0" +#119000000 +1# +b110 & +#120000000 +0# +#121000000 +1! +b111 $ +#122000000 +0! +#123000000 +1" +b111 % +#124000000 +0" +#125000000 +1# +b111 & +#126000000 +0# +#127000000 +1! +b1000 $ +#128000000 +0! +#129000000 +1! +#130000000 +0! +#131000000 +1" +b1000 % +#132000000 +0" +#133000000 +1# +b1000 & +#134000000 +0# +#135000000 +1" +b1001 % +#136000000 +0" +#137000000 +1! +b1001 $ +#138000000 +0! +#139000000 +1" +#140000000 +0" +#141000000 +1# +b1001 & +#142000000 +0# +#143000000 +1# +b1010 & +#144000000 +0# +#145000000 +1! +b1010 $ +#146000000 +0! +#147000000 +1# +#148000000 +0# +#149000000 +1! +#150000000 +0! +#151000000 +1! +#152000000 +0! +#153000000 +1! +#154000000 +0! +#155000000 +1# +#156000000 +0# +#157000000 +1! +#158000000 +0! +#159000000 +1" +b1010 % +#160000000 +0" +#161000000 +1! +b1011 $ +#162000000 +0! +#163000000 +1# +b1011 & +#164000000 +0# +#165000000 +1! +#166000000 +0! +#167000000 +1# +#168000000 +0# +#169000000 +1! +#170000000 +0! +#171000000 +1# +#172000000 +0# +#173000000 +1" +b1011 % +#174000000 +0" +#175000000 +1! +b1100 $ +#176000000 +0! +#177000000 +1! +#178000000 +0! +#179000000 +1# +b1100 & +#180000000 +0# +#181000000 +1" +b1100 % +#182000000 +0" +#183000000 +1" +b1101 % +#184000000 +0" +#185000000 +1! +b1101 $ +#186000000 +0! +#187000000 +1# +b1101 & +#188000000 +0# +#189000000 +1" +b1110 % +#190000000 +0" +#191000000 +1# +b1110 & +#192000000 +0# +#193000000 +1! +b1110 $ +#194000000 +0! +#195000000 +1# +b1111 & +#196000000 +0# +#197000000 +1# +#198000000 +0# +#199000000 +1! +b1111 $ +#200000000 +0! +#201000000 +1! +#202000000 +0! +#203000000 +1# +#204000000 +0# +#205000000 +1# +#206000000 +0# +#207000000 +1" +b1111 % +#208000000 +0" +#209000000 +1! +b10000 $ +#210000000 +0! +#211000000 +1# +b10000 & +#212000000 +0# +#213000000 +1# +#214000000 +0# +#215000000 +1# +#216000000 +0# +#217000000 +1" +b10000 % +#218000000 +0" +#219000000 +1! +b10001 $ +#220000000 +0! +#221000000 +1! +#222000000 +0! +#223000000 +1! +#224000000 +0! +#225000000 +1" +b10001 % +#226000000 +0" +#227000000 +1! +#228000000 +0! +#229000000 +1! +#230000000 +0! +#231000000 +1" +#232000000 +0" +#233000000 +1" +#234000000 +0" +#235000000 +1! +#236000000 +0! +#237000000 +1! +#238000000 +0! +#239000000 +1# +b10001 & +#240000000 +0# +#241000000 +1" +b10010 % +#242000000 +0" +#243000000 +1! +b10010 $ +#244000000 +0! +#245000000 +1" +#246000000 +0" +#247000000 +1! +#248000000 +0! +#249000000 +1" +#250000000 +0" +#251000000 +1! +#252000000 +0! +#253000000 +1" +#254000000 +0" +#255000000 +1" +#256000000 +0" +#257000000 +1" +#258000000 +0" +#259000000 +1! +#260000000 +0! +#261000000 +1" +#262000000 +0" +#263000000 +1# +b10010 & +#264000000 +0# +#265000000 +1" +b10011 % +#266000000 +0" +#267000000 +1! +b10011 $ +#268000000 +0! +#269000000 +1# +b10011 & +#270000000 +0# +#271000000 +1! +b10100 $ +#272000000 +0! +#273000000 +1" +b10100 % +#274000000 +0" +#275000000 +1! +#276000000 +0! +#277000000 +1# +b10100 & +#278000000 +0# +#279000000 +1" +b10101 % +#280000000 +0" +#281000000 +1" +#282000000 +0" +#283000000 +1! +b10101 $ +#284000000 +0! +#285000000 +1# +b10101 & +#286000000 +0# +#287000000 +1# +b10110 & +#288000000 +0# +#289000000 +1" +b10110 % +#290000000 +0" +#291000000 +1" +#292000000 +0" +#293000000 +1! +b10110 $ +#294000000 +0! +#295000000 +1! +b10111 $ +#296000000 +0! +#297000000 +1" +b10111 % +#298000000 +0" +#299000000 +1" +#300000000 +0" +#301000000 +1! +#302000000 +0! +#303000000 +1" +#304000000 +0" +#305000000 +1" +#306000000 +0" +#307000000 +1" +#308000000 +0" +#309000000 +1! +#310000000 +0! +#311000000 +1# +b10111 & +#312000000 +0# +#313000000 +1" +b11000 % +#314000000 +0" +#315000000 +1" +#316000000 +0" +#317000000 +1" +#318000000 +0" +#319000000 +1! +b11000 $ +#320000000 +0! +#321000000 +1" +#322000000 +0" +#323000000 +1" +#324000000 +0" +#325000000 +1" +#326000000 +0" +#327000000 +1" +#328000000 +0" +#329000000 +1" +#330000000 +0" +#331000000 +1" +#332000000 +0" +#333000000 +1" +#334000000 +0" +#335000000 +1# +b11000 & +#336000000 +0# +#337000000 +1" +b11001 % +#338000000 +0" +#339000000 +1" +#340000000 +0" +#341000000 +1# +b11001 & +#342000000 +0# +#343000000 +1! +b11001 $ +#344000000 +0! +#345000000 +1" +b11010 % +#346000000 +0" +#347000000 +1" +#348000000 +0" +#349000000 +1# +b11010 & +#350000000 +0# +#351000000 +1" +#352000000 +0" +#353000000 +1" +#354000000 +0" +#355000000 +1" +#356000000 +0" +#357000000 +1# +#358000000 +0# +#359000000 +1# +#360000000 +0# +#361000000 +1" +#362000000 +0" +#363000000 +1# +#364000000 +0# +#365000000 +1! +b11010 $ +#366000000 +0! +#367000000 +1! +b11011 $ +#368000000 +0! +#369000000 +1" +b11011 % +#370000000 +0" +#371000000 +1# +b11011 & +#372000000 +0# +#373000000 +1! +b11100 $ +#374000000 +0! +#375000000 +1" +b11100 % +#376000000 +0" +#377000000 +1" +#378000000 +0" +#379000000 +1# +b11100 & +#380000000 +0# +#381000000 +1! +b11101 $ +#382000000 +0! +#383000000 +1# +b11101 & +#384000000 +0# +#385000000 +1" +b11101 % +#386000000 +0" +#387000000 +1# +b11110 & +#388000000 +0# +#389000000 +1" +b11110 % +#390000000 +0" +#391000000 +1! +b11110 $ +#392000000 +0! +#393000000 +1" +b11111 % +#394000000 +0" +#395000000 +1# +b11111 & +#396000000 +0# +#397000000 +1" +#398000000 +0" +#399000000 +1" +#400000000 +0" +#401000000 +1" +#402000000 +0" +#403000000 +1# +#404000000 +0# +#405000000 +1" +#406000000 +0" +#407000000 +1# +#408000000 +0# +#409000000 +1" +#410000000 +0" +#411000000 +1# +#412000000 +0# +#413000000 +1# +#414000000 +0# +#415000000 +1! +b11111 $ +#416000000 +0! +#417000000 +1" +b100000 % +#418000000 +0" +#419000000 +1# +b100000 & +#420000000 +0# +#421000000 +1# +#422000000 +0# +#423000000 +1" +#424000000 +0" +#425000000 +1" +#426000000 +0" +#427000000 +1# +#428000000 +0# +#429000000 +1# +#430000000 +0# +#431000000 +1# +#432000000 +0# +#433000000 +1# +#434000000 +0# +#435000000 +1! +b100000 $ +#436000000 +0! +#437000000 +1! +b100001 $ +#438000000 +0! +#439000000 +1! +#440000000 +0! +#441000000 +1# +b100001 & +#442000000 +0# +#443000000 +1! +#444000000 +0! +#445000000 +1! +#446000000 +0! +#447000000 +1" +b100001 % +#448000000 +0" +#449000000 +1# +b100010 & +#450000000 +0# +#451000000 +1! +b100010 $ +#452000000 +0! +#453000000 +1! +#454000000 +0! +#455000000 +1# +#456000000 +0# +#457000000 +1# +#458000000 +0# +#459000000 +1! +#460000000 +0! +#461000000 +1" +b100010 % +#462000000 +0" +#463000000 +1! +b100011 $ +#464000000 +0! +#465000000 +1# +b100011 & +#466000000 +0# +#467000000 +1! +#468000000 +0! +#469000000 +1" +b100011 % +#470000000 +0" +#471000000 +1" +b100100 % +#472000000 +0" +#473000000 +1# +b100100 & +#474000000 +0# +#475000000 +1! +b100100 $ +#476000000 +0! +#477000000 +1" +b100101 % +#478000000 +0" +#479000000 +1# +b100101 & +#480000000 +0# +#481000000 +1# +#482000000 +0# +#483000000 +1! +b100101 $ +#484000000 +0! +#485000000 +1# +b100110 & +#486000000 +0# +#487000000 +1! +b100110 $ +#488000000 +0! +#489000000 +1# +#490000000 +0# +#491000000 +1! +#492000000 +0! +#493000000 +1# +#494000000 +0# +#495000000 +1" +b100110 % +#496000000 +0" +#497000000 +1# +b100111 & +#498000000 +0# +#499000000 +1! +b100111 $ +#500000000 +0! +#501000000 +1# +#502000000 +0# +#503000000 +1# +#504000000 +0# +#505000000 +1# +#506000000 +0# +#507000000 +1" +b100111 % +#508000000 +0" +#509000000 +1! +b101000 $ +#510000000 +0! +#511000000 +1! +#512000000 +0! +#513000000 +1# +b101000 & +#514000000 +0# +#515000000 +1" +b101000 % +#516000000 +0" +#517000000 +1! +b101001 $ +#518000000 +0! +#519000000 +1" +b101001 % +#520000000 +0" +#521000000 +1# +b101001 & +#522000000 +0# +#523000000 +1" +b101010 % +#524000000 +0" +#525000000 +1! +b101010 $ +#526000000 +0! +#527000000 +1# +b101010 & +#528000000 +0# +#529000000 +1# +b101011 & +#530000000 +0# +#531000000 +1" +b101011 % +#532000000 +0" +#533000000 +1" +#534000000 +0" +#535000000 +1! +b101011 $ +#536000000 +0! +#537000000 +1# +b101100 & +#538000000 +0# +#539000000 +1" +b101100 % +#540000000 +0" +#541000000 +1" +#542000000 +0" +#543000000 +1" +#544000000 +0" +#545000000 +1# +#546000000 +0# +#547000000 +1" +#548000000 +0" +#549000000 +1" +#550000000 +0" +#551000000 +1# +#552000000 +0# +#553000000 +1# +#554000000 +0# +#555000000 +1" +#556000000 +0" +#557000000 +1# +#558000000 +0# +#559000000 +1! +b101100 $ +#560000000 +0! +#561000000 +1# +b101101 & +#562000000 +0# +#563000000 +1" +b101101 % +#564000000 +0" +#565000000 +1# +#566000000 +0# +#567000000 +1" +#568000000 +0" +#569000000 +1# +#570000000 +0# +#571000000 +1" +#572000000 +0" +#573000000 +1# +#574000000 +0# +#575000000 +1# +#576000000 +0# +#577000000 +1# +#578000000 +0# +#579000000 +1# +#580000000 +0# +#581000000 +1! +b101101 $ +#582000000 +0! +#583000000 +1! +b101110 $ +#584000000 +0! +#585000000 +1# +b101110 & +#586000000 +0# +#587000000 +1# +#588000000 +0# +#589000000 +1! +#590000000 +0! +#591000000 +1" +b101110 % +#592000000 +0" +#593000000 +1# +b101111 & +#594000000 +0# +#595000000 +1# +#596000000 +0# +#597000000 +1! +b101111 $ +#598000000 +0! +#599000000 +1# +#600000000 +0# +#601000000 +1# +#602000000 +0# +#603000000 +1# +#604000000 +0# +#605000000 +1" +b101111 % +#606000000 +0" +#607000000 +1! +b110000 $ +#608000000 +0! +#609000000 +1# +b110000 & +#610000000 +0# +#611000000 +1# +#612000000 +0# +#613000000 +1" +b110000 % +#614000000 +0" +#615000000 +1" +b110001 % +#616000000 +0" +#617000000 +1# +b110001 & +#618000000 +0# +#619000000 +1# +#620000000 +0# +#621000000 +1" +#622000000 +0" +#623000000 +1# +#624000000 +0# +#625000000 +1# +#626000000 +0# +#627000000 +1# +#628000000 +0# +#629000000 +1# +#630000000 +0# +#631000000 +1! +b110001 $ +#632000000 +0! +#633000000 +1# +b110010 & +#634000000 +0# +#635000000 +1# +#636000000 +0# +#637000000 +1# +#638000000 +0# +#639000000 +1" +b110010 % +#640000000 +0" +#641000000 +1# +#642000000 +0# +#643000000 +1# +#644000000 +0# +#645000000 +1# +#646000000 +0# +#647000000 +1# +#648000000 +0#