From 9ee015e51e91c6832095ae9ced8210137ff8c734 Mon Sep 17 00:00:00 2001 From: Zachary Stewart Date: Mon, 1 May 2023 17:06:30 -0400 Subject: [PATCH 1/6] Update pulse channel to use system clock. Compute the pulse channel index only when a sample is requested. Work in progress: will not compile in this state, all other channels are broken. --- feo3boy-player/src/main.rs | 2 +- feo3boy/src/apu.rs | 901 +++++++++++++++++------------- feo3boy/src/bits.rs | 32 +- feo3boy/src/clock/mod.rs | 329 +++++++++++ feo3boy/src/clock/typed_cycles.rs | 464 +++++++++++++++ feo3boy/src/gb.rs | 104 ++-- feo3boy/src/lib.rs | 1 + feo3boy/src/memdev.rs | 11 + feo3boy/src/timer.rs | 290 +++++++--- 9 files changed, 1621 insertions(+), 513 deletions(-) create mode 100644 feo3boy/src/clock/mod.rs create mode 100644 feo3boy/src/clock/typed_cycles.rs diff --git a/feo3boy-player/src/main.rs b/feo3boy-player/src/main.rs index a0526fb..10be5c9 100644 --- a/feo3boy-player/src/main.rs +++ b/feo3boy-player/src/main.rs @@ -363,7 +363,7 @@ fn main() { break; } } else if args.mute { - let emu_time = gb.elapsed_time(); + let emu_time = gb.clock().elapsed_time(); if emu_time > scaled_time_since_startup { // Emulator has gotten ahead of realtime, time to break. break; diff --git a/feo3boy/src/apu.rs b/feo3boy/src/apu.rs index c7052e0..076ded2 100644 --- a/feo3boy/src/apu.rs +++ b/feo3boy/src/apu.rs @@ -1,10 +1,18 @@ +use std::f32::consts::PI; +use std::mem; use std::ops::{Mul, MulAssign}; +use std::time::Duration; use bitflags::bitflags; use log::{debug, info, trace}; -use crate::bits::BitGroup; +use crate::bits::{ApplyBitGroup, BitGroup}; +use crate::clock::{ + cycles_to_duration, ClockSnapshot, ClockSpeed, DCycle, SystemClock, SystemClockContext, + TimedChangeTracker, +}; use crate::memdev::{MemDevice, RelativeAddr}; +use crate::timer::TimerDivider; bitflags! { #[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash, MemDevice)] @@ -47,6 +55,67 @@ bitflags! { } } +/// Represents the APU-DIV counter which increments whenever the appropriate DIV bit +/// goes from 1 to 0. This counter is used to trigger various other increments within the +/// APU, including envelope sweep, sound length countdown, and channel 1 frequency sweep. +/// +/// It's somewhat unclear how those ticks are triggered from APU-DIV. Given that the +/// lowest one is 2x APU-DIV increment, I choose to treat them as a falling-edge detector +/// for bits 0, 1, and 2 of this register. +#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)] +pub struct ApuDiv { + /// Current value of this counter. + counter: u8, + /// Value of the counter as of the last APU-DIV tick (so this stays the same across + /// MTicks between APU-DIV ticks). + prev_counter: u8, +} + +impl ApuDiv { + /// Increment the counter, saving the previous counter value for falling-edge + /// detection. + /// + /// This method modifies the APU-DIV in-place, but also returns a copy of the updated + /// value of `self` for convenience. + fn tick_apu_div(&mut self) -> Self { + self.prev_counter = mem::replace(&mut self.counter, self.counter.wrapping_add(1)); + *self + } + + /// Check if the most-recent APU-DIV tick was a falling-edge for the 256 Hz (nominal) + /// cycle. + /// + /// Note that this will return true during all m-cycles between apu_div ticks when it + /// was true on the last apu_div tick, so it should only be used on the apu-div tick + /// cycle. + fn is_256hz_tick(&self) -> bool { + const BIT0: u8 = 1 << 0; + self.prev_counter & BIT0 == 1 && self.counter & BIT0 == 0 + } + + /// Check if the most-recent APU-DIV tick was a falling-edge for the 128 Hz (nominal) + /// cycle. + /// + /// Note that this will return true during all m-cycles between apu_div ticks when it + /// was true on the last apu_div tick, so it should only be used on the apu-div tick + /// cycle. + fn is_128hz_tick(&self) -> bool { + const BIT1: u8 = 1 << 1; + self.prev_counter & BIT1 == 1 && self.counter & BIT1 == 0 + } + + /// Check if the most-recent APU-DIV tick was a falling-edge for the 64 Hz (nominal) + /// cycle. + /// + /// Note that this will return true during all m-cycles between apu_div ticks when it + /// was true on the last apu_div tick, so it should only be used on the apu-div tick + /// cycle. + fn is_64hz_tick(&self) -> bool { + const BIT2: u8 = 1 << 2; + self.prev_counter & BIT2 == 1 && self.counter & BIT2 == 0 + } +} + impl SoundPan { fn channel_mix(&self, right: bool) -> ChannelMix { let shift = if right { 0 } else { 4 }; @@ -123,153 +192,143 @@ impl SoundVolume { } } -/// Represends sweep control settings. -#[derive(Default, Debug, Clone, Eq, PartialEq, Hash, MemDevice)] -#[memdev(bits, readable = SweepControl::RW_BITS, writable = SweepControl::RW_BITS)] -#[repr(transparent)] -pub struct SweepControl(u8); +#[derive(Default, Debug, Clone, PartialEq, Eq, MemDevice)] +#[memdev(byte, read = Self::read, write = Self::write)] +struct Sweep { + /// Equivalent to the slope control. When wavelength is recomputed due to a sweep-pace + /// increment completing, this value is used to determine how much to modify the + /// wavelength by. `L[t+1] = L[t] +/- L[t] / 2^`, since division by a power of + /// two is equivalent to shift, we call this shift. + shift: u8, + /// If set to 1, sweep subtracts from the wavelength, otherwise it adds. + sweep_subtract: bool, + /// Pace in the register visible to the CPU. Changing this does not change the time + /// remaining in the current sweep iteration until the next time the channel is + /// triggered or the the next sweep iteration starts. + /// + /// Pace is nominally in terms of number of 128 Hz ticks, in actuality it is in + /// increments of 4 APU-DIV ticks. + /// + /// Maximum value is 7, so pace * 4 still fits in a u8. + pace: u8, + /// Number of APU-DIV ticks remaining in the current sweep. This is stored in terms of + /// APU-DIV ticks. We effectively count down from pace * 4 to zero and trigger + /// whenever we reach zero. + ticks_in_sweep: u8, +} -impl SweepControl { +impl Sweep { const PACE: BitGroup = BitGroup(0b0111_0000); const SLOPE_DIR: BitGroup = BitGroup(0b0000_1000); const SLOPE_CTRL: BitGroup = BitGroup(0b0000_0111); - const RW_BITS: u8 = 0b0111_1111; - - /// Get the pace. - #[inline] - pub fn pace(&self) -> u8 { - Self::PACE.extract(self.0) - } - - /// Set the pace - #[inline] - pub fn set_pace(&mut self, val: u8) { - Self::PACE.apply(&mut self.0, val); - } - /// Get whether the slope is negative. - #[inline] - pub fn slope_negative(&self) -> bool { - Self::SLOPE_DIR.extract_bool(self.0) - } - - /// Get an i16 indicating the slope direction; -1 for negative, +1 for positive. - #[inline] - pub fn slope_dir(&self) -> i16 { - if self.slope_negative() { - -1 - } else { - 1 + /// Read this as a single byte register. + fn read(&self) -> u8 { + 0xff.with_bits(Self::PACE, self.pace) + .with_bits(Self::SLOPE_DIR, self.sweep_subtract as u8) + .with_bits(Self::SLOPE_CTRL, self.shift) + } + + /// Write this as a single byte register. + fn write(&mut self, value: u8) { + let pace = Self::PACE.extract(value); + let sweep_subtract = Self::SLOPE_DIR.extract_bool(value); + let shift = Self::SLOPE_CTRL.extract(value); + + if mem::replace(&mut self.pace, pace) == 0 || pace == 0 { + // If the old pace was zero or the new pace is zero, the new pace applies + // immediately. This implements both instantly stopping sweeps when pace is + // set to 0 and instantly starting sweeps when the pace is updated from 0, but + // not resetting the current sweep if the pace was non-zero. + self.start_sweep(); } + self.sweep_subtract = sweep_subtract; + self.shift = shift; } - /// Set the vin_right - #[inline] - pub fn set_slope_negative(&mut self, val: bool) { - Self::SLOPE_DIR.apply(&mut self.0, val as u8); - } - - /// Get the slope control. - #[inline] - pub fn slope_ctrl(&self) -> u8 { - Self::SLOPE_CTRL.extract(self.0) + /// Resets `ticks_in_sweep` to `4 * pace` to start a new sweep. + fn start_sweep(&mut self) { + self.ticks_in_sweep = self.pace * 4; } - /// Set the right volume. - #[inline] - pub fn set_slope_ctrl(&mut self, val: u8) { - Self::SLOPE_CTRL.apply(&mut self.0, val); - } - - /// Build a new [`Sweep`] from the current [`SweepControl`] settings. - fn new_sweep(&self) -> Sweep { - Sweep { - shift: self.slope_ctrl(), - step: self.slope_dir(), - pace: self.pace(), - ticks: 0, - } - } -} - -#[derive(Default, Debug, Clone, PartialEq, Eq)] -struct Sweep { - shift: u8, - step: i16, - pace: u8, - ticks: u8, -} - -impl Sweep { - fn apply(&self, wavelength: u16) -> u16 { + /// Get the next wavelength, given the current wavelength and sweep direction. Also + /// returns a boolean value indicating if the sweep should cause a wavelength overflow + /// (which should turn off the channel). Note that the overflow can still be set even + /// if `shift == 0`, which otherwise disables sweep. + fn next_wavelength(&self, wavelength: u16) -> (u16, bool) { + // Wavelength is 11 bits, so it should be no more than 0x7FF. + debug_assert!(wavelength <= 0x7FF); if self.shift == 0 { - wavelength + let would_overflow = wavelength * 2 > 0x7FF; + (wavelength, would_overflow) } else { - (wavelength as i16 + self.step * (wavelength as i16 >> self.shift)) as u16 + let delta = wavelength >> self.shift; + let new_wavelength = if self.sweep_subtract { + wavelength - delta + } else { + wavelength + delta + }; + let clamped_wavelength = new_wavelength & 0x7FF; + let overflowed = new_wavelength > 0x7FF; + (clamped_wavelength, overflowed) } } - fn tick(&mut self) -> bool { - if self.pace == 0 { + /// Run one apu-div tick on this register. Returns true if the sweep completed. + fn tick_apu_div(&mut self, apu_div: ApuDiv) -> bool { + if self.pace == 0 || !apu_div.is_128hz_tick() { return false; } - self.ticks += 1; - if self.ticks == self.pace { - self.ticks = 0; - true - } else { - false - } + self.ticks_in_sweep -= 1; + self.ticks_in_sweep == 0 } } -#[derive(Default, Debug, Clone, Eq, PartialEq, Hash, MemDevice)] -#[memdev(bits)] -#[repr(transparent)] -pub struct PulseTimer(u8); - -impl PulseTimer { - const DUTY_CYCLE: BitGroup = BitGroup(0b1100_0000); - const INIT_TIMER: BitGroup = BitGroup(0b0011_1111); - - /// Update the value in-place. - #[inline] - fn set(&mut self, val: u8) { - self.0 = val; - } +/// Length timer for a channel. This is a 6 bit register which counts up at 256 Hz +/// (nominal, tied to APU-DIV) when enabled. When it reaches 64 (i.e. overflows) the +/// channel shuts down. +#[derive(Default, Debug, Clone, Eq, PartialEq, Hash)] +pub struct LengthTimer { + /// Counter register of the length timer. + counter: u8, + /// Whether the length timer is enabled. + enabled: bool, +} - /// Get the duty cycle. - #[inline] - pub fn duty_cycle(&self) -> u8 { - Self::DUTY_CYCLE.extract(self.0) - } +impl LengthTimer { + const COUNTER_MASK: u8 = 0x3F; - /// Gets the duty cycle as a usize index. - #[inline] - pub fn duty(&self) -> usize { - self.duty_cycle() as usize + /// Sets the value of the timer. The value will be masked to 6 bits. + fn set_counter(&mut self, val: u8) { + self.counter = val & Self::COUNTER_MASK; } - /// Set the duty cycle. - #[inline] - pub fn set_duty_cycle(&mut self, val: u8) { - Self::DUTY_CYCLE.apply(&mut self.0, val); + /// Sets whether the counter is enabled. + fn set_enabled(&mut self, enabled: bool) { + self.enabled = enabled; } - /// Get the init timer - #[inline] - pub fn init_timer(&self) -> u8 { - Self::INIT_TIMER.extract(self.0) + /// Get the enabled value. + fn enabled(&self) -> bool { + self.enabled } - /// Set the init timer. - #[inline] - pub fn set_init_timer(&mut self, val: u8) { - Self::INIT_TIMER.apply(&mut self.0, val); + /// Tick the length counter if it is enabled and this cycle is a 256 Hz (nominal) + /// cycle of the apu-div. Returns true if the counter has hit 64 (and rolled over). + fn tick_apu_div(&mut self, apu_div: ApuDiv) -> bool { + if !self.enabled || !apu_div.is_256hz_tick() { + return false; + } + let inc = self.counter + 1; + self.counter = inc & Self::COUNTER_MASK; + inc > Self::COUNTER_MASK } } +/// Values set in the envelope control register as visible to the CPU. +/// +/// Changes to the enveolpe don't take effect until the channel is triggered. #[derive(Default, Debug, Clone, Eq, PartialEq, Hash, MemDevice)] #[memdev(bits)] #[repr(transparent)] @@ -299,23 +358,13 @@ impl EnvelopeControl { /// Get whether the direction is negative. #[inline] - pub fn direction_negative(&self) -> bool { - !Self::DIRECTION.extract_bool(self.0) - } - - /// Get a signed value indicating the direction, either `-1` or `1`. - #[inline] - pub fn direction(&self) -> i8 { - if self.direction_negative() { - -1 - } else { - 1 - } + pub fn direction_increasing(&self) -> bool { + Self::DIRECTION.extract_bool(self.0) } - /// Set whether the direction is negative + /// Set whether the direction is increasing. #[inline] - pub fn set_direction_negative(&mut self, val: bool) { + pub fn set_direction_increasing(&mut self, val: bool) { Self::DIRECTION.apply(&mut self.0, val as u8); } @@ -334,23 +383,20 @@ impl EnvelopeControl { pub fn dac_enabled(&self) -> bool { (self.0 & 0xf8) != 0 } - - fn new_envelope(&self) -> Envelope { - Envelope { - level: self.init_vol(), - step: self.direction(), - pace: self.pace(), - ticks: 0, - } - } } +/// Internal state of the envelope, not reloaded until the channel is triggered. #[derive(Default, Debug, Clone, PartialEq, Eq)] struct Envelope { + /// Volume level the envelope is set to from 0 to 8. level: i8, - step: i8, + /// True if the envelope is increasing. + direction: i8, + /// Pace is the number of APU-DIV 64 Hz (nominal) ticks before a level change. pace: u8, - ticks: u8, + /// Number of 64 Hz (nominal) ticks elapced at the specified pace. Changes to the + /// output level will happen when this reaches the value of `pace`. + elapsed_pace_ticks: u8, } impl Envelope { @@ -358,15 +404,23 @@ impl Envelope { self.level as u16 } - fn tick(&mut self) { - if self.pace > 0 { - self.ticks += 1; - if self.ticks == self.pace { - self.ticks = 0; - self.level = (self.level + self.step).clamp(0x0, 0xf); + fn tick_apu_div(&mut self, apu_div: ApuDiv) { + if self.pace > 0 && apu_div.is_64hz_tick() { + self.elapsed_pace_ticks += 1; + if self.elapsed_pace_ticks == self.pace { + self.elapsed_pace_ticks = 0; + self.level = (self.level + self.direction).clamp(0x0, 0xf); } } } + + /// Reload the internal envelope control from the register (for a channel trigger). + fn reload_register(&mut self, reg: &EnvelopeControl) { + self.level = reg.init_vol(); + self.direction = if reg.direction_increasing() { 1 } else { -1 }; + self.pace = reg.pace(); + self.elapsed_pace_ticks = 0; + } } #[derive(Default, Debug, Clone, Eq, PartialEq, Hash, MemDevice)] @@ -495,290 +549,187 @@ impl ChannelControl { } } -const HIGH_PASS_CUTOFF: f32 = 200.0; - -#[derive(Clone, Debug, Default)] -struct HighPassFilter { - last_input: Sample, - last_output: Sample, - alpha: f32, -} - -impl HighPassFilter { - fn set_sample_rate(&mut self, sample_rate: u32) { - self.alpha = 1.0 / (2.0 * 3.1416 / (sample_rate as f32) * HIGH_PASS_CUTOFF + 1.0); - } +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct PulseChannel { + /// Envelope register. Values here are only picked up when the channel is triggered. + pub envelope_control: EnvelopeControl, + /// Currently active envelope. + envelope: Envelope, + /// Sweep register and currently active sweep settings. + sweep: Sweep, - fn filter_sample(&mut self, input: Sample) -> Sample { - let output = Sample { - left: self.alpha * (input.left + self.last_output.left - self.last_input.left), - right: self.alpha * (input.right + self.last_output.right - self.last_input.right), - }; + /// Selected duty cycle. Part of the duty cycle/length register. Stored as an index + /// into the duty-cycle array. + duty_cycle: usize, - self.last_input = input; - self.last_output = output; + /// Length timer. When length is active, this counts up until it reaches 64. + length_timer: LengthTimer, - output - } -} - -// We'll worry about double speed another time -const CLOCK_SPEED: u32 = 4194304; - -// maximum period of any waveform in t cycles -#[allow(unused)] -const MAX_PERIOD: u32 = 131072; + /// Whether the channel is currently active. + active: bool, -#[derive(Clone, Debug)] -pub struct ApuState { - output_sample: Option, - output_period: f32, - sample_cursor: f32, - hpf: HighPassFilter, -} + /// 11-bit register which controls the rate that the channel sweeps through the Pulse + /// Table. Essentially, the bit selected in the pulse table increments every time a + /// counter starting from `wavelength` overflows, if incrementing at 1 MiHz (the + /// standard-speed m-cycle frequency, or once every 2 d-cycles). + wavelength: u16, -/// A single audio sample. -#[derive(Copy, Clone, Debug, Default, PartialEq)] -pub struct Sample { - /// Wave value for the left speaker. - pub left: f32, - /// Wave value for the right speaker. - pub right: f32, + /// When the channel was last triggered. + triggered: TimedChangeTracker<()>, } -impl Mul for Sample { - type Output = Sample; +impl PulseChannel { + // The duty cycle/length register: + /// Duty cycle portion of the length + duty cycle register. + const DUTY_CYCLE: BitGroup = BitGroup(0b1100_0000); + /// Length portion of the length + duty cycle register. + const LENGTH_TIMER: BitGroup = BitGroup(0b0011_1111); + + // The Wavelength high and control register: + /// Trigger bit of the wavelength-high/control register. + const TRIGGER: BitGroup = BitGroup(0b1000_0000); + /// The length enable portion of the wavelength-high/control register. + const LENGTH_ENABLE: BitGroup = BitGroup(0b0100_0000); + /// Wavelength-high portion of the wavelength-high/control register. + const WAVELENGTH_HIGH: BitGroup = BitGroup(0b0000_0111); - fn mul(self, rhs: f32) -> Self::Output { - Sample { - left: self.left * rhs, - right: self.right * rhs, - } + /// Table representing each possible duty cycle of the pulse channel. + /// + /// TODO: Why are these offset? Is it confirmed that these should start/end this way? + const PULSE_TABLE: [[u16; 8]; 4] = [ + [1, 1, 1, 1, 1, 1, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 0], + [0, 1, 1, 1, 1, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 1], + ]; + + /// Get the lower byte of the wavelength. + pub fn wavelength_low(&self) -> u8 { + let [wavelength_low, _] = self.wavelength.to_le_bytes(); + wavelength_low } -} -impl Mul for f32 { - type Output = Sample; - - #[inline] - fn mul(self, rhs: Sample) -> Self::Output { - rhs * self + /// Set the lower byte of the wavelength. + pub fn set_wavelength_low(&mut self, wavelength_low: u8) { + let [_, wavelength_high] = self.wavelength.to_le_bytes(); + self.wavelength = u16::from_le_bytes([wavelength_low, wavelength_high]); } -} -impl MulAssign for Sample { - #[inline] - fn mul_assign(&mut self, rhs: f32) { - *self = *self * rhs; + /// Read the wavelength high bits adn readable control bits. + pub fn wavelength_high_and_control(&self) -> u8 { + // length_enable is the only readable bit. + 0xff.with_bits(Self::LENGTH_ENABLE, self.length_timer.enabled()) } -} -impl ApuState { - pub fn new() -> Self { - ApuState { - // Number of samples saved is low -- the runner should be moving these to the - // audio system faster than we're generating them. - output_sample: None, - output_period: 0.0, - sample_cursor: 0.0, - hpf: HighPassFilter::default(), - } - } + /// Set the value of the wavelength high and control register. + pub fn set_wavelength_high_and_control(&mut self, value: u8) { + let trigger = Self::TRIGGER.extract_bool(value); + let length_enable = Self::LENGTH_ENABLE.extract_bool(value); + let wavelength_high = Self::WAVELENGTH_HIGH.extract(value); + let [wavelength_low, _] = self.wavelength.to_le_bytes(); - pub fn set_output_sample_rate(&mut self, sample_rate: u32) { - self.output_period = CLOCK_SPEED as f32 / sample_rate as f32; - self.hpf.set_sample_rate(sample_rate); - info!("Setting output period {}", self.output_period); + self.wavelength = u16::from_le_bytes([wavelength_low, wavelength_high]); + self.length_timer.set_enabled(length_enable); + if trigger { + self.triggered.set(()); + self.active = true; + self.sweep.start_sweep(); + self.envelope.reload_register(&self.envelope_control); + } } - pub fn consume_output_sample(&mut self) -> Option { - self.output_sample.take() + /// Reads out the value of the combined length and duty-cycle register. Since the + /// length register is write-only, this only allows viewing the duty cycle. + pub fn length_and_duty_cycle(&self) -> u8 { + 0xff.with_bits(Self::DUTY_CYCLE, self.duty_cycle as u8) } -} - -pub trait Channel { - fn get_sample(&self) -> f32; - fn read_control(&self) -> u8; - fn set_control(&mut self, value: u8); - fn set_length(&mut self, value: u8); - - fn check_trigger(&mut self); - fn apu_tick(&mut self); -} - -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct PulseChannel { - pub envelope_control: EnvelopeControl, - envelope: Envelope, - sweep_control: SweepControl, - sweep: Sweep, - pub timer: PulseTimer, - period: u32, - sample_cursor: u32, - phase_offset: u32, - active: bool, - triggered: bool, - wavelength: u16, - level: u16, - length_enable: bool, - length_acc: u8, - length_aticks: u8, - envelope_aticks: u8, - sweep_aticks: u8, -} - -const PULSE_TABLE: [[u16; 8]; 4] = [ - [1, 1, 1, 1, 1, 1, 1, 0], - [0, 1, 1, 1, 1, 1, 1, 0], - [0, 1, 1, 1, 1, 0, 0, 0], - [1, 0, 0, 0, 0, 0, 0, 1], -]; - -impl PulseChannel { - pub fn wavelength_low(&self) -> u8 { - (self.wavelength & 0xff) as u8 + /// Set the combined length and duty cycle register. + pub fn set_length_and_duty_cycle(&mut self, value: u8) { + let duty_cycle = Self::DUTY_CYCLE.extract(value); + let length = Self::LENGTH_TIMER.extract(value); + self.duty_cycle = duty_cycle as usize; + self.length_timer.set_counter(length); } - pub fn set_envelope(&mut self, value: u8) { - self.envelope_control.set(value); - //set an envelope phase offset? - //needs retrigger to take - } + /// Run one tick at the APU-DIV clock rate. + fn tick_apu_div(&mut self, apu_div: ApuDiv) { + if self.length_timer.tick_apu_div(apu_div) { + self.active = false; + } - pub fn set_wavelength_low(&mut self, low_byte: u8) { - self.wavelength = (self.wavelength & 0x700) | low_byte as u16; - self.generate_period(); + if self.sweep.tick_apu_div(apu_div) { + let (wavelength, overflowed) = self.sweep.next_wavelength(self.wavelength); + if overflowed { + self.wavelength = 0; + self.active = false; + } else { + self.wavelength = wavelength; + self.sweep.start_sweep(); + } + } + self.envelope.tick_apu_div(apu_div); } - fn generate_period(&mut self) { - self.period = 32 * (2048 - self.wavelength as u32); + /// Update any time-tracking fields with the current clock snapshot. + fn update_if_dirty(&mut self, now: ClockSnapshot) { + self.triggered.update_if_dirty(now); } - pub fn tick(&mut self, tcycles: u32) { - if self.period > 0 { - self.sample_cursor = (self.sample_cursor + tcycles) % self.period; - } + /// Get the index into the pulse table for the current time based on when the channel + /// was last triggered. + fn pulse_step(&self, now: DCycle) -> usize { + let cycles_since_triggered = now - self.triggered.changed_fixed_cycle(); + // The period is the time between increments of the index in the pulse table. It + // is in terms of number of 1 MiHz cycles between increments. Each such cycle is + // effectively 2 d-cycles. We multiply by 2 to put this in terms of D-cycles. + let period = (2048 - self.wavelength as u64) * 2; + // We have effectively incremented the pulse table index a number of times + // equivalent to the number of cycles elapsed divided by the number of cycles per + // increment. + let increments = cycles_since_triggered.as_u64() / period; + // The pulse table has 8 entries, so we wrap around to 8. + let index = increments % 8; + index as usize } -} -impl Channel for PulseChannel { - fn get_sample(&self) -> f32 { + /// Get a sample from this channel. + fn get_sample(&self, now: DCycle) -> f32 { if self.envelope_control.dac_enabled() && self.active { - let pulse_step = (((self.sample_cursor as u32 + self.phase_offset) % self.period) - / (self.period / 8)) as usize; + let pulse_step = self.pulse_step(now); debug!( "Sampling pulse channel from step {} of duty cycle {}", - pulse_step, - self.timer.duty() + pulse_step, self.duty_cycle, ); - let dac_input = PULSE_TABLE[self.timer.duty()][pulse_step] * self.envelope.level(); + let dac_input = Self::PULSE_TABLE[self.duty_cycle][pulse_step] * self.envelope.level(); 1.0 - (dac_input as f32 / 15.0 * 2.0) } else { 0.0 } } - - fn read_control(&self) -> u8 { - if self.length_enable { - ChannelControl::LENGTH_ENABLE.bits() - } else { - 0 - } - } - - fn set_control(&mut self, value: u8) { - let control = ChannelControl::from_bits_truncate(value); - - self.triggered = control.contains(ChannelControl::TRIGGER); - - self.length_enable = control.contains(ChannelControl::LENGTH_ENABLE); - self.wavelength = (self.wavelength & 0xff) | (control.wavelength_high() as u16) << 8; - self.generate_period(); - } - - fn set_length(&mut self, value: u8) { - self.timer.set(value); - self.length_acc = self.timer.init_timer(); - } - - fn check_trigger(&mut self) { - if self.triggered { - self.active = true; - self.triggered = false; - self.envelope = self.envelope_control.new_envelope(); - self.sweep = self.sweep_control.new_sweep(); - - info!( - "Pulse channel triggered at frequency {} with envelope {:?}, sweep control: {:x}, sweep {:?}, length_enable {}, length_acc {}", - CLOCK_SPEED / self.period, self.envelope, self.sweep_control.0, self.sweep, self.length_enable, self.length_acc - ); - } - } - - fn apu_tick(&mut self) { - self.length_aticks += 1; - self.envelope_aticks += 1; - self.sweep_aticks += 1; - - if self.sweep.pace == 0 { - self.sweep.pace = self.sweep_control.pace(); - } - - if self.length_aticks == 2 { - self.length_aticks = 0; - if self.length_enable { - self.length_acc = self.length_acc.wrapping_add(1); - if self.length_acc == 64 { - self.active = false; - } - } - } - if self.sweep_aticks == 4 { - self.sweep_aticks = 0; - if self.sweep.tick() { - let wavelength = self.sweep.apply(self.wavelength); - if wavelength > 0x7ff { - self.wavelength = 0; - self.active = false; - } else { - self.wavelength = wavelength; - self.generate_period(); - } - } - } - if self.envelope_aticks == 8 { - self.envelope_aticks = 0; - self.envelope.tick(); - } - } } impl MemDevice for PulseChannel { const LEN: usize = 5; fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { - match addr.index() { - 0x00 => self.sweep_control.read_byte_relative(addr), - 0x01 => self.timer.read_byte_relative(addr.offset_by(0x01)), - 0x02 => self - .envelope_control - .read_byte_relative(addr.offset_by(0x02)), + dispatch_memdev_byte!(PulseChannel, addr, |addr| { + 0x00 => self.sweep.read_byte_relative(addr), + 0x01 => self.length_and_duty_cycle(), + 0x02 => self.envelope_control.read_byte_relative(addr), 0x03 => self.wavelength_low(), - 0x04 => self.read_control(), - _ => panic!("Address {} out of range for PulseChannel", addr), - } + 0x04 => self.wavelength_high_and_control(), + }) } fn write_byte_relative(&mut self, addr: RelativeAddr, val: u8) { - match addr.index() { - 0x00 => self.sweep_control.write_byte_relative(addr, val), - 0x01 => self.set_length(val), - 0x02 => self.set_envelope(val), + dispatch_memdev_byte!(PulseChannel, addr, |addr| { + 0x00 => self.sweep.write_byte_relative(addr, val), + 0x01 => self.set_length_and_duty_cycle(val), + 0x02 => self.envelope_control.write_byte_relative(addr, val), 0x03 => self.set_wavelength_low(val), - 0x04 => self.set_control(val), - _ => panic!("Address {} out of range for PulseChannel", addr), - } + 0x04 => self.set_wavelength_high_and_control(val), + }) } memdev_bytes_from_byte!(PulseChannel); @@ -883,10 +834,7 @@ impl Channel for WavetableChannel { fn check_trigger(&mut self) { if self.triggered { - info!( - "Wavetable channel triggered at frequency {}", - CLOCK_SPEED / self.period - ); + info!("Wavetable channel triggered"); info!(" Wavetable sample table: {:?}", self.sample_table); self.active = true; @@ -1074,6 +1022,73 @@ impl MemDevice for NoiseChannel { memdev_bytes_from_byte!(NoiseChannel); } +const HIGH_PASS_CUTOFF: f32 = 200.0; + +#[derive(Clone, Debug, Default)] +struct HighPassFilter { + last_input: Sample, + last_output: Sample, + alpha: f32, +} + +impl HighPassFilter { + fn set_sample_rate(&mut self, sample_rate: u32) { + self.alpha = 1.0 / (2.0 * PI / (sample_rate as f32) * HIGH_PASS_CUTOFF + 1.0); + } + + fn filter_sample(&mut self, input: Sample) -> Sample { + let output = self.alpha + * Sample { + left: input.left + self.last_output.left - self.last_input.left, + right: input.right + self.last_output.right - self.last_input.right, + }; + + self.last_input = input; + self.last_output = output; + + output + } +} + +/// A single audio sample. +#[derive(Copy, Clone, Debug, Default, PartialEq)] +pub struct Sample { + /// Wave value for the left speaker. + pub left: f32, + /// Wave value for the right speaker. + pub right: f32, +} + +impl Mul for Sample { + type Output = Sample; + + fn mul(self, rhs: f32) -> Self::Output { + Sample { + left: self.left * rhs, + right: self.right * rhs, + } + } +} + +impl Mul for f32 { + type Output = Sample; + + #[inline] + fn mul(self, rhs: Sample) -> Self::Output { + Sample { + left: self * rhs.left, + right: self * rhs.right, + } + } +} + +impl MulAssign for Sample { + #[inline] + fn mul_assign(&mut self, rhs: f32) { + *self = *self * rhs; + } +} + /// Memory-mapped IO registers used by the APU. #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct ApuRegs { @@ -1100,12 +1115,97 @@ memdev_fields!(ApuRegs, len: 0x30, { 0x20..=0x2f => { ch3, skip_over: 5 }, }); -pub trait ApuContext { +impl ApuRegs { + /// Tick the APU-DIV given the status of the APU-DIV register as of this apu-div tick + /// cycle. Should only be called on the cycle when ApuDiv was incremented. + fn tick_apu_div(&mut self, apu_div: ApuDiv) { + self.ch1.tick_apu_div(apu_div); + self.ch2.tick_apu_div(apu_div); + } + + /// Update any time-tracking registers with the current clock snapshot. + pub fn update_if_dirty(&mut self, now: ClockSnapshot) { + self.ch1.update_if_dirty(now); + self.ch2.update_if_dirty(now); + } +} + +#[derive(Clone, Debug)] +pub struct ApuState { + /// The most recently generated output audio sample, if any. + output_sample: Option, + /// Number of samples generated per second. + sample_rate: u64, + /// Counter of what sample we are up to. This count is non-cumulative and only tracks + /// from the last time the sample rate was changed. + sample: u64, + /// Time that the sample rate was last changed. When the sample rate is changed, this + /// gets reset to the SystemClock time for the next cycle after the cycle when the + /// rate was changed. + sample_rate_changed_at: Option, + /// Previous value of the bit selected from the DIV register that drives the sound + /// clock. The sound clock triggers whenever this hits a falling edge. This bit is + /// slected from divider bit 13 or 14 depending on whether the system is running at + /// normal or double speed. + prev_divider_bit: bool, + /// Internal APU-DIV counter, incremented when APU-DIV changes. + apu_div: ApuDiv, + hpf: HighPassFilter, +} + +impl ApuState { + pub fn new() -> Self { + ApuState { + // Number of samples saved is low -- the runner should be moving these to the + // audio system faster than we're generating them. + output_sample: None, + sample_rate: 0, + sample: 0, + sample_rate_changed_at: Some(Duration::ZERO), + prev_divider_bit: false, + apu_div: ApuDiv::default(), + hpf: HighPassFilter::default(), + } + } + + pub fn set_output_sample_rate(&mut self, sample_rate: u32) { + self.sample_rate = sample_rate as u64; + self.sample = 0; + self.sample_rate_changed_at = None; + self.hpf.set_sample_rate(sample_rate); + } + + pub fn consume_output_sample(&mut self) -> Option { + self.output_sample.take() + } + + /// Calculate the time at which the next sample should be generated. + fn next_sample_time(&self) -> Duration { + let time_since_changed = cycles_to_duration(self.sample, self.sample_rate); + let changed_at = self + .sample_rate_changed_at + .expect("sample rate change time was not set"); + changed_at + time_since_changed + } + + /// Update the `sample_rate_changed_at` to be the time of the next sample at the + /// current sample rate, if it isn't set. + fn recompute_sample_time_if_needed(&mut self, clock: &SystemClock) { + if self.sample_rate_changed_at.is_none() { + self.sample_rate_changed_at = Some(clock.elapsed_time()); + } + } +} + +pub trait ApuContext: SystemClockContext { fn apu(&self) -> &ApuState; fn apu_mut(&mut self) -> &mut ApuState; fn apu_regs(&self) -> &ApuRegs; fn apu_regs_mut(&mut self) -> &mut ApuRegs; + + /// Get the timer divider register, needed to determine when the APU clock ticks. + fn divider(&self) -> &TimerDivider; } fn get_stereo_sample(ctx: &impl ApuContext, right: bool) -> f32 { @@ -1132,22 +1232,29 @@ fn get_stereo_sample(ctx: &impl ApuContext, right: bool) -> f32 { } /// to be called on divider bit 4 (5 double speed) falling edge -pub fn apu_tick(ctx: &mut impl ApuContext) { - ctx.apu_regs_mut().ch1.apu_tick(); - ctx.apu_regs_mut().ch2.apu_tick(); - ctx.apu_regs_mut().ch3.apu_tick(); - ctx.apu_regs_mut().ch4.apu_tick(); +pub fn tick_apu_div(ctx: &mut impl ApuContext) { + let apu_div = ctx.apu_mut().apu_div.tick_apu_div(); + ctx.apu_regs_mut().tick_apu_div(apu_div); } -pub fn tick(ctx: &mut impl ApuContext, tcycles: u64) { - let sample_cursor = ctx.apu().sample_cursor; +pub fn tick(ctx: &mut impl ApuContext) { + let divider_bit_selector = match ctx.clock().speed() { + ClockSpeed::Normal => 0x2000, + ClockSpeed::Double => 0x4000, + }; + let divider_bit = ctx.divider().value() & divider_bit_selector != 0; + let prev_divider_bit = mem::replace(&mut ctx.apu_mut().prev_divider_bit, divider_bit); + if prev_divider_bit && !divider_bit { + tick_apu_div(ctx); + } ctx.apu_regs_mut().ch1.check_trigger(); ctx.apu_regs_mut().ch2.check_trigger(); ctx.apu_regs_mut().ch3.check_trigger(); ctx.apu_regs_mut().ch4.check_trigger(); - if ctx.apu().output_period > 0.0 { + if ctx.apu().sample_rate > 0 { + let next_sample_time = ctx.apu().next_sample_time(); let next_sample = ctx.apu().output_period - sample_cursor; if tcycles as f32 > next_sample { ctx.apu_regs_mut().ch1.tick(next_sample as u32); @@ -1180,7 +1287,5 @@ pub fn tick(ctx: &mut impl ApuContext, tcycles: u64) { ctx.apu_regs_mut().ch3.tick(tcycles as u32); ctx.apu_regs_mut().ch4.tick(tcycles as u32); } - - ctx.apu_mut().sample_cursor = (sample_cursor + tcycles as f32) % ctx.apu().output_period; } } diff --git a/feo3boy/src/bits.rs b/feo3boy/src/bits.rs index b176791..dd87406 100644 --- a/feo3boy/src/bits.rs +++ b/feo3boy/src/bits.rs @@ -47,7 +47,17 @@ impl BitGroup { /// then apply it to `dest` with a mask. #[inline] pub fn apply(self, dest: &mut u8, val: u8) { - *dest = (*dest & !self.0) | ((val << self.0.trailing_zeros()) & self.0); + *dest = self.applied(*dest, val); + } + + /// Write `val` to the part of `dest` represented by this [`BitGroup`], returning the + /// result. + /// + /// Shift `val` to the left so that it starts at the same point as this `BitGroup` and + /// then apply it to `dest` with a mask. + #[inline] + pub const fn applied(self, dest: u8, val: u8) -> u8 { + (dest & !self.0) | ((val << self.0.trailing_zeros()) & self.0) } /// Write `val` to this bits-based value. @@ -61,3 +71,23 @@ impl BitGroup { *dest = B::from_bits_truncate(bits); } } + +/// Extension trait that adds the `with_bits` and `set_bits` methods to `u8`. +pub trait ApplyBitGroup { + /// Make a new copy of `self` with the bits specified by the given `BitGroup` set to + /// the `value` specified. + fn with_bits(self, group: BitGroup, value: u8) -> Self; + + /// Set the bits specified by the given `BitGroup` to the `value` specified, in-place. + fn set_bits(&mut self, group: BitGroup, value: u8); +} + +impl ApplyBitGroup for u8 { + fn with_bits(self, group: BitGroup, value: u8) -> Self { + group.applied(self, value) + } + + fn set_bits(&mut self, group: BitGroup, value: u8) { + *self = self.with_bits(group, value) + } +} diff --git a/feo3boy/src/clock/mod.rs b/feo3boy/src/clock/mod.rs new file mode 100644 index 0000000..b5495a4 --- /dev/null +++ b/feo3boy/src/clock/mod.rs @@ -0,0 +1,329 @@ +//! Implements a type for tracking the internal GB clock in both t-cycles, m-cycles and +//! Duration units relative to the GameBoy's system startup. + +use std::iter::FusedIterator; +use std::mem; +use std::ops::{Deref, Range}; +use std::time::Duration; + +mod typed_cycles; + +pub use typed_cycles::{DCycle, MCycle, TCycle}; + +/// Enum of possible system clock speeds. +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] +pub enum ClockSpeed { + /// Clock speed is normal (4 MiHz). + #[default] + Normal = 1, + /// Clock is at double speed (8 MiHz). + Double = 2, +} + +impl ClockSpeed { + /// Amount that systemc clock speed is multiplied by. + #[inline] + pub const fn speed_multiplier(self) -> u64 { + self as u64 + } + + /// Get the number of dcycles per mcycle at this clock speed. + #[inline] + pub const fn dcycles_per_mcycle(self) -> u64 { + match self { + ClockSpeed::Normal => 2, + ClockSpeed::Double => 1, + } + } +} + +/// Provides context access to the system clock. +pub trait SystemClockContext { + /// Access the system clock. + fn clock(&self) -> &SystemClock; +} + +/// Represents the system's actual clock. +#[derive(Debug, Default, Clone)] +pub struct SystemClock { + /// Number of m-cycles elapsed since startup. + /// + /// This overflows ever ~557 thousand years of game time. + mcycle: MCycle, + /// Number of d-cycles elapsed since startup. + dcycle: DCycle, + /// Speed that the clock is currently running at. + speed: ClockSpeed, +} + +impl SystemClock { + /// Get a new, zeroed system clock. + pub const fn new() -> Self { + Self { + mcycle: MCycle::ZERO, + dcycle: DCycle::ZERO, + speed: ClockSpeed::Normal, + } + } + + /// Get a snapshot of the current time in terms of variable m-cycles and fixed + /// d-cycles. + pub const fn snapshot(&self) -> ClockSnapshot { + ClockSnapshot { + mcycle: self.mcycle, + dcycle: self.dcycle, + } + } + + /// Gets the number of m-cycles elapsed since system startup. + /// + /// This will overflow after about 557 thousand years of playtime (or about half that + /// in doublespeed mode). + #[inline] + pub const fn elapsed_cycles(&self) -> MCycle { + self.mcycle + } + + /// Get the total duration that has elapsed since system startup. + #[inline] + pub fn elapsed_time(&self) -> Duration { + self.dcycle.duration() + } + + /// Get the range of durations which the current m-cycle fills. + /// + /// Note that this assumes that the current cycle occurs at the set speed, if + /// set_speed is called and then current_cycle_range is called again without advancing + /// the clock, this will yield a different range. + /// + /// It is unclear how the timing of set-speed should work, i.e. whether the next cycle + /// immediately after the current one occurs after a delay equal to the current speed + /// or the new speed. However, that can likely be dealt with higher up in the + /// eumulator, for example, by only applying a `set_speed` at the start of a cycle + /// (which would cause the tick where the speed change occured to proceed at the prior + /// speed). + pub fn current_cycle_range(&self) -> Range { + let start = self.elapsed_time(); + let end = (self.elapsed_fixed_cycles() + MCycle::new(1).as_dcycle(self.speed)).duration(); + start..end + } + + /// Get an iterator over the duration ranges of the 1 or 2 fixed-duration cycles in + /// the current M-cycle. If the clock is currently running at normal speed, this will + /// be an iterator of 2 ranges, one for each of the D-cycles in this M-cycle. If the + /// clock is running at double speed, this will be a single range equivalent to + /// current_cycle_range. + pub fn current_fixed_cycle_ranges( + &self, + ) -> impl Iterator> + DoubleEndedIterator + FusedIterator { + let dcycle = self.dcycle; + (0..self.speed.dcycles_per_mcycle()).map(move |i| { + let start = (dcycle + i).duration(); + let end = (dcycle + i + 1).duration(); + start..end + }) + } + + /// Get the number of cycles elapsed at *fixed* speed to the start of the current + /// MCycle. + pub const fn elapsed_fixed_cycles(&self) -> DCycle { + self.dcycle + } + + /// Returns the current clock speed. + #[inline] + pub const fn speed(&self) -> ClockSpeed { + self.speed + } + + /// Advance the system clock by 1 m-cycle. + pub fn advance1m(&mut self) { + // This will never overflow in practical use + self.mcycle += 1; + self.dcycle += self.speed.dcycles_per_mcycle(); + } + + /// Sets the new clock speed. + pub fn set_speed(&mut self, speed: ClockSpeed) { + self.speed = speed; + } +} + +/// Type for tracking what clock cycle a value is changed at. +/// +/// Values with change tracking are considered equal if have the same dirty state and were +/// updated at the same cycle. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct TimedChangeTracker { + /// Snapshot of the clock when the tracked value was last updated. + changed_at: ClockSnapshot, + /// If the current value has been modified without updating the clock-cycle. + is_dirty: bool, + /// The value stored. + value: T, +} + +impl TimedChangeTracker { + /// Create a new TimedChangeTracker with the given initial value, set to last changed + /// at time 0. + pub const fn new(initial: T) -> Self { + Self { + changed_at: ClockSnapshot::new(), + is_dirty: false, + value: initial, + } + } + + /// Get the currently stored value. This is always the most up-to-date value, + /// regardless of whether the time is set correctly. + #[inline] + pub const fn get(&self) -> &T { + &self.value + } + + /// Get the clock snapshot from when the value was last changed. + pub const fn changed_snapshot(&self) -> &ClockSnapshot { + debug_assert!( + !self.is_dirty, + "Value was changed but changed_cycle was not updated." + ); + &self.changed_at + } + + /// Get the m-cycle where the value was last changed. This may not be up to date. In + /// debug-mode, this panics if the cycle when the value was changed has not be updated + /// since the value last changed. + #[inline] + pub const fn changed_cycle(&self) -> MCycle { + self.changed_snapshot().elapsed_cycles() + } + + /// Get the d-cycle where the value was last changed. This may not be up to date. In + /// debug-mode, this panics if the cycle when the value was changed has not be updated + /// since the value last changed. + #[inline] + pub const fn changed_fixed_cycle(&self) -> DCycle { + self.changed_snapshot().elapsed_fixed_cycles() + } + + /// If the value was changed but the `changed_cycle` has not been updated, this sets + /// the `changed_cycle`. This should be called whenever the value might have been + /// written, before advancing the system clock, otherwise the changed at times will + /// become incorrect. + pub fn update_if_dirty(&mut self, now: ClockSnapshot) { + if self.is_dirty { + self.changed_at = now; + self.is_dirty = false; + } + } + + /// Set the value, marking the time as dirty. After this `update_if_dirty` must be + /// called to make sure the `changed_cycle` is accurate. Returns the previously stored + /// value. + /// + /// Calling this method always marks the value as dirty regardless of whether the + /// value has actually changed or the two values are equal. + pub fn set(&mut self, value: T) -> T { + self.is_dirty = true; + mem::replace(&mut self.value, value) + } + + /// Set the value and update the `changed_time` to the given value of `now`. + /// + /// Calling this method always marks the value as dirty regardless of whether the + /// value has actually changed or the two values are equal. + pub fn set_now(&mut self, value: T, now: ClockSnapshot) -> T { + self.changed_at = now; + // We have to clear dirty because its possible this is called after the value was + // changed wiht `set` but before `update_if_dirty`. + self.is_dirty = false; + mem::replace(&mut self.value, value) + } + + /// Set the value without updating the change tracker. This allows a new value to be + /// set without affecting the time tracking, for example if there are two possible + /// sources of changes and you only want to record times for one of them. + /// + /// In debug mode this will panic if the changed_cycle is dirty. + pub fn set_untracked(&mut self, value: T) -> T { + debug_assert!( + !self.is_dirty, + "Value was previously changed without updating last_changed_cycle" + ); + mem::replace(&mut self.value, value) + } +} + +// The derived default would result in the correct MCycle::ZERO and is_dirty: false, but +// doing it this way is more explicit. +impl Default for TimedChangeTracker { + #[inline] + fn default() -> Self { + Self::new(T::default()) + } +} + +impl Deref for TimedChangeTracker { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + self.get() + } +} + +/// Number of nanoseconds in a second, used when computing Durations from discrete units +/// with a given frequency. +const NANOS_PER_SEC: u64 = 1_000_000_000; + +/// Converts a count of a number of cycles of some type and the cycle rate in +/// cycles-per-second to the duration it would take for that number of cycles to elapse. +#[inline] +pub const fn cycles_to_duration(num_cycles: u64, cycles_per_second: u64) -> Duration { + let secs = num_cycles / cycles_per_second; + let rem = num_cycles % cycles_per_second; + let nanos = rem * NANOS_PER_SEC / cycles_per_second; + Duration::new(secs, nanos as u32) +} + +/// Snapshot of the SystemClock at a particular cycle. This records both the [`MCycle`] +/// and [`DCycle`], which makes it possible to tell both the number of CPU cycles elapsed +/// and the realtime duration elapsed. +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)] +pub struct ClockSnapshot { + /// Number of MCycles elapsed up to this snapshot. + mcycle: MCycle, + /// Number of DCycles elapsed up to this snapshot. + dcycle: DCycle, +} + +impl ClockSnapshot { + /// Create a new ClockSnapshot set to zero time. + pub const fn new() -> Self { + Self { + mcycle: MCycle::ZERO, + dcycle: DCycle::ZERO, + } + } + + /// Gets the number of m-cycles elapsed since system startup. + /// + /// This will overflow after about 557 thousand years of playtime (or about half that + /// in doublespeed mode). + #[inline] + pub const fn elapsed_cycles(&self) -> MCycle { + self.mcycle + } + + /// Get the total duration that has elapsed since system startup. + #[inline] + pub fn elapsed_time(&self) -> Duration { + self.dcycle.duration() + } + + /// Get the number of cycles elapsed at *fixed* speed to the start of the current + /// MCycle. + pub const fn elapsed_fixed_cycles(&self) -> DCycle { + self.dcycle + } +} diff --git a/feo3boy/src/clock/typed_cycles.rs b/feo3boy/src/clock/typed_cycles.rs new file mode 100644 index 0000000..9795b12 --- /dev/null +++ b/feo3boy/src/clock/typed_cycles.rs @@ -0,0 +1,464 @@ +//! This module just encapsulates basic arithmetic ops and other such trait +//! implementations for the [`MCycle`] and [`TCycle`] strong-typing zero-cost wrappers so +//! they don't clutter the main clock module. + +use std::ops::{Add, AddAssign, Sub, SubAssign}; +use std::time::Duration; + +use crate::clock::{cycles_to_duration, ClockSpeed}; + +/// Typed wrapper for m-cycles. +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[repr(transparent)] +pub struct MCycle { + /// Some number of m-cycles. + mcycle: u64, +} + +impl MCycle { + /// Zero MCycles. + pub const ZERO: Self = Self::new(0); + + /// Number of MCycles in a second at normal speed. When running at double-speed, this + /// is doubled. + const PER_SECOND: Self = Self::new(1_048_576); + + /// Create a new value with the given number of m-cycles. + #[inline] + pub const fn new(mcycle: u64) -> Self { + Self { mcycle } + } + + /// Get the number of t-cycles corresponding to this number of m-cycles. + #[inline] + pub const fn as_tcycle(self) -> TCycle { + TCycle::new(self.mcycle * 4) + } + + /// Get the number of MCycles as a u64. + #[inline] + pub const fn as_u64(self) -> u64 { + self.mcycle + } + + /// Get the number of d-cycles corresponding to this number of m-cycles at the given + /// clock speed. + #[inline] + pub const fn as_dcycle(self, speed: ClockSpeed) -> DCycle { + DCycle::new(self.mcycle * speed.dcycles_per_mcycle()) + } + + /// Get a duration corresponding to this number of m-cycles, rounded to down to the + /// next smallest number of nanoseconds. + pub const fn duration(self, speed: ClockSpeed) -> Duration { + let per_second = Self::PER_SECOND.mcycle * speed.speed_multiplier(); + cycles_to_duration(self.mcycle, per_second) + } + + /// Const implementation of adding two mcycles. + #[inline] + pub const fn add(self, rhs: Self) -> Self { + Self::new(self.mcycle + rhs.mcycle) + } + + /// Const implementation of subtracting two mcycles. + #[inline] + pub const fn sub(self, rhs: Self) -> Self { + Self::new(self.mcycle - rhs.mcycle) + } +} + +impl From for MCycle { + fn from(value: u64) -> Self { + Self::new(value) + } +} + +impl From for u64 { + fn from(value: MCycle) -> Self { + value.as_u64() + } +} + +impl Add for MCycle { + type Output = Self; + + #[inline] + fn add(self, rhs: Self) -> Self::Output { + self.add(rhs) + } +} + +impl AddAssign for MCycle { + #[inline] + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs; + } +} + +impl Add for MCycle { + type Output = Self; + + #[inline] + fn add(self, rhs: u64) -> Self::Output { + self + MCycle::new(rhs) + } +} + +impl AddAssign for MCycle { + #[inline] + fn add_assign(&mut self, rhs: u64) { + *self = *self + rhs; + } +} + +impl Add for MCycle { + type Output = TCycle; + + #[inline] + fn add(self, rhs: TCycle) -> Self::Output { + self.as_tcycle() + rhs + } +} + +impl Sub for MCycle { + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + self.sub(rhs) + } +} + +impl SubAssign for MCycle { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs; + } +} + +impl Sub for MCycle { + type Output = Self; + + #[inline] + fn sub(self, rhs: u64) -> Self::Output { + self - MCycle::new(rhs) + } +} + +impl SubAssign for MCycle { + #[inline] + fn sub_assign(&mut self, rhs: u64) { + *self = *self - rhs; + } +} + +impl Sub for MCycle { + type Output = TCycle; + + #[inline] + fn sub(self, rhs: TCycle) -> Self::Output { + self.as_tcycle() - rhs + } +} + +/// Typed wrapper for t-cycles. +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[repr(transparent)] +pub struct TCycle { + /// Some number of t-cycles. + tcycle: u64, +} + +impl TCycle { + /// Zero TCycles. + pub const ZERO: Self = Self::new(0); + + /// Number of TCycles in a second at normal speed. When running at double speed, this + /// is doubled. + pub const PER_SECOND: Self = MCycle::PER_SECOND.as_tcycle(); + + /// Create a new value with the given number of t-cycles. + #[inline] + pub const fn new(tcycle: u64) -> Self { + Self { tcycle } + } + + /// Get the number of MCycles as a u64. + #[inline] + pub const fn as_u64(self) -> u64 { + self.tcycle + } + + /// Get a duration corresponding to this number of t-cycles, rounded to down to the + /// next smallest number of nanoseconds. + pub const fn duration(self, speed: ClockSpeed) -> Duration { + let per_second = Self::PER_SECOND.tcycle * speed.speed_multiplier(); + cycles_to_duration(self.tcycle, per_second) + } + + /// Const implementation of adding two tcycles. + #[inline] + pub const fn add(self, rhs: Self) -> Self { + Self::new(self.tcycle + rhs.tcycle) + } + + /// Const implementation of subtracting two tcycles. + #[inline] + pub const fn sub(self, rhs: Self) -> Self { + Self::new(self.tcycle - rhs.tcycle) + } +} + +impl From for TCycle { + fn from(value: u64) -> Self { + Self::new(value) + } +} + +impl From for TCycle { + fn from(value: MCycle) -> Self { + value.as_tcycle() + } +} + +impl From for u64 { + fn from(value: TCycle) -> Self { + value.as_u64() + } +} + +impl Add for TCycle { + type Output = Self; + + #[inline] + fn add(self, rhs: Self) -> Self::Output { + self.add(rhs) + } +} + +impl AddAssign for TCycle { + #[inline] + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs; + } +} + +impl Add for TCycle { + type Output = Self; + + #[inline] + fn add(self, rhs: u64) -> Self::Output { + self + TCycle::new(rhs) + } +} + +impl AddAssign for TCycle { + #[inline] + fn add_assign(&mut self, rhs: u64) { + *self = *self + rhs; + } +} + +impl Add for TCycle { + type Output = Self; + + #[inline] + fn add(self, rhs: MCycle) -> Self::Output { + self + rhs.as_tcycle() + } +} + +impl AddAssign for TCycle { + #[inline] + fn add_assign(&mut self, rhs: MCycle) { + *self = *self + rhs; + } +} + +impl Sub for TCycle { + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + self.sub(rhs) + } +} + +impl SubAssign for TCycle { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs; + } +} + +impl Sub for TCycle { + type Output = Self; + + #[inline] + fn sub(self, rhs: u64) -> Self::Output { + self - TCycle::new(rhs) + } +} + +impl SubAssign for TCycle { + #[inline] + fn sub_assign(&mut self, rhs: u64) { + *self = *self - rhs; + } +} + +impl Sub for TCycle { + type Output = Self; + + #[inline] + fn sub(self, rhs: MCycle) -> Self::Output { + self - rhs.as_tcycle() + } +} + +impl SubAssign for TCycle { + #[inline] + fn sub_assign(&mut self, rhs: MCycle) { + *self = *self - rhs; + } +} + +/// D-cycle is a double-speed M-cycle. Unlike [`MCycle`] and [`TCycle`], the duration of a +/// D-cycle does not vary with the clock speed, which makes it more useful for measuring +/// time for parts of the system which aren't affected by clock speed changes. Because a +/// D-cycle always has a fixed duration, these are also referred to as 'fixed cycles'. +/// +/// When the CPU is running at normal speed, DCycle is half of an MCycle (2 TCycles). When +/// running at double-speed, DCycle is the same as MCycle (4 TCycles). +/// +/// Because of this, conversions are also different. While `MCycle` and `TCycle` can be +/// converted between each other and require a [`ClockSpeed`] to convert to [`Duration`], +/// `DCycle` can be converted to `Duration` independent of the clock speed, but needs a +/// clock speed to be converted to [`MCycle`] or [`TCycle`]. +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[repr(transparent)] +pub struct DCycle { + /// Some number of d-cycles. + dcycle: u64, +} + +impl DCycle { + /// Zero DCycles. + pub const ZERO: Self = Self::new(0); + + /// Number of DCycles in a second. This is independent of the [`ClockSpeed`]. + const PER_SECOND: Self = Self::new(2_097_152); + + /// Create a new value with the given number of m-cycles. + #[inline] + pub const fn new(dcycle: u64) -> Self { + Self { dcycle } + } + + /// Get the number of t-cycles corresponding to this number of d-cycles. + #[inline] + pub const fn as_tcycle(self, speed: ClockSpeed) -> TCycle { + TCycle::new(self.dcycle * 2 * speed.speed_multiplier()) + } + + /// Get the number of MCycles as a u64. + #[inline] + pub const fn as_u64(self) -> u64 { + self.dcycle + } + + /// Get a duration corresponding to this number of m-cycles, rounded to down to the + /// next smallest number of nanoseconds. + pub const fn duration(self) -> Duration { + cycles_to_duration(self.dcycle, Self::PER_SECOND.dcycle) + } + + /// Const implementation of adding two mcycles. + #[inline] + pub const fn add(self, rhs: Self) -> Self { + Self::new(self.dcycle + rhs.dcycle) + } + + /// Const implementation of subtracting two mcycles. + #[inline] + pub const fn sub(self, rhs: Self) -> Self { + Self::new(self.dcycle - rhs.dcycle) + } +} + +impl From for DCycle { + fn from(value: u64) -> Self { + Self::new(value) + } +} + +impl From for u64 { + fn from(value: DCycle) -> Self { + value.as_u64() + } +} + +impl Add for DCycle { + type Output = Self; + + #[inline] + fn add(self, rhs: Self) -> Self::Output { + self.add(rhs) + } +} + +impl AddAssign for DCycle { + #[inline] + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs; + } +} + +impl Add for DCycle { + type Output = Self; + + #[inline] + fn add(self, rhs: u64) -> Self::Output { + self + DCycle::new(rhs) + } +} + +impl AddAssign for DCycle { + #[inline] + fn add_assign(&mut self, rhs: u64) { + *self = *self + rhs; + } +} + +impl Sub for DCycle { + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + self.sub(rhs) + } +} + +impl SubAssign for DCycle { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs; + } +} + +impl Sub for DCycle { + type Output = Self; + + #[inline] + fn sub(self, rhs: u64) -> Self::Output { + self - DCycle::new(rhs) + } +} + +impl SubAssign for DCycle { + #[inline] + fn sub_assign(&mut self, rhs: u64) { + *self = *self - rhs; + } +} diff --git a/feo3boy/src/gb.rs b/feo3boy/src/gb.rs index 79955b5..eea5c45 100644 --- a/feo3boy/src/gb.rs +++ b/feo3boy/src/gb.rs @@ -1,7 +1,8 @@ use std::io::{self, Read, Write}; -use std::time::Duration; +use std::marker::PhantomData; use crate::apu::{self, ApuContext, ApuRegs, ApuState}; +use crate::clock::{SystemClock, SystemClockContext}; use crate::gbz80core::direct_executor::DirectExecutor; use crate::gbz80core::direct_executor_v2::DirectExecutorV2; use crate::gbz80core::executor::{Executor, ExecutorConfig}; @@ -13,7 +14,7 @@ use crate::interrupts::InterruptContext; use crate::memdev::{BiosRom, Cartridge, GbMmu, MemContext, Oam, SaveData, Vram}; use crate::ppu::{self, PpuContext, PpuRegs, PpuState}; use crate::serial::{self, SerialContext, SerialRegs, SerialState}; -use crate::timer::{self, TimerContext, TimerRegs, TimerState}; +use crate::timer::{self, TimerContext, TimerDivider, TimerRegs, TimerState}; /// Represents a "real" gameboy, by explicitly using the GbMmu for memory. #[derive(Clone, Debug)] @@ -36,9 +37,8 @@ pub struct Gb { display_ready: bool, /// State of the executor. executor_state: E::State, - /// Total number of m-cycles that have passed since the Gb started (not saved, - /// restarts when starting from a savestate or cartrige ram save). - mcycles: u64, + /// System clock tracking cycles and real time in the emulator. + clock: SystemClock, } impl Gb { @@ -76,19 +76,25 @@ where { /// Create a `Gb` with the given bios and cartridge, using the executor specified by /// this type. Works for executors with state types that implement `Default`. + #[inline] pub fn for_executor(bios: BiosRom, cart: Cartridge) -> Self { - Gb { - cpustate: Gbz80State::new(), - mmu: Box::new(GbMmu::new(bios, cart)), - button_states: ButtonStates::empty(), - serial: SerialState::new(), - timer: TimerState::new(), - apu: ApuState::new(), - ppu: PpuState::new(), - display_ready: false, - executor_state: E::State::default(), - mcycles: 0, + struct DefaultConfig(PhantomData<*const E>); + impl DefaultConfig { + const CONFIG: Self = Self(PhantomData); + } + impl ExecutorConfig for DefaultConfig + where + E: Executor, + E::State: Default, + { + type Executor = E; + + #[inline] + fn create_initial_state(&self) -> ::State { + E::State::default() + } } + Self::for_config(bios, cart, &DefaultConfig::::CONFIG) } } @@ -109,7 +115,7 @@ impl Gb { ppu: PpuState::new(), display_ready: false, executor_state: config.create_initial_state(), - mcycles: 0, + clock: SystemClock::new(), } } @@ -142,21 +148,9 @@ impl Gb { } } - /// Get the total number of mcycles elapsed since emulator start. - #[inline] - pub fn elapsed_mcycles(&self) -> u64 { - self.mcycles - } - - /// Get the total duration since the emulator started (based on the number of m-cycles - /// elapsed. - pub fn elapsed_time(&self) -> Duration { - const MCYCLES_PER_SEC: u64 = 1_048_576; - const NANOS_SPER_SEC: u64 = 1_000_000_000; - let secs = self.mcycles / MCYCLES_PER_SEC; - let rem = self.mcycles % MCYCLES_PER_SEC; - let nanos = rem * NANOS_SPER_SEC / MCYCLES_PER_SEC; - Duration::new(secs, nanos as u32) + /// Get a read-only reference to the GameBoy's internal system clock. + pub fn clock(&self) -> &SystemClock { + &self.clock } } @@ -188,15 +182,24 @@ impl ExecutorContext for Gb { } fn yield1m(&mut self) { - self.mcycles = self.mcycles.wrapping_add(1); + self.mmu.update_if_dirty(self.clock.snapshot()); // TODO: run background processing while yielded. // Continue processing serial while yielded. input::update(self); serial::tick(self, 4); // apu must update before timer to catch falling edges from CPU writes - apu::tick(self, 4); - timer::tick(self, 4); + apu::tick(self); ppu::tick(self, 4); + + self.clock.advance1m(); + // Within each m-cycle, the timer tick must run before the CPU tick. + // Timer behavior with simultaneous CPU writes is a bit strange and somewhat + // difficult to do correctly, since we have to treat things as sort of happening + // at the same time even though we do these two things sequentially. In effect, we + // want the timer to tick first so that the CPU will observe the correct state if + // it does a read, and then if the CPU did a write, we can patch the timer state + // based on the CPU's write on the next tick. + timer::tick(self); } } @@ -246,14 +249,17 @@ impl InputContext for Gb { self.button_states } + #[inline] fn set_button_states(&mut self, button_states: ButtonStates) { self.button_states = button_states; } + #[inline] fn button_reg(&self) -> ButtonRegister { self.mmu.io.buttons } + #[inline] fn set_button_reg(&mut self, buttons: ButtonRegister) { self.mmu.io.buttons = buttons; } @@ -265,14 +271,17 @@ impl SerialContext for Gb { &self.serial } + #[inline] fn serial_mut(&mut self) -> &mut SerialState { &mut self.serial } + #[inline] fn serial_regs(&self) -> &SerialRegs { &self.mmu.io.serial_regs } + #[inline] fn serial_regs_mut(&mut self) -> &mut SerialRegs { &mut self.mmu.io.serial_regs } @@ -284,14 +293,17 @@ impl TimerContext for Gb { &self.timer } + #[inline] fn timer_mut(&mut self) -> &mut TimerState { &mut self.timer } + #[inline] fn timer_regs(&self) -> &TimerRegs { &self.mmu.io.timer_regs } + #[inline] fn timer_regs_mut(&mut self) -> &mut TimerRegs { &mut self.mmu.io.timer_regs } @@ -303,17 +315,24 @@ impl ApuContext for Gb { &self.apu } + #[inline] fn apu_mut(&mut self) -> &mut ApuState { &mut self.apu } + #[inline] fn apu_regs(&self) -> &ApuRegs { &self.mmu.io.apu_regs } + #[inline] fn apu_regs_mut(&mut self) -> &mut ApuRegs { &mut self.mmu.io.apu_regs } + + fn divider(&self) -> &TimerDivider { + self.mmu.io.timer_regs.divider() + } } impl PpuContext for Gb { @@ -322,33 +341,48 @@ impl PpuContext for Gb { &self.ppu } + #[inline] fn ppu_mut(&mut self) -> &mut PpuState { &mut self.ppu } + #[inline] fn ppu_regs(&self) -> &PpuRegs { &self.mmu.io.ppu_regs } + #[inline] fn ppu_regs_mut(&mut self) -> &mut PpuRegs { &mut self.mmu.io.ppu_regs } + #[inline] fn vram(&self) -> &Vram { &self.mmu.vram } + #[inline] fn vram_mut(&mut self) -> &mut Vram { &mut self.mmu.vram } + #[inline] fn oam(&self) -> &Oam { &self.mmu.oam } + #[inline] fn oam_mut(&mut self) -> &mut Oam { &mut self.mmu.oam } + #[inline] fn display_ready(&mut self) { self.display_ready = true; } } + +impl SystemClockContext for Gb { + #[inline] + fn clock(&self) -> &SystemClock { + &self.clock + } +} diff --git a/feo3boy/src/lib.rs b/feo3boy/src/lib.rs index c4f6907..b864c5c 100644 --- a/feo3boy/src/lib.rs +++ b/feo3boy/src/lib.rs @@ -3,6 +3,7 @@ mod macros; pub mod apu; pub mod bits; +pub mod clock; pub mod gb; pub mod gbz80core; pub mod input; diff --git a/feo3boy/src/memdev.rs b/feo3boy/src/memdev.rs index 54931dc..359adbb 100644 --- a/feo3boy/src/memdev.rs +++ b/feo3boy/src/memdev.rs @@ -8,6 +8,7 @@ use thiserror::Error; pub use feo3boy_memdev_derive::MemDevice; use crate::apu::ApuRegs; +use crate::clock::ClockSnapshot; use crate::input::ButtonRegister; use crate::interrupts::{InterruptContext, InterruptEnable, InterruptFlags, Interrupts}; use crate::ppu::PpuRegs; @@ -1008,6 +1009,11 @@ impl MemMappedIo { pub fn new() -> Self { Default::default() } + + pub fn update_if_dirty(&mut self, now: ClockSnapshot) { + self.timer_regs.update_if_dirty(now); + self.apu_regs.update_if_dirty(now); + } } memdev_fields!(MemMappedIo, len: 0x80, { @@ -1114,6 +1120,11 @@ impl GbMmu { interrupt_enable: InterruptEnable(InterruptFlags::empty()), } } + + /// Update the modified-times of any memory locations that require time-tracking. + pub fn update_if_dirty(&mut self, now: ClockSnapshot) { + self.io.update_if_dirty(now); + } } impl Default for GbMmu { diff --git a/feo3boy/src/timer.rs b/feo3boy/src/timer.rs index fe199cd..c30747e 100644 --- a/feo3boy/src/timer.rs +++ b/feo3boy/src/timer.rs @@ -1,5 +1,7 @@ -use crate::apu::{self, ApuContext}; +use std::mem; + use crate::bits::BitGroup; +use crate::clock::{ClockSnapshot, MCycle, SystemClockContext, TimedChangeTracker}; use crate::interrupts::{InterruptContext, InterruptFlags, Interrupts}; use crate::memdev::{MemDevice, RelativeAddr}; @@ -14,20 +16,20 @@ impl TimerControl { const TIMER_ENABLE: BitGroup = BitGroup(0b0000_0100); const RW_BITS: u8 = 0b0000_0111; - pub fn bits(&self) -> u8 { + pub const fn bits(&self) -> u8 { self.0 } /// Get the timer period selecton number, this is *not* the actual period value /// selected. #[inline] - pub fn period_idx(&self) -> u8 { + pub const fn period_idx(&self) -> u8 { Self::TIMER_PERIOD.extract(self.0) } /// Get the actual period selected. #[inline] - pub fn period(&self) -> u16 { + pub const fn period(&self) -> u16 { match self.period_idx() { 0 => 1024, 1 => 16, @@ -37,6 +39,13 @@ impl TimerControl { } } + /// Get a mask which extracts the bit from the divider which corresponds to the + /// selected period. + #[inline] + const fn divider_bit(&self) -> u16 { + self.period() >> 1 + } + /// Set the timer period selecton number, this is *not* the actual period value /// selected. #[inline] @@ -46,7 +55,7 @@ impl TimerControl { /// Get the timer enable #[inline] - pub fn timer_enable(&self) -> bool { + pub const fn timer_enable(&self) -> bool { Self::TIMER_ENABLE.extract_bool(self.0) } @@ -57,13 +66,110 @@ impl TimerControl { } } +#[derive(Default, Debug, Clone, Eq, PartialEq)] +struct TimerCounter(TimedChangeTracker); + +impl TimerCounter { + /// Increment the timer and return true if it overflowed. + fn increment(&mut self) -> bool { + let (timer, overflow) = self.0.overflowing_add(1); + self.0.set_untracked(timer); + overflow + } +} + +#[derive(Default, Debug, Clone, Eq, PartialEq)] +pub struct TimerDivider(TimedChangeTracker); + +impl TimerDivider { + /// Update the timer divider based on the current m-cycle, and return the current + /// value. + fn update(&mut self, now: MCycle) -> u16 { + let ticks_since_update = now - self.0.changed_cycle(); + let new_divider = *self.0 as u64 + ticks_since_update.as_tcycle().as_u64(); + self.0.set_untracked(new_divider as u16); + *self.0 + } + + /// Clear the divider to zero and reset the modification time (mark dirty). + fn reset(&mut self) { + self.0.set(0); + } + + /// Get the portion of the divider register that is visible to the CPU. + fn cpu_visible_portion(&self) -> u8 { + // Is this faster than using a shift or division? Do all those options optimize to + // the same thing? No idea! + let [_, high] = self.0.to_le_bytes(); + high + } + + /// Get the raw value of the divider. + pub fn value(&self) -> u16 { + *self.0 + } + + /// Get the clock cycle when the divider was last reset by the CPU. + pub fn reset_cycle(&self) -> MCycle { + self.0.changed_cycle() + } +} + /// Memory-mapped IO registers used by the Timer. -#[derive(Default, Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct TimerRegs { - pub divider: u16, - pub timer: u8, - pub timer_mod: u8, - pub timer_control: TimerControl, + /// Divider is incremented at 16 KiHz or 32 KiHz at double speed. This register + /// controls when the other timer registers are incremented. + divider: TimerDivider, + /// Main counter register for the timer, incremented whenever the bit of `divider` + /// selected by `timer_period` goes from 1 to 0. + timer_counter: TimerCounter, + /// Timer modulo is the value that the timer counter gets reset to whenever it + /// overflows. + timer_mod: u8, + /// Control register for the timer which controls whether the timer is enabled and + /// what the period is. + timer_control: TimerControl, + /// Whether the timer is enabled, cached from timer_control. + timer_enable: bool, + /// The bit to be selected from the divider, derived from the timer period (cached + /// value of timer_control.period() >> 1). + selected_divider_bit: u16, +} + +impl TimerRegs { + /// Update the divider and counter if they were written since the last time their + /// changed_times were updated. + pub fn update_if_dirty(&mut self, now: ClockSnapshot) { + self.divider.0.update_if_dirty(now); + self.timer_counter.0.update_if_dirty(now); + } + + /// Set the value in the counter to the value in the `timer_mod`. + fn reset_counter_to_mod(&mut self) { + self.timer_counter.0.set_untracked(self.timer_mod); + } + + /// Get the divider register. + pub fn divider(&self) -> &TimerDivider { + &self.divider + } +} + +impl Default for TimerRegs { + fn default() -> Self { + let timer_control = TimerControl::default(); + let timer_enable = timer_control.timer_enable(); + let selected_divider_bit = timer_control.divider_bit(); + Self { + divider: Default::default(), + timer_counter: Default::default(), + timer_mod: Default::default(), + timer_control, + timer_enable, + selected_divider_bit, + } + } } impl MemDevice for TimerRegs { @@ -71,8 +177,8 @@ impl MemDevice for TimerRegs { fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { match addr.relative() { - 0x00 => (self.divider / 0x100) as u8, - 0x01 => self.timer, + 0x00 => self.divider.cpu_visible_portion(), + 0x01 => *self.timer_counter.0, 0x02 => self.timer_mod, 0x03 => self.timer_control.read_byte_relative(addr.offset_by(0x03)), _ => panic!("Address {} out of range for TimerRegs", addr), @@ -81,12 +187,17 @@ impl MemDevice for TimerRegs { fn write_byte_relative(&mut self, addr: RelativeAddr, val: u8) { match addr.relative() { - 0x00 => self.divider = 0, - 0x01 => self.timer = val, + 0x00 => self.divider.reset(), + 0x01 => { + self.timer_counter.0.set(val); + } 0x02 => self.timer_mod = val, - 0x03 => self - .timer_control - .write_byte_relative(addr.offset_by(0x03), val), + 0x03 => { + self.timer_control + .write_byte_relative(addr.offset_by(0x03), val); + self.timer_enable = self.timer_control.timer_enable(); + self.selected_divider_bit = self.timer_control.divider_bit(); + } _ => panic!("Address {} out of range for TimerRegs", addr), } } @@ -95,7 +206,7 @@ impl MemDevice for TimerRegs { } /// Context trait providing access to fields needed to service graphics. -pub trait TimerContext: InterruptContext + ApuContext { +pub trait TimerContext: InterruptContext + SystemClockContext { /// Get the timer state. fn timer(&self) -> &TimerState; @@ -108,10 +219,11 @@ pub trait TimerContext: InterruptContext + ApuContext { #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct TimerState { - timer_ticks: u64, - old_divider: u16, - interrupt_queued: bool, - interrupt_delay: u64, + /// The bit selected from the divider last cycle. The timer increments whenever the + /// bit selected from the divider goes from 1 to 0. + old_divider_bit: bool, + /// Tracks when the timer overflow + overflow_timing: Option, } impl TimerState { @@ -119,73 +231,95 @@ impl TimerState { Default::default() } - fn queue_interrupt(&mut self) { - self.interrupt_queued = true; - self.interrupt_delay = 4; + /// Set the interrupt to be triggered after appropriate delay. + fn queue_interrupt(&mut self, now: MCycle) { + self.overflow_timing = Some(TimerOverflowTiming { + overflowed_at: now, + interrupt_at: now + 1, + }); } } -fn increment_timer(ctx: &mut impl TimerContext) { - let (timer, overflow) = ctx.timer_regs().timer.overflowing_add(1); - - if overflow { - ctx.timer_mut().queue_interrupt(); - } - - ctx.timer_regs_mut().timer = timer; -} - -fn check_interrupt(ctx: &mut impl TimerContext, tcycles: u64) { - // an intervening write to the timer will discard the interrupt - if ctx.timer().interrupt_queued && ctx.timer_regs().timer == 0 { - if tcycles >= ctx.timer().interrupt_delay as u64 { - ctx.timer_mut().interrupt_queued = false; - let timer_mod = ctx.timer_regs().timer_mod; - ctx.timer_regs_mut().timer = timer_mod; - ctx.interrupts_mut().send(InterruptFlags::TIMER); - } else { - ctx.timer_mut().interrupt_delay -= tcycles; - } - } else { - ctx.timer_mut().interrupt_queued = false; - } +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] +struct TimerOverflowTiming { + /// The clock cycle when the timer overflowed. This is the cycle where the TIMA + /// register is set to 00 and readable as 00 by the CPU. + overflowed_at: MCycle, + /// The clock cycle when the interrupt should be sent. + interrupt_at: MCycle, } -pub fn tick(ctx: &mut impl TimerContext, tcycles: u64) { - let mut divider = ctx.timer_regs().divider; +pub fn tick(ctx: &mut impl TimerContext) { + let now = ctx.clock().elapsed_cycles(); - check_interrupt(ctx, tcycles); + // We need to handle weird behavior related to cycles when the timer overflows (copied + // from https://gbdev.io/pandocs/Timer_Obscure_Behaviour.html). + // + // [Z] [A] [B][C] + // SYS FD FE FF |00| 01 02 03 + // TIMA FF FF FF |00| 23 23 23 + // TMA 23 23 23 |23| 23 23 23 + // IF E0 E0 E0 |E0| E4 E4 E4 - if ctx.timer_regs().timer_control.timer_enable() { - let period = ctx.timer_regs().timer_control.period(); - - // falling edge case when divider has been reset - if (ctx.timer().old_divider & (period >> 1)) != 0 && (divider & (period >> 1)) == 0 { - increment_timer(ctx); - } else { - let mask = period - 1; - - if divider & mask + tcycles as u16 & mask > period || tcycles > period as u64 { - increment_timer(ctx) - } + if let Some(TimerOverflowTiming { + overflowed_at, + interrupt_at, + }) = ctx.timer().overflow_timing + { + if ctx.timer_regs().timer_counter.0.changed_cycle() == overflowed_at { + // The timer register written on the same cycle that it overflowed. This + // aborts the interrupt. + ctx.timer_mut().overflow_timing = None; + } else if now == interrupt_at { + // We are on the interrupt cycle. + ctx.timer_regs_mut().reset_counter_to_mod(); + ctx.interrupts_mut().send(InterruptFlags::TIMER); + } else if now > interrupt_at { + debug_assert!( + now == interrupt_at + 1, + "This assumes tick is called every m-cycle" + ); + // We have to wait until the next cycle to clear overflow_timing because we + // need to patch some of the timer registers if the CPU does a write in the + // same cycle as we reset the counter to the modulus. + ctx.timer_mut().overflow_timing = None; + // If the CPU wrote to the timer_mod register on the same cycle that we reset + // it to the mod, we should take the value written by the CPU. + // If the CPU wrote to the timer_counter register on the same cycle that we + // reset it to the mod, we should ignore the value written by the CPU and set + // to the mod anyway. + // Both of these are equivalent to overwriting timer_counter with the + // timer_mod the cycle after we queued the interrupt (in addition to doing so + // on the same cycle as we queued the interrupt, which we do in case the CPU + // is reading the timer_counter that cycle rather than writing). + // + // As long as tick is called every m-cycle, we don't have to worry about the + // timer being incremented in the interum, because this can only happen if the + // selected bit from the divider just went to zero and there's no way for it + // to become 1 and then 0 again in fewer than 3 m-cycles: + // + // - The fastest `period` is every-other m-cycle, so since the bit became zero + // on `overflowed_at`, the earliest it can become 1 is interrupt_at + 1 + // (which occurs below, so this code executes before that increment anyway). + // - The CPU could not be reseting that bit to 0 and changing timer_mod in the + // same cycle. CPU is too slow to do both of those things in the space + // between these timer ticks. + ctx.timer_regs_mut().reset_counter_to_mod(); } } - // Update APU at ~512 Hz - // period will have to be dynamic to support GBC double speed - let period = 0x2000; - // falling edge case when divider has been reset - if (ctx.timer().old_divider & (period >> 1)) != 0 && (divider & (period >> 1)) == 0 { - apu::apu_tick(ctx); - } else { - let mask = period - 1; + let divider = ctx.timer_regs_mut().divider.update(now); + let &TimerRegs { + timer_enable, + selected_divider_bit, + .. + } = ctx.timer_regs(); + let divider_bit = timer_enable && (divider & selected_divider_bit) != 0; + let old_divider_bit = mem::replace(&mut ctx.timer_mut().old_divider_bit, divider_bit); - if divider & mask + tcycles as u16 & mask > period || tcycles > period as u64 { - apu::apu_tick(ctx); + if old_divider_bit && !divider_bit { + if ctx.timer_regs_mut().timer_counter.increment() { + ctx.timer_mut().queue_interrupt(now); } } - - divider = divider.wrapping_add(tcycles as u16); - ctx.timer_mut().old_divider = ctx.timer_regs().divider; - ctx.timer_regs_mut().divider = divider; } From d2a87d7b9af2f59401be1180b6fd85e7763e9315 Mon Sep 17 00:00:00 2001 From: Zachary Stewart Date: Fri, 19 May 2023 18:48:54 -0400 Subject: [PATCH 2/6] WIP: Channel 3 --- feo3boy/src/apu.rs | 259 +++++++++++++++++++++++++++++++++------ feo3boy/src/bits.rs | 4 +- feo3boy/src/clock/mod.rs | 149 +++++++++++++++++----- 3 files changed, 342 insertions(+), 70 deletions(-) diff --git a/feo3boy/src/apu.rs b/feo3boy/src/apu.rs index 076ded2..cccce81 100644 --- a/feo3boy/src/apu.rs +++ b/feo3boy/src/apu.rs @@ -338,6 +338,8 @@ impl EnvelopeControl { const INIT_VOL: BitGroup = BitGroup(0b1111_0000); const DIRECTION: BitGroup = BitGroup(0b0000_1000); const PACE: BitGroup = BitGroup(0b0000_0111); + /// Bits which control the DAC enable. + const DAC_ENABLE: BitGroup = BitGroup(0b1111_1000); #[inline] fn set(&mut self, val: u8) { @@ -380,8 +382,9 @@ impl EnvelopeControl { Self::PACE.apply(&mut self.0, val); } + /// Returns true if the bits of the dac_enable portion of the register are non-zero. pub fn dac_enabled(&self) -> bool { - (self.0 & 0xf8) != 0 + Self::DAC_ENABLE.extract_bool(self.0) } } @@ -549,6 +552,164 @@ impl ChannelControl { } } +/// Utility for calculating the phase of a wavelength value at a particular time. +/// +/// - `NUM_STEPS` is the number of steps in the counter that the wavelength system is +/// incrementing, that is 8 for pulse channels and 32 for the wave channel. +/// +/// - `PERIOD_MULTIPLIER` is a multiplier to apply to the period (calculated as 2048 - +/// wavelength) to convert it to a number of D-Cycles. For pulse channels, this is 2, +/// since they step at fractions of 1 MiHz, while for channel 3 this is 1 since it +/// steps at fractions of 2 MiHz. +/// +/// This assumes that wavelength works similarly between channel 1/2 and 3. Specifically, +/// we assume that writing a new value to wavelength only takes effect when the previous +/// wavelength step completes. The documentation we have says explicitly that wavelength +/// changes written to channel 3 only apply the next time wave ram is read, but no such +/// thing is specified for channels 1 and 2. Reading the description in SameSuite, it says +/// that channel 1 frequency changes apply 'after the current sample' which sounds like +/// roughtly the same thing, so I think it is safe to assume that both of these work +/// similarly. +/// +/// The way wavelength appears to work is that the wavelength value is used as a reset-to +/// point for an 11 bit timer/counter register, and the sample is incremented whenever +/// that counter overflows. After overflow the counter resets to the wavelength value. +/// This neatly explains why changing the wavelength value doesn't take effect +/// immediately; the counter probably only resets on overflow or when the channel is +/// triggered. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +struct WavelengthCalculator { + /// The latest wavelength setting. + wavelength: u16, + /// The d-cycle when the currently-specified wavelength begins to apply. This is + /// either the time the channel was triggered or if the wavelength is changed, it is + /// the time when the previous wavelength count expires. Note that this might be in + /// the future! + wavelength_start_time: DCycle, + /// Value of the counter being incremented as of `wavelength_start_time`. If + /// `wavelength_start_time <= now`, the counter value is `initial_count + + /// overflow_count` where `overflow_count` is the number of times the wavelength + /// counter would have overflowed. If `wavelength_start_time > now`, the count is + /// exactly `initial_count - 1` The later is wrapped back to `NUM_STEPS - 1` in the + /// event of an underflow. + initial_count: u64, + + /// Triggering dirty tracker. When triggered, initial count and start time are reset. + /// Applies on the next update_if_dirty. + triggered: bool, + + /// Updated wavelength value. Applies on the next update_if_dirty. + new_wavelength: Option, +} + +impl + WavelengthCalculator +{ + /// Set the wavelength: note update_if_dirty is needed for the value to take effect. + /// + /// Triggering will take precedence over this. + #[inline] + fn set_wavelength(&mut self, wavelength: u16) { + debug_assert!(wavelength & !0x07ff == 0, "wavelength had high-order bits set"); + self.new_wavelength = Some(wavelength); + } + + /// Gets the 16 bit wavelength register. Note: if set_wavelength was called, this will return + /// the old wavelength until `update_if_dirty` is called. + #[inline] + fn wavelength(&self) -> u16 { + self.wavelength + } + + /// Set the wavelength triggered. `update_if_dirty` is needed for this to take effect. + #[inline] + fn trigger(&mut self) { + self.triggered = true; + } + + /// Check if a wavelength update is pending and + fn update_if_dirty(&mut self, now: ClockSnapshot) { + if self.triggered { + // It's fine to reset this to zero because wavelength_start_time is not in the + // future, so there is no chance of us needing to subtract. + self.initial_count = 0; + self.wavelength_start_time = now.elapsed_fixed_cycles(); + if let Some(new_wavelength) = self.new_wavelength.take() { + self.wavelength = new_wavelength; + } + self.triggered = false; + } else if let Some(new_wavelength) = self.new_wavelength.take() { + // If the end of the current duty step is in the future, we already changed + // the wavelength once and computed the time to change to the new wavelength + // value. We don't need to recompute wavelength_start_time or initial_count, + // we can just apply the wavelenght value. This also works fine if we are + // exactly on the cycle when the wavelength is changing. + // + // We only need to recompute the changeover cycle if we are in the middle of a + // duty step, that is if the start time is in the past. + if self.wavelength_start_time < now.elapsed_fixed_cycles() { + // The new wavelength_start_time will be the next smallest multiple of the + // period greater than or equal to now. + let cycles_since_wavelength_started = + now.elapsed_fixed_cycles() - self.wavelength_start_time; + let period = self.period(); + let increments = cycles_since_wavelength_started.as_u64() / period; + let current_count = self.initial_count + increments; + // The time since the start of the current duty step is the remainder after dividing + // by the period. This is the number of ticks left in + // `cycles_since_wavelength_started` between when the duty cycle started and `now`. + let ticks_since_current_duty_step_started = + cycles_since_wavelength_started.as_u64() % period; + // Calculate the number of cycles until the next period start. This is the length of + // the periiod minus the number of cycles we've already seen in the period. However, + // if ticks_since_current_duty_step_started is zero, we are already on a duty step + // boundary right now. We can cover the 'already on a boundary' case by taking this + // result mod the period length. + let ticks_until_duty_step_ends = + (period - ticks_since_current_duty_step_started) % period; + // The start time for the new wavelength is the end of the next duty cycle. + self.wavelength_start_time = + now.elapsed_fixed_cycles() + ticks_until_duty_step_ends; + // The initial count for the new wavelength is the current count if we are already + // at a boundary, otherwise it's one more than the current count. We apply modulus + // to keep the initial_count small, though that's probably unnecessary, since we'll + // never increment this enough to overflow u64. + self.initial_count = if ticks_until_duty_step_ends == 0 { + current_count % NUM_STEPS + } else { + // In this case, we may need to subtract 1 if the duty step is requested before + // the new wavelength starts. To make sure we can always subtract 1 without + // underflowing, we apply the `+ 1` *after* doing the `% NUM_STEPS` so the value + // is always at least 1. + current_count % NUM_STEPS + 1 + }; + } + self.wavelength = new_wavelength; + } + } + + /// Compute the current duty step for the wavelength. + fn duty_step(&self, now: DCycle) -> u64 { + if self.wavelength_start_time <= now { + let cycles_since_wavelength_started = now - self.wavelength_start_time; + // We have effectively incremented the duty step a number of times equivalent + // to the number of cycles elapsed divided by the number of cycles per + // increment. + let increments = cycles_since_wavelength_started.as_u64() / self.period(); + (self.initial_count + increments) % NUM_STEPS + } else { + // When subracting, we never need to apply the modulus, since that was done when + // calculating initial_count. + self.initial_count - 1 + } + } + + /// The period is the time between duty step increments, expressed in D-Cycles. + fn period(&self) -> u64 { + (2048 - self.wavelength) * PERIOD_MULTIPLIER; + } +} + #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct PulseChannel { /// Envelope register. Values here are only picked up when the channel is triggered. @@ -572,10 +733,7 @@ pub struct PulseChannel { /// Table. Essentially, the bit selected in the pulse table increments every time a /// counter starting from `wavelength` overflows, if incrementing at 1 MiHz (the /// standard-speed m-cycle frequency, or once every 2 d-cycles). - wavelength: u16, - - /// When the channel was last triggered. - triggered: TimedChangeTracker<()>, + wavelength: WavelengthCalculator<8, 2>, } impl PulseChannel { @@ -603,16 +761,22 @@ impl PulseChannel { [1, 0, 0, 0, 0, 0, 0, 1], ]; + /// Return true if the channel is active. + pub fn active(&self) -> bool { + self.active + } + /// Get the lower byte of the wavelength. pub fn wavelength_low(&self) -> u8 { - let [wavelength_low, _] = self.wavelength.to_le_bytes(); + let [wavelength_low, _] = self.wavelength.wavelength().to_le_bytes(); wavelength_low } - /// Set the lower byte of the wavelength. - pub fn set_wavelength_low(&mut self, wavelength_low: u8) { - let [_, wavelength_high] = self.wavelength.to_le_bytes(); - self.wavelength = u16::from_le_bytes([wavelength_low, wavelength_high]); + /// Set the lower byte of the wavelength. This should only be called from a memory access + /// through system ram, and generally only via the CPU. + fn set_wavelength_low(&mut self, wavelength_low: u8) { + let [_, wavelength_high] = self.wavelength.wavelength().to_le_bytes(); + self.wavelength.set_wavelength(u16::from_le_bytes([wavelength_low, wavelength_high])); } /// Read the wavelength high bits adn readable control bits. @@ -621,16 +785,19 @@ impl PulseChannel { 0xff.with_bits(Self::LENGTH_ENABLE, self.length_timer.enabled()) } - /// Set the value of the wavelength high and control register. - pub fn set_wavelength_high_and_control(&mut self, value: u8) { + /// Set the value of the wavelength high and control register. This should only be called from a + /// memory access through system ram, and generally only via the CPU. + fn set_wavelength_high_and_control(&mut self, value: u8) { let trigger = Self::TRIGGER.extract_bool(value); let length_enable = Self::LENGTH_ENABLE.extract_bool(value); let wavelength_high = Self::WAVELENGTH_HIGH.extract(value); - let [wavelength_low, _] = self.wavelength.to_le_bytes(); + let [wavelength_low, _] = self.wavelength.wavelength().to_le_bytes(); - self.wavelength = u16::from_le_bytes([wavelength_low, wavelength_high]); + self.wavelength.set_wavelength(u16::from_le_bytes([wavelength_low, wavelength_high])); self.length_timer.set_enabled(length_enable); - if trigger { + // Channel is turned on if triggered and the DAC is enabled. If the DAC is + // disabled, triggering does nothing. + if trigger && self.envelope_control.dac_enabled() { self.triggered.set(()); self.active = true; self.sweep.start_sweep(); @@ -645,7 +812,7 @@ impl PulseChannel { } /// Set the combined length and duty cycle register. - pub fn set_length_and_duty_cycle(&mut self, value: u8) { + fn set_length_and_duty_cycle(&mut self, value: u8) { let duty_cycle = Self::DUTY_CYCLE.extract(value); let length = Self::LENGTH_TIMER.extract(value); self.duty_cycle = duty_cycle as usize; @@ -673,29 +840,21 @@ impl PulseChannel { /// Update any time-tracking fields with the current clock snapshot. fn update_if_dirty(&mut self, now: ClockSnapshot) { - self.triggered.update_if_dirty(now); + self.wavelength.update_if_dirty(now); } /// Get the index into the pulse table for the current time based on when the channel /// was last triggered. fn pulse_step(&self, now: DCycle) -> usize { - let cycles_since_triggered = now - self.triggered.changed_fixed_cycle(); - // The period is the time between increments of the index in the pulse table. It - // is in terms of number of 1 MiHz cycles between increments. Each such cycle is - // effectively 2 d-cycles. We multiply by 2 to put this in terms of D-cycles. - let period = (2048 - self.wavelength as u64) * 2; - // We have effectively incremented the pulse table index a number of times - // equivalent to the number of cycles elapsed divided by the number of cycles per - // increment. - let increments = cycles_since_triggered.as_u64() / period; - // The pulse table has 8 entries, so we wrap around to 8. - let index = increments % 8; - index as usize + self.wavelength.duty_step(now) as usize } /// Get a sample from this channel. fn get_sample(&self, now: DCycle) -> f32 { - if self.envelope_control.dac_enabled() && self.active { + // We don't check the DAC here because anytime a memory write disables the DAC, we + // turn the channel active to false as well, and don't allow a write to turn the + // channel on unless the DAC is active. + if self.active { let pulse_step = self.pulse_step(now); debug!( "Sampling pulse channel from step {} of duty cycle {}", @@ -726,7 +885,13 @@ impl MemDevice for PulseChannel { dispatch_memdev_byte!(PulseChannel, addr, |addr| { 0x00 => self.sweep.write_byte_relative(addr, val), 0x01 => self.set_length_and_duty_cycle(val), - 0x02 => self.envelope_control.write_byte_relative(addr, val), + 0x02 => { + self.envelope_control.write_byte_relative(addr, val); + if !self.envelope_control.dac_enabled() { + // Disabling the DAC disables the channel. + self.active = false; + } + }, 0x03 => self.set_wavelength_low(val), 0x04 => self.set_wavelength_high_and_control(val), }) @@ -735,19 +900,41 @@ impl MemDevice for PulseChannel { memdev_bytes_from_byte!(PulseChannel); } +/// Provides the state of the wavetable channel (Channel 3). #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct WavetableChannel { + /// Whether the DAC is enabled. Unlike other channels, this is controlled directly + /// rather than via some of the envelope bits. + dac_enable: bool, + /// Whether the channel is active. The channel can be disabled with the dac still + /// active, but not the other way around. + active: bool, + + /// Length timer for the channel. On Channel 3, the length timer is 8 bits long, + /// however we (currently) treat it as still just 6 bits. It is unclear what happens + /// if you write a length greater than 64 to the length register on channel 3. Does + /// that disable it immediately, since the length is already above 64? Or does the + /// value get truncated and we count up to 64 from there? + /// + /// In the current setup we assume the value is truncated to 6 bits. + length_timer: LengthTimer, + + /// Output level setting (this is the register which would have the envelope function, but + /// channel 3 has no envelope functionality). + output_level: u8, + + /// Wavelength position calculator for the location in the sample table. + wavelength: WavelengthCalculator<32, 1>, + + /// Sample to emit when first triggered. + initial_sample: u8, + period: u32, phase_offset: u32, - enabled: bool, - active: bool, triggered: bool, sample_cursor: u32, wavelength: u16, level_shift: u8, - length_enable: bool, - length_acc: u8, - length_aticks: u8, /// Contains sample values which have been split in half to allow nybbles to be /// efficiently indexed when sampling. sample_table: [u8; 32], diff --git a/feo3boy/src/bits.rs b/feo3boy/src/bits.rs index dd87406..febc2cf 100644 --- a/feo3boy/src/bits.rs +++ b/feo3boy/src/bits.rs @@ -43,8 +43,8 @@ impl BitGroup { /// Write `val` to the part of `dest` represented by this [`BitGroup`]. /// - /// Shift `val` to the left so that it starts at the same point as this `BitGroup` and - /// then apply it to `dest` with a mask. + /// Shift `val` to the left so that it starts at the same point as this `BitGroup` and then + /// apply it to `dest` with a mask. #[inline] pub fn apply(self, dest: &mut u8, val: u8) { *dest = self.applied(*dest, val); diff --git a/feo3boy/src/clock/mod.rs b/feo3boy/src/clock/mod.rs index b5495a4..34b453f 100644 --- a/feo3boy/src/clock/mod.rs +++ b/feo3boy/src/clock/mod.rs @@ -90,38 +90,13 @@ impl SystemClock { self.dcycle.duration() } - /// Get the range of durations which the current m-cycle fills. - /// - /// Note that this assumes that the current cycle occurs at the set speed, if - /// set_speed is called and then current_cycle_range is called again without advancing - /// the clock, this will yield a different range. - /// - /// It is unclear how the timing of set-speed should work, i.e. whether the next cycle - /// immediately after the current one occurs after a delay equal to the current speed - /// or the new speed. However, that can likely be dealt with higher up in the - /// eumulator, for example, by only applying a `set_speed` at the start of a cycle - /// (which would cause the tick where the speed change occured to proceed at the prior - /// speed). - pub fn current_cycle_range(&self) -> Range { - let start = self.elapsed_time(); - let end = (self.elapsed_fixed_cycles() + MCycle::new(1).as_dcycle(self.speed)).duration(); - start..end - } - - /// Get an iterator over the duration ranges of the 1 or 2 fixed-duration cycles in - /// the current M-cycle. If the clock is currently running at normal speed, this will - /// be an iterator of 2 ranges, one for each of the D-cycles in this M-cycle. If the - /// clock is running at double speed, this will be a single range equivalent to - /// current_cycle_range. - pub fn current_fixed_cycle_ranges( - &self, - ) -> impl Iterator> + DoubleEndedIterator + FusedIterator { - let dcycle = self.dcycle; - (0..self.speed.dcycles_per_mcycle()).map(move |i| { - let start = (dcycle + i).duration(); - let end = (dcycle + i + 1).duration(); - start..end - }) + /// Get an interator over the d-cycles in the current m-cycle. This is either a single + /// item (if running in double-speed mode) or a a pair of d-cycles (if running at + /// normal speed). + pub fn current_cycle_fixed_cycles(&self) -> DCycleIter { + DCycleIter { + cycles: self.dcycle..self.dcycle + self.speed.dcycles_per_mcycle(), + } } /// Get the number of cycles elapsed at *fixed* speed to the start of the current @@ -149,6 +124,116 @@ impl SystemClock { } } +/// An iterator over a range of D-Cycles. +pub struct DCycleIter { + /// The d-cycle values to iterator over. + /// Invariant: ensure that end is always greater than or equal to start. + cycles: Range, +} + +impl DCycleIter { + /// Get an interator over the time ranges covered by the d-cycles in this iterator, + /// assuming the cycles are a count from the start of emulator time. + fn time_ranges(self) -> DCycleTimeRanges { + let start = DCycle::new(self.cycles.start).duration(); + DCycleTimeRanges { + cycles: self, + next_start: start, + } + } +} + +impl Iterator for DCycleIter { + type Item = DCycle; + + fn next(&mut self) -> Option { + self.cycles.next().map(DCycle::new) + } + + fn size_hint(&self) -> (usize, Option) { + self.cycles.size_hint() + } + + fn nth(&mut self, n: usize) -> Option { + self.cycles.nth(n).map(DCycle::new) + } + + fn last(self) -> Option { + self.cycles.last().map(DCycle::new) + } + + fn max(self) -> Option { + self.cycles.max().map(DCycle::new) + } + + fn min(self) -> Option { + self.cycles.min().map(DCycle::new) + } +} + +impl DoubleEndedIterator for DCycleIter { + fn next_back(&mut self) -> Option { + self.cycles.next_back().map(DCycle::new) + } + + fn nth_back(&mut self, n: usize) -> Option { + self.cycles.nth_back(n).map(DCycle::new) + } +} + +impl FusedIterator for DCycleIter {} + +/// Iterator over DCycles and their corresponding time ranges. +pub struct DCycleTimeRanges { + /// Iterator over the d-cycles. + cycles: DCycleIter, + /// Start time of the next cycle that will be returned from DCycleIter. Used to avoid + /// recomputing the time value unnecessarily. + next_start: Duration, +} + +impl Iterator for DCycleTimeRanges { + type Item = (DCycle, Range); + + fn next(&mut self) -> Option { + self.cycles.next().map(|d| { + let end = (d + 1).duration(); + let start = mem::replace(&mut self.next_start, end); + (d, start..end) + }) + } + + fn size_hint(&self) -> (usize, Option) { + self.cycles.size_hint() + } + + fn nth(&mut self, n: usize) -> Option { + if n == 0 { + self.next() + } else { + self.cycles.nth(n).map(|d| { + let start = d.duration(); + let end = (d + 1).duration(); + self.next_start = end; + (d, start..end) + }) + } + } + + fn last(mut self) -> Option { + self.cycles.last().map(|d| { + let start = d.duration(); + let end = (d + 1).duration(); + // This is unnecessary since self is consumed but is done to maintain + // invariants. + self.next_start = end; + (d, start..end) + }) + } +} + +impl FusedIterator for DCycleTimeRanges {} + /// Type for tracking what clock cycle a value is changed at. /// /// Values with change tracking are considered equal if have the same dirty state and were From 820e5c480d46aabd342e331556748750c0128849 Mon Sep 17 00:00:00 2001 From: Zachary Stewart Date: Mon, 22 May 2023 13:18:51 -0400 Subject: [PATCH 3/6] Get APU stuff into new state, work on getting GB into new state. --- feo3boy-memdev-derive/src/bitflags.rs | 8 +- feo3boy-memdev-derive/src/bitswrapper.rs | 8 +- feo3boy-memdev-derive/src/bytewrapper.rs | 8 +- feo3boy-memdev-derive/src/passthrough.rs | 16 +- feo3boy/src/apu.rs | 1350 +++++---- feo3boy/src/apu/lfsr.rs | 23 + feo3boy/src/apu/lfsr15.txt | 3279 ++++++++++++++++++++++ feo3boy/src/apu/lfsr15_rev.txt | 3279 ++++++++++++++++++++++ feo3boy/src/apu/lfsr7.txt | 15 + feo3boy/src/apu/lfsr7_rev.txt | 15 + feo3boy/src/apu/lfsr_tables.py | 104 + feo3boy/src/clock/mod.rs | 57 +- feo3boy/src/gb.rs | 13 +- feo3boy/src/gbz80core/externdefs.rs | 7 +- feo3boy/src/gbz80core/mod.rs | 238 +- feo3boy/src/interrupts.rs | 48 +- feo3boy/src/macros.rs | 87 +- feo3boy/src/memdev.rs | 408 +-- feo3boy/src/memdev/cartridge.rs | 199 +- feo3boy/src/ppu.rs | 76 +- feo3boy/src/timer.rs | 70 +- 21 files changed, 8062 insertions(+), 1246 deletions(-) create mode 100644 feo3boy/src/apu/lfsr.rs create mode 100644 feo3boy/src/apu/lfsr15.txt create mode 100644 feo3boy/src/apu/lfsr15_rev.txt create mode 100644 feo3boy/src/apu/lfsr7.txt create mode 100644 feo3boy/src/apu/lfsr7_rev.txt create mode 100755 feo3boy/src/apu/lfsr_tables.py diff --git a/feo3boy-memdev-derive/src/bitflags.rs b/feo3boy-memdev-derive/src/bitflags.rs index c862129..0fb25cf 100644 --- a/feo3boy-memdev-derive/src/bitflags.rs +++ b/feo3boy-memdev-derive/src/bitflags.rs @@ -30,19 +30,19 @@ pub fn build_flags(item: &DeriveInput, flags: &BitFlags) -> Result impl #feo3boy::memdev::MemDevice for #ty { const LEN: usize = 1; - fn read_byte_relative(&self, addr: #feo3boy::memdev::RelativeAddr) -> u8 { + fn read_byte_relative(&self, _ctx: &#feo3boy::memdev::ReadCtx, addr: #feo3boy::memdev::RelativeAddr) -> u8 { const READABLE_BITS: #ty = #readable; #feo3boy::check_addr!(#ty, addr); #read } - fn write_byte_relative(&mut self, addr: #feo3boy::memdev::RelativeAddr, val: u8) { + fn write_byte_relative(&mut self, _ctx: &#feo3boy::memdev::WriteCtx, addr: #feo3boy::memdev::RelativeAddr, val: u8) { const WRITABLE_BITS: #ty = #writable; #feo3boy::check_addr!(#ty, addr); #write; } - fn read_bytes_relative(&self, addr: #feo3boy::memdev::RelativeAddr, data: &mut [u8]) { + fn read_bytes_relative(&self, _ctx: &#feo3boy::memdev::ReadCtx, addr: #feo3boy::memdev::RelativeAddr, data: &mut [u8]) { const READABLE_BITS: #ty = #readable; #feo3boy::check_addr!(#ty, addr, data.len()); match data.first_mut() { @@ -51,7 +51,7 @@ pub fn build_flags(item: &DeriveInput, flags: &BitFlags) -> Result } } - fn write_bytes_relative(&mut self, addr: #feo3boy::memdev::RelativeAddr, data: &[u8]) { + fn write_bytes_relative(&mut self, _ctx: &#feo3boy::memdev::WriteCtx, addr: #feo3boy::memdev::RelativeAddr, data: &[u8]) { const WRITABLE_BITS: #ty = #writable; #feo3boy::check_addr!(#ty, addr, data.len()); match data.first().copied() { diff --git a/feo3boy-memdev-derive/src/bitswrapper.rs b/feo3boy-memdev-derive/src/bitswrapper.rs index 1d6e26c..5e116cf 100644 --- a/feo3boy-memdev-derive/src/bitswrapper.rs +++ b/feo3boy-memdev-derive/src/bitswrapper.rs @@ -72,19 +72,19 @@ pub fn build_bits_wrapper(item: &DeriveInput, wrapper: &BitsWrapper) -> Result u8 { + fn read_byte_relative(&self, _ctx: &#feo3boy::memdev::ReadCtx, addr: #feo3boy::memdev::RelativeAddr) -> u8 { const READABLE_BITS: u8 = #readable; #feo3boy::check_addr!(#ty, addr); #read } - fn write_byte_relative(&mut self, addr: #feo3boy::memdev::RelativeAddr, val: u8) { + fn write_byte_relative(&mut self, _ctx: &#feo3boy::memdev::WriteCtx, addr: #feo3boy::memdev::RelativeAddr, val: u8) { const WRITABLE_BITS: u8 = #writable; #feo3boy::check_addr!(#ty, addr); #write; } - fn read_bytes_relative(&self, addr: #feo3boy::memdev::RelativeAddr, data: &mut [u8]) { + fn read_bytes_relative(&self, _ctx: &#feo3boy::memdev::ReadCtx, addr: #feo3boy::memdev::RelativeAddr, data: &mut [u8]) { const READABLE_BITS: u8 = #readable; #feo3boy::check_addr!(#ty, addr, data.len()); match data.first_mut() { @@ -93,7 +93,7 @@ pub fn build_bits_wrapper(item: &DeriveInput, wrapper: &BitsWrapper) -> Result Result u8 { + fn read_byte_relative(&self, _ctx: &#feo3boy::memdev::ReadCtx, addr: #feo3boy::memdev::RelativeAddr) -> u8 { #feo3boy::check_addr!(#ty, addr); #read(self) } - fn write_byte_relative(&mut self, addr: #feo3boy::memdev::RelativeAddr, val: u8) { + fn write_byte_relative(&mut self, _ctx: &#feo3boy::memdev::WriteCtx, addr: #feo3boy::memdev::RelativeAddr, val: u8) { #feo3boy::check_addr!(#ty, addr); #write(self, val) } - fn read_bytes_relative(&self, addr: #feo3boy::memdev::RelativeAddr, data: &mut [u8]) { + fn read_bytes_relative(&self, _ctx: &#feo3boy::memdev::ReadCtx, addr: #feo3boy::memdev::RelativeAddr, data: &mut [u8]) { #feo3boy::check_addr!(#ty, addr, data.len()); match data.first_mut() { Some(data) => *data = #read(self), @@ -34,7 +34,7 @@ pub fn build_byte_wrapper(item: &DeriveInput, wrapper: &ByteWrapper) -> Result #write(self, val), diff --git a/feo3boy-memdev-derive/src/passthrough.rs b/feo3boy-memdev-derive/src/passthrough.rs index 251c7cb..a1be437 100644 --- a/feo3boy-memdev-derive/src/passthrough.rs +++ b/feo3boy-memdev-derive/src/passthrough.rs @@ -53,20 +53,20 @@ pub fn build_passthrough(item: &DeriveInput) -> Result { impl #feo3boy::memdev::MemDevice for #ty { const LEN: usize = <#field_ty as #feo3boy::memdev::MemDevice>::LEN; - fn read_byte_relative(&self, addr: #feo3boy::memdev::RelativeAddr) -> u8 { - #feo3boy::memdev::MemDevice::read_byte_relative(&self.#field, addr) + fn read_byte_relative(&self, ctx: &#feo3boy::memdev::ReadCtx, addr: #feo3boy::memdev::RelativeAddr) -> u8 { + #feo3boy::memdev::MemDevice::read_byte_relative(&self.#field, ctx, addr) } - fn read_bytes_relative(&self, addr: #feo3boy::memdev::RelativeAddr, data: &mut [u8]) { - #feo3boy::memdev::MemDevice::read_bytes_relative(&self.#field, addr, data) + fn read_bytes_relative(&self, ctx: &#feo3boy::memdev::ReadCtx, addr: #feo3boy::memdev::RelativeAddr, data: &mut [u8]) { + #feo3boy::memdev::MemDevice::read_bytes_relative(&self.#field, ctx, addr, data) } - fn write_byte_relative(&mut self, addr: #feo3boy::memdev::RelativeAddr, val: u8) { - #feo3boy::memdev::MemDevice::write_byte_relative(&mut self.#field, addr, val) + fn write_byte_relative(&mut self, ctx: &#feo3boy::memdev::WriteCtx, addr: #feo3boy::memdev::RelativeAddr, val: u8) { + #feo3boy::memdev::MemDevice::write_byte_relative(&mut self.#field, ctx, addr, val) } - fn write_bytes_relative(&mut self, addr: #feo3boy::memdev::RelativeAddr, data: &[u8]) { - #feo3boy::memdev::MemDevice::write_bytes_relative(&mut self.#field, addr, data) + fn write_bytes_relative(&mut self, ctx: &#feo3boy::memdev::WriteCtx, addr: #feo3boy::memdev::RelativeAddr, data: &[u8]) { + #feo3boy::memdev::MemDevice::write_bytes_relative(&mut self.#field, ctx, addr, data) } } }) diff --git a/feo3boy/src/apu.rs b/feo3boy/src/apu.rs index cccce81..d88bbe8 100644 --- a/feo3boy/src/apu.rs +++ b/feo3boy/src/apu.rs @@ -1,19 +1,18 @@ use std::f32::consts::PI; -use std::mem; use std::ops::{Mul, MulAssign}; use std::time::Duration; +use std::{fmt, mem}; use bitflags::bitflags; -use log::{debug, info, trace}; +use log::{debug, warn}; use crate::bits::{ApplyBitGroup, BitGroup}; -use crate::clock::{ - cycles_to_duration, ClockSnapshot, ClockSpeed, DCycle, SystemClock, SystemClockContext, - TimedChangeTracker, -}; -use crate::memdev::{MemDevice, RelativeAddr}; +use crate::clock::{cycles_to_duration, ClockSpeed, DCycle, SystemClockContext}; +use crate::memdev::{MemDevice, ReadCtx, RelativeAddr, WriteCtx}; use crate::timer::TimerDivider; +mod lfsr; + bitflags! { #[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash, MemDevice)] #[memdev(bitflags, writable = SoundEnable::WRITABLE)] @@ -39,6 +38,24 @@ bitflags! { } } +impl ChannelMix { + /// Return the channel mix multiplier for the specified channel. Returns 0.25 if `self` contains + /// the specified channel, otherwise returns 0.0. + fn get_channel_mix_multiplier(self, channel: ChannelMix) -> f32 { + if self.contains(channel) { + 0.25 + } else { + 0.0 + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum SoundSide { + Left, + Right, +} + bitflags! { #[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash, MemDevice)] #[memdev(bitflags)] @@ -55,6 +72,15 @@ bitflags! { } } +impl SoundPan { + fn channel_mix(&self, side: SoundSide) -> ChannelMix { + ChannelMix::from_bits_truncate(match side { + SoundSide::Left => self.bits() >> 4, + SoundSide::Right => self.bits(), + }) + } +} + /// Represents the APU-DIV counter which increments whenever the appropriate DIV bit /// goes from 1 to 0. This counter is used to trigger various other increments within the /// APU, including envelope sweep, sound length countdown, and channel 1 frequency sweep. @@ -116,14 +142,6 @@ impl ApuDiv { } } -impl SoundPan { - fn channel_mix(&self, right: bool) -> ChannelMix { - let shift = if right { 0 } else { 4 }; - - ChannelMix::from_bits_truncate(self.bits() >> shift) - } -} - /// Represents sound and volume settings. #[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash, MemDevice)] #[memdev(bits)] @@ -184,10 +202,12 @@ impl SoundVolume { Self::VOL_RIGHT.apply(&mut self.0, val); } - fn vol_multiplier(&self, right: bool) -> f32 { - let shift = if right { 0 } else { 4 }; - let vol_digital = (self.0 >> shift) & 0x7; - + /// Get the volumen multiplier. + fn vol_multiplier(&self, side: SoundSide) -> f32 { + let vol_digital = match side { + SoundSide::Left => self.vol_left(), + SoundSide::Right => self.vol_right(), + }; (0.125 * (vol_digital as f32 + 1.0)).max(1.0) } } @@ -426,132 +446,6 @@ impl Envelope { } } -#[derive(Default, Debug, Clone, Eq, PartialEq, Hash, MemDevice)] -#[memdev(bits, readable = WavetableLevel::RW_BITS, writable = WavetableLevel::RW_BITS)] -#[repr(transparent)] -pub struct WavetableLevel(u8); - -impl WavetableLevel { - const LEVEL: BitGroup = BitGroup(0b0110_0000); - const RW_BITS: u8 = 0b0110_1000; - - /// Get the level. - #[inline] - pub fn level(self) -> u8 { - Self::LEVEL.extract(self.0) - } - - /// Set the level. - #[inline] - pub fn set_level(&mut self, val: u8) { - Self::LEVEL.apply(&mut self.0, val); - } -} - -#[derive(Default, Debug, Clone, Eq, PartialEq, Hash, MemDevice)] -#[memdev(bits)] -#[repr(transparent)] -pub struct NoiseControl(u8); - -impl NoiseControl { - const CLOCK_SHIFT: BitGroup = BitGroup(0b1111_0000); - const LFSR_WIDTH: BitGroup = BitGroup(0b0000_1000); - const CLOCK_DIV: BitGroup = BitGroup(0b0000_0111); - - fn set(&mut self, val: u8) { - self.0 = val; - } - - /// Get the clock shift. - #[inline] - pub fn clock_shift(&self) -> u8 { - Self::CLOCK_SHIFT.extract(self.0) - } - - /// Set the clock shift. - #[inline] - pub fn set_clock_shift(&mut self, val: u8) { - Self::CLOCK_SHIFT.apply(&mut self.0, val); - } - - /// Get the LFSR width bit. - #[inline] - pub fn lfsr_width(&self) -> bool { - Self::LFSR_WIDTH.extract_bool(self.0) - } - - /// Get the LFSR mask based on whether the LFSR bit is set. - fn lfsr_mask(&self) -> u16 { - if self.lfsr_width() { - 0x4080 - } else { - 0x4000 - } - } - - /// Set the LFSR width bit. - #[inline] - pub fn set_lfsr_width(&mut self, val: bool) { - Self::LFSR_WIDTH.apply(&mut self.0, val as u8); - } - - /// Get the clock div. - #[inline] - pub fn clock_div(&self) -> u8 { - Self::CLOCK_DIV.extract(self.0) - } - - /// Set the clock div. - #[inline] - pub fn set_clock_div(&mut self, val: u8) { - Self::CLOCK_DIV.apply(&mut self.0, val); - } - - fn period(&self) -> u32 { - let r = self.clock_div() as u32; - let s = self.clock_shift() as u32; - - trace!("r: {}, s: {}", r, s); - - if r == 0 { - 1 << (s + 3) - } else { - r << (s + 4) - } - } -} - -bitflags! { - #[derive(Default, Debug, Copy, Clone, Eq, PartialEq, MemDevice)] - #[memdev(bitflags, readable = ChannelControl::READABLE)] - pub struct ChannelControl : u8 { - const TRIGGER = 0b1000_0000; - const LENGTH_ENABLE = 0b0100_0000; - /// Bit 0 of `WAVELENGTH_HIGH`. This pseudo-flag allows `from_bits_truncate` to - /// set this bit independently of the rest of the wavelength bits. - const WAVELENGTH_HIGH_B0 = 0b0000_0001; - /// Bit 1 of `WAVELENGTH_HIGH`. This pseudo-flag allows `from_bits_truncate` to - /// set this bit independently of the rest of the wavelength bits. - const WAVELENGTH_HIGH_B1 = 0b0000_0010; - /// Bit 2 of `WAVELENGTH_HIGH`. This pseudo-flag allows `from_bits_truncate` to - /// set this bit independently of the rest of the wavelength bits. - const WAVELENGTH_HIGH_B2 = 0b0000_0100; - const READABLE = 0b0100_0000; - } -} - -impl ChannelControl { - const WAVELENGTH_HIGH: BitGroup = BitGroup(0b0000_0111); - - pub fn wavelength_high(self) -> u8 { - Self::WAVELENGTH_HIGH.extract(self.bits()) - } - - pub fn set_wavelength_high(&mut self, val: u8) { - Self::WAVELENGTH_HIGH.apply_bits(self, val); - } -} - /// Utility for calculating the phase of a wavelength value at a particular time. /// /// - `NUM_STEPS` is the number of steps in the counter that the wavelength system is @@ -577,7 +471,7 @@ impl ChannelControl { /// This neatly explains why changing the wavelength value doesn't take effect /// immediately; the counter probably only resets on overflow or when the channel is /// triggered. -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Default, Debug, Eq, PartialEq, Hash)] struct WavelengthCalculator { /// The latest wavelength setting. wavelength: u16, @@ -593,99 +487,89 @@ struct WavelengthCalculator /// exactly `initial_count - 1` The later is wrapped back to `NUM_STEPS - 1` in the /// event of an underflow. initial_count: u64, - - /// Triggering dirty tracker. When triggered, initial count and start time are reset. - /// Applies on the next update_if_dirty. - triggered: bool, - - /// Updated wavelength value. Applies on the next update_if_dirty. - new_wavelength: Option, + /// Last time the wavelength was triggered. This is used to tell if we are currently in the + /// first cycle. + triggered_time: DCycle, + /// Length of the initial period in d-cycles. + initial_period: DCycle, } impl WavelengthCalculator { - /// Set the wavelength: note update_if_dirty is needed for the value to take effect. - /// - /// Triggering will take precedence over this. + /// Set the wavelength, recalcuating the start time and initial count. #[inline] - fn set_wavelength(&mut self, wavelength: u16) { - debug_assert!(wavelength & !0x07ff == 0, "wavelength had high-order bits set"); - self.new_wavelength = Some(wavelength); + fn set_wavelength(&mut self, now: DCycle, wavelength: u16) { + debug_assert!( + wavelength & !0x07ff == 0, + "wavelength had high-order bits set" + ); + + // If the end of the current duty step is in the future, we already changed + // the wavelength once and computed the time to change to the new wavelength + // value. We don't need to recompute wavelength_start_time or initial_count, + // we can just apply the wavelenght value. This also works fine if we are + // exactly on the cycle when the wavelength is changing. + // + // We only need to recompute the changeover cycle if we are in the middle of a + // duty step, that is if the start time is in the past. + if self.wavelength_start_time < now { + // The new wavelength_start_time will be the next smallest multiple of the + // period greater than or equal to now. + let cycles_since_wavelength_started = now - self.wavelength_start_time; + let period = self.period(); + let increments = cycles_since_wavelength_started.as_u64() / period.as_u64(); + let current_count = self.initial_count + increments; + // The time since the start of the current duty step is the remainder after dividing + // by the period. This is the number of ticks left in + // `cycles_since_wavelength_started` between when the duty cycle started and `now`. + let ticks_since_current_duty_step_started = + cycles_since_wavelength_started.as_u64() % period.as_u64(); + // Calculate the number of cycles until the next period start. This is the length of + // the periiod minus the number of cycles we've already seen in the period. However, + // if ticks_since_current_duty_step_started is zero, we are already on a duty step + // boundary right now. We can cover the 'already on a boundary' case by taking this + // result mod the period length. + let ticks_until_duty_step_ends = + (period - ticks_since_current_duty_step_started).as_u64() % period.as_u64(); + // The start time for the new wavelength is the end of the next duty cycle. + self.wavelength_start_time = now + ticks_until_duty_step_ends; + // The initial count for the new wavelength is the current count if we are already + // at a boundary, otherwise it's one more than the current count. We apply modulus + // to keep the initial_count small, though that's probably unnecessary, since we'll + // never increment this enough to overflow u64. + self.initial_count = if ticks_until_duty_step_ends == 0 { + current_count % NUM_STEPS + } else { + // In this case, we may need to subtract 1 if the duty step is requested before + // the new wavelength starts. To make sure we can always subtract 1 without + // underflowing, we apply the `+ 1` *after* doing the `% NUM_STEPS` so the value + // is always at least 1. + current_count % NUM_STEPS + 1 + }; + } + self.wavelength = wavelength; + + // If we updated the wavelength at the same time that the wavelength was triggered, we + // should change the initial_period, which is used to tell if we are still on the first + // cycle. + if self.triggered_time == now { + self.initial_period = self.period(); + } } - /// Gets the 16 bit wavelength register. Note: if set_wavelength was called, this will return - /// the old wavelength until `update_if_dirty` is called. + /// Gets the 16 bit wavelength register. #[inline] fn wavelength(&self) -> u16 { self.wavelength } - /// Set the wavelength triggered. `update_if_dirty` is needed for this to take effect. + /// Set the wavelength triggered. #[inline] - fn trigger(&mut self) { - self.triggered = true; - } - - /// Check if a wavelength update is pending and - fn update_if_dirty(&mut self, now: ClockSnapshot) { - if self.triggered { - // It's fine to reset this to zero because wavelength_start_time is not in the - // future, so there is no chance of us needing to subtract. - self.initial_count = 0; - self.wavelength_start_time = now.elapsed_fixed_cycles(); - if let Some(new_wavelength) = self.new_wavelength.take() { - self.wavelength = new_wavelength; - } - self.triggered = false; - } else if let Some(new_wavelength) = self.new_wavelength.take() { - // If the end of the current duty step is in the future, we already changed - // the wavelength once and computed the time to change to the new wavelength - // value. We don't need to recompute wavelength_start_time or initial_count, - // we can just apply the wavelenght value. This also works fine if we are - // exactly on the cycle when the wavelength is changing. - // - // We only need to recompute the changeover cycle if we are in the middle of a - // duty step, that is if the start time is in the past. - if self.wavelength_start_time < now.elapsed_fixed_cycles() { - // The new wavelength_start_time will be the next smallest multiple of the - // period greater than or equal to now. - let cycles_since_wavelength_started = - now.elapsed_fixed_cycles() - self.wavelength_start_time; - let period = self.period(); - let increments = cycles_since_wavelength_started.as_u64() / period; - let current_count = self.initial_count + increments; - // The time since the start of the current duty step is the remainder after dividing - // by the period. This is the number of ticks left in - // `cycles_since_wavelength_started` between when the duty cycle started and `now`. - let ticks_since_current_duty_step_started = - cycles_since_wavelength_started.as_u64() % period; - // Calculate the number of cycles until the next period start. This is the length of - // the periiod minus the number of cycles we've already seen in the period. However, - // if ticks_since_current_duty_step_started is zero, we are already on a duty step - // boundary right now. We can cover the 'already on a boundary' case by taking this - // result mod the period length. - let ticks_until_duty_step_ends = - (period - ticks_since_current_duty_step_started) % period; - // The start time for the new wavelength is the end of the next duty cycle. - self.wavelength_start_time = - now.elapsed_fixed_cycles() + ticks_until_duty_step_ends; - // The initial count for the new wavelength is the current count if we are already - // at a boundary, otherwise it's one more than the current count. We apply modulus - // to keep the initial_count small, though that's probably unnecessary, since we'll - // never increment this enough to overflow u64. - self.initial_count = if ticks_until_duty_step_ends == 0 { - current_count % NUM_STEPS - } else { - // In this case, we may need to subtract 1 if the duty step is requested before - // the new wavelength starts. To make sure we can always subtract 1 without - // underflowing, we apply the `+ 1` *after* doing the `% NUM_STEPS` so the value - // is always at least 1. - current_count % NUM_STEPS + 1 - }; - } - self.wavelength = new_wavelength; - } + fn trigger(&mut self, now: DCycle) { + self.initial_count = 0; + self.wavelength_start_time = now; + self.triggered_time = now; } /// Compute the current duty step for the wavelength. @@ -695,7 +579,7 @@ impl // We have effectively incremented the duty step a number of times equivalent // to the number of cycles elapsed divided by the number of cycles per // increment. - let increments = cycles_since_wavelength_started.as_u64() / self.period(); + let increments = cycles_since_wavelength_started.as_u64() / self.period().as_u64(); (self.initial_count + increments) % NUM_STEPS } else { // When subracting, we never need to apply the modulus, since that was done when @@ -705,15 +589,42 @@ impl } /// The period is the time between duty step increments, expressed in D-Cycles. - fn period(&self) -> u64 { - (2048 - self.wavelength) * PERIOD_MULTIPLIER; + fn period(&self) -> DCycle { + DCycle::new((2048 - self.wavelength as u64) * PERIOD_MULTIPLIER) + } + + /// Return true if we are in the first cycle. This is used by Channel 3 to determine if it + /// should emit its old initial sample. + fn is_first_duty_cycle(&self, now: DCycle) -> bool { + self.triggered_time + self.initial_period < now + } + + /// Get the duty step as-of 1 d-cycle ago, or none if we are in the first duty cycle. + /// + /// This is used by channel 3 to set `initial_step` when it is being shut down so that it plays + /// the correct initial sample the next time it is triggered. + /// + /// Important! This should never be used on the same cycle as `set_wavelength`. If that happens, + /// it may return an incorrect duty step value because `duty_step()` assumes that time never + /// runs backwards. As long as wavelength is only written through memory, that should never + /// happen in practice, as turning off the channel (disabling the dac or the whole audio system) + /// requires a write to a different memory location than the wavelength, so that should be + /// sufficient to guarantee that there is at least one cycle between those. + fn get_last_duty_step(&self, now: DCycle) -> Option { + if self.is_first_duty_cycle(now) { + // There is no previous duty step if we are on the first step. In this case, channel 3 + // should not update its initial_step, since no new sample has been read. + None + } else { + Some(self.duty_step(now - 1)) + } } } #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct PulseChannel { /// Envelope register. Values here are only picked up when the channel is triggered. - pub envelope_control: EnvelopeControl, + envelope_control: EnvelopeControl, /// Currently active envelope. envelope: Envelope, /// Sweep register and currently active sweep settings. @@ -766,39 +677,35 @@ impl PulseChannel { self.active } - /// Get the lower byte of the wavelength. - pub fn wavelength_low(&self) -> u8 { - let [wavelength_low, _] = self.wavelength.wavelength().to_le_bytes(); - wavelength_low - } - /// Set the lower byte of the wavelength. This should only be called from a memory access /// through system ram, and generally only via the CPU. - fn set_wavelength_low(&mut self, wavelength_low: u8) { + fn set_wavelength_low(&mut self, now: DCycle, wavelength_low: u8) { let [_, wavelength_high] = self.wavelength.wavelength().to_le_bytes(); - self.wavelength.set_wavelength(u16::from_le_bytes([wavelength_low, wavelength_high])); + self.wavelength + .set_wavelength(now, u16::from_le_bytes([wavelength_low, wavelength_high])); } - /// Read the wavelength high bits adn readable control bits. - pub fn wavelength_high_and_control(&self) -> u8 { + /// Only the length bit is readable, so this is only intended for implementing memdev. + fn wavelength_high_and_control(&self) -> u8 { // length_enable is the only readable bit. - 0xff.with_bits(Self::LENGTH_ENABLE, self.length_timer.enabled()) + 0xff.with_bits(Self::LENGTH_ENABLE, self.length_timer.enabled() as u8) } /// Set the value of the wavelength high and control register. This should only be called from a /// memory access through system ram, and generally only via the CPU. - fn set_wavelength_high_and_control(&mut self, value: u8) { + fn set_wavelength_high_and_control(&mut self, now: DCycle, value: u8) { let trigger = Self::TRIGGER.extract_bool(value); let length_enable = Self::LENGTH_ENABLE.extract_bool(value); let wavelength_high = Self::WAVELENGTH_HIGH.extract(value); let [wavelength_low, _] = self.wavelength.wavelength().to_le_bytes(); - self.wavelength.set_wavelength(u16::from_le_bytes([wavelength_low, wavelength_high])); + self.wavelength + .set_wavelength(now, u16::from_le_bytes([wavelength_low, wavelength_high])); self.length_timer.set_enabled(length_enable); // Channel is turned on if triggered and the DAC is enabled. If the DAC is // disabled, triggering does nothing. if trigger && self.envelope_control.dac_enabled() { - self.triggered.set(()); + self.wavelength.trigger(now); self.active = true; self.sweep.start_sweep(); self.envelope.reload_register(&self.envelope_control); @@ -807,7 +714,7 @@ impl PulseChannel { /// Reads out the value of the combined length and duty-cycle register. Since the /// length register is write-only, this only allows viewing the duty cycle. - pub fn length_and_duty_cycle(&self) -> u8 { + fn length_and_duty_cycle(&self) -> u8 { 0xff.with_bits(Self::DUTY_CYCLE, self.duty_cycle as u8) } @@ -820,42 +727,31 @@ impl PulseChannel { } /// Run one tick at the APU-DIV clock rate. - fn tick_apu_div(&mut self, apu_div: ApuDiv) { + fn tick_apu_div(&mut self, now: DCycle, apu_div: ApuDiv) { if self.length_timer.tick_apu_div(apu_div) { self.active = false; } if self.sweep.tick_apu_div(apu_div) { - let (wavelength, overflowed) = self.sweep.next_wavelength(self.wavelength); + let (wavelength, overflowed) = self.sweep.next_wavelength(self.wavelength.wavelength()); if overflowed { - self.wavelength = 0; + self.wavelength.set_wavelength(now, 0); self.active = false; } else { - self.wavelength = wavelength; + self.wavelength.set_wavelength(now, wavelength); self.sweep.start_sweep(); } } self.envelope.tick_apu_div(apu_div); } - /// Update any time-tracking fields with the current clock snapshot. - fn update_if_dirty(&mut self, now: ClockSnapshot) { - self.wavelength.update_if_dirty(now); - } - - /// Get the index into the pulse table for the current time based on when the channel - /// was last triggered. - fn pulse_step(&self, now: DCycle) -> usize { - self.wavelength.duty_step(now) as usize - } - /// Get a sample from this channel. fn get_sample(&self, now: DCycle) -> f32 { // We don't check the DAC here because anytime a memory write disables the DAC, we // turn the channel active to false as well, and don't allow a write to turn the // channel on unless the DAC is active. if self.active { - let pulse_step = self.pulse_step(now); + let pulse_step = self.wavelength.duty_step(now) as usize; debug!( "Sampling pulse channel from step {} of duty cycle {}", pulse_step, self.duty_cycle, @@ -871,29 +767,29 @@ impl PulseChannel { impl MemDevice for PulseChannel { const LEN: usize = 5; - fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { + fn read_byte_relative(&self, ctx: &ReadCtx, addr: RelativeAddr) -> u8 { dispatch_memdev_byte!(PulseChannel, addr, |addr| { - 0x00 => self.sweep.read_byte_relative(addr), + 0x00 => self.sweep.read_byte_relative(ctx, addr), 0x01 => self.length_and_duty_cycle(), - 0x02 => self.envelope_control.read_byte_relative(addr), - 0x03 => self.wavelength_low(), + 0x02 => self.envelope_control.read_byte_relative(ctx, addr), + 0x03 => 0xff, // wavelength is write-only. 0x04 => self.wavelength_high_and_control(), }) } - fn write_byte_relative(&mut self, addr: RelativeAddr, val: u8) { + fn write_byte_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, val: u8) { dispatch_memdev_byte!(PulseChannel, addr, |addr| { - 0x00 => self.sweep.write_byte_relative(addr, val), + 0x00 => self.sweep.write_byte_relative(ctx, addr, val), 0x01 => self.set_length_and_duty_cycle(val), 0x02 => { - self.envelope_control.write_byte_relative(addr, val); + self.envelope_control.write_byte_relative(ctx, addr, val); if !self.envelope_control.dac_enabled() { // Disabling the DAC disables the channel. self.active = false; } }, - 0x03 => self.set_wavelength_low(val), - 0x04 => self.set_wavelength_high_and_control(val), + 0x03 => self.set_wavelength_low(ctx.atime().elapsed_fixed_cycles(), val), + 0x04 => self.set_wavelength_high_and_control(ctx.atime().elapsed_fixed_cycles(),val), }) } @@ -905,7 +801,7 @@ impl MemDevice for PulseChannel { pub struct WavetableChannel { /// Whether the DAC is enabled. Unlike other channels, this is controlled directly /// rather than via some of the envelope bits. - dac_enable: bool, + dac_enabled: bool, /// Whether the channel is active. The channel can be disabled with the dac still /// active, but not the other way around. active: bool, @@ -921,7 +817,10 @@ pub struct WavetableChannel { /// Output level setting (this is the register which would have the envelope function, but /// channel 3 has no envelope functionality). - output_level: u8, + /// + /// The supported levels are mute, 100%, 50%, and 25%, so this is stored as a right-shift (mute + /// is implemented by a shift of 4, which works since samples are at most 4 bits). + output_level: u32, /// Wavelength position calculator for the location in the sample table. wavelength: WavelengthCalculator<32, 1>, @@ -929,117 +828,148 @@ pub struct WavetableChannel { /// Sample to emit when first triggered. initial_sample: u8, - period: u32, - phase_offset: u32, - triggered: bool, - sample_cursor: u32, - wavelength: u16, - level_shift: u8, /// Contains sample values which have been split in half to allow nybbles to be /// efficiently indexed when sampling. sample_table: [u8; 32], } impl WavetableChannel { - pub fn wavelength_low(&self) -> u8 { - (self.wavelength & 0xff) as u8 - } + /// Bit of the dac enable register which is actually used to control the DAC. + const DAC_ENABLED: BitGroup = BitGroup(0b1000_0000); - pub fn set_wavelength_low(&mut self, low_byte: u8) { - self.wavelength = (self.wavelength & 0x700) | low_byte as u16; - self.generate_period(); - } - - pub fn get_samples(&self, samples: usize) -> u8 { - let base = samples * 2; - (self.sample_table[base] << 4) + self.sample_table[base + 1] - } + /// Level selection. (Would be envelope in other channels). + const LEVEL: BitGroup = BitGroup(0b0110_0000); - pub fn set_enable(&mut self, value: u8) { - self.enabled = (value & 0x80) != 0; - } + // The Wavelength high and control register: + /// Trigger bit of the wavelength-high/control register. + const TRIGGER: BitGroup = BitGroup(0b1000_0000); + /// The length enable portion of the wavelength-high/control register. + const LENGTH_ENABLE: BitGroup = BitGroup(0b0100_0000); + /// Wavelength-high portion of the wavelength-high/control register. + const WAVELENGTH_HIGH: BitGroup = BitGroup(0b0000_0111); - pub fn set_samples(&mut self, samples: usize, value: u8) { - let base = samples * 2; - self.sample_table[base] = (value & 0xf0) >> 4; - self.sample_table[base + 1] = value & 0xf; + /// Read the value of the DAC enable register for a memory read. + fn dac_enabled_reg(&self) -> u8 { + 0xff.with_bits(Self::DAC_ENABLED, self.dac_enabled as u8) + } + + /// Update the dac_enabled value. + fn set_dac_enabled(&self, now: DCycle, val: u8) { + let dac_enabled = Self::DAC_ENABLED.extract_bool(val); + // If we are now turning off the dac (and the channel was active), we need to capture the + // last-read sample into initial_sample for next time. + if !dac_enabled { + if self.dac_enabled && self.active { + // If no index is returned, we haven't read a new sample since the last time we + // started, so we shouldn't update the initial_sample. + if let Some(index) = self.wavelength.get_last_duty_step(now) { + self.initial_sample = self.sample_table[index as usize]; + } + } + self.active = false; + } + self.dac_enabled = dac_enabled; + } + + /// Get the value of the level register. + fn level_reg(&self) -> u8 { + // Unsure what the other bits should read as. Since most things seem to default to 1 if not + // connected, we're using 0xff like most other places. + 0xff.with_bits(Self::LEVEL, match self.output_level { + 4 => 0b00, + 0 => 0b01, + 1 => 0b10, + 2 => 0b11, + _ => panic!("Invalid level setting, allowed values are 4 (mute), 0 (100%), 1 (50%), and 2 (25%)."), + }) } - pub fn set_level(&mut self, value: u8) { - let value = (value >> 5) & 0x3; - self.level_shift = if value == 0 { 4 } else { value - 1 }; + /// Set the level value. + fn set_level(&mut self, val: u8) { + self.output_level = match Self::LEVEL.extract(val) { + 0b00 => 4, + 0b01 => 0, + 0b10 => 1, + 0b11 => 2, + _ => unreachable!(), + }; } - pub fn generate_period(&mut self) { - self.period = 64 * (2048 - self.wavelength as u32); + /// Set the lower byte of the wavelength. This should only be called from a memory access + /// through system ram, and generally only via the CPU. + fn set_wavelength_low(&mut self, now: DCycle, wavelength_low: u8) { + let [_, wavelength_high] = self.wavelength.wavelength().to_le_bytes(); + self.wavelength + .set_wavelength(now, u16::from_le_bytes([wavelength_low, wavelength_high])); } - pub fn tick(&mut self, tcycles: u32) { - if self.period > 0 { - self.sample_cursor = (self.sample_cursor + tcycles) % self.period; - } + /// Read the wavelength high bits and readable control bits. Note: only the length enable bit is + /// readable. This is only intended for implementing memdev. + fn wavelength_high_and_control(&self) -> u8 { + // length_enable is the only readable bit. + 0xff.with_bits(Self::LENGTH_ENABLE, self.length_timer.enabled() as u8) } -} -impl Channel for WavetableChannel { - fn get_sample(&self) -> f32 { - if self.enabled && self.active { - let wavetable_step = (((self.sample_cursor as u32 + self.phase_offset) % self.period) - / (self.period / 32)) as usize; - debug!( - "Sampling wavetable channel from step {} of sample table.", - wavetable_step - ); - let dac_input = (self.sample_table[wavetable_step] as u16) >> self.level_shift; - 1.0 - (dac_input as f32 / 15.0 * 2.0) - } else { - 0.0 - } - } + /// Set the value of the wavelength high and control register. This should only be called from a + /// memory access through system ram, and generally only via the CPU. + fn set_wavelength_high_and_control(&mut self, now: DCycle, value: u8) { + let trigger = Self::TRIGGER.extract_bool(value); + let length_enable = Self::LENGTH_ENABLE.extract_bool(value); + let wavelength_high = Self::WAVELENGTH_HIGH.extract(value); + let [wavelength_low, _] = self.wavelength.wavelength().to_le_bytes(); - fn read_control(&self) -> u8 { - if self.length_enable { - ChannelControl::LENGTH_ENABLE.bits() - } else { - 0 + self.wavelength + .set_wavelength(now, u16::from_le_bytes([wavelength_low, wavelength_high])); + self.length_timer.set_enabled(length_enable); + // Channel is turned on if triggered and the DAC is enabled. If the DAC is + // disabled, triggering does nothing. + if trigger && self.dac_enabled { + self.wavelength.trigger(now); + self.active = true; } } - fn set_control(&mut self, value: u8) { - let control = ChannelControl::from_bits_truncate(value); - - self.triggered = control.contains(ChannelControl::TRIGGER); - - self.length_enable = control.contains(ChannelControl::LENGTH_ENABLE); - self.wavelength = (self.wavelength & 0xff) | (control.wavelength_high() as u16) << 8; - self.generate_period(); + /// Read the sample pair at the given address relative to the start of the sample table. + fn get_sample_pair(&self, samples: RelativeAddr) -> u8 { + let base = samples.index() * 2; + (self.sample_table[base] << 4) | self.sample_table[base + 1] } - fn set_length(&mut self, value: u8) { - self.length_acc = value; + /// Set the sample pair at the given address relative to the start of the sample table. + fn set_sample_pair(&mut self, samples: RelativeAddr, value: u8) { + let base = samples.index() * 2; + self.sample_table[base] = (value & 0xf0) >> 4; + self.sample_table[base + 1] = value & 0xf; } - fn check_trigger(&mut self) { - if self.triggered { - info!("Wavetable channel triggered"); - info!(" Wavetable sample table: {:?}", self.sample_table); + /// Run one tick at the APU-DIV clock rate. + fn tick_apu_div(&mut self, now: DCycle, apu_div: ApuDiv) { + if self.length_timer.tick_apu_div(apu_div) { + // When we stop the channel due to length expired, we need to update the initial_sample + // for next time the channel is triggered. - self.active = true; - self.triggered = false; + // If no index is returned, we haven't read a new sample since the last time we started, + // so we shouldn't update the initial_sample. + if let Some(index) = self.wavelength.get_last_duty_step(now) { + self.initial_sample = self.sample_table[index as usize]; + } + self.active = false; } } - fn apu_tick(&mut self) { - self.length_aticks += 1; - - if self.length_aticks == 2 { - self.length_aticks = 0; - if self.length_enable { - self.length_acc = self.length_acc.wrapping_add(1); - if self.length_acc == 64 { - self.active = false; - } - } + /// Get a sample from this channel. + fn get_sample(&self, now: DCycle) -> f32 { + // When dac gets disabled, we also deactivate the channel, so no need to check dac_enable. + if self.active { + let dac_input = if self.wavelength.is_first_duty_cycle(now) { + self.initial_sample + } else { + let wavetable_step = self.wavelength.duty_step(now) as usize; + self.sample_table[wavetable_step] + }; + 1.0 - (dac_input as f32 / 15.0 * 2.0) + } else { + 0.0 } } } @@ -1047,166 +977,501 @@ impl Channel for WavetableChannel { impl MemDevice for WavetableChannel { const LEN: usize = 0x15; - fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { - match addr.relative() { - // Channel settings block. - 0x00..=0x04 => 0xff, + fn read_byte_relative(&self, ctx: &ReadCtx, addr: RelativeAddr) -> u8 { + dispatch_memdev_byte!(WavetableChannel, addr, |addr| { + 0x00 => self.dac_enabled_reg(), + 0x01 => 0xff, // length timer is write-only. + 0x02 => self.level_reg(), + 0x03 => 0xff, // wavelength is write-only. + 0x04 => self.wavelength_high_and_control(), // Channel "samples" block. ApuRegs will remap this portion to the correct // offset. - 0x05..=0x14 => self.get_samples(addr.offset_by(0x05).index()), - _ => panic!("Address {addr} out of range for WavetableChannel"), - } + 0x05..=0x14 => self.get_sample_pair(addr), + }) } - fn write_byte_relative(&mut self, addr: RelativeAddr, val: u8) { - match addr.relative() { - // Channel settings block. - 0x00 => self.set_enable(val), - 0x01 => self.set_length(val), + fn write_byte_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, val: u8) { + dispatch_memdev_byte!(WavetableChannel, addr, |addr| { + 0x00 => self.set_dac_enabled(ctx.atime().elapsed_fixed_cycles(), val), + 0x01 => self.length_timer.set_counter(val), 0x02 => self.set_level(val), - 0x03 => self.set_wavelength_low(val), - 0x04 => self.set_control(val), + 0x03 => self.set_wavelength_low(ctx.atime().elapsed_fixed_cycles(), val), + 0x04 => self.set_wavelength_high_and_control(ctx.atime().elapsed_fixed_cycles(), val), // Channel "samples" block. ApuRegs will remap this portion to the correct // offset. - 0x05..=0x14 => self.set_samples(addr.offset_by(0x05).index(), val), - _ => panic!("Address {addr} out of range for WavetableChannel"), - } + 0x05 => self.set_sample_pair(addr, val), + }) } memdev_bytes_from_byte!(WavetableChannel); } +/// A trait for types which implement the LFSR used in the noise channel. +trait LinearFeedbackShiftRegister: Clone + fmt::Debug + Default + PartialEq + Eq { + /// Apply the 'increment/shift' operation of the LFSR the given number of times and return bit 0 + /// at the end of the operation. The output is always either 0 or 1. + fn increment_by_and_get_output(&mut self, increments: u64) -> u16; + + /// Return true if the LFSR is using short mode. + fn short(&self) -> bool; + + /// Set the width mode of the LFSR. + fn set_short(&mut self, short_width: bool); + + /// Reset the LFSR. + fn reset(&mut self); +} + +/// Directly implements the LFSR by doing the actual shifts. #[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct NoiseChannel { - phase_offset: u32, - enabled: bool, - active: bool, - triggered: bool, - noise_control: NoiseControl, - lfsr: u16, - envelope_control: EnvelopeControl, - envelope: Envelope, - length_enable: bool, - length_acc: u8, - length_aticks: u8, - envelope_aticks: u8, - lfsr_ticks: u32, +struct DirectLinearFeedbackShiftRegister { + /// Whether to use the shorter width (7 bits) instead of 15 bits. + short_width: bool, + + /// The contents of the LFSR register. + value: u16, } -impl NoiseChannel { - pub fn set_noise_control(&mut self, value: u8) { - self.noise_control.set(value); +impl LinearFeedbackShiftRegister for DirectLinearFeedbackShiftRegister { + fn increment_by_and_get_output(&mut self, increments: u64) -> u16 { + /// Take an applier function and initial value and return the final value after n + /// increments. + /// + /// The applier function should only set the relevant bits, not shift the result down. + #[inline] + fn do_increments u16>(apply: F, mut value: u16, increments: u64) -> u16 { + for _ in 0..increments { + // the bit is the nxor of the last two bits. + let bit = (!(((value & 0b10) >> 1) ^ (value & 0b01))) & 1; + // Apply the bit and shift down. + value = apply(value, bit) >> 1; + } + value + } + + #[inline(always)] + fn apply_long(value: u16, bit: u16) -> u16 { + (value & 0x7fff) | (bit << 15) + } + + #[inline(always)] + fn apply_short(value: u16, bit: u16) -> u16 { + (value & 0x7f7f) | (bit << 15) | (bit << 7) + } + + // This ensures the conditional is not applied within a tight loop. + self.value = if self.short_width { + do_increments(apply_short, self.value, increments) + } else { + do_increments(apply_long, self.value, increments) + }; + self.value & 1 + } + + fn short(&self) -> bool { + self.short_width } - pub fn set_envelope(&mut self, value: u8) { - self.envelope_control.set(value); - //set an envelope phase offset? - //needs retrigger to take + fn set_short(&mut self, short_width: bool) { + self.short_width = short_width; } - // Unlike the other channels noise has state updates that - // need to be updated faster than the div-apu tick - pub fn tick(&mut self, tcycles: u32) { - self.lfsr_ticks += tcycles; - if self.lfsr_ticks > self.noise_control.period() { - self.lfsr_ticks -= self.noise_control.period(); + fn reset(&mut self) { + self.value = 0; + } +} - let feedback_bits = if (self.lfsr & 0x1) ^ ((self.lfsr >> 1) & 0x1) == 0 { - 0x4080 - } else { - 0x0 - }; +/// Represents the LFSR used in the Noise channel. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +struct TabledLinearFeedbackShiftRegister { + /// Whether to use the shorter width (7 bits) instead of 15 bits. + short_width: bool, + + /// Bits from the upper portion of the LFSR which need to be reapplied when switching from 7 + /// bits to 15 bits, if we haven't gone enough steps for them to all be shifted away. + saved_high_bits: u16, + /// Increment during which we changed from wide to short. This is used only to determine how + /// many bits from `saved_high_bits` should be reapplied when changing from 7 bit mode back to + /// 15 bit mode. + changed_increment: u64, + + /// Offset applied to elapsed_increments before indexing into the LFSR table. This is used to + /// adjust for changes between 15 bit mode and 7 bit mode. + index_offset: u64, + /// Number of increments that have elapsed. + elapsed_increments: u64, + + /// If locked, the LFSR is in a state where it can only emit 1 (which is not covered by the LFSR + /// table). + locked: bool, +} - let lfsr_mask = self.noise_control.lfsr_mask(); - self.lfsr = ((self.lfsr & !lfsr_mask) | (feedback_bits & lfsr_mask)) >> 1; - } +impl TabledLinearFeedbackShiftRegister { + /// Get the current LFSR15 index. + fn idx15(&self) -> usize { + ((self.elapsed_increments + self.index_offset) % lfsr::LFSR15_PATTERN.len() as u64) as usize + } + + /// Get the current LFSR7 index. + fn idx7(&self) -> usize { + ((self.elapsed_increments + self.index_offset) % lfsr::LFSR7_PATTERN.len() as u64) as usize } } -impl Channel for NoiseChannel { - fn get_sample(&self) -> f32 { - if self.envelope_control.dac_enabled() && self.active { - let dac_input = (self.lfsr & 0x1) * self.envelope.level(); - 1.0 - (dac_input as f32 / 15.0 * 2.0) +impl LinearFeedbackShiftRegister for TabledLinearFeedbackShiftRegister { + /// Increment the LFSR `increments` number of times. + /// + /// The return value will be either 0 or 1. + fn increment_by_and_get_output(&mut self, increments: u64) -> u16 { + self.elapsed_increments += increments; + if self.locked { + 1 + } else if self.short_width { + lfsr::LFSR7_PATTERN[self.idx7()] & 0x01 } else { - 0.0 + lfsr::LFSR15_PATTERN[self.idx15()] & 0x01 } } - fn read_control(&self) -> u8 { - if self.length_enable { - ChannelControl::LENGTH_ENABLE.bits() - } else { - 0 + fn short(&self) -> bool { + self.short_width + } + + fn set_short(&mut self, short_width: bool) { + if short_width != self.short_width { + if short_width { + if self.locked { + // If we were locked at 15 bits, we are still going to be locked at 7 bits, and + // don't need to change anything, but to be sure we convert back correctly + // later, we'll store the locked bit pattern in saved_high_bits and update the + // changed_increment. + self.changed_increment = self.elapsed_increments; + self.saved_high_bits = 0x7f80; + } else { + // Changing from wide to short, so we need to recalculate the index_offset such + // that the current value of `elapsed_increments` has the same value for the + // 7-bit secton as we have for the 15-bit section. + + // Get the current 15 bit value. + let value = lfsr::LFSR15_PATTERN[self.idx15()]; + + // Store information about the high bits from the 15 bit register so we can + // restore them later if needed. + self.changed_increment = self.elapsed_increments; + self.saved_high_bits = value & 0x7f80; + + // Get just the last bits of the 7-bit portion. This is the relevant part and is + // used in the reverse lookup table to figure out where we are in the 7-bit + // sequence. + let masked_lower_bits = value & 0x007f; + + match lfsr::LFSR7_REVERSE_LOOKUP.get(masked_lower_bits as usize) { + // If the index is out of bounds, the value is all 1 and will cause us to be + // locked in 7 bit mode. + None => { + self.locked = true; + } + Some(&idx7) => { + let pat_len = lfsr::LFSR7_PATTERN.len() as u64; + let idx7 = idx7 as u64; + // Find an index_offset which, when added to the current increment + // produces the correct current index into the LFSR7_PATTERN. + // + // Basically, we're trying to solve `index = elapsed + offset` but mod + // pat_len, which is `index - elapsed = offset`. Since `index` and + // `offset` are `% pat_len`, we adjust the formula to avoid underflow. + // + // Essentially, we add pat_len to idx7 to make sure it is large enough + // to subtract from, and then mod elapsed by `pat_len` to ensure it is + // smaller than `idx7 + pat_len`. This produces an index_offset which is + // somewhere in the range `1..(2 * pat_len)`. + // + // Since mod will be applied again when calculating the final index, we + // don't need to apply mod to index_offset again here. + self.index_offset = + idx7 + pat_len - (self.elapsed_increments % pat_len); + } + }; + } + } else { + // Changing from 7 to 15 can result in unlocking if done soon enough! + + // Get the current value of the LFSR assuming we have shifted enough times that bits + // from 15 bit mode have all been shifted away. We will later reapply bits from 15 + // bit mode if necessary. + let base_value = if self.locked { + 0x7fff + } else { + lfsr::LFSR7_PATTERN[self.idx7()] + }; + + // Amount that saved_high_bits have been shifted down. + let amount_shifted = self.elapsed_increments - self.changed_increment; + // Figure out the actual value of the LFSR by reapplying the saved_high_bits. + let value = if amount_shifted < 8 { + let reapplied = (self.saved_high_bits >> amount_shifted) & 0x7f8; + let reapply_mask = (0x7f8 >> amount_shifted) & 0x7f8; + (base_value & !reapply_mask) | (reapplied & reapply_mask) + } else { + base_value + }; + + match lfsr::LFSR15_REVERSE_LOOKUP.get(value as usize) { + None => { + self.locked = true; + } + Some(&idx15) => { + let pat_len = lfsr::LFSR15_PATTERN.len() as u64; + let idx15 = idx15 as u64; + // Find an index_offset which, when added to the current increment + // produces the correct current index into the LFSR15_PATTERN. + // + // The math here works exactly the same as for 7 bit mode. + self.index_offset = idx15 + pat_len - (self.elapsed_increments % pat_len); + } + } + } } } - fn set_control(&mut self, value: u8) { - let control = ChannelControl::from_bits_truncate(value); + fn reset(&mut self) { + // We don't change the long/short setting but do reset all the fields used in calculating + // the current value (to set the output back to 0). - self.triggered = control.contains(ChannelControl::TRIGGER); + // The changed_increment and saved_high_bits need to be reset in case we are in 7 bit mode + // and switch back to 15 bit mode on the firs cycle, so the zeroes from 15 bit mode will be + // reapplied. + self.changed_increment = 0; + self.saved_high_bits = 0; + self.elapsed_increments = 0; + self.index_offset = 0; + self.locked = false; + } +} - self.length_enable = control.contains(ChannelControl::LENGTH_ENABLE); +/// The noise channel (Channel 4). +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct NoiseChannel { + /// Timer that controls turning off the noise channel. + length_timer: LengthTimer, + + /// Envelope register. Values here are only picked up when the channel is triggered. + envelope_control: EnvelopeControl, + /// Currently active envelope. + envelope: Envelope, + + /// The LFSR which determines the actual output of the channel. + lfsr: LFSR, + + /// Shift applied when determining the period. + clock_shift: u8, + /// Divider used when determining the period. + clock_divider: u8, + + /// DCycle when the currently set period (based on clock shift and divider) should take/took + /// effect. Note that many periods at this period length may have elapsed since this time, this + /// is just the time used as the basis for calculing how many steps have elapsed. + period_start_time: DCycle, + /// Value of the counter as of `period_start_time`. If `period_start_time <= now`, the counter + /// value is `initial_increments + times_incremented`, where `times_incremented` is the number + /// of times the counter would have incremented since `period_start_time` at the current period + /// length. If `period_start_time > now`, the count is exactly `initial_increments - 1`. + initial_increments: u64, + /// This is the number of increments passed to the LFSR. This is used to control how much the + /// LFSR is advanced whenever a sample is read. + consumed_increments: u64, + + /// Whether the channel is currently active. + active: bool, +} + +impl NoiseChannel { + // Noise Control Register Fields. + /// Clock shift field of the noise control register. + const CLOCK_SHIFT: BitGroup = BitGroup(0b1111_0000); + /// LFSR width field of the noise control register. + const LFSR_WIDTH: BitGroup = BitGroup(0b0000_1000); + /// Clock div field of the noise control register. + const CLOCK_DIV: BitGroup = BitGroup(0b0000_0111); + + // The control register: + /// Trigger bit of the control register. + const TRIGGER: BitGroup = BitGroup(0b1000_0000); + /// The length enable portion of the control register. + const LENGTH_ENABLE: BitGroup = BitGroup(0b0100_0000); + + /// Get the value of the noise control register. + /// + /// Only intended for implementing memory access. + fn noise_control(&self) -> u8 { + 0xff.with_bits(Self::CLOCK_SHIFT, self.clock_shift) + .with_bits(Self::LFSR_WIDTH, self.lfsr.short() as u8) + .with_bits(Self::CLOCK_DIV, self.clock_divider) } - fn set_length(&mut self, value: u8) { - self.length_acc = value & 0x1f; + /// Set the value of the noise control register. + /// + /// Only intended for implementing memory access. + fn set_noise_control(&mut self, now: DCycle, value: u8) { + let clock_shift = Self::CLOCK_SHIFT.extract(value); + let short = Self::LFSR_WIDTH.extract_bool(value); + let clock_div = Self::CLOCK_DIV.extract(value); + + self.lfsr.set_short(short); + + // We need to recalculate the period start time and initial number of increments for the new + // shift/div. + // The logic here is very similar to what is used in WavelengthCalculator, just the way we + // compute the period is different. + // + // If the end of the current period is in the future, we already changed the period once and + // computed the time to switch over, so we just need to update to the new period. This is + // also fine if we are exactly on the cycle when the period is changing. + // + // We only need to recompute the changeover cycle if we are in the middle of a period, that + // is if the start time for the current period is in the past. + if self.period_start_time < now { + // The new period_start_time will be the next smallest multiple of the period greater + // than or equal to now. + let cycles_since_period_started = now - self.period_start_time; + let period = self.period(); + let increments = cycles_since_period_started.as_u64() / period.as_u64(); + let current_increments = self.initial_increments + increments; + + // The time since the start of the current increment is the remainder after dividing + // by the period. This is the number of ticks left in + // `cycles_since_period_started` between when the last increment happened and `now`. + let ticks_since_current_increment_started = + cycles_since_period_started.as_u64() % period.as_u64(); + // Calculate the number of cycles until the next period start. This is the length of + // the periiod minus the number of cycles we've already seen in the period. However, + // if ticks_since_current_increment_started is zero, we are already on an increment + // boundary right now. We can cover the 'already on a boundary' case by taking this + // result mod the period length. + let ticks_until_increment_ends = + (period - ticks_since_current_increment_started).as_u64() % period.as_u64(); + + // The start time for the new period is the end of the current increment. + self.period_start_time = now + ticks_until_increment_ends; + + // The initial number of increments for the new period is the current value if this + // happens on a period boundary, otherwise it is one more than the current nubmer of + // increments if the new period starts in the future. If the number of increments is + // requested before the new period starts, the value will be `initial_increments - 1`. + self.initial_increments = current_increments; + if ticks_until_increment_ends > 0 { + self.initial_increments += 1; + } + } + self.clock_shift = clock_shift; + self.clock_divider = clock_div; + // We don't update consumed increments until those increments are actually sent to the LFSR. + } + + /// Get the readable portion of the control register. + /// + /// Only intended for implementing memory access. + fn control(&self) -> u8 { + // Only length_enable is readable. + 0xff.with_bits(Self::LENGTH_ENABLE, self.length_timer.enabled() as u8) } - fn check_trigger(&mut self) { - if self.triggered { + /// Set the control register. + /// + /// Only intended for implementing memory access. + fn set_control(&mut self, now: DCycle, value: u8) { + let trigger = Self::TRIGGER.extract_bool(value); + let length_enable = Self::LENGTH_ENABLE.extract_bool(value); + + self.length_timer.set_enabled(length_enable); + + // Channel is turned on if triggered and the DAC is enabled. If the DAC is + // disabled, triggering does nothing. + if trigger && self.envelope_control.dac_enabled() { self.active = true; - self.triggered = false; - self.lfsr = 0; - self.envelope = self.envelope_control.new_envelope(); - info!( - "Noise channel triggered with envelope {:?}, length_enable {}, length_acc {}", - self.envelope, self.length_enable, self.length_acc - ); + self.envelope.reload_register(&self.envelope_control); + self.lfsr.reset(); + self.consumed_increments = 0; + self.initial_increments = 0; + self.period_start_time = now; } } - fn apu_tick(&mut self) { - self.length_aticks += 1; - self.envelope_aticks += 1; + /// Calculate the length of the period in d-cycles. + fn period(&self) -> DCycle { + DCycle::new(if self.clock_divider == 0 { + 1 << (self.clock_shift + 2) + } else { + (self.clock_divider as u64) << (self.clock_shift + 3) + }) + } - if self.length_aticks == 2 { - self.length_aticks = 0; - if self.length_enable { - self.length_acc = self.length_acc.wrapping_add(1); - if self.length_acc == 64 { - self.active = false; - } - } + /// Get the current number of increments at a given time. Assumes time doesn't move backwards + /// from when the period is updated. + fn increments(&self, now: DCycle) -> u64 { + if self.period_start_time <= now { + let cycles_since_period_start = now - self.period_start_time; + let increments = cycles_since_period_start.as_u64() / self.period().as_u64(); + self.initial_increments + increments + } else { + self.initial_increments - 1 } - if self.envelope_aticks == 8 { - self.envelope_aticks = 0; - self.envelope.tick(); + } + + /// Update the LFSR and get the current sample. + /// + /// Mut is needed so the LFSR can be updated. + fn get_sample(&mut self, now: DCycle) -> f32 { + // If the DAC is off, we always set active to false, so we don't need to check the DAC here. + if self.active { + let increments = self.increments(now); + let new_increments = increments - self.consumed_increments; + self.consumed_increments = increments; + let dac_input = + self.lfsr.increment_by_and_get_output(new_increments) * self.envelope.level(); + 1.0 - (dac_input as f32 / 15.0 * 2.0) + } else { + 0.0 } } + + /// Apply the APU-DIV ticks. + fn tick_apu_div(&mut self, apu_div: ApuDiv) { + if self.length_timer.tick_apu_div(apu_div) { + self.active = false; + } + self.envelope.tick_apu_div(apu_div); + } } -impl MemDevice for NoiseChannel { +impl MemDevice for NoiseChannel { const LEN: usize = 4; - fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { - match addr.relative() { - 0x00..=0x03 => 0xff, - _ => panic!("Address {addr} out of range for NoiseChannel"), - } + fn read_byte_relative(&self, ctx: &ReadCtx, addr: RelativeAddr) -> u8 { + dispatch_memdev_byte!(NoiseChannel, addr, |addr| { + 0x00 => 0xff, + 0x01 => self.envelope_control.read_byte_relative(ctx, addr), + 0x02 => self.noise_control(), + 0x03 => self.control(), + }) } - fn write_byte_relative(&mut self, addr: RelativeAddr, val: u8) { - match addr.relative() { - 0x00 => self.set_length(val), - 0x01 => self.set_envelope(val), - 0x02 => self.set_noise_control(val), - 0x03 => self.set_control(val), - _ => panic!("Address {addr} out of range for NoiseChannel"), - } + fn write_byte_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, val: u8) { + dispatch_memdev_byte!(NoiseChannel, addr, |addr| { + 0x00 => self.length_timer.set_counter(val), + 0x01 => { + self.envelope_control.write_byte_relative(ctx, addr, val); + if !self.envelope_control.dac_enabled() { + // Disabling the DAC disables the channel. + self.active = false; + } + }, + 0x02 => self.set_noise_control(ctx.atime().elapsed_fixed_cycles(), val), + 0x03 => self.set_control(ctx.atime().elapsed_fixed_cycles(), val), + }) } - memdev_bytes_from_byte!(NoiseChannel); + memdev_bytes_from_byte!(NoiseChannel); } const HIGH_PASS_CUTOFF: f32 = 200.0; @@ -1282,7 +1547,7 @@ pub struct ApuRegs { pub ch1: PulseChannel, pub ch2: PulseChannel, pub ch3: WavetableChannel, - pub ch4: NoiseChannel, + pub ch4: NoiseChannel, pub sound_volume: SoundVolume, pub sound_pan: SoundPan, pub sound_enable: SoundEnable, @@ -1305,15 +1570,11 @@ memdev_fields!(ApuRegs, len: 0x30, { impl ApuRegs { /// Tick the APU-DIV given the status of the APU-DIV register as of this apu-div tick /// cycle. Should only be called on the cycle when ApuDiv was incremented. - fn tick_apu_div(&mut self, apu_div: ApuDiv) { - self.ch1.tick_apu_div(apu_div); - self.ch2.tick_apu_div(apu_div); - } - - /// Update any time-tracking registers with the current clock snapshot. - pub fn update_if_dirty(&mut self, now: ClockSnapshot) { - self.ch1.update_if_dirty(now); - self.ch2.update_if_dirty(now); + fn tick_apu_div(&mut self, now: DCycle, apu_div: ApuDiv) { + self.ch1.tick_apu_div(now, apu_div); + self.ch2.tick_apu_div(now, apu_div); + self.ch3.tick_apu_div(now, apu_div); + self.ch4.tick_apu_div(apu_div); } } @@ -1327,9 +1588,10 @@ pub struct ApuState { /// from the last time the sample rate was changed. sample: u64, /// Time that the sample rate was last changed. When the sample rate is changed, this - /// gets reset to the SystemClock time for the next cycle after the cycle when the - /// rate was changed. - sample_rate_changed_at: Option, + /// gets reset to the SystemClock time. + sample_rate_changed_at: Duration, + /// Cached time that the next sample should be taken at. + next_sample_time: Duration, /// Previous value of the bit selected from the DIV register that drives the sound /// clock. The sound clock triggers whenever this hits a falling edge. This bit is /// slected from divider bit 13 or 14 depending on whether the system is running at @@ -1348,39 +1610,32 @@ impl ApuState { output_sample: None, sample_rate: 0, sample: 0, - sample_rate_changed_at: Some(Duration::ZERO), + sample_rate_changed_at: Duration::ZERO, + next_sample_time: Duration::ZERO, prev_divider_bit: false, apu_div: ApuDiv::default(), hpf: HighPassFilter::default(), } } - pub fn set_output_sample_rate(&mut self, sample_rate: u32) { + /// Set the output sample rate. The current clock snapshot should be provided. + pub(crate) fn set_output_sample_rate(&mut self, now: DCycle, sample_rate: u32) { self.sample_rate = sample_rate as u64; self.sample = 0; - self.sample_rate_changed_at = None; + self.sample_rate_changed_at = now.duration(); + self.next_sample_time = self.sample_rate_changed_at; self.hpf.set_sample_rate(sample_rate); } - pub fn consume_output_sample(&mut self) -> Option { + pub(crate) fn consume_output_sample(&mut self) -> Option { self.output_sample.take() } - /// Calculate the time at which the next sample should be generated. - fn next_sample_time(&self) -> Duration { + /// Increments the number of samples by 1 and updates the time for the next sample. + fn increment_sample_count(&mut self) { + self.sample += 1; let time_since_changed = cycles_to_duration(self.sample, self.sample_rate); - let changed_at = self - .sample_rate_changed_at - .expect("sample rate change time was not set"); - changed_at + time_since_changed - } - - /// Update the `sample_rate_changed_at` to be the time of the next sample at the - /// current sample rate, if it isn't set. - fn recompute_sample_time_if_needed(&mut self, clock: &SystemClock) { - if self.sample_rate_changed_at.is_none() { - self.sample_rate_changed_at = Some(clock.elapsed_time()); - } + self.next_sample_time = self.sample_rate_changed_at + time_since_changed } } @@ -1395,33 +1650,11 @@ pub trait ApuContext: SystemClockContext { fn divider(&self) -> &TimerDivider; } -fn get_stereo_sample(ctx: &impl ApuContext, right: bool) -> f32 { - let channel_mix = ctx.apu_regs().sound_pan.channel_mix(right); - - ctx.apu_regs().sound_volume.vol_multiplier(right) - * (if channel_mix.contains(ChannelMix::CH1) { - 0.25 * ctx.apu_regs().ch1.get_sample() - } else { - 0.0 - } + if channel_mix.contains(ChannelMix::CH2) { - 0.25 * ctx.apu_regs().ch2.get_sample() - } else { - 0.0 - } + if channel_mix.contains(ChannelMix::CH3) { - 0.25 * ctx.apu_regs().ch3.get_sample() - } else { - 0.0 - } + if channel_mix.contains(ChannelMix::CH4) { - 0.25 * ctx.apu_regs().ch4.get_sample() - } else { - 0.0 - }) -} - /// to be called on divider bit 4 (5 double speed) falling edge pub fn tick_apu_div(ctx: &mut impl ApuContext) { + let now = ctx.clock().elapsed_fixed_cycles(); let apu_div = ctx.apu_mut().apu_div.tick_apu_div(); - ctx.apu_regs_mut().tick_apu_div(apu_div); + ctx.apu_regs_mut().tick_apu_div(now, apu_div); } pub fn tick(ctx: &mut impl ApuContext) { @@ -1429,50 +1662,61 @@ pub fn tick(ctx: &mut impl ApuContext) { ClockSpeed::Normal => 0x2000, ClockSpeed::Double => 0x4000, }; - let divider_bit = ctx.divider().value() & divider_bit_selector != 0; + let divider_bit = ctx.divider().value(ctx.clock().elapsed_cycles()) & divider_bit_selector != 0; let prev_divider_bit = mem::replace(&mut ctx.apu_mut().prev_divider_bit, divider_bit); if prev_divider_bit && !divider_bit { tick_apu_div(ctx); } - ctx.apu_regs_mut().ch1.check_trigger(); - ctx.apu_regs_mut().ch2.check_trigger(); - ctx.apu_regs_mut().ch3.check_trigger(); - ctx.apu_regs_mut().ch4.check_trigger(); - if ctx.apu().sample_rate > 0 { - let next_sample_time = ctx.apu().next_sample_time(); - let next_sample = ctx.apu().output_period - sample_cursor; - if tcycles as f32 > next_sample { - ctx.apu_regs_mut().ch1.tick(next_sample as u32); - ctx.apu_regs_mut().ch2.tick(next_sample as u32); - ctx.apu_regs_mut().ch3.tick(next_sample as u32); - ctx.apu_regs_mut().ch4.tick(next_sample as u32); - - let sample = Sample { - left: get_stereo_sample(ctx, false), - right: get_stereo_sample(ctx, true), - }; - let stereo_sample = ctx.apu_mut().hpf.filter_sample(sample); - ctx.apu_mut().output_sample = Some(stereo_sample); - - ctx.apu_regs_mut() - .ch1 - .tick(tcycles as u32 - next_sample as u32); - ctx.apu_regs_mut() - .ch2 - .tick(tcycles as u32 - next_sample as u32); - ctx.apu_regs_mut() - .ch3 - .tick(tcycles as u32 - next_sample as u32); - ctx.apu_regs_mut() - .ch4 - .tick(tcycles as u32 - next_sample as u32); - } else { - ctx.apu_regs_mut().ch1.tick(tcycles as u32); - ctx.apu_regs_mut().ch2.tick(tcycles as u32); - ctx.apu_regs_mut().ch3.tick(tcycles as u32); - ctx.apu_regs_mut().ch4.tick(tcycles as u32); + let now = ctx.clock().elapsed_time(); + if ctx.apu().next_sample_time < now { + // Sample output has fallen behind the system clock somehow, we will reset the next + // sample time to the current time and then emit samples from then. + // + // This should only be possible if the sample rate is faster than the d-cycle rate. + warn!("Sample output behind, resetting to now"); + ctx.apu_mut().sample = 0; + ctx.apu_mut().sample_rate_changed_at = now; + ctx.apu_mut().next_sample_time = now; + } + for (cycle, time_range) in ctx.clock().current_cycle_fixed_cycles().time_ranges() { + if time_range.start <= ctx.apu().next_sample_time + && ctx.apu().next_sample_time < time_range.end + { + ctx.apu_mut().increment_sample_count(); + + let channel_mix_left = ctx.apu_regs().sound_pan.channel_mix(SoundSide::Left); + let vol_mul_left = ctx.apu_regs().sound_volume.vol_multiplier(SoundSide::Left); + + let channel_mix_right = ctx.apu_regs().sound_pan.channel_mix(SoundSide::Right); + let vol_mul_right = ctx.apu_regs().sound_volume.vol_multiplier(SoundSide::Left); + + let ch1 = ctx.apu_regs().ch1.get_sample(cycle); + let ch2 = ctx.apu_regs().ch2.get_sample(cycle); + let ch3 = ctx.apu_regs().ch3.get_sample(cycle); + let ch4 = ctx.apu_regs_mut().ch4.get_sample(cycle); + + fn blend_samples( + mix: ChannelMix, + vol_mul: f32, + ch1: f32, + ch2: f32, + ch3: f32, + ch4: f32, + ) -> f32 { + vol_mul + * (mix.get_channel_mix_multiplier(ChannelMix::CH1) * ch1 + + mix.get_channel_mix_multiplier(ChannelMix::CH2) * ch2 + + mix.get_channel_mix_multiplier(ChannelMix::CH3) * ch3 + + mix.get_channel_mix_multiplier(ChannelMix::CH4) * ch4) + } + + ctx.apu_mut().output_sample = Some(Sample { + left: blend_samples(channel_mix_left, vol_mul_left, ch1, ch2, ch3, ch4), + right: blend_samples(channel_mix_right, vol_mul_right, ch1, ch2, ch3, ch4), + }) + } } } } diff --git a/feo3boy/src/apu/lfsr.rs b/feo3boy/src/apu/lfsr.rs new file mode 100644 index 0000000..5d94854 --- /dev/null +++ b/feo3boy/src/apu/lfsr.rs @@ -0,0 +1,23 @@ +/// This table contains the LFSR sequence for 15-bit mode. Each entry in the table is the bit +/// pattern for the LFSR indexed by the number of times the LFSR has been updated, minus one. So +/// after the first update, the LFSR value (before shifting) is `LFSR15_PATTERN[0]`, and the bit +/// output is `LFSR15_PATTERN[0] & 1`. +pub(super) static LFSR15_PATTERN: [u16; 0x7fff] = include!("lfsr15.txt"); + +/// Reverse lookup table for the position in the LFSR. To find the current position in the pattern +/// table for a given value, shift it to the right by one and index it here. That will tell you the +/// equivalent index in LFSR15_PATTERN. Note that 0xfffe and 0xffff are not covered: those result in +/// the LFSR locking up and are not part of the normal pattern. +pub(super) static LFSR15_REVERSE_LOOKUP: [usize; 0x7fff] = include!("lfsr15_rev.txt"); + +/// This table contains the LFSR sequence for 7-bit mode. Each entry in the table is the bit +/// pattern for the LFSR indexed by the number of times the LFSR has been updated, minus one. So +/// after the first update, the LFSR value (before shifting) is `LFSR15_PATTERN[0]`, and the bit +/// output is `LFSR15_PATTERN[0] & 1`. +pub(super) static LFSR7_PATTERN: [u16; 0x7f] = include!("lfsr7.txt"); + +/// Reverse lookup table for the position in the LFSR. To find the current position in the pattern +/// table for a given value, shift it to the right by one and index it here. That will tell you the +/// equivalent index in LFSR7_PATTERN. Note that 0xfe and 0xff are not covered: those result in +/// the LFSR locking up and are not part of the normal pattern. +pub(super) static LFSR7_REVERSE_LOOKUP: [usize; 0x7f] = include!("lfsr7_rev.txt"); diff --git a/feo3boy/src/apu/lfsr15.txt b/feo3boy/src/apu/lfsr15.txt new file mode 100644 index 0000000..08abef8 --- /dev/null +++ b/feo3boy/src/apu/lfsr15.txt @@ -0,0 +1,3279 @@ +[ + 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x7f00, 0x7f80, 0x7fc0, + 0x7fe0, 0x7ff0, 0x7ff8, 0x7ffc, 0x7ffe, 0x3fff, 0x5fff, 0x6fff, 0x77ff, 0x7bff, + 0x7dff, 0x7eff, 0x7f7f, 0x7fbf, 0x7fdf, 0x7fef, 0x7ff7, 0x7ffb, 0x7ffd, 0x3ffe, + 0x1fff, 0x4fff, 0x67ff, 0x73ff, 0x79ff, 0x7cff, 0x7e7f, 0x7f3f, 0x7f9f, 0x7fcf, + 0x7fe7, 0x7ff3, 0x7ff9, 0x3ffc, 0x5ffe, 0x2fff, 0x57ff, 0x6bff, 0x75ff, 0x7aff, + 0x7d7f, 0x7ebf, 0x7f5f, 0x7faf, 0x7fd7, 0x7feb, 0x7ff5, 0x3ffa, 0x1ffd, 0x0ffe, + 0x07ff, 0x43ff, 0x61ff, 0x70ff, 0x787f, 0x7c3f, 0x7e1f, 0x7f0f, 0x7f87, 0x7fc3, + 0x7fe1, 0x3ff0, 0x5ff8, 0x6ffc, 0x77fe, 0x3bff, 0x5dff, 0x6eff, 0x777f, 0x7bbf, + 0x7ddf, 0x7eef, 0x7f77, 0x7fbb, 0x7fdd, 0x3fee, 0x1ff7, 0x4ffb, 0x67fd, 0x33fe, + 0x19ff, 0x4cff, 0x667f, 0x733f, 0x799f, 0x7ccf, 0x7e67, 0x7f33, 0x7f99, 0x3fcc, + 0x5fe6, 0x2ff3, 0x57f9, 0x2bfc, 0x55fe, 0x2aff, 0x557f, 0x6abf, 0x755f, 0x7aaf, + 0x7d57, 0x7eab, 0x7f55, 0x3faa, 0x1fd5, 0x0fea, 0x07f5, 0x03fa, 0x01fd, 0x00fe, + 0x007f, 0x403f, 0x601f, 0x700f, 0x7807, 0x7c03, 0x7e01, 0x3f00, 0x5f80, 0x6fc0, + 0x77e0, 0x7bf0, 0x7df8, 0x7efc, 0x7f7e, 0x3fbf, 0x5fdf, 0x6fef, 0x77f7, 0x7bfb, + 0x7dfd, 0x3efe, 0x1f7f, 0x4fbf, 0x67df, 0x73ef, 0x79f7, 0x7cfb, 0x7e7d, 0x3f3e, + 0x1f9f, 0x4fcf, 0x67e7, 0x73f3, 0x79f9, 0x3cfc, 0x5e7e, 0x2f3f, 0x579f, 0x6bcf, + 0x75e7, 0x7af3, 0x7d79, 0x3ebc, 0x5f5e, 0x2faf, 0x57d7, 0x6beb, 0x75f5, 0x3afa, + 0x1d7d, 0x0ebe, 0x075f, 0x43af, 0x61d7, 0x70eb, 0x7875, 0x3c3a, 0x1e1d, 0x0f0e, + 0x0787, 0x43c3, 0x61e1, 0x30f0, 0x5878, 0x6c3c, 0x761e, 0x3b0f, 0x5d87, 0x6ec3, + 0x7761, 0x3bb0, 0x5dd8, 0x6eec, 0x7776, 0x3bbb, 0x5ddd, 0x2eee, 0x1777, 0x4bbb, + 0x65dd, 0x32ee, 0x1977, 0x4cbb, 0x665d, 0x332e, 0x1997, 0x4ccb, 0x6665, 0x3332, + 0x1999, 0x0ccc, 0x4666, 0x2333, 0x5199, 0x28cc, 0x5466, 0x2a33, 0x5519, 0x2a8c, + 0x5546, 0x2aa3, 0x5551, 0x2aa8, 0x5554, 0x6aaa, 0x3555, 0x1aaa, 0x0d55, 0x06aa, + 0x0355, 0x01aa, 0x00d5, 0x006a, 0x0035, 0x001a, 0x000d, 0x0006, 0x0003, 0x4001, + 0x2000, 0x5000, 0x6800, 0x7400, 0x7a00, 0x7d00, 0x7e80, 0x7f40, 0x7fa0, 0x7fd0, + 0x7fe8, 0x7ff4, 0x7ffa, 0x3ffd, 0x1ffe, 0x0fff, 0x47ff, 0x63ff, 0x71ff, 0x78ff, + 0x7c7f, 0x7e3f, 0x7f1f, 0x7f8f, 0x7fc7, 0x7fe3, 0x7ff1, 0x3ff8, 0x5ffc, 0x6ffe, + 0x37ff, 0x5bff, 0x6dff, 0x76ff, 0x7b7f, 0x7dbf, 0x7edf, 0x7f6f, 0x7fb7, 0x7fdb, + 0x7fed, 0x3ff6, 0x1ffb, 0x4ffd, 0x27fe, 0x13ff, 0x49ff, 0x64ff, 0x727f, 0x793f, + 0x7c9f, 0x7e4f, 0x7f27, 0x7f93, 0x7fc9, 0x3fe4, 0x5ff2, 0x2ff9, 0x17fc, 0x4bfe, + 0x25ff, 0x52ff, 0x697f, 0x74bf, 0x7a5f, 0x7d2f, 0x7e97, 0x7f4b, 0x7fa5, 0x3fd2, + 0x1fe9, 0x0ff4, 0x47fa, 0x23fd, 0x11fe, 0x08ff, 0x447f, 0x623f, 0x711f, 0x788f, + 0x7c47, 0x7e23, 0x7f11, 0x3f88, 0x5fc4, 0x6fe2, 0x37f1, 0x1bf8, 0x4dfc, 0x66fe, + 0x337f, 0x59bf, 0x6cdf, 0x766f, 0x7b37, 0x7d9b, 0x7ecd, 0x3f66, 0x1fb3, 0x4fd9, + 0x27ec, 0x53f6, 0x29fb, 0x54fd, 0x2a7e, 0x153f, 0x4a9f, 0x654f, 0x72a7, 0x7953, + 0x7ca9, 0x3e54, 0x5f2a, 0x2f95, 0x17ca, 0x0be5, 0x05f2, 0x02f9, 0x017c, 0x40be, + 0x205f, 0x502f, 0x6817, 0x740b, 0x7a05, 0x3d02, 0x1e81, 0x0f40, 0x47a0, 0x63d0, + 0x71e8, 0x78f4, 0x7c7a, 0x3e3d, 0x1f1e, 0x0f8f, 0x47c7, 0x63e3, 0x71f1, 0x38f8, + 0x5c7c, 0x6e3e, 0x371f, 0x5b8f, 0x6dc7, 0x76e3, 0x7b71, 0x3db8, 0x5edc, 0x6f6e, + 0x37b7, 0x5bdb, 0x6ded, 0x36f6, 0x1b7b, 0x4dbd, 0x26de, 0x136f, 0x49b7, 0x64db, + 0x726d, 0x3936, 0x1c9b, 0x4e4d, 0x2726, 0x1393, 0x49c9, 0x24e4, 0x5272, 0x2939, + 0x149c, 0x4a4e, 0x2527, 0x5293, 0x6949, 0x34a4, 0x5a52, 0x2d29, 0x1694, 0x4b4a, + 0x25a5, 0x12d2, 0x0969, 0x04b4, 0x425a, 0x212d, 0x1096, 0x084b, 0x4425, 0x2212, + 0x1109, 0x0884, 0x4442, 0x2221, 0x1110, 0x4888, 0x6444, 0x7222, 0x3911, 0x1c88, + 0x4e44, 0x6722, 0x3391, 0x19c8, 0x4ce4, 0x6672, 0x3339, 0x199c, 0x4cce, 0x2667, + 0x5333, 0x6999, 0x34cc, 0x5a66, 0x2d33, 0x5699, 0x2b4c, 0x55a6, 0x2ad3, 0x5569, + 0x2ab4, 0x555a, 0x2aad, 0x1556, 0x0aab, 0x4555, 0x22aa, 0x1155, 0x08aa, 0x0455, + 0x022a, 0x0115, 0x008a, 0x0045, 0x0022, 0x0011, 0x0008, 0x4004, 0x6002, 0x3001, + 0x1800, 0x4c00, 0x6600, 0x7300, 0x7980, 0x7cc0, 0x7e60, 0x7f30, 0x7f98, 0x7fcc, + 0x7fe6, 0x3ff3, 0x5ff9, 0x2ffc, 0x57fe, 0x2bff, 0x55ff, 0x6aff, 0x757f, 0x7abf, + 0x7d5f, 0x7eaf, 0x7f57, 0x7fab, 0x7fd5, 0x3fea, 0x1ff5, 0x0ffa, 0x07fd, 0x03fe, + 0x01ff, 0x40ff, 0x607f, 0x703f, 0x781f, 0x7c0f, 0x7e07, 0x7f03, 0x7f81, 0x3fc0, + 0x5fe0, 0x6ff0, 0x77f8, 0x7bfc, 0x7dfe, 0x3eff, 0x5f7f, 0x6fbf, 0x77df, 0x7bef, + 0x7df7, 0x7efb, 0x7f7d, 0x3fbe, 0x1fdf, 0x4fef, 0x67f7, 0x73fb, 0x79fd, 0x3cfe, + 0x1e7f, 0x4f3f, 0x679f, 0x73cf, 0x79e7, 0x7cf3, 0x7e79, 0x3f3c, 0x5f9e, 0x2fcf, + 0x57e7, 0x6bf3, 0x75f9, 0x3afc, 0x5d7e, 0x2ebf, 0x575f, 0x6baf, 0x75d7, 0x7aeb, + 0x7d75, 0x3eba, 0x1f5d, 0x0fae, 0x07d7, 0x43eb, 0x61f5, 0x30fa, 0x187d, 0x0c3e, + 0x061f, 0x430f, 0x6187, 0x70c3, 0x7861, 0x3c30, 0x5e18, 0x6f0c, 0x7786, 0x3bc3, + 0x5de1, 0x2ef0, 0x5778, 0x6bbc, 0x75de, 0x3aef, 0x5d77, 0x6ebb, 0x775d, 0x3bae, + 0x1dd7, 0x4eeb, 0x6775, 0x33ba, 0x19dd, 0x0cee, 0x0677, 0x433b, 0x619d, 0x30ce, + 0x1867, 0x4c33, 0x6619, 0x330c, 0x5986, 0x2cc3, 0x5661, 0x2b30, 0x5598, 0x6acc, + 0x7566, 0x3ab3, 0x5d59, 0x2eac, 0x5756, 0x2bab, 0x55d5, 0x2aea, 0x1575, 0x0aba, + 0x055d, 0x02ae, 0x0157, 0x40ab, 0x6055, 0x302a, 0x1815, 0x0c0a, 0x0605, 0x0302, + 0x0181, 0x00c0, 0x4060, 0x6030, 0x7018, 0x780c, 0x7c06, 0x3e03, 0x5f01, 0x2f80, + 0x57c0, 0x6be0, 0x75f0, 0x7af8, 0x7d7c, 0x7ebe, 0x3f5f, 0x5faf, 0x6fd7, 0x77eb, + 0x7bf5, 0x3dfa, 0x1efd, 0x0f7e, 0x07bf, 0x43df, 0x61ef, 0x70f7, 0x787b, 0x7c3d, + 0x3e1e, 0x1f0f, 0x4f87, 0x67c3, 0x73e1, 0x39f0, 0x5cf8, 0x6e7c, 0x773e, 0x3b9f, + 0x5dcf, 0x6ee7, 0x7773, 0x7bb9, 0x3ddc, 0x5eee, 0x2f77, 0x57bb, 0x6bdd, 0x35ee, + 0x1af7, 0x4d7b, 0x66bd, 0x335e, 0x19af, 0x4cd7, 0x666b, 0x7335, 0x399a, 0x1ccd, + 0x0e66, 0x0733, 0x4399, 0x21cc, 0x50e6, 0x2873, 0x5439, 0x2a1c, 0x550e, 0x2a87, + 0x5543, 0x6aa1, 0x3550, 0x5aa8, 0x6d54, 0x76aa, 0x3b55, 0x1daa, 0x0ed5, 0x076a, + 0x03b5, 0x01da, 0x00ed, 0x0076, 0x003b, 0x401d, 0x200e, 0x1007, 0x4803, 0x6401, + 0x3200, 0x5900, 0x6c80, 0x7640, 0x7b20, 0x7d90, 0x7ec8, 0x7f64, 0x7fb2, 0x3fd9, + 0x1fec, 0x4ff6, 0x27fb, 0x53fd, 0x29fe, 0x14ff, 0x4a7f, 0x653f, 0x729f, 0x794f, + 0x7ca7, 0x7e53, 0x7f29, 0x3f94, 0x5fca, 0x2fe5, 0x17f2, 0x0bf9, 0x05fc, 0x42fe, + 0x217f, 0x50bf, 0x685f, 0x742f, 0x7a17, 0x7d0b, 0x7e85, 0x3f42, 0x1fa1, 0x0fd0, + 0x47e8, 0x63f4, 0x71fa, 0x38fd, 0x1c7e, 0x0e3f, 0x471f, 0x638f, 0x71c7, 0x78e3, + 0x7c71, 0x3e38, 0x5f1c, 0x6f8e, 0x37c7, 0x5be3, 0x6df1, 0x36f8, 0x5b7c, 0x6dbe, + 0x36df, 0x5b6f, 0x6db7, 0x76db, 0x7b6d, 0x3db6, 0x1edb, 0x4f6d, 0x27b6, 0x13db, + 0x49ed, 0x24f6, 0x127b, 0x493d, 0x249e, 0x124f, 0x4927, 0x6493, 0x7249, 0x3924, + 0x5c92, 0x2e49, 0x1724, 0x4b92, 0x25c9, 0x12e4, 0x4972, 0x24b9, 0x125c, 0x492e, + 0x2497, 0x524b, 0x6925, 0x3492, 0x1a49, 0x0d24, 0x4692, 0x2349, 0x11a4, 0x48d2, + 0x2469, 0x1234, 0x491a, 0x248d, 0x1246, 0x0923, 0x4491, 0x2248, 0x5124, 0x6892, + 0x3449, 0x1a24, 0x4d12, 0x2689, 0x1344, 0x49a2, 0x24d1, 0x1268, 0x4934, 0x649a, + 0x324d, 0x1926, 0x0c93, 0x4649, 0x2324, 0x5192, 0x28c9, 0x1464, 0x4a32, 0x2519, + 0x128c, 0x4946, 0x24a3, 0x5251, 0x2928, 0x5494, 0x6a4a, 0x3525, 0x1a92, 0x0d49, + 0x06a4, 0x4352, 0x21a9, 0x10d4, 0x486a, 0x2435, 0x121a, 0x090d, 0x0486, 0x0243, + 0x4121, 0x2090, 0x5048, 0x6824, 0x7412, 0x3a09, 0x1d04, 0x4e82, 0x2741, 0x13a0, + 0x49d0, 0x64e8, 0x7274, 0x793a, 0x3c9d, 0x1e4e, 0x0f27, 0x4793, 0x63c9, 0x31e4, + 0x58f2, 0x2c79, 0x163c, 0x4b1e, 0x258f, 0x52c7, 0x6963, 0x74b1, 0x3a58, 0x5d2c, + 0x6e96, 0x374b, 0x5ba5, 0x2dd2, 0x16e9, 0x0b74, 0x45ba, 0x22dd, 0x116e, 0x08b7, + 0x445b, 0x622d, 0x3116, 0x188b, 0x4c45, 0x2622, 0x1311, 0x0988, 0x44c4, 0x6262, + 0x3131, 0x1898, 0x4c4c, 0x6626, 0x3313, 0x5989, 0x2cc4, 0x5662, 0x2b31, 0x1598, + 0x4acc, 0x6566, 0x32b3, 0x5959, 0x2cac, 0x5656, 0x2b2b, 0x5595, 0x2aca, 0x1565, + 0x0ab2, 0x0559, 0x02ac, 0x4156, 0x20ab, 0x5055, 0x282a, 0x1415, 0x0a0a, 0x0505, + 0x0282, 0x0141, 0x00a0, 0x4050, 0x6028, 0x7014, 0x780a, 0x3c05, 0x1e02, 0x0f01, + 0x0780, 0x43c0, 0x61e0, 0x70f0, 0x7878, 0x7c3c, 0x7e1e, 0x3f0f, 0x5f87, 0x6fc3, + 0x77e1, 0x3bf0, 0x5df8, 0x6efc, 0x777e, 0x3bbf, 0x5ddf, 0x6eef, 0x7777, 0x7bbb, + 0x7ddd, 0x3eee, 0x1f77, 0x4fbb, 0x67dd, 0x33ee, 0x19f7, 0x4cfb, 0x667d, 0x333e, + 0x199f, 0x4ccf, 0x6667, 0x7333, 0x7999, 0x3ccc, 0x5e66, 0x2f33, 0x5799, 0x2bcc, + 0x55e6, 0x2af3, 0x5579, 0x2abc, 0x555e, 0x2aaf, 0x5557, 0x6aab, 0x7555, 0x3aaa, + 0x1d55, 0x0eaa, 0x0755, 0x03aa, 0x01d5, 0x00ea, 0x0075, 0x003a, 0x001d, 0x000e, + 0x0007, 0x4003, 0x6001, 0x3000, 0x5800, 0x6c00, 0x7600, 0x7b00, 0x7d80, 0x7ec0, + 0x7f60, 0x7fb0, 0x7fd8, 0x7fec, 0x7ff6, 0x3ffb, 0x5ffd, 0x2ffe, 0x17ff, 0x4bff, + 0x65ff, 0x72ff, 0x797f, 0x7cbf, 0x7e5f, 0x7f2f, 0x7f97, 0x7fcb, 0x7fe5, 0x3ff2, + 0x1ff9, 0x0ffc, 0x47fe, 0x23ff, 0x51ff, 0x68ff, 0x747f, 0x7a3f, 0x7d1f, 0x7e8f, + 0x7f47, 0x7fa3, 0x7fd1, 0x3fe8, 0x5ff4, 0x6ffa, 0x37fd, 0x1bfe, 0x0dff, 0x46ff, + 0x637f, 0x71bf, 0x78df, 0x7c6f, 0x7e37, 0x7f1b, 0x7f8d, 0x3fc6, 0x1fe3, 0x4ff1, + 0x27f8, 0x53fc, 0x69fe, 0x34ff, 0x5a7f, 0x6d3f, 0x769f, 0x7b4f, 0x7da7, 0x7ed3, + 0x7f69, 0x3fb4, 0x5fda, 0x2fed, 0x17f6, 0x0bfb, 0x45fd, 0x22fe, 0x117f, 0x48bf, + 0x645f, 0x722f, 0x7917, 0x7c8b, 0x7e45, 0x3f22, 0x1f91, 0x0fc8, 0x47e4, 0x63f2, + 0x31f9, 0x18fc, 0x4c7e, 0x263f, 0x531f, 0x698f, 0x74c7, 0x7a63, 0x7d31, 0x3e98, + 0x5f4c, 0x6fa6, 0x37d3, 0x5be9, 0x2df4, 0x56fa, 0x2b7d, 0x15be, 0x0adf, 0x456f, + 0x62b7, 0x715b, 0x78ad, 0x3c56, 0x1e2b, 0x4f15, 0x278a, 0x13c5, 0x09e2, 0x04f1, + 0x0278, 0x413c, 0x609e, 0x304f, 0x5827, 0x6c13, 0x7609, 0x3b04, 0x5d82, 0x2ec1, + 0x1760, 0x4bb0, 0x65d8, 0x72ec, 0x7976, 0x3cbb, 0x5e5d, 0x2f2e, 0x1797, 0x4bcb, + 0x65e5, 0x32f2, 0x1979, 0x0cbc, 0x465e, 0x232f, 0x5197, 0x68cb, 0x7465, 0x3a32, + 0x1d19, 0x0e8c, 0x4746, 0x23a3, 0x51d1, 0x28e8, 0x5474, 0x6a3a, 0x351d, 0x1a8e, + 0x0d47, 0x46a3, 0x6351, 0x31a8, 0x58d4, 0x6c6a, 0x3635, 0x1b1a, 0x0d8d, 0x06c6, + 0x0363, 0x41b1, 0x20d8, 0x506c, 0x6836, 0x341b, 0x5a0d, 0x2d06, 0x1683, 0x4b41, + 0x25a0, 0x52d0, 0x6968, 0x74b4, 0x7a5a, 0x3d2d, 0x1e96, 0x0f4b, 0x47a5, 0x23d2, + 0x11e9, 0x08f4, 0x447a, 0x223d, 0x111e, 0x088f, 0x4447, 0x6223, 0x7111, 0x3888, + 0x5c44, 0x6e22, 0x3711, 0x1b88, 0x4dc4, 0x66e2, 0x3371, 0x19b8, 0x4cdc, 0x666e, + 0x3337, 0x599b, 0x6ccd, 0x3666, 0x1b33, 0x4d99, 0x26cc, 0x5366, 0x29b3, 0x54d9, + 0x2a6c, 0x5536, 0x2a9b, 0x554d, 0x2aa6, 0x1553, 0x4aa9, 0x2554, 0x52aa, 0x2955, + 0x14aa, 0x0a55, 0x052a, 0x0295, 0x014a, 0x00a5, 0x0052, 0x0029, 0x0014, 0x400a, + 0x2005, 0x1002, 0x0801, 0x0400, 0x4200, 0x6100, 0x7080, 0x7840, 0x7c20, 0x7e10, + 0x7f08, 0x7f84, 0x7fc2, 0x3fe1, 0x1ff0, 0x4ff8, 0x67fc, 0x73fe, 0x39ff, 0x5cff, + 0x6e7f, 0x773f, 0x7b9f, 0x7dcf, 0x7ee7, 0x7f73, 0x7fb9, 0x3fdc, 0x5fee, 0x2ff7, + 0x57fb, 0x6bfd, 0x35fe, 0x1aff, 0x4d7f, 0x66bf, 0x735f, 0x79af, 0x7cd7, 0x7e6b, + 0x7f35, 0x3f9a, 0x1fcd, 0x0fe6, 0x07f3, 0x43f9, 0x21fc, 0x50fe, 0x287f, 0x543f, + 0x6a1f, 0x750f, 0x7a87, 0x7d43, 0x7ea1, 0x3f50, 0x5fa8, 0x6fd4, 0x77ea, 0x3bf5, + 0x1dfa, 0x0efd, 0x077e, 0x03bf, 0x41df, 0x60ef, 0x7077, 0x783b, 0x7c1d, 0x3e0e, + 0x1f07, 0x4f83, 0x67c1, 0x33e0, 0x59f0, 0x6cf8, 0x767c, 0x7b3e, 0x3d9f, 0x5ecf, + 0x6f67, 0x77b3, 0x7bd9, 0x3dec, 0x5ef6, 0x2f7b, 0x57bd, 0x2bde, 0x15ef, 0x4af7, + 0x657b, 0x72bd, 0x395e, 0x1caf, 0x4e57, 0x672b, 0x7395, 0x39ca, 0x1ce5, 0x0e72, + 0x0739, 0x039c, 0x41ce, 0x20e7, 0x5073, 0x6839, 0x341c, 0x5a0e, 0x2d07, 0x5683, + 0x6b41, 0x35a0, 0x5ad0, 0x6d68, 0x76b4, 0x7b5a, 0x3dad, 0x1ed6, 0x0f6b, 0x47b5, + 0x23da, 0x11ed, 0x08f6, 0x047b, 0x423d, 0x211e, 0x108f, 0x4847, 0x6423, 0x7211, + 0x3908, 0x5c84, 0x6e42, 0x3721, 0x1b90, 0x4dc8, 0x66e4, 0x7372, 0x39b9, 0x1cdc, + 0x4e6e, 0x2737, 0x539b, 0x69cd, 0x34e6, 0x1a73, 0x4d39, 0x269c, 0x534e, 0x29a7, + 0x54d3, 0x6a69, 0x3534, 0x5a9a, 0x2d4d, 0x16a6, 0x0b53, 0x45a9, 0x22d4, 0x516a, + 0x28b5, 0x145a, 0x0a2d, 0x0516, 0x028b, 0x4145, 0x20a2, 0x1051, 0x0828, 0x4414, + 0x620a, 0x3105, 0x1882, 0x0c41, 0x0620, 0x4310, 0x6188, 0x70c4, 0x7862, 0x3c31, + 0x1e18, 0x4f0c, 0x6786, 0x33c3, 0x59e1, 0x2cf0, 0x5678, 0x6b3c, 0x759e, 0x3acf, + 0x5d67, 0x6eb3, 0x7759, 0x3bac, 0x5dd6, 0x2eeb, 0x5775, 0x2bba, 0x15dd, 0x0aee, + 0x0577, 0x42bb, 0x615d, 0x30ae, 0x1857, 0x4c2b, 0x6615, 0x330a, 0x1985, 0x0cc2, + 0x0661, 0x0330, 0x4198, 0x60cc, 0x7066, 0x3833, 0x5c19, 0x2e0c, 0x5706, 0x2b83, + 0x55c1, 0x2ae0, 0x5570, 0x6ab8, 0x755c, 0x7aae, 0x3d57, 0x5eab, 0x6f55, 0x37aa, + 0x1bd5, 0x0dea, 0x06f5, 0x037a, 0x01bd, 0x00de, 0x006f, 0x4037, 0x601b, 0x700d, + 0x3806, 0x1c03, 0x4e01, 0x2700, 0x5380, 0x69c0, 0x74e0, 0x7a70, 0x7d38, 0x7e9c, + 0x7f4e, 0x3fa7, 0x5fd3, 0x6fe9, 0x37f4, 0x5bfa, 0x2dfd, 0x16fe, 0x0b7f, 0x45bf, + 0x62df, 0x716f, 0x78b7, 0x7c5b, 0x7e2d, 0x3f16, 0x1f8b, 0x4fc5, 0x27e2, 0x13f1, + 0x09f8, 0x44fc, 0x627e, 0x313f, 0x589f, 0x6c4f, 0x7627, 0x7b13, 0x7d89, 0x3ec4, + 0x5f62, 0x2fb1, 0x17d8, 0x4bec, 0x65f6, 0x32fb, 0x597d, 0x2cbe, 0x165f, 0x4b2f, + 0x6597, 0x72cb, 0x7965, 0x3cb2, 0x1e59, 0x0f2c, 0x4796, 0x23cb, 0x51e5, 0x28f2, + 0x1479, 0x0a3c, 0x451e, 0x228f, 0x5147, 0x68a3, 0x7451, 0x3a28, 0x5d14, 0x6e8a, + 0x3745, 0x1ba2, 0x0dd1, 0x06e8, 0x4374, 0x61ba, 0x30dd, 0x186e, 0x0c37, 0x461b, + 0x630d, 0x3186, 0x18c3, 0x4c61, 0x2630, 0x5318, 0x698c, 0x74c6, 0x3a63, 0x5d31, + 0x2e98, 0x574c, 0x6ba6, 0x35d3, 0x5ae9, 0x2d74, 0x56ba, 0x2b5d, 0x15ae, 0x0ad7, + 0x456b, 0x62b5, 0x315a, 0x18ad, 0x0c56, 0x062b, 0x4315, 0x218a, 0x10c5, 0x0862, + 0x0431, 0x0218, 0x410c, 0x6086, 0x3043, 0x5821, 0x2c10, 0x5608, 0x6b04, 0x7582, + 0x3ac1, 0x1d60, 0x4eb0, 0x6758, 0x73ac, 0x79d6, 0x3ceb, 0x5e75, 0x2f3a, 0x179d, + 0x0bce, 0x05e7, 0x42f3, 0x6179, 0x30bc, 0x585e, 0x2c2f, 0x5617, 0x6b0b, 0x7585, + 0x3ac2, 0x1d61, 0x0eb0, 0x4758, 0x63ac, 0x71d6, 0x38eb, 0x5c75, 0x2e3a, 0x171d, + 0x0b8e, 0x05c7, 0x42e3, 0x6171, 0x30b8, 0x585c, 0x6c2e, 0x3617, 0x5b0b, 0x6d85, + 0x36c2, 0x1b61, 0x0db0, 0x46d8, 0x636c, 0x71b6, 0x38db, 0x5c6d, 0x2e36, 0x171b, + 0x4b8d, 0x25c6, 0x12e3, 0x4971, 0x24b8, 0x525c, 0x692e, 0x3497, 0x5a4b, 0x6d25, + 0x3692, 0x1b49, 0x0da4, 0x46d2, 0x2369, 0x11b4, 0x48da, 0x246d, 0x1236, 0x091b, + 0x448d, 0x2246, 0x1123, 0x4891, 0x2448, 0x5224, 0x6912, 0x3489, 0x1a44, 0x4d22, + 0x2691, 0x1348, 0x49a4, 0x64d2, 0x3269, 0x1934, 0x4c9a, 0x264d, 0x1326, 0x0993, + 0x44c9, 0x2264, 0x5132, 0x2899, 0x144c, 0x4a26, 0x2513, 0x5289, 0x2944, 0x54a2, + 0x2a51, 0x1528, 0x4a94, 0x654a, 0x32a5, 0x1952, 0x0ca9, 0x0654, 0x432a, 0x2195, + 0x10ca, 0x0865, 0x0432, 0x0219, 0x010c, 0x4086, 0x2043, 0x5021, 0x2810, 0x5408, + 0x6a04, 0x7502, 0x3a81, 0x1d40, 0x4ea0, 0x6750, 0x73a8, 0x79d4, 0x7cea, 0x3e75, + 0x1f3a, 0x0f9d, 0x07ce, 0x03e7, 0x41f3, 0x60f9, 0x307c, 0x583e, 0x2c1f, 0x560f, + 0x6b07, 0x7583, 0x7ac1, 0x3d60, 0x5eb0, 0x6f58, 0x77ac, 0x7bd6, 0x3deb, 0x5ef5, + 0x2f7a, 0x17bd, 0x0bde, 0x05ef, 0x42f7, 0x617b, 0x70bd, 0x385e, 0x1c2f, 0x4e17, + 0x670b, 0x7385, 0x39c2, 0x1ce1, 0x0e70, 0x4738, 0x639c, 0x71ce, 0x38e7, 0x5c73, + 0x6e39, 0x371c, 0x5b8e, 0x2dc7, 0x56e3, 0x6b71, 0x35b8, 0x5adc, 0x6d6e, 0x36b7, + 0x5b5b, 0x6dad, 0x36d6, 0x1b6b, 0x4db5, 0x26da, 0x136d, 0x09b6, 0x04db, 0x426d, + 0x2136, 0x109b, 0x484d, 0x2426, 0x1213, 0x4909, 0x2484, 0x5242, 0x2921, 0x1490, + 0x4a48, 0x6524, 0x7292, 0x3949, 0x1ca4, 0x4e52, 0x2729, 0x1394, 0x49ca, 0x24e5, + 0x1272, 0x0939, 0x049c, 0x424e, 0x2127, 0x5093, 0x6849, 0x3424, 0x5a12, 0x2d09, + 0x1684, 0x4b42, 0x25a1, 0x12d0, 0x4968, 0x64b4, 0x725a, 0x392d, 0x1c96, 0x0e4b, + 0x4725, 0x2392, 0x11c9, 0x08e4, 0x4472, 0x2239, 0x111c, 0x488e, 0x2447, 0x5223, + 0x6911, 0x3488, 0x5a44, 0x6d22, 0x3691, 0x1b48, 0x4da4, 0x66d2, 0x3369, 0x19b4, + 0x4cda, 0x266d, 0x1336, 0x099b, 0x44cd, 0x2266, 0x1133, 0x4899, 0x244c, 0x5226, + 0x2913, 0x5489, 0x2a44, 0x5522, 0x2a91, 0x1548, 0x4aa4, 0x6552, 0x32a9, 0x1954, + 0x4caa, 0x2655, 0x132a, 0x0995, 0x04ca, 0x0265, 0x0132, 0x0099, 0x004c, 0x4026, + 0x2013, 0x5009, 0x2804, 0x5402, 0x2a01, 0x1500, 0x4a80, 0x6540, 0x72a0, 0x7950, + 0x7ca8, 0x7e54, 0x7f2a, 0x3f95, 0x1fca, 0x0fe5, 0x07f2, 0x03f9, 0x01fc, 0x40fe, + 0x207f, 0x503f, 0x681f, 0x740f, 0x7a07, 0x7d03, 0x7e81, 0x3f40, 0x5fa0, 0x6fd0, + 0x77e8, 0x7bf4, 0x7dfa, 0x3efd, 0x1f7e, 0x0fbf, 0x47df, 0x63ef, 0x71f7, 0x78fb, + 0x7c7d, 0x3e3e, 0x1f1f, 0x4f8f, 0x67c7, 0x73e3, 0x79f1, 0x3cf8, 0x5e7c, 0x6f3e, + 0x379f, 0x5bcf, 0x6de7, 0x76f3, 0x7b79, 0x3dbc, 0x5ede, 0x2f6f, 0x57b7, 0x6bdb, + 0x75ed, 0x3af6, 0x1d7b, 0x4ebd, 0x275e, 0x13af, 0x49d7, 0x64eb, 0x7275, 0x393a, + 0x1c9d, 0x0e4e, 0x0727, 0x4393, 0x61c9, 0x30e4, 0x5872, 0x2c39, 0x161c, 0x4b0e, + 0x2587, 0x52c3, 0x6961, 0x34b0, 0x5a58, 0x6d2c, 0x7696, 0x3b4b, 0x5da5, 0x2ed2, + 0x1769, 0x0bb4, 0x45da, 0x22ed, 0x1176, 0x08bb, 0x445d, 0x222e, 0x1117, 0x488b, + 0x6445, 0x3222, 0x1911, 0x0c88, 0x4644, 0x6322, 0x3191, 0x18c8, 0x4c64, 0x6632, + 0x3319, 0x198c, 0x4cc6, 0x2663, 0x5331, 0x2998, 0x54cc, 0x6a66, 0x3533, 0x5a99, + 0x2d4c, 0x56a6, 0x2b53, 0x55a9, 0x2ad4, 0x556a, 0x2ab5, 0x155a, 0x0aad, 0x0556, + 0x02ab, 0x4155, 0x20aa, 0x1055, 0x082a, 0x0415, 0x020a, 0x0105, 0x0082, 0x0041, + 0x0020, 0x4010, 0x6008, 0x7004, 0x7802, 0x3c01, 0x1e00, 0x4f00, 0x6780, 0x73c0, + 0x79e0, 0x7cf0, 0x7e78, 0x7f3c, 0x7f9e, 0x3fcf, 0x5fe7, 0x6ff3, 0x77f9, 0x3bfc, + 0x5dfe, 0x2eff, 0x577f, 0x6bbf, 0x75df, 0x7aef, 0x7d77, 0x7ebb, 0x7f5d, 0x3fae, + 0x1fd7, 0x4feb, 0x67f5, 0x33fa, 0x19fd, 0x0cfe, 0x067f, 0x433f, 0x619f, 0x70cf, + 0x7867, 0x7c33, 0x7e19, 0x3f0c, 0x5f86, 0x2fc3, 0x57e1, 0x2bf0, 0x55f8, 0x6afc, + 0x757e, 0x3abf, 0x5d5f, 0x6eaf, 0x7757, 0x7bab, 0x7dd5, 0x3eea, 0x1f75, 0x0fba, + 0x07dd, 0x03ee, 0x01f7, 0x40fb, 0x607d, 0x303e, 0x181f, 0x4c0f, 0x6607, 0x7303, + 0x7981, 0x3cc0, 0x5e60, 0x6f30, 0x7798, 0x7bcc, 0x7de6, 0x3ef3, 0x5f79, 0x2fbc, + 0x57de, 0x2bef, 0x55f7, 0x6afb, 0x757d, 0x3abe, 0x1d5f, 0x4eaf, 0x6757, 0x73ab, + 0x79d5, 0x3cea, 0x1e75, 0x0f3a, 0x079d, 0x03ce, 0x01e7, 0x40f3, 0x6079, 0x303c, + 0x581e, 0x2c0f, 0x5607, 0x6b03, 0x7581, 0x3ac0, 0x5d60, 0x6eb0, 0x7758, 0x7bac, + 0x7dd6, 0x3eeb, 0x5f75, 0x2fba, 0x17dd, 0x0bee, 0x05f7, 0x42fb, 0x617d, 0x30be, + 0x185f, 0x4c2f, 0x6617, 0x730b, 0x7985, 0x3cc2, 0x1e61, 0x0f30, 0x4798, 0x63cc, + 0x71e6, 0x38f3, 0x5c79, 0x2e3c, 0x571e, 0x2b8f, 0x55c7, 0x6ae3, 0x7571, 0x3ab8, + 0x5d5c, 0x6eae, 0x3757, 0x5bab, 0x6dd5, 0x36ea, 0x1b75, 0x0dba, 0x06dd, 0x036e, + 0x01b7, 0x40db, 0x606d, 0x3036, 0x181b, 0x4c0d, 0x2606, 0x1303, 0x4981, 0x24c0, + 0x5260, 0x6930, 0x7498, 0x7a4c, 0x7d26, 0x3e93, 0x5f49, 0x2fa4, 0x57d2, 0x2be9, + 0x15f4, 0x4afa, 0x257d, 0x12be, 0x095f, 0x44af, 0x6257, 0x712b, 0x7895, 0x3c4a, + 0x1e25, 0x0f12, 0x0789, 0x03c4, 0x41e2, 0x20f1, 0x1078, 0x483c, 0x641e, 0x320f, + 0x5907, 0x6c83, 0x7641, 0x3b20, 0x5d90, 0x6ec8, 0x7764, 0x7bb2, 0x3dd9, 0x1eec, + 0x4f76, 0x27bb, 0x53dd, 0x29ee, 0x14f7, 0x4a7b, 0x653d, 0x329e, 0x194f, 0x4ca7, + 0x6653, 0x7329, 0x3994, 0x5cca, 0x2e65, 0x1732, 0x0b99, 0x05cc, 0x42e6, 0x2173, + 0x50b9, 0x285c, 0x542e, 0x2a17, 0x550b, 0x6a85, 0x3542, 0x1aa1, 0x0d50, 0x46a8, + 0x6354, 0x71aa, 0x38d5, 0x1c6a, 0x0e35, 0x071a, 0x038d, 0x01c6, 0x00e3, 0x4071, + 0x2038, 0x501c, 0x680e, 0x3407, 0x5a03, 0x6d01, 0x3680, 0x5b40, 0x6da0, 0x76d0, + 0x7b68, 0x7db4, 0x7eda, 0x3f6d, 0x1fb6, 0x0fdb, 0x47ed, 0x23f6, 0x11fb, 0x48fd, + 0x247e, 0x123f, 0x491f, 0x648f, 0x7247, 0x7923, 0x7c91, 0x3e48, 0x5f24, 0x6f92, + 0x37c9, 0x1be4, 0x4df2, 0x26f9, 0x137c, 0x49be, 0x24df, 0x526f, 0x6937, 0x749b, + 0x7a4d, 0x3d26, 0x1e93, 0x4f49, 0x27a4, 0x53d2, 0x29e9, 0x14f4, 0x4a7a, 0x253d, + 0x129e, 0x094f, 0x44a7, 0x6253, 0x7129, 0x3894, 0x5c4a, 0x2e25, 0x1712, 0x0b89, + 0x05c4, 0x42e2, 0x2171, 0x10b8, 0x485c, 0x642e, 0x3217, 0x590b, 0x6c85, 0x3642, + 0x1b21, 0x0d90, 0x46c8, 0x6364, 0x71b2, 0x38d9, 0x1c6c, 0x4e36, 0x271b, 0x538d, + 0x29c6, 0x14e3, 0x4a71, 0x2538, 0x529c, 0x694e, 0x34a7, 0x5a53, 0x6d29, 0x3694, + 0x5b4a, 0x2da5, 0x16d2, 0x0b69, 0x05b4, 0x42da, 0x216d, 0x10b6, 0x085b, 0x442d, + 0x2216, 0x110b, 0x4885, 0x2442, 0x1221, 0x0910, 0x4488, 0x6244, 0x7122, 0x3891, + 0x1c48, 0x4e24, 0x6712, 0x3389, 0x19c4, 0x4ce2, 0x2671, 0x1338, 0x499c, 0x64ce, + 0x3267, 0x5933, 0x6c99, 0x364c, 0x5b26, 0x2d93, 0x56c9, 0x2b64, 0x55b2, 0x2ad9, + 0x156c, 0x4ab6, 0x255b, 0x52ad, 0x2956, 0x14ab, 0x4a55, 0x252a, 0x1295, 0x094a, + 0x04a5, 0x0252, 0x0129, 0x0094, 0x404a, 0x2025, 0x1012, 0x0809, 0x0404, 0x4202, + 0x2101, 0x1080, 0x4840, 0x6420, 0x7210, 0x7908, 0x7c84, 0x7e42, 0x3f21, 0x1f90, + 0x4fc8, 0x67e4, 0x73f2, 0x39f9, 0x1cfc, 0x4e7e, 0x273f, 0x539f, 0x69cf, 0x74e7, + 0x7a73, 0x7d39, 0x3e9c, 0x5f4e, 0x2fa7, 0x57d3, 0x6be9, 0x35f4, 0x5afa, 0x2d7d, + 0x16be, 0x0b5f, 0x45af, 0x62d7, 0x716b, 0x78b5, 0x3c5a, 0x1e2d, 0x0f16, 0x078b, + 0x43c5, 0x21e2, 0x10f1, 0x0878, 0x443c, 0x621e, 0x310f, 0x5887, 0x6c43, 0x7621, + 0x3b10, 0x5d88, 0x6ec4, 0x7762, 0x3bb1, 0x1dd8, 0x4eec, 0x6776, 0x33bb, 0x59dd, + 0x2cee, 0x1677, 0x4b3b, 0x659d, 0x32ce, 0x1967, 0x4cb3, 0x6659, 0x332c, 0x5996, + 0x2ccb, 0x5665, 0x2b32, 0x1599, 0x0acc, 0x4566, 0x22b3, 0x5159, 0x28ac, 0x5456, + 0x2a2b, 0x5515, 0x2a8a, 0x1545, 0x0aa2, 0x0551, 0x02a8, 0x4154, 0x60aa, 0x3055, + 0x182a, 0x0c15, 0x060a, 0x0305, 0x0182, 0x00c1, 0x0060, 0x4030, 0x6018, 0x700c, + 0x7806, 0x3c03, 0x5e01, 0x2f00, 0x5780, 0x6bc0, 0x75e0, 0x7af0, 0x7d78, 0x7ebc, + 0x7f5e, 0x3faf, 0x5fd7, 0x6feb, 0x77f5, 0x3bfa, 0x1dfd, 0x0efe, 0x077f, 0x43bf, + 0x61df, 0x70ef, 0x7877, 0x7c3b, 0x7e1d, 0x3f0e, 0x1f87, 0x4fc3, 0x67e1, 0x33f0, + 0x59f8, 0x6cfc, 0x767e, 0x3b3f, 0x5d9f, 0x6ecf, 0x7767, 0x7bb3, 0x7dd9, 0x3eec, + 0x5f76, 0x2fbb, 0x57dd, 0x2bee, 0x15f7, 0x4afb, 0x657d, 0x32be, 0x195f, 0x4caf, + 0x6657, 0x732b, 0x7995, 0x3cca, 0x1e65, 0x0f32, 0x0799, 0x03cc, 0x41e6, 0x20f3, + 0x5079, 0x283c, 0x541e, 0x2a0f, 0x5507, 0x6a83, 0x7541, 0x3aa0, 0x5d50, 0x6ea8, + 0x7754, 0x7baa, 0x3dd5, 0x1eea, 0x0f75, 0x07ba, 0x03dd, 0x01ee, 0x00f7, 0x407b, + 0x603d, 0x301e, 0x180f, 0x4c07, 0x6603, 0x7301, 0x3980, 0x5cc0, 0x6e60, 0x7730, + 0x7b98, 0x7dcc, 0x7ee6, 0x3f73, 0x5fb9, 0x2fdc, 0x57ee, 0x2bf7, 0x55fb, 0x6afd, + 0x357e, 0x1abf, 0x4d5f, 0x66af, 0x7357, 0x79ab, 0x7cd5, 0x3e6a, 0x1f35, 0x0f9a, + 0x07cd, 0x03e6, 0x01f3, 0x40f9, 0x207c, 0x503e, 0x281f, 0x540f, 0x6a07, 0x7503, + 0x7a81, 0x3d40, 0x5ea0, 0x6f50, 0x77a8, 0x7bd4, 0x7dea, 0x3ef5, 0x1f7a, 0x0fbd, + 0x07de, 0x03ef, 0x41f7, 0x60fb, 0x707d, 0x383e, 0x1c1f, 0x4e0f, 0x6707, 0x7383, + 0x79c1, 0x3ce0, 0x5e70, 0x6f38, 0x779c, 0x7bce, 0x3de7, 0x5ef3, 0x6f79, 0x37bc, + 0x5bde, 0x2def, 0x56f7, 0x6b7b, 0x75bd, 0x3ade, 0x1d6f, 0x4eb7, 0x675b, 0x73ad, + 0x39d6, 0x1ceb, 0x4e75, 0x273a, 0x139d, 0x09ce, 0x04e7, 0x4273, 0x6139, 0x309c, + 0x584e, 0x2c27, 0x5613, 0x6b09, 0x3584, 0x5ac2, 0x2d61, 0x16b0, 0x4b58, 0x65ac, + 0x72d6, 0x396b, 0x5cb5, 0x2e5a, 0x172d, 0x0b96, 0x05cb, 0x42e5, 0x2172, 0x10b9, + 0x085c, 0x442e, 0x2217, 0x510b, 0x6885, 0x3442, 0x1a21, 0x0d10, 0x4688, 0x6344, + 0x71a2, 0x38d1, 0x1c68, 0x4e34, 0x671a, 0x338d, 0x19c6, 0x0ce3, 0x4671, 0x2338, + 0x519c, 0x68ce, 0x3467, 0x5a33, 0x6d19, 0x368c, 0x5b46, 0x2da3, 0x56d1, 0x2b68, + 0x55b4, 0x6ada, 0x356d, 0x1ab6, 0x0d5b, 0x46ad, 0x2356, 0x11ab, 0x48d5, 0x246a, + 0x1235, 0x091a, 0x048d, 0x0246, 0x0123, 0x4091, 0x2048, 0x5024, 0x6812, 0x3409, + 0x1a04, 0x4d02, 0x2681, 0x1340, 0x49a0, 0x64d0, 0x7268, 0x7934, 0x7c9a, 0x3e4d, + 0x1f26, 0x0f93, 0x47c9, 0x23e4, 0x51f2, 0x28f9, 0x147c, 0x4a3e, 0x251f, 0x528f, + 0x6947, 0x74a3, 0x7a51, 0x3d28, 0x5e94, 0x6f4a, 0x37a5, 0x1bd2, 0x0de9, 0x06f4, + 0x437a, 0x21bd, 0x10de, 0x086f, 0x4437, 0x621b, 0x710d, 0x3886, 0x1c43, 0x4e21, + 0x2710, 0x5388, 0x69c4, 0x74e2, 0x3a71, 0x1d38, 0x4e9c, 0x674e, 0x33a7, 0x59d3, + 0x6ce9, 0x3674, 0x5b3a, 0x2d9d, 0x16ce, 0x0b67, 0x45b3, 0x62d9, 0x316c, 0x58b6, + 0x2c5b, 0x562d, 0x2b16, 0x158b, 0x4ac5, 0x2562, 0x12b1, 0x0958, 0x44ac, 0x6256, + 0x312b, 0x5895, 0x2c4a, 0x1625, 0x0b12, 0x0589, 0x02c4, 0x4162, 0x20b1, 0x1058, + 0x482c, 0x6416, 0x320b, 0x5905, 0x2c82, 0x1641, 0x0b20, 0x4590, 0x62c8, 0x7164, + 0x78b2, 0x3c59, 0x1e2c, 0x4f16, 0x278b, 0x53c5, 0x29e2, 0x14f1, 0x0a78, 0x453c, + 0x629e, 0x314f, 0x58a7, 0x6c53, 0x7629, 0x3b14, 0x5d8a, 0x2ec5, 0x1762, 0x0bb1, + 0x05d8, 0x42ec, 0x6176, 0x30bb, 0x585d, 0x2c2e, 0x1617, 0x4b0b, 0x6585, 0x32c2, + 0x1961, 0x0cb0, 0x4658, 0x632c, 0x7196, 0x38cb, 0x5c65, 0x2e32, 0x1719, 0x0b8c, + 0x45c6, 0x22e3, 0x5171, 0x28b8, 0x545c, 0x6a2e, 0x3517, 0x5a8b, 0x6d45, 0x36a2, + 0x1b51, 0x0da8, 0x46d4, 0x636a, 0x31b5, 0x18da, 0x0c6d, 0x0636, 0x031b, 0x418d, + 0x20c6, 0x1063, 0x4831, 0x2418, 0x520c, 0x6906, 0x3483, 0x5a41, 0x2d20, 0x5690, + 0x6b48, 0x75a4, 0x7ad2, 0x3d69, 0x1eb4, 0x4f5a, 0x27ad, 0x13d6, 0x09eb, 0x44f5, + 0x227a, 0x113d, 0x089e, 0x044f, 0x4227, 0x6113, 0x7089, 0x3844, 0x5c22, 0x2e11, + 0x1708, 0x4b84, 0x65c2, 0x32e1, 0x1970, 0x4cb8, 0x665c, 0x732e, 0x3997, 0x5ccb, + 0x6e65, 0x3732, 0x1b99, 0x0dcc, 0x46e6, 0x2373, 0x51b9, 0x28dc, 0x546e, 0x2a37, + 0x551b, 0x6a8d, 0x3546, 0x1aa3, 0x4d51, 0x26a8, 0x5354, 0x69aa, 0x34d5, 0x1a6a, + 0x0d35, 0x069a, 0x034d, 0x01a6, 0x00d3, 0x4069, 0x2034, 0x501a, 0x280d, 0x1406, + 0x0a03, 0x4501, 0x2280, 0x5140, 0x68a0, 0x7450, 0x7a28, 0x7d14, 0x7e8a, 0x3f45, + 0x1fa2, 0x0fd1, 0x07e8, 0x43f4, 0x61fa, 0x30fd, 0x187e, 0x0c3f, 0x461f, 0x630f, + 0x7187, 0x78c3, 0x7c61, 0x3e30, 0x5f18, 0x6f8c, 0x77c6, 0x3be3, 0x5df1, 0x2ef8, + 0x577c, 0x6bbe, 0x35df, 0x5aef, 0x6d77, 0x76bb, 0x7b5d, 0x3dae, 0x1ed7, 0x4f6b, + 0x67b5, 0x33da, 0x19ed, 0x0cf6, 0x067b, 0x433d, 0x219e, 0x10cf, 0x4867, 0x6433, + 0x7219, 0x390c, 0x5c86, 0x2e43, 0x5721, 0x2b90, 0x55c8, 0x6ae4, 0x7572, 0x3ab9, + 0x1d5c, 0x4eae, 0x2757, 0x53ab, 0x69d5, 0x34ea, 0x1a75, 0x0d3a, 0x069d, 0x034e, + 0x01a7, 0x40d3, 0x6069, 0x3034, 0x581a, 0x2c0d, 0x1606, 0x0b03, 0x4581, 0x22c0, + 0x5160, 0x68b0, 0x7458, 0x7a2c, 0x7d16, 0x3e8b, 0x5f45, 0x2fa2, 0x17d1, 0x0be8, + 0x45f4, 0x62fa, 0x317d, 0x18be, 0x0c5f, 0x462f, 0x6317, 0x718b, 0x78c5, 0x3c62, + 0x1e31, 0x0f18, 0x478c, 0x63c6, 0x31e3, 0x58f1, 0x2c78, 0x563c, 0x6b1e, 0x358f, + 0x5ac7, 0x6d63, 0x76b1, 0x3b58, 0x5dac, 0x6ed6, 0x376b, 0x5bb5, 0x2dda, 0x16ed, + 0x0b76, 0x05bb, 0x42dd, 0x216e, 0x10b7, 0x485b, 0x642d, 0x3216, 0x190b, 0x4c85, + 0x2642, 0x1321, 0x0990, 0x44c8, 0x6264, 0x7132, 0x3899, 0x1c4c, 0x4e26, 0x2713, + 0x5389, 0x29c4, 0x54e2, 0x2a71, 0x1538, 0x4a9c, 0x654e, 0x32a7, 0x5953, 0x6ca9, + 0x3654, 0x5b2a, 0x2d95, 0x16ca, 0x0b65, 0x05b2, 0x02d9, 0x016c, 0x40b6, 0x205b, + 0x502d, 0x2816, 0x140b, 0x4a05, 0x2502, 0x1281, 0x0940, 0x44a0, 0x6250, 0x7128, + 0x7894, 0x7c4a, 0x3e25, 0x1f12, 0x0f89, 0x07c4, 0x43e2, 0x21f1, 0x10f8, 0x487c, + 0x643e, 0x321f, 0x590f, 0x6c87, 0x7643, 0x7b21, 0x3d90, 0x5ec8, 0x6f64, 0x77b2, + 0x3bd9, 0x1dec, 0x4ef6, 0x277b, 0x53bd, 0x29de, 0x14ef, 0x4a77, 0x653b, 0x729d, + 0x394e, 0x1ca7, 0x4e53, 0x6729, 0x3394, 0x59ca, 0x2ce5, 0x1672, 0x0b39, 0x059c, + 0x42ce, 0x2167, 0x50b3, 0x6859, 0x342c, 0x5a16, 0x2d0b, 0x5685, 0x2b42, 0x15a1, + 0x0ad0, 0x4568, 0x62b4, 0x715a, 0x38ad, 0x1c56, 0x0e2b, 0x4715, 0x238a, 0x11c5, + 0x08e2, 0x0471, 0x0238, 0x411c, 0x608e, 0x3047, 0x5823, 0x6c11, 0x3608, 0x5b04, + 0x6d82, 0x36c1, 0x1b60, 0x4db0, 0x66d8, 0x736c, 0x79b6, 0x3cdb, 0x5e6d, 0x2f36, + 0x179b, 0x4bcd, 0x25e6, 0x12f3, 0x4979, 0x24bc, 0x525e, 0x292f, 0x5497, 0x6a4b, + 0x7525, 0x3a92, 0x1d49, 0x0ea4, 0x4752, 0x23a9, 0x11d4, 0x48ea, 0x2475, 0x123a, + 0x091d, 0x048e, 0x0247, 0x4123, 0x6091, 0x3048, 0x5824, 0x6c12, 0x3609, 0x1b04, + 0x4d82, 0x26c1, 0x1360, 0x49b0, 0x64d8, 0x726c, 0x7936, 0x3c9b, 0x5e4d, 0x2f26, + 0x1793, 0x4bc9, 0x25e4, 0x52f2, 0x2979, 0x14bc, 0x4a5e, 0x252f, 0x5297, 0x694b, + 0x74a5, 0x3a52, 0x1d29, 0x0e94, 0x474a, 0x23a5, 0x11d2, 0x08e9, 0x0474, 0x423a, + 0x211d, 0x108e, 0x0847, 0x4423, 0x6211, 0x3108, 0x5884, 0x6c42, 0x3621, 0x1b10, + 0x4d88, 0x66c4, 0x7362, 0x39b1, 0x1cd8, 0x4e6c, 0x6736, 0x339b, 0x59cd, 0x2ce6, + 0x1673, 0x4b39, 0x259c, 0x52ce, 0x2967, 0x54b3, 0x6a59, 0x352c, 0x5a96, 0x2d4b, + 0x56a5, 0x2b52, 0x15a9, 0x0ad4, 0x456a, 0x22b5, 0x115a, 0x08ad, 0x0456, 0x022b, + 0x4115, 0x208a, 0x1045, 0x0822, 0x0411, 0x0208, 0x4104, 0x6082, 0x3041, 0x1820, + 0x4c10, 0x6608, 0x7304, 0x7982, 0x3cc1, 0x1e60, 0x4f30, 0x6798, 0x73cc, 0x79e6, + 0x3cf3, 0x5e79, 0x2f3c, 0x579e, 0x2bcf, 0x55e7, 0x6af3, 0x7579, 0x3abc, 0x5d5e, + 0x2eaf, 0x5757, 0x6bab, 0x75d5, 0x3aea, 0x1d75, 0x0eba, 0x075d, 0x03ae, 0x01d7, + 0x40eb, 0x6075, 0x303a, 0x181d, 0x0c0e, 0x0607, 0x4303, 0x6181, 0x30c0, 0x5860, + 0x6c30, 0x7618, 0x7b0c, 0x7d86, 0x3ec3, 0x5f61, 0x2fb0, 0x57d8, 0x6bec, 0x75f6, + 0x3afb, 0x5d7d, 0x2ebe, 0x175f, 0x4baf, 0x65d7, 0x72eb, 0x7975, 0x3cba, 0x1e5d, + 0x0f2e, 0x0797, 0x43cb, 0x61e5, 0x30f2, 0x1879, 0x0c3c, 0x461e, 0x230f, 0x5187, + 0x68c3, 0x7461, 0x3a30, 0x5d18, 0x6e8c, 0x7746, 0x3ba3, 0x5dd1, 0x2ee8, 0x5774, + 0x6bba, 0x35dd, 0x1aee, 0x0d77, 0x46bb, 0x635d, 0x31ae, 0x18d7, 0x4c6b, 0x6635, + 0x331a, 0x198d, 0x0cc6, 0x0663, 0x4331, 0x2198, 0x50cc, 0x6866, 0x3433, 0x5a19, + 0x2d0c, 0x5686, 0x2b43, 0x55a1, 0x2ad0, 0x5568, 0x6ab4, 0x755a, 0x3aad, 0x1d56, + 0x0eab, 0x4755, 0x23aa, 0x11d5, 0x08ea, 0x0475, 0x023a, 0x011d, 0x008e, 0x0047, + 0x4023, 0x6011, 0x3008, 0x5804, 0x6c02, 0x3601, 0x1b00, 0x4d80, 0x66c0, 0x7360, + 0x79b0, 0x7cd8, 0x7e6c, 0x7f36, 0x3f9b, 0x5fcd, 0x2fe6, 0x17f3, 0x4bf9, 0x25fc, + 0x52fe, 0x297f, 0x54bf, 0x6a5f, 0x752f, 0x7a97, 0x7d4b, 0x7ea5, 0x3f52, 0x1fa9, + 0x0fd4, 0x47ea, 0x23f5, 0x11fa, 0x08fd, 0x047e, 0x023f, 0x411f, 0x608f, 0x7047, + 0x7823, 0x7c11, 0x3e08, 0x5f04, 0x6f82, 0x37c1, 0x1be0, 0x4df0, 0x66f8, 0x737c, + 0x79be, 0x3cdf, 0x5e6f, 0x6f37, 0x779b, 0x7bcd, 0x3de6, 0x1ef3, 0x4f79, 0x27bc, + 0x53de, 0x29ef, 0x54f7, 0x6a7b, 0x753d, 0x3a9e, 0x1d4f, 0x4ea7, 0x6753, 0x73a9, + 0x39d4, 0x5cea, 0x2e75, 0x173a, 0x0b9d, 0x05ce, 0x02e7, 0x4173, 0x60b9, 0x305c, + 0x582e, 0x2c17, 0x560b, 0x6b05, 0x3582, 0x1ac1, 0x0d60, 0x46b0, 0x6358, 0x71ac, + 0x78d6, 0x3c6b, 0x5e35, 0x2f1a, 0x178d, 0x0bc6, 0x05e3, 0x42f1, 0x2178, 0x50bc, + 0x685e, 0x342f, 0x5a17, 0x6d0b, 0x7685, 0x3b42, 0x1da1, 0x0ed0, 0x4768, 0x63b4, + 0x71da, 0x38ed, 0x1c76, 0x0e3b, 0x471d, 0x238e, 0x11c7, 0x48e3, 0x6471, 0x3238, + 0x591c, 0x6c8e, 0x3647, 0x5b23, 0x6d91, 0x36c8, 0x5b64, 0x6db2, 0x36d9, 0x1b6c, + 0x4db6, 0x26db, 0x536d, 0x29b6, 0x14db, 0x4a6d, 0x2536, 0x129b, 0x494d, 0x24a6, + 0x1253, 0x4929, 0x2494, 0x524a, 0x2925, 0x1492, 0x0a49, 0x0524, 0x4292, 0x2149, + 0x10a4, 0x4852, 0x2429, 0x1214, 0x490a, 0x2485, 0x1242, 0x0921, 0x0490, 0x4248, + 0x6124, 0x7092, 0x3849, 0x1c24, 0x4e12, 0x2709, 0x1384, 0x49c2, 0x24e1, 0x1270, + 0x4938, 0x649c, 0x724e, 0x3927, 0x5c93, 0x6e49, 0x3724, 0x5b92, 0x2dc9, 0x16e4, + 0x4b72, 0x25b9, 0x12dc, 0x496e, 0x24b7, 0x525b, 0x692d, 0x3496, 0x1a4b, 0x4d25, + 0x2692, 0x1349, 0x09a4, 0x44d2, 0x2269, 0x1134, 0x489a, 0x244d, 0x1226, 0x0913, + 0x4489, 0x2244, 0x5122, 0x2891, 0x1448, 0x4a24, 0x6512, 0x3289, 0x1944, 0x4ca2, + 0x2651, 0x1328, 0x4994, 0x64ca, 0x3265, 0x1932, 0x0c99, 0x064c, 0x4326, 0x2193, + 0x50c9, 0x2864, 0x5432, 0x2a19, 0x150c, 0x4a86, 0x2543, 0x52a1, 0x2950, 0x54a8, + 0x6a54, 0x752a, 0x3a95, 0x1d4a, 0x0ea5, 0x0752, 0x03a9, 0x01d4, 0x40ea, 0x2075, + 0x103a, 0x081d, 0x040e, 0x0207, 0x4103, 0x6081, 0x3040, 0x5820, 0x6c10, 0x7608, + 0x7b04, 0x7d82, 0x3ec1, 0x1f60, 0x4fb0, 0x67d8, 0x73ec, 0x79f6, 0x3cfb, 0x5e7d, + 0x2f3e, 0x179f, 0x4bcf, 0x65e7, 0x72f3, 0x7979, 0x3cbc, 0x5e5e, 0x2f2f, 0x5797, + 0x6bcb, 0x75e5, 0x3af2, 0x1d79, 0x0ebc, 0x475e, 0x23af, 0x51d7, 0x68eb, 0x7475, + 0x3a3a, 0x1d1d, 0x0e8e, 0x0747, 0x43a3, 0x61d1, 0x30e8, 0x5874, 0x6c3a, 0x361d, + 0x1b0e, 0x0d87, 0x46c3, 0x6361, 0x31b0, 0x58d8, 0x6c6c, 0x7636, 0x3b1b, 0x5d8d, + 0x2ec6, 0x1763, 0x4bb1, 0x25d8, 0x52ec, 0x6976, 0x34bb, 0x5a5d, 0x2d2e, 0x1697, + 0x4b4b, 0x65a5, 0x32d2, 0x1969, 0x0cb4, 0x465a, 0x232d, 0x1196, 0x08cb, 0x4465, + 0x2232, 0x1119, 0x088c, 0x4446, 0x2223, 0x5111, 0x2888, 0x5444, 0x6a22, 0x3511, + 0x1a88, 0x4d44, 0x66a2, 0x3351, 0x19a8, 0x4cd4, 0x666a, 0x3335, 0x199a, 0x0ccd, + 0x0666, 0x0333, 0x4199, 0x20cc, 0x5066, 0x2833, 0x5419, 0x2a0c, 0x5506, 0x2a83, + 0x5541, 0x2aa0, 0x5550, 0x6aa8, 0x7554, 0x7aaa, 0x3d55, 0x1eaa, 0x0f55, 0x07aa, + 0x03d5, 0x01ea, 0x00f5, 0x007a, 0x003d, 0x001e, 0x000f, 0x4007, 0x6003, 0x7001, + 0x3800, 0x5c00, 0x6e00, 0x7700, 0x7b80, 0x7dc0, 0x7ee0, 0x7f70, 0x7fb8, 0x7fdc, + 0x7fee, 0x3ff7, 0x5ffb, 0x6ffd, 0x37fe, 0x1bff, 0x4dff, 0x66ff, 0x737f, 0x79bf, + 0x7cdf, 0x7e6f, 0x7f37, 0x7f9b, 0x7fcd, 0x3fe6, 0x1ff3, 0x4ff9, 0x27fc, 0x53fe, + 0x29ff, 0x54ff, 0x6a7f, 0x753f, 0x7a9f, 0x7d4f, 0x7ea7, 0x7f53, 0x7fa9, 0x3fd4, + 0x5fea, 0x2ff5, 0x17fa, 0x0bfd, 0x05fe, 0x02ff, 0x417f, 0x60bf, 0x705f, 0x782f, + 0x7c17, 0x7e0b, 0x7f05, 0x3f82, 0x1fc1, 0x0fe0, 0x47f0, 0x63f8, 0x71fc, 0x78fe, + 0x3c7f, 0x5e3f, 0x6f1f, 0x778f, 0x7bc7, 0x7de3, 0x7ef1, 0x3f78, 0x5fbc, 0x6fde, + 0x37ef, 0x5bf7, 0x6dfb, 0x76fd, 0x3b7e, 0x1dbf, 0x4edf, 0x676f, 0x73b7, 0x79db, + 0x7ced, 0x3e76, 0x1f3b, 0x4f9d, 0x27ce, 0x13e7, 0x49f3, 0x64f9, 0x327c, 0x593e, + 0x2c9f, 0x564f, 0x6b27, 0x7593, 0x7ac9, 0x3d64, 0x5eb2, 0x2f59, 0x17ac, 0x4bd6, + 0x25eb, 0x52f5, 0x297a, 0x14bd, 0x0a5e, 0x052f, 0x4297, 0x614b, 0x70a5, 0x3852, + 0x1c29, 0x0e14, 0x470a, 0x2385, 0x11c2, 0x08e1, 0x0470, 0x4238, 0x611c, 0x708e, + 0x3847, 0x5c23, 0x6e11, 0x3708, 0x5b84, 0x6dc2, 0x36e1, 0x1b70, 0x4db8, 0x66dc, + 0x736e, 0x39b7, 0x5cdb, 0x6e6d, 0x3736, 0x1b9b, 0x4dcd, 0x26e6, 0x1373, 0x49b9, + 0x24dc, 0x526e, 0x2937, 0x549b, 0x6a4d, 0x3526, 0x1a93, 0x4d49, 0x26a4, 0x5352, + 0x29a9, 0x14d4, 0x4a6a, 0x2535, 0x129a, 0x094d, 0x04a6, 0x0253, 0x4129, 0x2094, + 0x504a, 0x2825, 0x1412, 0x0a09, 0x0504, 0x4282, 0x2141, 0x10a0, 0x4850, 0x6428, + 0x7214, 0x790a, 0x3c85, 0x1e42, 0x0f21, 0x0790, 0x43c8, 0x61e4, 0x70f2, 0x3879, + 0x1c3c, 0x4e1e, 0x270f, 0x5387, 0x69c3, 0x74e1, 0x3a70, 0x5d38, 0x6e9c, 0x774e, + 0x3ba7, 0x5dd3, 0x6ee9, 0x3774, 0x5bba, 0x2ddd, 0x16ee, 0x0b77, 0x45bb, 0x62dd, + 0x316e, 0x18b7, 0x4c5b, 0x662d, 0x3316, 0x198b, 0x4cc5, 0x2662, 0x1331, 0x0998, + 0x44cc, 0x6266, 0x3133, 0x5899, 0x2c4c, 0x5626, 0x2b13, 0x5589, 0x2ac4, 0x5562, + 0x2ab1, 0x1558, 0x4aac, 0x6556, 0x32ab, 0x5955, 0x2caa, 0x1655, 0x0b2a, 0x0595, + 0x02ca, 0x0165, 0x00b2, 0x0059, 0x002c, 0x4016, 0x200b, 0x5005, 0x2802, 0x1401, + 0x0a00, 0x4500, 0x6280, 0x7140, 0x78a0, 0x7c50, 0x7e28, 0x7f14, 0x7f8a, 0x3fc5, + 0x1fe2, 0x0ff1, 0x07f8, 0x43fc, 0x61fe, 0x30ff, 0x587f, 0x6c3f, 0x761f, 0x7b0f, + 0x7d87, 0x7ec3, 0x7f61, 0x3fb0, 0x5fd8, 0x6fec, 0x77f6, 0x3bfb, 0x5dfd, 0x2efe, + 0x177f, 0x4bbf, 0x65df, 0x72ef, 0x7977, 0x7cbb, 0x7e5d, 0x3f2e, 0x1f97, 0x4fcb, + 0x67e5, 0x33f2, 0x19f9, 0x0cfc, 0x467e, 0x233f, 0x519f, 0x68cf, 0x7467, 0x7a33, + 0x7d19, 0x3e8c, 0x5f46, 0x2fa3, 0x57d1, 0x2be8, 0x55f4, 0x6afa, 0x357d, 0x1abe, + 0x0d5f, 0x46af, 0x6357, 0x71ab, 0x78d5, 0x3c6a, 0x1e35, 0x0f1a, 0x078d, 0x03c6, + 0x01e3, 0x40f1, 0x2078, 0x503c, 0x681e, 0x340f, 0x5a07, 0x6d03, 0x7681, 0x3b40, + 0x5da0, 0x6ed0, 0x7768, 0x7bb4, 0x7dda, 0x3eed, 0x1f76, 0x0fbb, 0x47dd, 0x23ee, + 0x11f7, 0x48fb, 0x647d, 0x323e, 0x191f, 0x4c8f, 0x6647, 0x7323, 0x7991, 0x3cc8, + 0x5e64, 0x6f32, 0x3799, 0x1bcc, 0x4de6, 0x26f3, 0x5379, 0x29bc, 0x54de, 0x2a6f, + 0x5537, 0x6a9b, 0x754d, 0x3aa6, 0x1d53, 0x4ea9, 0x2754, 0x53aa, 0x29d5, 0x14ea, + 0x0a75, 0x053a, 0x029d, 0x014e, 0x00a7, 0x4053, 0x6029, 0x3014, 0x580a, 0x2c05, + 0x1602, 0x0b01, 0x0580, 0x42c0, 0x6160, 0x70b0, 0x7858, 0x7c2c, 0x7e16, 0x3f0b, + 0x5f85, 0x2fc2, 0x17e1, 0x0bf0, 0x45f8, 0x62fc, 0x717e, 0x38bf, 0x5c5f, 0x6e2f, + 0x7717, 0x7b8b, 0x7dc5, 0x3ee2, 0x1f71, 0x0fb8, 0x47dc, 0x63ee, 0x31f7, 0x58fb, + 0x6c7d, 0x363e, 0x1b1f, 0x4d8f, 0x66c7, 0x7363, 0x79b1, 0x3cd8, 0x5e6c, 0x6f36, + 0x379b, 0x5bcd, 0x2de6, 0x16f3, 0x4b79, 0x25bc, 0x52de, 0x296f, 0x54b7, 0x6a5b, + 0x752d, 0x3a96, 0x1d4b, 0x4ea5, 0x2752, 0x13a9, 0x09d4, 0x44ea, 0x2275, 0x113a, + 0x089d, 0x044e, 0x0227, 0x4113, 0x6089, 0x3044, 0x5822, 0x2c11, 0x1608, 0x4b04, + 0x6582, 0x32c1, 0x1960, 0x4cb0, 0x6658, 0x732c, 0x7996, 0x3ccb, 0x5e65, 0x2f32, + 0x1799, 0x0bcc, 0x45e6, 0x22f3, 0x5179, 0x28bc, 0x545e, 0x2a2f, 0x5517, 0x6a8b, + 0x7545, 0x3aa2, 0x1d51, 0x0ea8, 0x4754, 0x63aa, 0x31d5, 0x18ea, 0x0c75, 0x063a, + 0x031d, 0x018e, 0x00c7, 0x4063, 0x6031, 0x3018, 0x580c, 0x6c06, 0x3603, 0x5b01, + 0x2d80, 0x56c0, 0x6b60, 0x75b0, 0x7ad8, 0x7d6c, 0x7eb6, 0x3f5b, 0x5fad, 0x2fd6, + 0x17eb, 0x4bf5, 0x25fa, 0x12fd, 0x097e, 0x04bf, 0x425f, 0x612f, 0x7097, 0x784b, + 0x7c25, 0x3e12, 0x1f09, 0x0f84, 0x47c2, 0x23e1, 0x11f0, 0x48f8, 0x647c, 0x723e, + 0x391f, 0x5c8f, 0x6e47, 0x7723, 0x7b91, 0x3dc8, 0x5ee4, 0x6f72, 0x37b9, 0x1bdc, + 0x4dee, 0x26f7, 0x537b, 0x69bd, 0x34de, 0x1a6f, 0x4d37, 0x669b, 0x734d, 0x39a6, + 0x1cd3, 0x4e69, 0x2734, 0x539a, 0x29cd, 0x14e6, 0x0a73, 0x4539, 0x229c, 0x514e, + 0x28a7, 0x5453, 0x6a29, 0x3514, 0x5a8a, 0x2d45, 0x16a2, 0x0b51, 0x05a8, 0x42d4, + 0x616a, 0x30b5, 0x185a, 0x0c2d, 0x0616, 0x030b, 0x4185, 0x20c2, 0x1061, 0x0830, + 0x4418, 0x620c, 0x7106, 0x3883, 0x5c41, 0x2e20, 0x5710, 0x6b88, 0x75c4, 0x7ae2, + 0x3d71, 0x1eb8, 0x4f5c, 0x67ae, 0x33d7, 0x59eb, 0x6cf5, 0x367a, 0x1b3d, 0x0d9e, + 0x06cf, 0x4367, 0x61b3, 0x70d9, 0x386c, 0x5c36, 0x2e1b, 0x570d, 0x2b86, 0x15c3, + 0x4ae1, 0x2570, 0x52b8, 0x695c, 0x74ae, 0x3a57, 0x5d2b, 0x6e95, 0x374a, 0x1ba5, + 0x0dd2, 0x06e9, 0x0374, 0x41ba, 0x20dd, 0x106e, 0x0837, 0x441b, 0x620d, 0x3106, + 0x1883, 0x4c41, 0x2620, 0x5310, 0x6988, 0x74c4, 0x7a62, 0x3d31, 0x1e98, 0x4f4c, + 0x67a6, 0x33d3, 0x59e9, 0x2cf4, 0x567a, 0x2b3d, 0x159e, 0x0acf, 0x4567, 0x62b3, + 0x7159, 0x38ac, 0x5c56, 0x2e2b, 0x5715, 0x2b8a, 0x15c5, 0x0ae2, 0x0571, 0x02b8, + 0x415c, 0x60ae, 0x3057, 0x582b, 0x6c15, 0x360a, 0x1b05, 0x0d82, 0x06c1, 0x0360, + 0x41b0, 0x60d8, 0x706c, 0x7836, 0x3c1b, 0x5e0d, 0x2f06, 0x1783, 0x4bc1, 0x25e0, + 0x52f0, 0x6978, 0x74bc, 0x7a5e, 0x3d2f, 0x5e97, 0x6f4b, 0x77a5, 0x3bd2, 0x1de9, + 0x0ef4, 0x477a, 0x23bd, 0x11de, 0x08ef, 0x4477, 0x623b, 0x711d, 0x388e, 0x1c47, + 0x4e23, 0x6711, 0x3388, 0x59c4, 0x6ce2, 0x3671, 0x1b38, 0x4d9c, 0x66ce, 0x3367, + 0x59b3, 0x6cd9, 0x366c, 0x5b36, 0x2d9b, 0x56cd, 0x2b66, 0x15b3, 0x4ad9, 0x256c, + 0x52b6, 0x295b, 0x54ad, 0x2a56, 0x152b, 0x4a95, 0x254a, 0x12a5, 0x0952, 0x04a9, + 0x0254, 0x412a, 0x2095, 0x104a, 0x0825, 0x0412, 0x0209, 0x0104, 0x4082, 0x2041, + 0x1020, 0x4810, 0x6408, 0x7204, 0x7902, 0x3c81, 0x1e40, 0x4f20, 0x6790, 0x73c8, + 0x79e4, 0x7cf2, 0x3e79, 0x1f3c, 0x4f9e, 0x27cf, 0x53e7, 0x69f3, 0x74f9, 0x3a7c, + 0x5d3e, 0x2e9f, 0x574f, 0x6ba7, 0x75d3, 0x7ae9, 0x3d74, 0x5eba, 0x2f5d, 0x17ae, + 0x0bd7, 0x45eb, 0x62f5, 0x317a, 0x18bd, 0x0c5e, 0x062f, 0x4317, 0x618b, 0x70c5, + 0x3862, 0x1c31, 0x0e18, 0x470c, 0x6386, 0x31c3, 0x58e1, 0x2c70, 0x5638, 0x6b1c, + 0x758e, 0x3ac7, 0x5d63, 0x6eb1, 0x3758, 0x5bac, 0x6dd6, 0x36eb, 0x5b75, 0x2dba, + 0x16dd, 0x0b6e, 0x05b7, 0x42db, 0x616d, 0x30b6, 0x185b, 0x4c2d, 0x2616, 0x130b, + 0x4985, 0x24c2, 0x1261, 0x0930, 0x4498, 0x624c, 0x7126, 0x3893, 0x5c49, 0x2e24, + 0x5712, 0x2b89, 0x15c4, 0x4ae2, 0x2571, 0x12b8, 0x495c, 0x64ae, 0x3257, 0x592b, + 0x6c95, 0x364a, 0x1b25, 0x0d92, 0x06c9, 0x0364, 0x41b2, 0x20d9, 0x106c, 0x4836, + 0x241b, 0x520d, 0x2906, 0x1483, 0x4a41, 0x2520, 0x5290, 0x6948, 0x74a4, 0x7a52, + 0x3d29, 0x1e94, 0x4f4a, 0x27a5, 0x13d2, 0x09e9, 0x04f4, 0x427a, 0x213d, 0x109e, + 0x084f, 0x4427, 0x6213, 0x7109, 0x3884, 0x5c42, 0x2e21, 0x1710, 0x4b88, 0x65c4, + 0x72e2, 0x3971, 0x1cb8, 0x4e5c, 0x672e, 0x3397, 0x59cb, 0x6ce5, 0x3672, 0x1b39, + 0x0d9c, 0x46ce, 0x2367, 0x51b3, 0x68d9, 0x346c, 0x5a36, 0x2d1b, 0x568d, 0x2b46, + 0x15a3, 0x4ad1, 0x2568, 0x52b4, 0x695a, 0x34ad, 0x1a56, 0x0d2b, 0x4695, 0x234a, + 0x11a5, 0x08d2, 0x0469, 0x0234, 0x411a, 0x208d, 0x1046, 0x0823, 0x4411, 0x2208, + 0x5104, 0x6882, 0x3441, 0x1a20, 0x4d10, 0x6688, 0x7344, 0x79a2, 0x3cd1, 0x1e68, + 0x4f34, 0x679a, 0x33cd, 0x19e6, 0x0cf3, 0x4679, 0x233c, 0x519e, 0x28cf, 0x5467, + 0x6a33, 0x7519, 0x3a8c, 0x5d46, 0x2ea3, 0x5751, 0x2ba8, 0x55d4, 0x6aea, 0x3575, + 0x1aba, 0x0d5d, 0x06ae, 0x0357, 0x41ab, 0x60d5, 0x306a, 0x1835, 0x0c1a, 0x060d, + 0x0306, 0x0183, 0x40c1, 0x2060, 0x5030, 0x6818, 0x740c, 0x7a06, 0x3d03, 0x5e81, + 0x2f40, 0x57a0, 0x6bd0, 0x75e8, 0x7af4, 0x7d7a, 0x3ebd, 0x1f5e, 0x0faf, 0x47d7, + 0x63eb, 0x71f5, 0x38fa, 0x1c7d, 0x0e3e, 0x071f, 0x438f, 0x61c7, 0x70e3, 0x7871, + 0x3c38, 0x5e1c, 0x6f0e, 0x3787, 0x5bc3, 0x6de1, 0x36f0, 0x5b78, 0x6dbc, 0x76de, + 0x3b6f, 0x5db7, 0x6edb, 0x776d, 0x3bb6, 0x1ddb, 0x4eed, 0x2776, 0x13bb, 0x49dd, + 0x24ee, 0x1277, 0x493b, 0x649d, 0x324e, 0x1927, 0x4c93, 0x6649, 0x3324, 0x5992, + 0x2cc9, 0x1664, 0x4b32, 0x2599, 0x12cc, 0x4966, 0x24b3, 0x5259, 0x292c, 0x5496, + 0x2a4b, 0x5525, 0x2a92, 0x1549, 0x0aa4, 0x4552, 0x22a9, 0x1154, 0x48aa, 0x2455, + 0x122a, 0x0915, 0x048a, 0x0245, 0x0122, 0x0091, 0x0048, 0x4024, 0x6012, 0x3009, + 0x1804, 0x4c02, 0x2601, 0x1300, 0x4980, 0x64c0, 0x7260, 0x7930, 0x7c98, 0x7e4c, + 0x7f26, 0x3f93, 0x5fc9, 0x2fe4, 0x57f2, 0x2bf9, 0x15fc, 0x4afe, 0x257f, 0x52bf, + 0x695f, 0x74af, 0x7a57, 0x7d2b, 0x7e95, 0x3f4a, 0x1fa5, 0x0fd2, 0x07e9, 0x03f4, + 0x41fa, 0x20fd, 0x107e, 0x083f, 0x441f, 0x620f, 0x7107, 0x7883, 0x7c41, 0x3e20, + 0x5f10, 0x6f88, 0x77c4, 0x7be2, 0x3df1, 0x1ef8, 0x4f7c, 0x67be, 0x33df, 0x59ef, + 0x6cf7, 0x767b, 0x7b3d, 0x3d9e, 0x1ecf, 0x4f67, 0x67b3, 0x73d9, 0x39ec, 0x5cf6, + 0x2e7b, 0x573d, 0x2b9e, 0x15cf, 0x4ae7, 0x6573, 0x72b9, 0x395c, 0x5cae, 0x2e57, + 0x572b, 0x6b95, 0x35ca, 0x1ae5, 0x0d72, 0x06b9, 0x035c, 0x41ae, 0x20d7, 0x506b, + 0x6835, 0x341a, 0x1a0d, 0x0d06, 0x0683, 0x4341, 0x21a0, 0x50d0, 0x6868, 0x7434, + 0x7a1a, 0x3d0d, 0x1e86, 0x0f43, 0x47a1, 0x23d0, 0x51e8, 0x68f4, 0x747a, 0x3a3d, + 0x1d1e, 0x0e8f, 0x4747, 0x63a3, 0x71d1, 0x38e8, 0x5c74, 0x6e3a, 0x371d, 0x1b8e, + 0x0dc7, 0x46e3, 0x6371, 0x31b8, 0x58dc, 0x6c6e, 0x3637, 0x5b1b, 0x6d8d, 0x36c6, + 0x1b63, 0x4db1, 0x26d8, 0x536c, 0x69b6, 0x34db, 0x5a6d, 0x2d36, 0x169b, 0x4b4d, + 0x25a6, 0x12d3, 0x4969, 0x24b4, 0x525a, 0x292d, 0x1496, 0x0a4b, 0x4525, 0x2292, + 0x1149, 0x08a4, 0x4452, 0x2229, 0x1114, 0x488a, 0x2445, 0x1222, 0x0911, 0x0488, + 0x4244, 0x6122, 0x3091, 0x1848, 0x4c24, 0x6612, 0x3309, 0x1984, 0x4cc2, 0x2661, + 0x1330, 0x4998, 0x64cc, 0x7266, 0x3933, 0x5c99, 0x2e4c, 0x5726, 0x2b93, 0x55c9, + 0x2ae4, 0x5572, 0x2ab9, 0x155c, 0x4aae, 0x2557, 0x52ab, 0x6955, 0x34aa, 0x1a55, + 0x0d2a, 0x0695, 0x034a, 0x01a5, 0x00d2, 0x0069, 0x0034, 0x401a, 0x200d, 0x1006, + 0x0803, 0x4401, 0x2200, 0x5100, 0x6880, 0x7440, 0x7a20, 0x7d10, 0x7e88, 0x7f44, + 0x7fa2, 0x3fd1, 0x1fe8, 0x4ff4, 0x67fa, 0x33fd, 0x19fe, 0x0cff, 0x467f, 0x633f, + 0x719f, 0x78cf, 0x7c67, 0x7e33, 0x7f19, 0x3f8c, 0x5fc6, 0x2fe3, 0x57f1, 0x2bf8, + 0x55fc, 0x6afe, 0x357f, 0x5abf, 0x6d5f, 0x76af, 0x7b57, 0x7dab, 0x7ed5, 0x3f6a, + 0x1fb5, 0x0fda, 0x07ed, 0x03f6, 0x01fb, 0x40fd, 0x207e, 0x103f, 0x481f, 0x640f, + 0x7207, 0x7903, 0x7c81, 0x3e40, 0x5f20, 0x6f90, 0x77c8, 0x7be4, 0x7df2, 0x3ef9, + 0x1f7c, 0x4fbe, 0x27df, 0x53ef, 0x69f7, 0x74fb, 0x7a7d, 0x3d3e, 0x1e9f, 0x4f4f, + 0x67a7, 0x73d3, 0x79e9, 0x3cf4, 0x5e7a, 0x2f3d, 0x179e, 0x0bcf, 0x45e7, 0x62f3, + 0x7179, 0x38bc, 0x5c5e, 0x2e2f, 0x5717, 0x6b8b, 0x75c5, 0x3ae2, 0x1d71, 0x0eb8, + 0x475c, 0x63ae, 0x31d7, 0x58eb, 0x6c75, 0x363a, 0x1b1d, 0x0d8e, 0x06c7, 0x4363, + 0x61b1, 0x30d8, 0x586c, 0x6c36, 0x361b, 0x5b0d, 0x2d86, 0x16c3, 0x4b61, 0x25b0, + 0x52d8, 0x696c, 0x74b6, 0x3a5b, 0x5d2d, 0x2e96, 0x174b, 0x4ba5, 0x25d2, 0x12e9, + 0x0974, 0x44ba, 0x225d, 0x112e, 0x0897, 0x444b, 0x6225, 0x3112, 0x1889, 0x0c44, + 0x4622, 0x2311, 0x1188, 0x48c4, 0x6462, 0x3231, 0x1918, 0x4c8c, 0x6646, 0x3323, + 0x5991, 0x2cc8, 0x5664, 0x6b32, 0x3599, 0x1acc, 0x4d66, 0x26b3, 0x5359, 0x29ac, + 0x54d6, 0x2a6b, 0x5535, 0x2a9a, 0x154d, 0x0aa6, 0x0553, 0x42a9, 0x2154, 0x50aa, + 0x2855, 0x142a, 0x0a15, 0x050a, 0x0285, 0x0142, 0x00a1, 0x0050, 0x4028, 0x6014, + 0x700a, 0x3805, 0x1c02, 0x0e01, 0x0700, 0x4380, 0x61c0, 0x70e0, 0x7870, 0x7c38, + 0x7e1c, 0x7f0e, 0x3f87, 0x5fc3, 0x6fe1, 0x37f0, 0x5bf8, 0x6dfc, 0x76fe, 0x3b7f, + 0x5dbf, 0x6edf, 0x776f, 0x7bb7, 0x7ddb, 0x7eed, 0x3f76, 0x1fbb, 0x4fdd, 0x27ee, + 0x13f7, 0x49fb, 0x64fd, 0x327e, 0x193f, 0x4c9f, 0x664f, 0x7327, 0x7993, 0x7cc9, + 0x3e64, 0x5f32, 0x2f99, 0x17cc, 0x4be6, 0x25f3, 0x52f9, 0x297c, 0x54be, 0x2a5f, + 0x552f, 0x6a97, 0x754b, 0x7aa5, 0x3d52, 0x1ea9, 0x0f54, 0x47aa, 0x23d5, 0x11ea, + 0x08f5, 0x047a, 0x023d, 0x011e, 0x008f, 0x4047, 0x6023, 0x7011, 0x3808, 0x5c04, + 0x6e02, 0x3701, 0x1b80, 0x4dc0, 0x66e0, 0x7370, 0x79b8, 0x7cdc, 0x7e6e, 0x3f37, + 0x5f9b, 0x6fcd, 0x37e6, 0x1bf3, 0x4df9, 0x26fc, 0x537e, 0x29bf, 0x54df, 0x6a6f, + 0x7537, 0x7a9b, 0x7d4d, 0x3ea6, 0x1f53, 0x4fa9, 0x27d4, 0x53ea, 0x29f5, 0x14fa, + 0x0a7d, 0x053e, 0x029f, 0x414f, 0x60a7, 0x7053, 0x7829, 0x3c14, 0x5e0a, 0x2f05, + 0x1782, 0x0bc1, 0x05e0, 0x42f0, 0x6178, 0x70bc, 0x785e, 0x3c2f, 0x5e17, 0x6f0b, + 0x7785, 0x3bc2, 0x1de1, 0x0ef0, 0x4778, 0x63bc, 0x71de, 0x38ef, 0x5c77, 0x6e3b, + 0x771d, 0x3b8e, 0x1dc7, 0x4ee3, 0x6771, 0x33b8, 0x59dc, 0x6cee, 0x3677, 0x5b3b, + 0x6d9d, 0x36ce, 0x1b67, 0x4db3, 0x66d9, 0x336c, 0x59b6, 0x2cdb, 0x566d, 0x2b36, + 0x159b, 0x4acd, 0x2566, 0x12b3, 0x4959, 0x24ac, 0x5256, 0x292b, 0x5495, 0x2a4a, + 0x1525, 0x0a92, 0x0549, 0x02a4, 0x4152, 0x20a9, 0x1054, 0x482a, 0x2415, 0x120a, + 0x0905, 0x0482, 0x0241, 0x0120, 0x4090, 0x6048, 0x7024, 0x7812, 0x3c09, 0x1e04, + 0x4f02, 0x2781, 0x13c0, 0x49e0, 0x64f0, 0x7278, 0x793c, 0x7c9e, 0x3e4f, 0x5f27, + 0x6f93, 0x77c9, 0x3be4, 0x5df2, 0x2ef9, 0x177c, 0x4bbe, 0x25df, 0x52ef, 0x6977, + 0x74bb, 0x7a5d, 0x3d2e, 0x1e97, 0x4f4b, 0x67a5, 0x33d2, 0x19e9, 0x0cf4, 0x467a, + 0x233d, 0x119e, 0x08cf, 0x4467, 0x6233, 0x7119, 0x388c, 0x5c46, 0x2e23, 0x5711, + 0x2b88, 0x55c4, 0x6ae2, 0x3571, 0x1ab8, 0x4d5c, 0x66ae, 0x3357, 0x59ab, 0x6cd5, + 0x366a, 0x1b35, 0x0d9a, 0x06cd, 0x0366, 0x01b3, 0x40d9, 0x206c, 0x5036, 0x281b, + 0x540d, 0x2a06, 0x1503, 0x4a81, 0x2540, 0x52a0, 0x6950, 0x74a8, 0x7a54, 0x7d2a, + 0x3e95, 0x1f4a, 0x0fa5, 0x07d2, 0x03e9, 0x01f4, 0x40fa, 0x207d, 0x103e, 0x081f, + 0x440f, 0x6207, 0x7103, 0x7881, 0x3c40, 0x5e20, 0x6f10, 0x7788, 0x7bc4, 0x7de2, + 0x3ef1, 0x1f78, 0x4fbc, 0x67de, 0x33ef, 0x59f7, 0x6cfb, 0x767d, 0x3b3e, 0x1d9f, + 0x4ecf, 0x6767, 0x73b3, 0x79d9, 0x3cec, 0x5e76, 0x2f3b, 0x579d, 0x2bce, 0x15e7, + 0x4af3, 0x6579, 0x32bc, 0x595e, 0x2caf, 0x5657, 0x6b2b, 0x7595, 0x3aca, 0x1d65, + 0x0eb2, 0x0759, 0x03ac, 0x41d6, 0x20eb, 0x5075, 0x283a, 0x141d, 0x0a0e, 0x0507, + 0x4283, 0x6141, 0x30a0, 0x5850, 0x6c28, 0x7614, 0x7b0a, 0x3d85, 0x1ec2, 0x0f61, + 0x07b0, 0x43d8, 0x61ec, 0x70f6, 0x387b, 0x5c3d, 0x2e1e, 0x170f, 0x4b87, 0x65c3, + 0x72e1, 0x3970, 0x5cb8, 0x6e5c, 0x772e, 0x3b97, 0x5dcb, 0x6ee5, 0x3772, 0x1bb9, + 0x0ddc, 0x46ee, 0x2377, 0x51bb, 0x68dd, 0x346e, 0x1a37, 0x4d1b, 0x668d, 0x3346, + 0x19a3, 0x4cd1, 0x2668, 0x5334, 0x699a, 0x34cd, 0x1a66, 0x0d33, 0x4699, 0x234c, + 0x51a6, 0x28d3, 0x5469, 0x2a34, 0x551a, 0x2a8d, 0x1546, 0x0aa3, 0x4551, 0x22a8, + 0x5154, 0x68aa, 0x3455, 0x1a2a, 0x0d15, 0x068a, 0x0345, 0x01a2, 0x00d1, 0x0068, + 0x4034, 0x601a, 0x300d, 0x1806, 0x0c03, 0x4601, 0x2300, 0x5180, 0x68c0, 0x7460, + 0x7a30, 0x7d18, 0x7e8c, 0x7f46, 0x3fa3, 0x5fd1, 0x2fe8, 0x57f4, 0x6bfa, 0x35fd, + 0x1afe, 0x0d7f, 0x46bf, 0x635f, 0x71af, 0x78d7, 0x7c6b, 0x7e35, 0x3f1a, 0x1f8d, + 0x0fc6, 0x07e3, 0x43f1, 0x21f8, 0x50fc, 0x687e, 0x343f, 0x5a1f, 0x6d0f, 0x7687, + 0x7b43, 0x7da1, 0x3ed0, 0x5f68, 0x6fb4, 0x77da, 0x3bed, 0x1df6, 0x0efb, 0x477d, + 0x23be, 0x11df, 0x48ef, 0x6477, 0x723b, 0x791d, 0x3c8e, 0x1e47, 0x4f23, 0x6791, + 0x33c8, 0x59e4, 0x6cf2, 0x3679, 0x1b3c, 0x4d9e, 0x26cf, 0x5367, 0x69b3, 0x74d9, + 0x3a6c, 0x5d36, 0x2e9b, 0x574d, 0x2ba6, 0x15d3, 0x4ae9, 0x2574, 0x52ba, 0x295d, + 0x14ae, 0x0a57, 0x452b, 0x6295, 0x314a, 0x18a5, 0x0c52, 0x0629, 0x0314, 0x418a, + 0x20c5, 0x1062, 0x0831, 0x0418, 0x420c, 0x6106, 0x3083, 0x5841, 0x2c20, 0x5610, + 0x6b08, 0x7584, 0x7ac2, 0x3d61, 0x1eb0, 0x4f58, 0x67ac, 0x73d6, 0x39eb, 0x5cf5, + 0x2e7a, 0x173d, 0x0b9e, 0x05cf, 0x42e7, 0x6173, 0x70b9, 0x385c, 0x5c2e, 0x2e17, + 0x570b, 0x6b85, 0x35c2, 0x1ae1, 0x0d70, 0x46b8, 0x635c, 0x71ae, 0x38d7, 0x5c6b, + 0x6e35, 0x371a, 0x1b8d, 0x0dc6, 0x06e3, 0x4371, 0x21b8, 0x50dc, 0x686e, 0x3437, + 0x5a1b, 0x6d0d, 0x3686, 0x1b43, 0x4da1, 0x26d0, 0x5368, 0x69b4, 0x74da, 0x3a6d, + 0x1d36, 0x0e9b, 0x474d, 0x23a6, 0x11d3, 0x48e9, 0x2474, 0x523a, 0x291d, 0x148e, + 0x0a47, 0x4523, 0x6291, 0x3148, 0x58a4, 0x6c52, 0x3629, 0x1b14, 0x4d8a, 0x26c5, + 0x1362, 0x09b1, 0x04d8, 0x426c, 0x6136, 0x309b, 0x584d, 0x2c26, 0x1613, 0x4b09, + 0x2584, 0x52c2, 0x2961, 0x14b0, 0x4a58, 0x652c, 0x7296, 0x394b, 0x5ca5, 0x2e52, + 0x1729, 0x0b94, 0x45ca, 0x22e5, 0x1172, 0x08b9, 0x045c, 0x422e, 0x2117, 0x508b, + 0x6845, 0x3422, 0x1a11, 0x0d08, 0x4684, 0x6342, 0x31a1, 0x18d0, 0x4c68, 0x6634, + 0x731a, 0x398d, 0x1cc6, 0x0e63, 0x4731, 0x2398, 0x51cc, 0x68e6, 0x3473, 0x5a39, + 0x2d1c, 0x568e, 0x2b47, 0x55a3, 0x6ad1, 0x3568, 0x5ab4, 0x6d5a, 0x36ad, 0x1b56, + 0x0dab, 0x46d5, 0x236a, 0x11b5, 0x08da, 0x046d, 0x0236, 0x011b, 0x408d, 0x2046, + 0x1023, 0x4811, 0x2408, 0x5204, 0x6902, 0x3481, 0x1a40, 0x4d20, 0x6690, 0x7348, + 0x79a4, 0x7cd2, 0x3e69, 0x1f34, 0x4f9a, 0x27cd, 0x13e6, 0x09f3, 0x44f9, 0x227c, + 0x513e, 0x289f, 0x544f, 0x6a27, 0x7513, 0x7a89, 0x3d44, 0x5ea2, 0x2f51, 0x17a8, + 0x4bd4, 0x65ea, 0x32f5, 0x197a, 0x0cbd, 0x065e, 0x032f, 0x4197, 0x60cb, 0x7065, + 0x3832, 0x1c19, 0x0e0c, 0x4706, 0x2383, 0x51c1, 0x28e0, 0x5470, 0x6a38, 0x751c, + 0x7a8e, 0x3d47, 0x5ea3, 0x6f51, 0x37a8, 0x5bd4, 0x6dea, 0x36f5, 0x1b7a, 0x0dbd, + 0x06de, 0x036f, 0x41b7, 0x60db, 0x706d, 0x3836, 0x1c1b, 0x4e0d, 0x2706, 0x1383, + 0x49c1, 0x24e0, 0x5270, 0x6938, 0x749c, 0x7a4e, 0x3d27, 0x5e93, 0x6f49, 0x37a4, + 0x5bd2, 0x2de9, 0x16f4, 0x4b7a, 0x25bd, 0x12de, 0x096f, 0x44b7, 0x625b, 0x712d, + 0x3896, 0x1c4b, 0x4e25, 0x2712, 0x1389, 0x09c4, 0x44e2, 0x2271, 0x1138, 0x489c, + 0x644e, 0x3227, 0x5913, 0x6c89, 0x3644, 0x5b22, 0x2d91, 0x16c8, 0x4b64, 0x65b2, + 0x32d9, 0x196c, 0x4cb6, 0x265b, 0x532d, 0x2996, 0x14cb, 0x4a65, 0x2532, 0x1299, + 0x094c, 0x44a6, 0x2253, 0x5129, 0x2894, 0x544a, 0x2a25, 0x1512, 0x0a89, 0x0544, + 0x42a2, 0x2151, 0x10a8, 0x4854, 0x642a, 0x3215, 0x190a, 0x0c85, 0x0642, 0x0321, + 0x0190, 0x40c8, 0x6064, 0x7032, 0x3819, 0x1c0c, 0x4e06, 0x2703, 0x5381, 0x29c0, + 0x54e0, 0x6a70, 0x7538, 0x7a9c, 0x7d4e, 0x3ea7, 0x5f53, 0x6fa9, 0x37d4, 0x5bea, + 0x2df5, 0x16fa, 0x0b7d, 0x05be, 0x02df, 0x416f, 0x60b7, 0x705b, 0x782d, 0x3c16, + 0x1e0b, 0x4f05, 0x2782, 0x13c1, 0x09e0, 0x44f0, 0x6278, 0x713c, 0x789e, 0x3c4f, + 0x5e27, 0x6f13, 0x7789, 0x3bc4, 0x5de2, 0x2ef1, 0x1778, 0x4bbc, 0x65de, 0x32ef, + 0x5977, 0x6cbb, 0x765d, 0x3b2e, 0x1d97, 0x4ecb, 0x6765, 0x33b2, 0x19d9, 0x0cec, + 0x4676, 0x233b, 0x519d, 0x28ce, 0x1467, 0x4a33, 0x6519, 0x328c, 0x5946, 0x2ca3, + 0x5651, 0x2b28, 0x5594, 0x6aca, 0x3565, 0x1ab2, 0x0d59, 0x06ac, 0x4356, 0x21ab, + 0x50d5, 0x286a, 0x1435, 0x0a1a, 0x050d, 0x0286, 0x0143, 0x40a1, 0x2050, 0x5028, + 0x6814, 0x740a, 0x3a05, 0x1d02, 0x0e81, 0x0740, 0x43a0, 0x61d0, 0x70e8, 0x7874, + 0x7c3a, 0x3e1d, 0x1f0e, 0x0f87, 0x47c3, 0x63e1, 0x31f0, 0x58f8, 0x6c7c, 0x763e, + 0x3b1f, 0x5d8f, 0x6ec7, 0x7763, 0x7bb1, 0x3dd8, 0x5eec, 0x6f76, 0x37bb, 0x5bdd, + 0x2dee, 0x16f7, 0x4b7b, 0x65bd, 0x32de, 0x196f, 0x4cb7, 0x665b, 0x732d, 0x3996, + 0x1ccb, 0x4e65, 0x2732, 0x1399, 0x09cc, 0x44e6, 0x2273, 0x5139, 0x289c, 0x544e, + 0x2a27, 0x5513, 0x6a89, 0x3544, 0x5aa2, 0x2d51, 0x16a8, 0x4b54, 0x65aa, 0x32d5, + 0x196a, 0x0cb5, 0x065a, 0x032d, 0x0196, 0x00cb, 0x4065, 0x2032, 0x1019, 0x080c, + 0x4406, 0x2203, 0x5101, 0x2880, 0x5440, 0x6a20, 0x7510, 0x7a88, 0x7d44, 0x7ea2, + 0x3f51, 0x1fa8, 0x4fd4, 0x67ea, 0x33f5, 0x19fa, 0x0cfd, 0x067e, 0x033f, 0x419f, + 0x60cf, 0x7067, 0x7833, 0x7c19, 0x3e0c, 0x5f06, 0x2f83, 0x57c1, 0x2be0, 0x55f0, + 0x6af8, 0x757c, 0x7abe, 0x3d5f, 0x5eaf, 0x6f57, 0x77ab, 0x7bd5, 0x3dea, 0x1ef5, + 0x0f7a, 0x07bd, 0x03de, 0x01ef, 0x40f7, 0x607b, 0x703d, 0x381e, 0x1c0f, 0x4e07, + 0x6703, 0x7381, 0x39c0, 0x5ce0, 0x6e70, 0x7738, 0x7b9c, 0x7dce, 0x3ee7, 0x5f73, + 0x6fb9, 0x37dc, 0x5bee, 0x2df7, 0x56fb, 0x6b7d, 0x35be, 0x1adf, 0x4d6f, 0x66b7, + 0x735b, 0x79ad, 0x3cd6, 0x1e6b, 0x4f35, 0x279a, 0x13cd, 0x09e6, 0x04f3, 0x4279, + 0x213c, 0x509e, 0x284f, 0x5427, 0x6a13, 0x7509, 0x3a84, 0x5d42, 0x2ea1, 0x1750, + 0x4ba8, 0x65d4, 0x72ea, 0x3975, 0x1cba, 0x0e5d, 0x072e, 0x0397, 0x41cb, 0x60e5, + 0x3072, 0x1839, 0x0c1c, 0x460e, 0x2307, 0x5183, 0x68c1, 0x3460, 0x5a30, 0x6d18, + 0x768c, 0x7b46, 0x3da3, 0x5ed1, 0x2f68, 0x57b4, 0x6bda, 0x35ed, 0x1af6, 0x0d7b, + 0x46bd, 0x235e, 0x11af, 0x48d7, 0x646b, 0x7235, 0x391a, 0x1c8d, 0x0e46, 0x0723, + 0x4391, 0x21c8, 0x50e4, 0x6872, 0x3439, 0x1a1c, 0x4d0e, 0x2687, 0x5343, 0x69a1, + 0x34d0, 0x5a68, 0x6d34, 0x769a, 0x3b4d, 0x1da6, 0x0ed3, 0x4769, 0x23b4, 0x51da, + 0x28ed, 0x1476, 0x0a3b, 0x451d, 0x228e, 0x1147, 0x48a3, 0x6451, 0x3228, 0x5914, + 0x6c8a, 0x3645, 0x1b22, 0x0d91, 0x06c8, 0x4364, 0x61b2, 0x30d9, 0x186c, 0x4c36, + 0x261b, 0x530d, 0x2986, 0x14c3, 0x4a61, 0x2530, 0x5298, 0x694c, 0x74a6, 0x3a53, + 0x5d29, 0x2e94, 0x574a, 0x2ba5, 0x15d2, 0x0ae9, 0x0574, 0x42ba, 0x215d, 0x10ae, + 0x0857, 0x442b, 0x6215, 0x310a, 0x1885, 0x0c42, 0x0621, 0x0310, 0x4188, 0x60c4, + 0x7062, 0x3831, 0x1c18, 0x4e0c, 0x6706, 0x3383, 0x59c1, 0x2ce0, 0x5670, 0x6b38, + 0x759c, 0x7ace, 0x3d67, 0x5eb3, 0x6f59, 0x37ac, 0x5bd6, 0x2deb, 0x56f5, 0x2b7a, + 0x15bd, 0x0ade, 0x056f, 0x42b7, 0x615b, 0x70ad, 0x3856, 0x1c2b, 0x4e15, 0x270a, + 0x1385, 0x09c2, 0x04e1, 0x0270, 0x4138, 0x609c, 0x704e, 0x3827, 0x5c13, 0x6e09, + 0x3704, 0x5b82, 0x2dc1, 0x16e0, 0x4b70, 0x65b8, 0x72dc, 0x796e, 0x3cb7, 0x5e5b, + 0x6f2d, 0x3796, 0x1bcb, 0x4de5, 0x26f2, 0x1379, 0x09bc, 0x44de, 0x226f, 0x5137, + 0x689b, 0x744d, 0x3a26, 0x1d13, 0x4e89, 0x2744, 0x53a2, 0x29d1, 0x14e8, 0x4a74, + 0x653a, 0x329d, 0x194e, 0x0ca7, 0x4653, 0x6329, 0x3194, 0x58ca, 0x2c65, 0x1632, + 0x0b19, 0x058c, 0x42c6, 0x2163, 0x50b1, 0x2858, 0x542c, 0x6a16, 0x350b, 0x5a85, + 0x2d42, 0x16a1, 0x0b50, 0x45a8, 0x62d4, 0x716a, 0x38b5, 0x1c5a, 0x0e2d, 0x0716, + 0x038b, 0x41c5, 0x20e2, 0x1071, 0x0838, 0x441c, 0x620e, 0x3107, 0x5883, 0x6c41, + 0x3620, 0x5b10, 0x6d88, 0x76c4, 0x7b62, 0x3db1, 0x1ed8, 0x4f6c, 0x67b6, 0x33db, + 0x59ed, 0x2cf6, 0x167b, 0x4b3d, 0x259e, 0x12cf, 0x4967, 0x64b3, 0x7259, 0x392c, + 0x5c96, 0x2e4b, 0x5725, 0x2b92, 0x15c9, 0x0ae4, 0x4572, 0x22b9, 0x115c, 0x48ae, + 0x2457, 0x522b, 0x6915, 0x348a, 0x1a45, 0x0d22, 0x0691, 0x0348, 0x41a4, 0x60d2, + 0x3069, 0x1834, 0x4c1a, 0x260d, 0x1306, 0x0983, 0x44c1, 0x2260, 0x5130, 0x6898, + 0x744c, 0x7a26, 0x3d13, 0x5e89, 0x2f44, 0x57a2, 0x2bd1, 0x15e8, 0x4af4, 0x657a, + 0x32bd, 0x195e, 0x0caf, 0x4657, 0x632b, 0x7195, 0x38ca, 0x1c65, 0x0e32, 0x0719, + 0x038c, 0x41c6, 0x20e3, 0x5071, 0x2838, 0x541c, 0x6a0e, 0x3507, 0x5a83, 0x6d41, + 0x36a0, 0x5b50, 0x6da8, 0x76d4, 0x7b6a, 0x3db5, 0x1eda, 0x0f6d, 0x07b6, 0x03db, + 0x41ed, 0x20f6, 0x107b, 0x483d, 0x241e, 0x120f, 0x4907, 0x6483, 0x7241, 0x3920, + 0x5c90, 0x6e48, 0x7724, 0x7b92, 0x3dc9, 0x1ee4, 0x4f72, 0x27b9, 0x13dc, 0x49ee, + 0x24f7, 0x527b, 0x693d, 0x349e, 0x1a4f, 0x4d27, 0x6693, 0x7349, 0x39a4, 0x5cd2, + 0x2e69, 0x1734, 0x4b9a, 0x25cd, 0x12e6, 0x0973, 0x44b9, 0x225c, 0x512e, 0x2897, + 0x544b, 0x6a25, 0x3512, 0x1a89, 0x0d44, 0x46a2, 0x2351, 0x11a8, 0x48d4, 0x646a, + 0x3235, 0x191a, 0x0c8d, 0x0646, 0x0323, 0x4191, 0x20c8, 0x5064, 0x6832, 0x3419, + 0x1a0c, 0x4d06, 0x2683, 0x5341, 0x29a0, 0x54d0, 0x6a68, 0x7534, 0x7a9a, 0x3d4d, + 0x1ea6, 0x0f53, 0x47a9, 0x23d4, 0x51ea, 0x28f5, 0x147a, 0x0a3d, 0x051e, 0x028f, + 0x4147, 0x60a3, 0x7051, 0x3828, 0x5c14, 0x6e0a, 0x3705, 0x1b82, 0x0dc1, 0x06e0, + 0x4370, 0x61b8, 0x70dc, 0x786e, 0x3c37, 0x5e1b, 0x6f0d, 0x3786, 0x1bc3, 0x4de1, + 0x26f0, 0x5378, 0x69bc, 0x74de, 0x3a6f, 0x5d37, 0x6e9b, 0x774d, 0x3ba6, 0x1dd3, + 0x4ee9, 0x2774, 0x53ba, 0x29dd, 0x14ee, 0x0a77, 0x453b, 0x629d, 0x314e, 0x18a7, + 0x4c53, 0x6629, 0x3314, 0x598a, 0x2cc5, 0x1662, 0x0b31, 0x0598, 0x42cc, 0x6166, + 0x30b3, 0x5859, 0x2c2c, 0x5616, 0x2b0b, 0x5585, 0x2ac2, 0x1561, 0x0ab0, 0x4558, + 0x62ac, 0x7156, 0x38ab, 0x5c55, 0x2e2a, 0x1715, 0x0b8a, 0x05c5, 0x02e2, 0x0171, + 0x00b8, 0x405c, 0x602e, 0x3017, 0x580b, 0x6c05, 0x3602, 0x1b01, 0x0d80, 0x46c0, + 0x6360, 0x71b0, 0x78d8, 0x7c6c, 0x7e36, 0x3f1b, 0x5f8d, 0x2fc6, 0x17e3, 0x4bf1, + 0x25f8, 0x52fc, 0x697e, 0x34bf, 0x5a5f, 0x6d2f, 0x7697, 0x7b4b, 0x7da5, 0x3ed2, + 0x1f69, 0x0fb4, 0x47da, 0x23ed, 0x11f6, 0x08fb, 0x447d, 0x223e, 0x111f, 0x488f, + 0x6447, 0x7223, 0x7911, 0x3c88, 0x5e44, 0x6f22, 0x3791, 0x1bc8, 0x4de4, 0x66f2, + 0x3379, 0x19bc, 0x4cde, 0x266f, 0x5337, 0x699b, 0x74cd, 0x3a66, 0x1d33, 0x4e99, + 0x274c, 0x53a6, 0x29d3, 0x54e9, 0x2a74, 0x553a, 0x2a9d, 0x154e, 0x0aa7, 0x4553, + 0x62a9, 0x3154, 0x58aa, 0x2c55, 0x162a, 0x0b15, 0x058a, 0x02c5, 0x0162, 0x00b1, + 0x0058, 0x402c, 0x6016, 0x300b, 0x5805, 0x2c02, 0x1601, 0x0b00, 0x4580, 0x62c0, + 0x7160, 0x78b0, 0x7c58, 0x7e2c, 0x7f16, 0x3f8b, 0x5fc5, 0x2fe2, 0x17f1, 0x0bf8, + 0x45fc, 0x62fe, 0x317f, 0x58bf, 0x6c5f, 0x762f, 0x7b17, 0x7d8b, 0x7ec5, 0x3f62, + 0x1fb1, 0x0fd8, 0x47ec, 0x63f6, 0x31fb, 0x58fd, 0x2c7e, 0x163f, 0x4b1f, 0x658f, + 0x72c7, 0x7963, 0x7cb1, 0x3e58, 0x5f2c, 0x6f96, 0x37cb, 0x5be5, 0x2df2, 0x16f9, + 0x0b7c, 0x45be, 0x22df, 0x516f, 0x68b7, 0x745b, 0x7a2d, 0x3d16, 0x1e8b, 0x4f45, + 0x27a2, 0x13d1, 0x09e8, 0x44f4, 0x627a, 0x313d, 0x189e, 0x0c4f, 0x4627, 0x6313, + 0x7189, 0x38c4, 0x5c62, 0x2e31, 0x1718, 0x4b8c, 0x65c6, 0x32e3, 0x5971, 0x2cb8, + 0x565c, 0x6b2e, 0x3597, 0x5acb, 0x6d65, 0x36b2, 0x1b59, 0x0dac, 0x46d6, 0x236b, + 0x51b5, 0x28da, 0x146d, 0x0a36, 0x051b, 0x428d, 0x2146, 0x10a3, 0x4851, 0x2428, + 0x5214, 0x690a, 0x3485, 0x1a42, 0x0d21, 0x0690, 0x4348, 0x61a4, 0x70d2, 0x3869, + 0x1c34, 0x4e1a, 0x270d, 0x1386, 0x09c3, 0x44e1, 0x2270, 0x5138, 0x689c, 0x744e, + 0x3a27, 0x5d13, 0x6e89, 0x3744, 0x5ba2, 0x2dd1, 0x16e8, 0x4b74, 0x65ba, 0x32dd, + 0x196e, 0x0cb7, 0x465b, 0x632d, 0x3196, 0x18cb, 0x4c65, 0x2632, 0x1319, 0x098c, + 0x44c6, 0x2263, 0x5131, 0x2898, 0x544c, 0x6a26, 0x3513, 0x5a89, 0x2d44, 0x56a2, + 0x2b51, 0x15a8, 0x4ad4, 0x656a, 0x32b5, 0x195a, 0x0cad, 0x0656, 0x032b, 0x4195, + 0x20ca, 0x1065, 0x0832, 0x0419, 0x020c, 0x4106, 0x2083, 0x5041, 0x2820, 0x5410, + 0x6a08, 0x7504, 0x7a82, 0x3d41, 0x1ea0, 0x4f50, 0x67a8, 0x73d4, 0x79ea, 0x3cf5, + 0x1e7a, 0x0f3d, 0x079e, 0x03cf, 0x41e7, 0x60f3, 0x7079, 0x383c, 0x5c1e, 0x2e0f, + 0x5707, 0x6b83, 0x75c1, 0x3ae0, 0x5d70, 0x6eb8, 0x775c, 0x7bae, 0x3dd7, 0x5eeb, + 0x6f75, 0x37ba, 0x1bdd, 0x0dee, 0x06f7, 0x437b, 0x61bd, 0x30de, 0x186f, 0x4c37, + 0x661b, 0x730d, 0x3986, 0x1cc3, 0x4e61, 0x2730, 0x5398, 0x69cc, 0x74e6, 0x3a73, + 0x5d39, 0x2e9c, 0x574e, 0x2ba7, 0x55d3, 0x6ae9, 0x3574, 0x5aba, 0x2d5d, 0x16ae, + 0x0b57, 0x45ab, 0x62d5, 0x316a, 0x18b5, 0x0c5a, 0x062d, 0x0316, 0x018b, 0x40c5, + 0x2062, 0x1031, 0x0818, 0x440c, 0x6206, 0x3103, 0x5881, 0x2c40, 0x5620, 0x6b10, + 0x7588, 0x7ac4, 0x7d62, 0x3eb1, 0x1f58, 0x4fac, 0x67d6, 0x33eb, 0x59f5, 0x2cfa, + 0x167d, 0x0b3e, 0x059f, 0x42cf, 0x6167, 0x70b3, 0x7859, 0x3c2c, 0x5e16, 0x2f0b, + 0x5785, 0x2bc2, 0x15e1, 0x0af0, 0x4578, 0x62bc, 0x715e, 0x38af, 0x5c57, 0x6e2b, + 0x7715, 0x3b8a, 0x1dc5, 0x0ee2, 0x0771, 0x03b8, 0x41dc, 0x60ee, 0x3077, 0x583b, + 0x6c1d, 0x360e, 0x1b07, 0x4d83, 0x66c1, 0x3360, 0x59b0, 0x6cd8, 0x766c, 0x7b36, + 0x3d9b, 0x5ecd, 0x2f66, 0x17b3, 0x4bd9, 0x25ec, 0x52f6, 0x297b, 0x54bd, 0x2a5e, + 0x152f, 0x4a97, 0x654b, 0x72a5, 0x3952, 0x1ca9, 0x0e54, 0x472a, 0x2395, 0x11ca, + 0x08e5, 0x0472, 0x0239, 0x011c, 0x408e, 0x2047, 0x5023, 0x6811, 0x3408, 0x5a04, + 0x6d02, 0x3681, 0x1b40, 0x4da0, 0x66d0, 0x7368, 0x79b4, 0x7cda, 0x3e6d, 0x1f36, + 0x0f9b, 0x47cd, 0x23e6, 0x11f3, 0x48f9, 0x247c, 0x523e, 0x291f, 0x548f, 0x6a47, + 0x7523, 0x7a91, 0x3d48, 0x5ea4, 0x6f52, 0x37a9, 0x1bd4, 0x4dea, 0x26f5, 0x137a, + 0x09bd, 0x04de, 0x026f, 0x4137, 0x609b, 0x704d, 0x3826, 0x1c13, 0x4e09, 0x2704, + 0x5382, 0x29c1, 0x14e0, 0x4a70, 0x6538, 0x729c, 0x794e, 0x3ca7, 0x5e53, 0x6f29, + 0x3794, 0x5bca, 0x2de5, 0x16f2, 0x0b79, 0x05bc, 0x42de, 0x216f, 0x50b7, 0x685b, + 0x742d, 0x3a16, 0x1d0b, 0x4e85, 0x2742, 0x13a1, 0x09d0, 0x44e8, 0x6274, 0x713a, + 0x389d, 0x1c4e, 0x0e27, 0x4713, 0x6389, 0x31c4, 0x58e2, 0x2c71, 0x1638, 0x4b1c, + 0x658e, 0x32c7, 0x5963, 0x6cb1, 0x3658, 0x5b2c, 0x6d96, 0x36cb, 0x5b65, 0x2db2, + 0x16d9, 0x0b6c, 0x45b6, 0x22db, 0x516d, 0x28b6, 0x145b, 0x4a2d, 0x2516, 0x128b, + 0x4945, 0x24a2, 0x1251, 0x0928, 0x4494, 0x624a, 0x3125, 0x1892, 0x0c49, 0x0624, + 0x4312, 0x2189, 0x10c4, 0x4862, 0x2431, 0x1218, 0x490c, 0x6486, 0x3243, 0x5921, + 0x2c90, 0x5648, 0x6b24, 0x7592, 0x3ac9, 0x1d64, 0x4eb2, 0x2759, 0x13ac, 0x49d6, + 0x24eb, 0x5275, 0x293a, 0x149d, 0x0a4e, 0x0527, 0x4293, 0x6149, 0x30a4, 0x5852, + 0x2c29, 0x1614, 0x4b0a, 0x2585, 0x12c2, 0x0961, 0x04b0, 0x4258, 0x612c, 0x7096, + 0x384b, 0x5c25, 0x2e12, 0x1709, 0x0b84, 0x45c2, 0x22e1, 0x1170, 0x48b8, 0x645c, + 0x722e, 0x3917, 0x5c8b, 0x6e45, 0x3722, 0x1b91, 0x0dc8, 0x46e4, 0x6372, 0x31b9, + 0x18dc, 0x4c6e, 0x2637, 0x531b, 0x698d, 0x34c6, 0x1a63, 0x4d31, 0x2698, 0x534c, + 0x69a6, 0x34d3, 0x5a69, 0x2d34, 0x569a, 0x2b4d, 0x15a6, 0x0ad3, 0x4569, 0x22b4, + 0x515a, 0x28ad, 0x1456, 0x0a2b, 0x4515, 0x228a, 0x1145, 0x08a2, 0x0451, 0x0228, + 0x4114, 0x608a, 0x3045, 0x1822, 0x0c11, 0x0608, 0x4304, 0x6182, 0x30c1, 0x1860, + 0x4c30, 0x6618, 0x730c, 0x7986, 0x3cc3, 0x5e61, 0x2f30, 0x5798, 0x6bcc, 0x75e6, + 0x3af3, 0x5d79, 0x2ebc, 0x575e, 0x2baf, 0x55d7, 0x6aeb, 0x7575, 0x3aba, 0x1d5d, + 0x0eae, 0x0757, 0x43ab, 0x61d5, 0x30ea, 0x1875, 0x0c3a, 0x061d, 0x030e, 0x0187, + 0x40c3, 0x6061, 0x3030, 0x5818, 0x6c0c, 0x7606, 0x3b03, 0x5d81, 0x2ec0, 0x5760, + 0x6bb0, 0x75d8, 0x7aec, 0x7d76, 0x3ebb, 0x5f5d, 0x2fae, 0x17d7, 0x4beb, 0x65f5, + 0x32fa, 0x197d, 0x0cbe, 0x065f, 0x432f, 0x6197, 0x70cb, 0x7865, 0x3c32, 0x1e19, + 0x0f0c, 0x4786, 0x23c3, 0x51e1, 0x28f0, 0x5478, 0x6a3c, 0x751e, 0x3a8f, 0x5d47, + 0x6ea3, 0x7751, 0x3ba8, 0x5dd4, 0x6eea, 0x3775, 0x1bba, 0x0ddd, 0x06ee, 0x0377, + 0x41bb, 0x60dd, 0x306e, 0x1837, 0x4c1b, 0x660d, 0x3306, 0x1983, 0x4cc1, 0x2660, + 0x5330, 0x6998, 0x74cc, 0x7a66, 0x3d33, 0x5e99, 0x2f4c, 0x57a6, 0x2bd3, 0x55e9, + 0x2af4, 0x557a, 0x2abd, 0x155e, 0x0aaf, 0x4557, 0x62ab, 0x7155, 0x38aa, 0x1c55, + 0x0e2a, 0x0715, 0x038a, 0x01c5, 0x00e2, 0x0071, 0x0038, 0x401c, 0x600e, 0x3007, + 0x5803, 0x6c01, 0x3600, 0x5b00, 0x6d80, 0x76c0, 0x7b60, 0x7db0, 0x7ed8, 0x7f6c, + 0x7fb6, 0x3fdb, 0x5fed, 0x2ff6, 0x17fb, 0x4bfd, 0x25fe, 0x12ff, 0x497f, 0x64bf, + 0x725f, 0x792f, 0x7c97, 0x7e4b, 0x7f25, 0x3f92, 0x1fc9, 0x0fe4, 0x47f2, 0x23f9, + 0x11fc, 0x48fe, 0x247f, 0x523f, 0x691f, 0x748f, 0x7a47, 0x7d23, 0x7e91, 0x3f48, + 0x5fa4, 0x6fd2, 0x37e9, 0x1bf4, 0x4dfa, 0x26fd, 0x137e, 0x09bf, 0x44df, 0x626f, + 0x7137, 0x789b, 0x7c4d, 0x3e26, 0x1f13, 0x4f89, 0x27c4, 0x53e2, 0x29f1, 0x14f8, + 0x4a7c, 0x653e, 0x329f, 0x594f, 0x6ca7, 0x7653, 0x7b29, 0x3d94, 0x5eca, 0x2f65, + 0x17b2, 0x0bd9, 0x05ec, 0x42f6, 0x217b, 0x50bd, 0x285e, 0x142f, 0x4a17, 0x650b, + 0x7285, 0x3942, 0x1ca1, 0x0e50, 0x4728, 0x6394, 0x71ca, 0x38e5, 0x1c72, 0x0e39, + 0x071c, 0x438e, 0x21c7, 0x50e3, 0x6871, 0x3438, 0x5a1c, 0x6d0e, 0x3687, 0x5b43, + 0x6da1, 0x36d0, 0x5b68, 0x6db4, 0x76da, 0x3b6d, 0x1db6, 0x0edb, 0x476d, 0x23b6, + 0x11db, 0x48ed, 0x2476, 0x123b, 0x491d, 0x248e, 0x1247, 0x4923, 0x6491, 0x3248, + 0x5924, 0x6c92, 0x3649, 0x1b24, 0x4d92, 0x26c9, 0x1364, 0x49b2, 0x24d9, 0x126c, + 0x4936, 0x249b, 0x524d, 0x2926, 0x1493, 0x4a49, 0x2524, 0x5292, 0x2949, 0x14a4, + 0x4a52, 0x2529, 0x1294, 0x494a, 0x24a5, 0x1252, 0x0929, 0x0494, 0x424a, 0x2125, + 0x1092, 0x0849, 0x0424, 0x4212, 0x2109, 0x1084, 0x4842, 0x2421, 0x1210, 0x4908, + 0x6484, 0x7242, 0x3921, 0x1c90, 0x4e48, 0x6724, 0x7392, 0x39c9, 0x1ce4, 0x4e72, + 0x2739, 0x139c, 0x49ce, 0x24e7, 0x5273, 0x6939, 0x349c, 0x5a4e, 0x2d27, 0x5693, + 0x6b49, 0x35a4, 0x5ad2, 0x2d69, 0x16b4, 0x4b5a, 0x25ad, 0x12d6, 0x096b, 0x44b5, + 0x225a, 0x112d, 0x0896, 0x044b, 0x4225, 0x2112, 0x1089, 0x0844, 0x4422, 0x2211, + 0x1108, 0x4884, 0x6442, 0x3221, 0x1910, 0x4c88, 0x6644, 0x7322, 0x3991, 0x1cc8, + 0x4e64, 0x6732, 0x3399, 0x19cc, 0x4ce6, 0x2673, 0x5339, 0x299c, 0x54ce, 0x2a67, + 0x5533, 0x6a99, 0x354c, 0x5aa6, 0x2d53, 0x56a9, 0x2b54, 0x55aa, 0x2ad5, 0x156a, + 0x0ab5, 0x055a, 0x02ad, 0x0156, 0x00ab, 0x4055, 0x202a, 0x1015, 0x080a, 0x0405, + 0x0202, 0x0101, 0x0080, 0x4040, 0x6020, 0x7010, 0x7808, 0x7c04, 0x7e02, 0x3f01, + 0x1f80, 0x4fc0, 0x67e0, 0x73f0, 0x79f8, 0x7cfc, 0x7e7e, 0x3f3f, 0x5f9f, 0x6fcf, + 0x77e7, 0x7bf3, 0x7df9, 0x3efc, 0x5f7e, 0x2fbf, 0x57df, 0x6bef, 0x75f7, 0x7afb, + 0x7d7d, 0x3ebe, 0x1f5f, 0x4faf, 0x67d7, 0x73eb, 0x79f5, 0x3cfa, 0x1e7d, 0x0f3e, + 0x079f, 0x43cf, 0x61e7, 0x70f3, 0x7879, 0x3c3c, 0x5e1e, 0x2f0f, 0x5787, 0x6bc3, + 0x75e1, 0x3af0, 0x5d78, 0x6ebc, 0x775e, 0x3baf, 0x5dd7, 0x6eeb, 0x7775, 0x3bba, + 0x1ddd, 0x0eee, 0x0777, 0x43bb, 0x61dd, 0x30ee, 0x1877, 0x4c3b, 0x661d, 0x330e, + 0x1987, 0x4cc3, 0x6661, 0x3330, 0x5998, 0x6ccc, 0x7666, 0x3b33, 0x5d99, 0x2ecc, + 0x5766, 0x2bb3, 0x55d9, 0x2aec, 0x5576, 0x2abb, 0x555d, 0x2aae, 0x1557, 0x4aab, + 0x6555, 0x32aa, 0x1955, 0x0caa, 0x0655, 0x032a, 0x0195, 0x00ca, 0x0065, 0x0032, + 0x0019, 0x000c, 0x4006, 0x2003, 0x5001, 0x2800, 0x5400, 0x6a00, 0x7500, 0x7a80, + 0x7d40, 0x7ea0, 0x7f50, 0x7fa8, 0x7fd4, 0x7fea, 0x3ff5, 0x1ffa, 0x0ffd, 0x07fe, + 0x03ff, 0x41ff, 0x60ff, 0x707f, 0x783f, 0x7c1f, 0x7e0f, 0x7f07, 0x7f83, 0x7fc1, + 0x3fe0, 0x5ff0, 0x6ff8, 0x77fc, 0x7bfe, 0x3dff, 0x5eff, 0x6f7f, 0x77bf, 0x7bdf, + 0x7def, 0x7ef7, 0x7f7b, 0x7fbd, 0x3fde, 0x1fef, 0x4ff7, 0x67fb, 0x73fd, 0x39fe, + 0x1cff, 0x4e7f, 0x673f, 0x739f, 0x79cf, 0x7ce7, 0x7e73, 0x7f39, 0x3f9c, 0x5fce, + 0x2fe7, 0x57f3, 0x6bf9, 0x35fc, 0x5afe, 0x2d7f, 0x56bf, 0x6b5f, 0x75af, 0x7ad7, + 0x7d6b, 0x7eb5, 0x3f5a, 0x1fad, 0x0fd6, 0x07eb, 0x43f5, 0x21fa, 0x10fd, 0x087e, + 0x043f, 0x421f, 0x610f, 0x7087, 0x7843, 0x7c21, 0x3e10, 0x5f08, 0x6f84, 0x77c2, + 0x3be1, 0x1df0, 0x4ef8, 0x677c, 0x73be, 0x39df, 0x5cef, 0x6e77, 0x773b, 0x7b9d, + 0x3dce, 0x1ee7, 0x4f73, 0x67b9, 0x33dc, 0x59ee, 0x2cf7, 0x567b, 0x6b3d, 0x359e, + 0x1acf, 0x4d67, 0x66b3, 0x7359, 0x39ac, 0x5cd6, 0x2e6b, 0x5735, 0x2b9a, 0x15cd, + 0x0ae6, 0x0573, 0x42b9, 0x215c, 0x50ae, 0x2857, 0x542b, 0x6a15, 0x350a, 0x1a85, + 0x0d42, 0x06a1, 0x0350, 0x41a8, 0x60d4, 0x706a, 0x3835, 0x1c1a, 0x0e0d, 0x0706, + 0x0383, 0x41c1, 0x20e0, 0x5070, 0x6838, 0x741c, 0x7a0e, 0x3d07, 0x5e83, 0x6f41, + 0x37a0, 0x5bd0, 0x6de8, 0x76f4, 0x7b7a, 0x3dbd, 0x1ede, 0x0f6f, 0x47b7, 0x63db, + 0x71ed, 0x38f6, 0x1c7b, 0x4e3d, 0x271e, 0x138f, 0x49c7, 0x64e3, 0x7271, 0x3938, + 0x5c9c, 0x6e4e, 0x3727, 0x5b93, 0x6dc9, 0x36e4, 0x5b72, 0x2db9, 0x16dc, 0x4b6e, + 0x25b7, 0x52db, 0x696d, 0x34b6, 0x1a5b, 0x4d2d, 0x2696, 0x134b, 0x49a5, 0x24d2, + 0x1269, 0x0934, 0x449a, 0x224d, 0x1126, 0x0893, 0x4449, 0x2224, 0x5112, 0x2889, + 0x1444, 0x4a22, 0x2511, 0x1288, 0x4944, 0x64a2, 0x3251, 0x1928, 0x4c94, 0x664a, + 0x3325, 0x1992, 0x0cc9, 0x0664, 0x4332, 0x2199, 0x10cc, 0x4866, 0x2433, 0x5219, + 0x290c, 0x5486, 0x2a43, 0x5521, 0x2a90, 0x5548, 0x6aa4, 0x7552, 0x3aa9, 0x1d54, + 0x4eaa, 0x2755, 0x13aa, 0x09d5, 0x04ea, 0x0275, 0x013a, 0x009d, 0x004e, 0x0027, + 0x4013, 0x6009, 0x3004, 0x5802, 0x2c01, 0x1600, 0x4b00, 0x6580, 0x72c0, 0x7960, + 0x7cb0, 0x7e58, 0x7f2c, 0x7f96, 0x3fcb, 0x5fe5, 0x2ff2, 0x17f9, 0x0bfc, 0x45fe, + 0x22ff, 0x517f, 0x68bf, 0x745f, 0x7a2f, 0x7d17, 0x7e8b, 0x7f45, 0x3fa2, 0x1fd1, + 0x0fe8, 0x47f4, 0x63fa, 0x31fd, 0x18fe, 0x0c7f, 0x463f, 0x631f, 0x718f, 0x78c7, + 0x7c63, 0x7e31, 0x3f18, 0x5f8c, 0x6fc6, 0x37e3, 0x5bf1, 0x2df8, 0x56fc, 0x6b7e, + 0x35bf, 0x5adf, 0x6d6f, 0x76b7, 0x7b5b, 0x7dad, 0x3ed6, 0x1f6b, 0x4fb5, 0x27da, + 0x13ed, 0x09f6, 0x04fb, 0x427d, 0x213e, 0x109f, 0x484f, 0x6427, 0x7213, 0x7909, + 0x3c84, 0x5e42, 0x2f21, 0x1790, 0x4bc8, 0x65e4, 0x72f2, 0x3979, 0x1cbc, 0x4e5e, + 0x272f, 0x5397, 0x69cb, 0x74e5, 0x3a72, 0x1d39, 0x0e9c, 0x474e, 0x23a7, 0x51d3, + 0x68e9, 0x3474, 0x5a3a, 0x2d1d, 0x168e, 0x0b47, 0x45a3, 0x62d1, 0x3168, 0x58b4, + 0x6c5a, 0x362d, 0x1b16, 0x0d8b, 0x46c5, 0x2362, 0x11b1, 0x08d8, 0x446c, 0x6236, + 0x311b, 0x588d, 0x2c46, 0x1623, 0x4b11, 0x2588, 0x52c4, 0x6962, 0x34b1, 0x1a58, + 0x4d2c, 0x6696, 0x334b, 0x59a5, 0x2cd2, 0x1669, 0x0b34, 0x459a, 0x22cd, 0x1166, + 0x08b3, 0x4459, 0x222c, 0x5116, 0x288b, 0x5445, 0x2a22, 0x1511, 0x0a88, 0x4544, + 0x62a2, 0x3151, 0x18a8, 0x4c54, 0x662a, 0x3315, 0x198a, 0x0cc5, 0x0662, 0x0331, + 0x0198, 0x40cc, 0x6066, 0x3033, 0x5819, 0x2c0c, 0x5606, 0x2b03, 0x5581, 0x2ac0, + 0x5560, 0x6ab0, 0x7558, 0x7aac, 0x7d56, 0x3eab, 0x5f55, 0x2faa, 0x17d5, 0x0bea, + 0x05f5, 0x02fa, 0x017d, 0x00be, 0x005f, 0x402f, 0x6017, 0x700b, 0x7805, 0x3c02, + 0x1e01, 0x0f00, 0x4780, 0x63c0, 0x71e0, 0x78f0, 0x7c78, 0x7e3c, 0x7f1e, 0x3f8f, + 0x5fc7, 0x6fe3, 0x77f1, 0x3bf8, 0x5dfc, 0x6efe, 0x377f, 0x5bbf, 0x6ddf, 0x76ef, + 0x7b77, 0x7dbb, 0x7edd, 0x3f6e, 0x1fb7, 0x4fdb, 0x67ed, 0x33f6, 0x19fb, 0x4cfd, + 0x267e, 0x133f, 0x499f, 0x64cf, 0x7267, 0x7933, 0x7c99, 0x3e4c, 0x5f26, 0x2f93, + 0x57c9, 0x2be4, 0x55f2, 0x2af9, 0x157c, 0x4abe, 0x255f, 0x52af, 0x6957, 0x74ab, + 0x7a55, 0x3d2a, 0x1e95, 0x0f4a, 0x07a5, 0x03d2, 0x01e9, 0x00f4, 0x407a, 0x203d, + 0x101e, 0x080f, 0x4407, 0x6203, 0x7101, 0x3880, 0x5c40, 0x6e20, 0x7710, 0x7b88, + 0x7dc4, 0x7ee2, 0x3f71, 0x1fb8, 0x4fdc, 0x67ee, 0x33f7, 0x59fb, 0x6cfd, 0x367e, + 0x1b3f, 0x4d9f, 0x66cf, 0x7367, 0x79b3, 0x7cd9, 0x3e6c, 0x5f36, 0x2f9b, 0x57cd, + 0x2be6, 0x15f3, 0x4af9, 0x257c, 0x52be, 0x295f, 0x54af, 0x6a57, 0x752b, 0x7a95, + 0x3d4a, 0x1ea5, 0x0f52, 0x07a9, 0x03d4, 0x41ea, 0x20f5, 0x107a, 0x083d, 0x041e, + 0x020f, 0x4107, 0x6083, 0x7041, 0x3820, 0x5c10, 0x6e08, 0x7704, 0x7b82, 0x3dc1, + 0x1ee0, 0x4f70, 0x67b8, 0x73dc, 0x79ee, 0x3cf7, 0x5e7b, 0x6f3d, 0x379e, 0x1bcf, + 0x4de7, 0x66f3, 0x7379, 0x39bc, 0x5cde, 0x2e6f, 0x5737, 0x6b9b, 0x75cd, 0x3ae6, + 0x1d73, 0x4eb9, 0x275c, 0x53ae, 0x29d7, 0x54eb, 0x6a75, 0x353a, 0x1a9d, 0x0d4e, + 0x06a7, 0x4353, 0x61a9, 0x30d4, 0x586a, 0x2c35, 0x161a, 0x0b0d, 0x0586, 0x02c3, + 0x4161, 0x20b0, 0x5058, 0x682c, 0x7416, 0x3a0b, 0x5d05, 0x2e82, 0x1741, 0x0ba0, + 0x45d0, 0x62e8, 0x7174, 0x78ba, 0x3c5d, 0x1e2e, 0x0f17, 0x478b, 0x63c5, 0x31e2, + 0x18f1, 0x0c78, 0x463c, 0x631e, 0x318f, 0x58c7, 0x6c63, 0x7631, 0x3b18, 0x5d8c, + 0x6ec6, 0x3763, 0x5bb1, 0x2dd8, 0x56ec, 0x6b76, 0x35bb, 0x5add, 0x2d6e, 0x16b7, + 0x4b5b, 0x65ad, 0x32d6, 0x196b, 0x4cb5, 0x265a, 0x132d, 0x0996, 0x04cb, 0x4265, + 0x2132, 0x1099, 0x084c, 0x4426, 0x2213, 0x5109, 0x2884, 0x5442, 0x2a21, 0x1510, + 0x4a88, 0x6544, 0x72a2, 0x3951, 0x1ca8, 0x4e54, 0x672a, 0x3395, 0x19ca, 0x0ce5, + 0x0672, 0x0339, 0x019c, 0x40ce, 0x2067, 0x5033, 0x6819, 0x340c, 0x5a06, 0x2d03, + 0x5681, 0x2b40, 0x55a0, 0x6ad0, 0x7568, 0x7ab4, 0x7d5a, 0x3ead, 0x1f56, 0x0fab, + 0x47d5, 0x23ea, 0x11f5, 0x08fa, 0x047d, 0x023e, 0x011f, 0x408f, 0x6047, 0x7023, + 0x7811, 0x3c08, 0x5e04, 0x6f02, 0x3781, 0x1bc0, 0x4de0, 0x66f0, 0x7378, 0x79bc, + 0x7cde, 0x3e6f, 0x5f37, 0x6f9b, 0x77cd, 0x3be6, 0x1df3, 0x4ef9, 0x277c, 0x53be, + 0x29df, 0x54ef, 0x6a77, 0x753b, 0x7a9d, 0x3d4e, 0x1ea7, 0x4f53, 0x67a9, 0x33d4, + 0x59ea, 0x2cf5, 0x167a, 0x0b3d, 0x059e, 0x02cf, 0x4167, 0x60b3, 0x7059, 0x382c, + 0x5c16, 0x2e0b, 0x5705, 0x2b82, 0x15c1, 0x0ae0, 0x4570, 0x62b8, 0x715c, 0x78ae, + 0x3c57, 0x5e2b, 0x6f15, 0x378a, 0x1bc5, 0x0de2, 0x06f1, 0x0378, 0x41bc, 0x60de, + 0x306f, 0x5837, 0x6c1b, 0x760d, 0x3b06, 0x1d83, 0x4ec1, 0x2760, 0x53b0, 0x69d8, + 0x74ec, 0x7a76, 0x3d3b, 0x5e9d, 0x2f4e, 0x17a7, 0x4bd3, 0x65e9, 0x32f4, 0x597a, + 0x2cbd, 0x165e, 0x0b2f, 0x4597, 0x62cb, 0x7165, 0x38b2, 0x1c59, 0x0e2c, 0x4716, + 0x238b, 0x51c5, 0x28e2, 0x1471, 0x0a38, 0x451c, 0x628e, 0x3147, 0x58a3, 0x6c51, + 0x3628, 0x5b14, 0x6d8a, 0x36c5, 0x1b62, 0x0db1, 0x06d8, 0x436c, 0x61b6, 0x30db, + 0x586d, 0x2c36, 0x161b, 0x4b0d, 0x2586, 0x12c3, 0x4961, 0x24b0, 0x5258, 0x692c, + 0x7496, 0x3a4b, 0x5d25, 0x2e92, 0x1749, 0x0ba4, 0x45d2, 0x22e9, 0x1174, 0x48ba, + 0x245d, 0x122e, 0x0917, 0x448b, 0x6245, 0x3122, 0x1891, 0x0c48, 0x4624, 0x6312, + 0x3189, 0x18c4, 0x4c62, 0x2631, 0x1318, 0x498c, 0x64c6, 0x3263, 0x5931, 0x2c98, + 0x564c, 0x6b26, 0x3593, 0x5ac9, 0x2d64, 0x56b2, 0x2b59, 0x15ac, 0x4ad6, 0x256b, + 0x52b5, 0x295a, 0x14ad, 0x0a56, 0x052b, 0x4295, 0x214a, 0x10a5, 0x0852, 0x0429, + 0x0214, 0x410a, 0x2085, 0x1042, 0x0821, 0x0410, 0x4208, 0x6104, 0x7082, 0x3841, + 0x1c20, 0x4e10, 0x6708, 0x7384, 0x79c2, 0x3ce1, 0x1e70, 0x4f38, 0x679c, 0x73ce, + 0x39e7, 0x5cf3, 0x6e79, 0x373c, 0x5b9e, 0x2dcf, 0x56e7, 0x6b73, 0x75b9, 0x3adc, + 0x5d6e, 0x2eb7, 0x575b, 0x6bad, 0x35d6, 0x1aeb, 0x4d75, 0x26ba, 0x135d, 0x09ae, + 0x04d7, 0x426b, 0x6135, 0x309a, 0x184d, 0x0c26, 0x0613, 0x4309, 0x2184, 0x50c2, + 0x2861, 0x1430, 0x4a18, 0x650c, 0x7286, 0x3943, 0x5ca1, 0x2e50, 0x5728, 0x6b94, + 0x75ca, 0x3ae5, 0x1d72, 0x0eb9, 0x075c, 0x43ae, 0x21d7, 0x50eb, 0x6875, 0x343a, + 0x1a1d, 0x0d0e, 0x0687, 0x4343, 0x61a1, 0x30d0, 0x5868, 0x6c34, 0x761a, 0x3b0d, + 0x1d86, 0x0ec3, 0x4761, 0x23b0, 0x51d8, 0x68ec, 0x7476, 0x3a3b, 0x5d1d, 0x2e8e, + 0x1747, 0x4ba3, 0x65d1, 0x32e8, 0x5974, 0x6cba, 0x365d, 0x1b2e, 0x0d97, 0x46cb, + 0x6365, 0x31b2, 0x18d9, 0x0c6c, 0x4636, 0x231b, 0x518d, 0x28c6, 0x1463, 0x4a31, + 0x2518, 0x528c, 0x6946, 0x34a3, 0x5a51, 0x2d28, 0x5694, 0x6b4a, 0x35a5, 0x1ad2, + 0x0d69, 0x06b4, 0x435a, 0x21ad, 0x10d6, 0x086b, 0x4435, 0x221a, 0x110d, 0x0886, + 0x0443, 0x4221, 0x2110, 0x5088, 0x6844, 0x7422, 0x3a11, 0x1d08, 0x4e84, 0x6742, + 0x33a1, 0x19d0, 0x4ce8, 0x6674, 0x733a, 0x399d, 0x1cce, 0x0e67, 0x4733, 0x6399, + 0x31cc, 0x58e6, 0x2c73, 0x5639, 0x2b1c, 0x558e, 0x2ac7, 0x5563, 0x6ab1, 0x3558, + 0x5aac, 0x6d56, 0x36ab, 0x5b55, 0x2daa, 0x16d5, 0x0b6a, 0x05b5, 0x02da, 0x016d, + 0x00b6, 0x005b, 0x402d, 0x2016, 0x100b, 0x4805, 0x2402, 0x1201, 0x0900, 0x4480, + 0x6240, 0x7120, 0x7890, 0x7c48, 0x7e24, 0x7f12, 0x3f89, 0x1fc4, 0x4fe2, 0x27f1, + 0x13f8, 0x49fc, 0x64fe, 0x327f, 0x593f, 0x6c9f, 0x764f, 0x7b27, 0x7d93, 0x7ec9, + 0x3f64, 0x5fb2, 0x2fd9, 0x17ec, 0x4bf6, 0x25fb, 0x52fd, 0x297e, 0x14bf, 0x4a5f, + 0x652f, 0x7297, 0x794b, 0x7ca5, 0x3e52, 0x1f29, 0x0f94, 0x47ca, 0x23e5, 0x11f2, + 0x08f9, 0x047c, 0x423e, 0x211f, 0x508f, 0x6847, 0x7423, 0x7a11, 0x3d08, 0x5e84, + 0x6f42, 0x37a1, 0x1bd0, 0x4de8, 0x66f4, 0x737a, 0x39bd, 0x1cde, 0x0e6f, 0x4737, + 0x639b, 0x71cd, 0x38e6, 0x1c73, 0x4e39, 0x271c, 0x538e, 0x29c7, 0x54e3, 0x6a71, + 0x3538, 0x5a9c, 0x6d4e, 0x36a7, 0x5b53, 0x6da9, 0x36d4, 0x5b6a, 0x2db5, 0x16da, + 0x0b6d, 0x05b6, 0x02db, 0x416d, 0x20b6, 0x105b, 0x482d, 0x2416, 0x120b, 0x4905, + 0x2482, 0x1241, 0x0920, 0x4490, 0x6248, 0x7124, 0x7892, 0x3c49, 0x1e24, 0x4f12, + 0x2789, 0x13c4, 0x49e2, 0x24f1, 0x1278, 0x493c, 0x649e, 0x324f, 0x5927, 0x6c93, + 0x7649, 0x3b24, 0x5d92, 0x2ec9, 0x1764, 0x4bb2, 0x25d9, 0x12ec, 0x4976, 0x24bb, + 0x525d, 0x292e, 0x1497, 0x4a4b, 0x6525, 0x3292, 0x1949, 0x0ca4, 0x4652, 0x2329, + 0x1194, 0x48ca, 0x2465, 0x1232, 0x0919, 0x048c, 0x4246, 0x2123, 0x5091, 0x2848, + 0x5424, 0x6a12, 0x3509, 0x1a84, 0x4d42, 0x26a1, 0x1350, 0x49a8, 0x64d4, 0x726a, + 0x3935, 0x1c9a, 0x0e4d, 0x0726, 0x0393, 0x41c9, 0x20e4, 0x5072, 0x2839, 0x141c, + 0x4a0e, 0x2507, 0x5283, 0x6941, 0x34a0, 0x5a50, 0x6d28, 0x7694, 0x7b4a, 0x3da5, + 0x1ed2, 0x0f69, 0x07b4, 0x43da, 0x21ed, 0x10f6, 0x087b, 0x443d, 0x221e, 0x110f, + 0x4887, 0x6443, 0x7221, 0x3910, 0x5c88, 0x6e44, 0x7722, 0x3b91, 0x1dc8, 0x4ee4, + 0x6772, 0x33b9, 0x19dc, 0x4cee, 0x2677, 0x533b, 0x699d, 0x34ce, 0x1a67, 0x4d33, + 0x6699, 0x334c, 0x59a6, 0x2cd3, 0x5669, 0x2b34, 0x559a, 0x2acd, 0x1566, 0x0ab3, + 0x4559, 0x22ac, 0x5156, 0x28ab, 0x5455, 0x2a2a, 0x1515, 0x0a8a, 0x0545, 0x02a2, + 0x0151, 0x00a8, 0x4054, 0x602a, 0x3015, 0x180a, 0x0c05, 0x0602, 0x0301, 0x0180, + 0x40c0, 0x6060, 0x7030, 0x7818, 0x7c0c, 0x7e06, 0x3f03, 0x5f81, 0x2fc0, 0x57e0, + 0x6bf0, 0x75f8, 0x7afc, 0x7d7e, 0x3ebf, 0x5f5f, 0x6faf, 0x77d7, 0x7beb, 0x7df5, + 0x3efa, 0x1f7d, 0x0fbe, 0x07df, 0x43ef, 0x61f7, 0x70fb, 0x787d, 0x3c3e, 0x1e1f, + 0x4f0f, 0x6787, 0x73c3, 0x79e1, 0x3cf0, 0x5e78, 0x6f3c, 0x779e, 0x3bcf, 0x5de7, + 0x6ef3, 0x7779, 0x3bbc, 0x5dde, 0x2eef, 0x5777, 0x6bbb, 0x75dd, 0x3aee, 0x1d77, + 0x4ebb, 0x675d, 0x33ae, 0x19d7, 0x4ceb, 0x6675, 0x333a, 0x199d, 0x0cce, 0x0667, + 0x4333, 0x6199, 0x30cc, 0x5866, 0x2c33, 0x5619, 0x2b0c, 0x5586, 0x2ac3, 0x5561, + 0x2ab0, 0x5558, 0x6aac, 0x7556, 0x3aab, 0x5d55, 0x2eaa, 0x1755, 0x0baa, 0x05d5, + 0x02ea, 0x0175, 0x00ba, 0x005d, 0x002e, 0x0017, 0x400b, 0x6005, 0x3002, 0x1801, + 0x0c00, 0x4600, 0x6300, 0x7180, 0x78c0, 0x7c60, 0x7e30, 0x7f18, 0x7f8c, 0x7fc6, + 0x3fe3, 0x5ff1, 0x2ff8, 0x57fc, 0x6bfe, 0x35ff, 0x5aff, 0x6d7f, 0x76bf, 0x7b5f, + 0x7daf, 0x7ed7, 0x7f6b, 0x7fb5, 0x3fda, 0x1fed, 0x0ff6, 0x07fb, 0x43fd, 0x21fe, + 0x10ff, 0x487f, 0x643f, 0x721f, 0x790f, 0x7c87, 0x7e43, 0x7f21, 0x3f90, 0x5fc8, + 0x6fe4, 0x77f2, 0x3bf9, 0x1dfc, 0x4efe, 0x277f, 0x53bf, 0x69df, 0x74ef, 0x7a77, + 0x7d3b, 0x7e9d, 0x3f4e, 0x1fa7, 0x4fd3, 0x67e9, 0x33f4, 0x59fa, 0x2cfd, 0x167e, + 0x0b3f, 0x459f, 0x62cf, 0x7167, 0x78b3, 0x7c59, 0x3e2c, 0x5f16, 0x2f8b, 0x57c5, + 0x2be2, 0x15f1, 0x0af8, 0x457c, 0x62be, 0x315f, 0x58af, 0x6c57, 0x762b, 0x7b15, + 0x3d8a, 0x1ec5, 0x0f62, 0x07b1, 0x03d8, 0x41ec, 0x60f6, 0x307b, 0x583d, 0x2c1e, + 0x160f, 0x4b07, 0x6583, 0x72c1, 0x3960, 0x5cb0, 0x6e58, 0x772c, 0x7b96, 0x3dcb, + 0x5ee5, 0x2f72, 0x17b9, 0x0bdc, 0x45ee, 0x22f7, 0x517b, 0x68bd, 0x345e, 0x1a2f, + 0x4d17, 0x668b, 0x7345, 0x39a2, 0x1cd1, 0x0e68, 0x4734, 0x639a, 0x31cd, 0x18e6, + 0x0c73, 0x4639, 0x231c, 0x518e, 0x28c7, 0x5463, 0x6a31, 0x3518, 0x5a8c, 0x6d46, + 0x36a3, 0x5b51, 0x2da8, 0x56d4, 0x6b6a, 0x35b5, 0x1ada, 0x0d6d, 0x06b6, 0x035b, + 0x41ad, 0x20d6, 0x106b, 0x4835, 0x241a, 0x120d, 0x0906, 0x0483, 0x4241, 0x2120, + 0x5090, 0x6848, 0x7424, 0x7a12, 0x3d09, 0x1e84, 0x4f42, 0x27a1, 0x13d0, 0x49e8, + 0x64f4, 0x727a, 0x393d, 0x1c9e, 0x0e4f, 0x4727, 0x6393, 0x71c9, 0x38e4, 0x5c72, + 0x2e39, 0x171c, 0x4b8e, 0x25c7, 0x52e3, 0x6971, 0x34b8, 0x5a5c, 0x6d2e, 0x3697, + 0x5b4b, 0x6da5, 0x36d2, 0x1b69, 0x0db4, 0x46da, 0x236d, 0x11b6, 0x08db, 0x446d, + 0x2236, 0x111b, 0x488d, 0x2446, 0x1223, 0x4911, 0x2488, 0x5244, 0x6922, 0x3491, + 0x1a48, 0x4d24, 0x6692, 0x3349, 0x19a4, 0x4cd2, 0x2669, 0x1334, 0x499a, 0x24cd, + 0x1266, 0x0933, 0x4499, 0x224c, 0x5126, 0x2893, 0x5449, 0x2a24, 0x5512, 0x2a89, + 0x1544, 0x4aa2, 0x2551, 0x12a8, 0x4954, 0x64aa, 0x3255, 0x192a, 0x0c95, 0x064a, + 0x0325, 0x0192, 0x00c9, 0x0064, 0x4032, 0x2019, 0x100c, 0x4806, 0x2403, 0x5201, + 0x2900, 0x5480, 0x6a40, 0x7520, 0x7a90, 0x7d48, 0x7ea4, 0x7f52, 0x3fa9, 0x1fd4, + 0x4fea, 0x27f5, 0x13fa, 0x09fd, 0x04fe, 0x027f, 0x413f, 0x609f, 0x704f, 0x7827, + 0x7c13, 0x7e09, 0x3f04, 0x5f82, 0x2fc1, 0x17e0, 0x4bf0, 0x65f8, 0x72fc, 0x797e, + 0x3cbf, 0x5e5f, 0x6f2f, 0x7797, 0x7bcb, 0x7de5, 0x3ef2, 0x1f79, 0x0fbc, 0x47de, + 0x23ef, 0x51f7, 0x68fb, 0x747d, 0x3a3e, 0x1d1f, 0x4e8f, 0x6747, 0x73a3, 0x79d1, + 0x3ce8, 0x5e74, 0x6f3a, 0x379d, 0x1bce, 0x0de7, 0x46f3, 0x6379, 0x31bc, 0x58de, + 0x2c6f, 0x5637, 0x6b1b, 0x758d, 0x3ac6, 0x1d63, 0x4eb1, 0x2758, 0x53ac, 0x69d6, + 0x34eb, 0x5a75, 0x2d3a, 0x169d, 0x0b4e, 0x05a7, 0x42d3, 0x6169, 0x30b4, 0x585a, + 0x2c2d, 0x1616, 0x0b0b, 0x4585, 0x22c2, 0x1161, 0x08b0, 0x4458, 0x622c, 0x7116, + 0x388b, 0x5c45, 0x2e22, 0x1711, 0x0b88, 0x45c4, 0x62e2, 0x3171, 0x18b8, 0x4c5c, + 0x662e, 0x3317, 0x598b, 0x6cc5, 0x3662, 0x1b31, 0x0d98, 0x46cc, 0x6366, 0x31b3, + 0x58d9, 0x2c6c, 0x5636, 0x2b1b, 0x558d, 0x2ac6, 0x1563, 0x4ab1, 0x2558, 0x52ac, + 0x6956, 0x34ab, 0x5a55, 0x2d2a, 0x1695, 0x0b4a, 0x05a5, 0x02d2, 0x0169, 0x00b4, + 0x405a, 0x202d, 0x1016, 0x080b, 0x4405, 0x2202, 0x1101, 0x0880, 0x4440, 0x6220, + 0x7110, 0x7888, 0x7c44, 0x7e22, 0x3f11, 0x1f88, 0x4fc4, 0x67e2, 0x33f1, 0x19f8, + 0x4cfc, 0x667e, 0x333f, 0x599f, 0x6ccf, 0x7667, 0x7b33, 0x7d99, 0x3ecc, 0x5f66, + 0x2fb3, 0x57d9, 0x2bec, 0x55f6, 0x2afb, 0x557d, 0x2abe, 0x155f, 0x4aaf, 0x6557, + 0x72ab, 0x7955, 0x3caa, 0x1e55, 0x0f2a, 0x0795, 0x03ca, 0x01e5, 0x00f2, 0x0079, + 0x003c, 0x401e, 0x200f, 0x5007, 0x6803, 0x7401, 0x3a00, 0x5d00, 0x6e80, 0x7740, + 0x7ba0, 0x7dd0, 0x7ee8, 0x7f74, 0x7fba, 0x3fdd, 0x1fee, 0x0ff7, 0x47fb, 0x63fd, + 0x31fe, 0x18ff, 0x4c7f, 0x663f, 0x731f, 0x798f, 0x7cc7, 0x7e63, 0x7f31, 0x3f98, + 0x5fcc, 0x6fe6, 0x37f3, 0x5bf9, 0x2dfc, 0x56fe, 0x2b7f, 0x55bf, 0x6adf, 0x756f, + 0x7ab7, 0x7d5b, 0x7ead, 0x3f56, 0x1fab, 0x4fd5, 0x27ea, 0x13f5, 0x09fa, 0x04fd, + 0x027e, 0x013f, 0x409f, 0x604f, 0x7027, 0x7813, 0x7c09, 0x3e04, 0x5f02, 0x2f81, + 0x17c0, 0x4be0, 0x65f0, 0x72f8, 0x797c, 0x7cbe, 0x3e5f, 0x5f2f, 0x6f97, 0x77cb, + 0x7be5, 0x3df2, 0x1ef9, 0x0f7c, 0x47be, 0x23df, 0x51ef, 0x68f7, 0x747b, 0x7a3d, + 0x3d1e, 0x1e8f, 0x4f47, 0x67a3, 0x73d1, 0x39e8, 0x5cf4, 0x6e7a, 0x373d, 0x1b9e, + 0x0dcf, 0x46e7, 0x6373, 0x71b9, 0x38dc, 0x5c6e, 0x2e37, 0x571b, 0x6b8d, 0x35c6, + 0x1ae3, 0x4d71, 0x26b8, 0x535c, 0x69ae, 0x34d7, 0x5a6b, 0x6d35, 0x369a, 0x1b4d, + 0x0da6, 0x06d3, 0x4369, 0x21b4, 0x50da, 0x286d, 0x1436, 0x0a1b, 0x450d, 0x2286, + 0x1143, 0x48a1, 0x2450, 0x5228, 0x6914, 0x748a, 0x3a45, 0x1d22, 0x0e91, 0x0748, + 0x43a4, 0x61d2, 0x30e9, 0x1874, 0x4c3a, 0x261d, 0x130e, 0x0987, 0x44c3, 0x6261, + 0x3130, 0x5898, 0x6c4c, 0x7626, 0x3b13, 0x5d89, 0x2ec4, 0x5762, 0x2bb1, 0x15d8, + 0x4aec, 0x6576, 0x32bb, 0x595d, 0x2cae, 0x1657, 0x4b2b, 0x6595, 0x32ca, 0x1965, + 0x0cb2, 0x0659, 0x032c, 0x4196, 0x20cb, 0x5065, 0x2832, 0x1419, 0x0a0c, 0x4506, + 0x2283, 0x5141, 0x28a0, 0x5450, 0x6a28, 0x7514, 0x7a8a, 0x3d45, 0x1ea2, 0x0f51, + 0x07a8, 0x43d4, 0x61ea, 0x30f5, 0x187a, 0x0c3d, 0x061e, 0x030f, 0x4187, 0x60c3, + 0x7061, 0x3830, 0x5c18, 0x6e0c, 0x7706, 0x3b83, 0x5dc1, 0x2ee0, 0x5770, 0x6bb8, + 0x75dc, 0x7aee, 0x3d77, 0x5ebb, 0x6f5d, 0x37ae, 0x1bd7, 0x4deb, 0x66f5, 0x337a, + 0x19bd, 0x0cde, 0x066f, 0x4337, 0x619b, 0x70cd, 0x3866, 0x1c33, 0x4e19, 0x270c, + 0x5386, 0x29c3, 0x54e1, 0x2a70, 0x5538, 0x6a9c, 0x754e, 0x3aa7, 0x5d53, 0x6ea9, + 0x3754, 0x5baa, 0x2dd5, 0x16ea, 0x0b75, 0x05ba, 0x02dd, 0x016e, 0x00b7, 0x405b, + 0x602d, 0x3016, 0x180b, 0x4c05, 0x2602, 0x1301, 0x0980, 0x44c0, 0x6260, 0x7130, + 0x7898, 0x7c4c, 0x7e26, 0x3f13, 0x5f89, 0x2fc4, 0x57e2, 0x2bf1, 0x15f8, 0x4afc, + 0x657e, 0x32bf, 0x595f, 0x6caf, 0x7657, 0x7b2b, 0x7d95, 0x3eca, 0x1f65, 0x0fb2, + 0x07d9, 0x03ec, 0x41f6, 0x20fb, 0x507d, 0x283e, 0x141f, 0x4a0f, 0x6507, 0x7283, + 0x7941, 0x3ca0, 0x5e50, 0x6f28, 0x7794, 0x7bca, 0x3de5, 0x1ef2, 0x0f79, 0x07bc, + 0x43de, 0x21ef, 0x50f7, 0x687b, 0x743d, 0x3a1e, 0x1d0f, 0x4e87, 0x6743, 0x73a1, + 0x39d0, 0x5ce8, 0x6e74, 0x773a, 0x3b9d, 0x1dce, 0x0ee7, 0x4773, 0x63b9, 0x31dc, + 0x58ee, 0x2c77, 0x563b, 0x6b1d, 0x358e, 0x1ac7, 0x4d63, 0x66b1, 0x3358, 0x59ac, + 0x6cd6, 0x366b, 0x5b35, 0x2d9a, 0x16cd, 0x0b66, 0x05b3, 0x42d9, 0x216c, 0x50b6, + 0x285b, 0x542d, 0x2a16, 0x150b, 0x4a85, 0x2542, 0x12a1, 0x0950, 0x44a8, 0x6254, + 0x712a, 0x3895, 0x1c4a, 0x0e25, 0x0712, 0x0389, 0x01c4, 0x40e2, 0x2071, 0x1038, + 0x481c, 0x640e, 0x3207, 0x5903, 0x6c81, 0x3640, 0x5b20, 0x6d90, 0x76c8, 0x7b64, + 0x7db2, 0x3ed9, 0x1f6c, 0x4fb6, 0x27db, 0x53ed, 0x29f6, 0x14fb, 0x4a7d, 0x253e, + 0x129f, 0x494f, 0x64a7, 0x7253, 0x7929, 0x3c94, 0x5e4a, 0x2f25, 0x1792, 0x0bc9, + 0x05e4, 0x42f2, 0x2179, 0x10bc, 0x485e, 0x242f, 0x5217, 0x690b, 0x7485, 0x3a42, + 0x1d21, 0x0e90, 0x4748, 0x63a4, 0x71d2, 0x38e9, 0x1c74, 0x4e3a, 0x271d, 0x138e, + 0x09c7, 0x44e3, 0x6271, 0x3138, 0x589c, 0x6c4e, 0x3627, 0x5b13, 0x6d89, 0x36c4, + 0x5b62, 0x2db1, 0x16d8, 0x4b6c, 0x65b6, 0x32db, 0x596d, 0x2cb6, 0x165b, 0x4b2d, + 0x2596, 0x12cb, 0x4965, 0x24b2, 0x1259, 0x092c, 0x4496, 0x224b, 0x5125, 0x2892, + 0x1449, 0x0a24, 0x4512, 0x2289, 0x1144, 0x48a2, 0x2451, 0x1228, 0x4914, 0x648a, + 0x3245, 0x1922, 0x0c91, 0x0648, 0x4324, 0x6192, 0x30c9, 0x1864, 0x4c32, 0x2619, + 0x130c, 0x4986, 0x24c3, 0x5261, 0x2930, 0x5498, 0x6a4c, 0x7526, 0x3a93, 0x5d49, + 0x2ea4, 0x5752, 0x2ba9, 0x15d4, 0x4aea, 0x2575, 0x12ba, 0x095d, 0x04ae, 0x0257, + 0x412b, 0x6095, 0x304a, 0x1825, 0x0c12, 0x0609, 0x0304, 0x4182, 0x20c1, 0x1060, + 0x4830, 0x6418, 0x720c, 0x7906, 0x3c83, 0x5e41, 0x2f20, 0x5790, 0x6bc8, 0x75e4, + 0x7af2, 0x3d79, 0x1ebc, 0x4f5e, 0x27af, 0x53d7, 0x69eb, 0x74f5, 0x3a7a, 0x1d3d, + 0x0e9e, 0x074f, 0x43a7, 0x61d3, 0x70e9, 0x3874, 0x5c3a, 0x2e1d, 0x170e, 0x0b87, + 0x45c3, 0x62e1, 0x3170, 0x58b8, 0x6c5c, 0x762e, 0x3b17, 0x5d8b, 0x6ec5, 0x3762, + 0x1bb1, 0x0dd8, 0x46ec, 0x6376, 0x31bb, 0x58dd, 0x2c6e, 0x1637, 0x4b1b, 0x658d, + 0x32c6, 0x1963, 0x4cb1, 0x2658, 0x532c, 0x6996, 0x34cb, 0x5a65, 0x2d32, 0x1699, + 0x0b4c, 0x45a6, 0x22d3, 0x5169, 0x28b4, 0x545a, 0x2a2d, 0x1516, 0x0a8b, 0x4545, + 0x22a2, 0x1151, 0x08a8, 0x4454, 0x622a, 0x3115, 0x188a, 0x0c45, 0x0622, 0x0311, + 0x0188, 0x40c4, 0x6062, 0x3031, 0x1818, 0x4c0c, 0x6606, 0x3303, 0x5981, 0x2cc0, + 0x5660, 0x6b30, 0x7598, 0x7acc, 0x7d66, 0x3eb3, 0x5f59, 0x2fac, 0x57d6, 0x2beb, + 0x55f5, 0x2afa, 0x157d, 0x0abe, 0x055f, 0x42af, 0x6157, 0x70ab, 0x7855, 0x3c2a, + 0x1e15, 0x0f0a, 0x0785, 0x03c2, 0x01e1, 0x00f0, 0x4078, 0x603c, 0x701e, 0x380f, + 0x5c07, 0x6e03, 0x7701, 0x3b80, 0x5dc0, 0x6ee0, 0x7770, 0x7bb8, 0x7ddc, 0x7eee, + 0x3f77, 0x5fbb, 0x6fdd, 0x37ee, 0x1bf7, 0x4dfb, 0x66fd, 0x337e, 0x19bf, 0x4cdf, + 0x666f, 0x7337, 0x799b, 0x7ccd, 0x3e66, 0x1f33, 0x4f99, 0x27cc, 0x53e6, 0x29f3, + 0x54f9, 0x2a7c, 0x553e, 0x2a9f, 0x554f, 0x6aa7, 0x7553, 0x7aa9, 0x3d54, 0x5eaa, + 0x2f55, 0x17aa, 0x0bd5, 0x05ea, 0x02f5, 0x017a, 0x00bd, 0x005e, 0x002f, 0x4017, + 0x600b, 0x7005, 0x3802, 0x1c01, 0x0e00, 0x4700, 0x6380, 0x71c0, 0x78e0, 0x7c70, + 0x7e38, 0x7f1c, 0x7f8e, 0x3fc7, 0x5fe3, 0x6ff1, 0x37f8, 0x5bfc, 0x6dfe, 0x36ff, + 0x5b7f, 0x6dbf, 0x76df, 0x7b6f, 0x7db7, 0x7edb, 0x7f6d, 0x3fb6, 0x1fdb, 0x4fed, + 0x27f6, 0x13fb, 0x49fd, 0x24fe, 0x127f, 0x493f, 0x649f, 0x724f, 0x7927, 0x7c93, + 0x7e49, 0x3f24, 0x5f92, 0x2fc9, 0x17e4, 0x4bf2, 0x25f9, 0x12fc, 0x497e, 0x24bf, + 0x525f, 0x692f, 0x7497, 0x7a4b, 0x7d25, 0x3e92, 0x1f49, 0x0fa4, 0x47d2, 0x23e9, + 0x11f4, 0x48fa, 0x247d, 0x123e, 0x091f, 0x448f, 0x6247, 0x7123, 0x7891, 0x3c48, + 0x5e24, 0x6f12, 0x3789, 0x1bc4, 0x4de2, 0x26f1, 0x1378, 0x49bc, 0x64de, 0x326f, + 0x5937, 0x6c9b, 0x764d, 0x3b26, 0x1d93, 0x4ec9, 0x2764, 0x53b2, 0x29d9, 0x14ec, + 0x4a76, 0x253b, 0x529d, 0x294e, 0x14a7, 0x4a53, 0x6529, 0x3294, 0x594a, 0x2ca5, + 0x1652, 0x0b29, 0x0594, 0x42ca, 0x2165, 0x10b2, 0x0859, 0x042c, 0x4216, 0x210b, + 0x5085, 0x2842, 0x1421, 0x0a10, 0x4508, 0x6284, 0x7142, 0x38a1, 0x1c50, 0x4e28, + 0x6714, 0x738a, 0x39c5, 0x1ce2, 0x0e71, 0x0738, 0x439c, 0x61ce, 0x30e7, 0x5873, + 0x6c39, 0x361c, 0x5b0e, 0x2d87, 0x56c3, 0x6b61, 0x35b0, 0x5ad8, 0x6d6c, 0x76b6, + 0x3b5b, 0x5dad, 0x2ed6, 0x176b, 0x4bb5, 0x25da, 0x12ed, 0x0976, 0x04bb, 0x425d, + 0x212e, 0x1097, 0x484b, 0x6425, 0x3212, 0x1909, 0x0c84, 0x4642, 0x2321, 0x1190, + 0x48c8, 0x6464, 0x7232, 0x3919, 0x1c8c, 0x4e46, 0x2723, 0x5391, 0x29c8, 0x54e4, + 0x6a72, 0x3539, 0x1a9c, 0x4d4e, 0x26a7, 0x5353, 0x69a9, 0x34d4, 0x5a6a, 0x2d35, + 0x169a, 0x0b4d, 0x05a6, 0x02d3, 0x4169, 0x20b4, 0x505a, 0x282d, 0x1416, 0x0a0b, + 0x4505, 0x2282, 0x1141, 0x08a0, 0x4450, 0x6228, 0x7114, 0x788a, 0x3c45, 0x1e22, + 0x0f11, 0x0788, 0x43c4, 0x61e2, 0x30f1, 0x1878, 0x4c3c, 0x661e, 0x330f, 0x5987, + 0x6cc3, 0x7661, 0x3b30, 0x5d98, 0x6ecc, 0x7766, 0x3bb3, 0x5dd9, 0x2eec, 0x5776, + 0x2bbb, 0x55dd, 0x2aee, 0x1577, 0x4abb, 0x655d, 0x32ae, 0x1957, 0x4cab, 0x6655, + 0x332a, 0x1995, 0x0cca, 0x0665, 0x0332, 0x0199, 0x00cc, 0x4066, 0x2033, 0x5019, + 0x280c, 0x5406, 0x2a03, 0x5501, 0x2a80, 0x5540, 0x6aa0, 0x7550, 0x7aa8, 0x7d54, + 0x7eaa, 0x3f55, 0x1faa, 0x0fd5, 0x07ea, 0x03f5, 0x01fa, 0x00fd, 0x007e, 0x003f, + 0x401f, 0x600f, 0x7007, 0x7803, 0x7c01, 0x3e00, 0x5f00, 0x6f80, 0x77c0, 0x7be0, + 0x7df0, 0x7ef8, 0x7f7c, 0x7fbe, 0x3fdf, 0x5fef, 0x6ff7, 0x77fb, 0x7bfd, 0x3dfe, + 0x1eff, 0x4f7f, 0x67bf, 0x73df, 0x79ef, 0x7cf7, 0x7e7b, 0x7f3d, 0x3f9e, 0x1fcf, + 0x4fe7, 0x67f3, 0x73f9, 0x39fc, 0x5cfe, 0x2e7f, 0x573f, 0x6b9f, 0x75cf, 0x7ae7, + 0x7d73, 0x7eb9, 0x3f5c, 0x5fae, 0x2fd7, 0x57eb, 0x6bf5, 0x35fa, 0x1afd, 0x0d7e, + 0x06bf, 0x435f, 0x61af, 0x70d7, 0x786b, 0x7c35, 0x3e1a, 0x1f0d, 0x0f86, 0x07c3, + 0x43e1, 0x21f0, 0x50f8, 0x687c, 0x743e, 0x3a1f, 0x5d0f, 0x6e87, 0x7743, 0x7ba1, + 0x3dd0, 0x5ee8, 0x6f74, 0x77ba, 0x3bdd, 0x1dee, 0x0ef7, 0x477b, 0x63bd, 0x31de, + 0x18ef, 0x4c77, 0x663b, 0x731d, 0x398e, 0x1cc7, 0x4e63, 0x6731, 0x3398, 0x59cc, + 0x6ce6, 0x3673, 0x5b39, 0x2d9c, 0x56ce, 0x2b67, 0x55b3, 0x6ad9, 0x356c, 0x5ab6, + 0x2d5b, 0x56ad, 0x2b56, 0x15ab, 0x4ad5, 0x256a, 0x12b5, 0x095a, 0x04ad, 0x0256, + 0x012b, 0x4095, 0x204a, 0x1025, 0x0812, 0x0409, 0x0204, 0x4102, 0x2081, 0x1040, + 0x4820, 0x6410, 0x7208, 0x7904, 0x7c82, 0x3e41, 0x1f20, 0x4f90, 0x67c8, 0x73e4, + 0x79f2, 0x3cf9, 0x1e7c, 0x4f3e, 0x279f, 0x53cf, 0x69e7, 0x74f3, 0x7a79, 0x3d3c, + 0x5e9e, 0x2f4f, 0x57a7, 0x6bd3, 0x75e9, 0x3af4, 0x5d7a, 0x2ebd, 0x175e, 0x0baf, + 0x45d7, 0x62eb, 0x7175, 0x38ba, 0x1c5d, 0x0e2e, 0x0717, 0x438b, 0x61c5, 0x30e2, + 0x1871, 0x0c38, 0x461c, 0x630e, 0x3187, 0x58c3, 0x6c61, 0x3630, 0x5b18, 0x6d8c, + 0x76c6, 0x3b63, 0x5db1, 0x2ed8, 0x576c, 0x6bb6, 0x35db, 0x5aed, 0x2d76, 0x16bb, + 0x4b5d, 0x25ae, 0x12d7, 0x496b, 0x64b5, 0x325a, 0x192d, 0x0c96, 0x064b, 0x4325, + 0x2192, 0x10c9, 0x0864, 0x4432, 0x2219, 0x110c, 0x4886, 0x2443, 0x5221, 0x2910, + 0x5488, 0x6a44, 0x7522, 0x3a91, 0x1d48, 0x4ea4, 0x6752, 0x33a9, 0x19d4, 0x4cea, + 0x2675, 0x133a, 0x099d, 0x04ce, 0x0267, 0x4133, 0x6099, 0x304c, 0x5826, 0x2c13, + 0x5609, 0x2b04, 0x5582, 0x2ac1, 0x1560, 0x4ab0, 0x6558, 0x72ac, 0x7956, 0x3cab, + 0x5e55, 0x2f2a, 0x1795, 0x0bca, 0x05e5, 0x02f2, 0x0179, 0x00bc, 0x405e, 0x202f, + 0x5017, 0x680b, 0x7405, 0x3a02, 0x1d01, 0x0e80, 0x4740, 0x63a0, 0x71d0, 0x78e8, + 0x7c74, 0x7e3a, 0x3f1d, 0x1f8e, 0x0fc7, 0x47e3, 0x63f1, 0x31f8, 0x58fc, 0x6c7e, + 0x363f, 0x5b1f, 0x6d8f, 0x76c7, 0x7b63, 0x7db1, 0x3ed8, 0x5f6c, 0x6fb6, 0x37db, + 0x5bed, 0x2df6, 0x16fb, 0x4b7d, 0x25be, 0x12df, 0x496f, 0x64b7, 0x725b, 0x792d, + 0x3c96, 0x1e4b, 0x4f25, 0x2792, 0x13c9, 0x09e4, 0x44f2, 0x2279, 0x113c, 0x489e, + 0x244f, 0x5227, 0x6913, 0x7489, 0x3a44, 0x5d22, 0x2e91, 0x1748, 0x4ba4, 0x65d2, + 0x32e9, 0x1974, 0x4cba, 0x265d, 0x132e, 0x0997, 0x44cb, 0x6265, 0x3132, 0x1899, + 0x0c4c, 0x4626, 0x2313, 0x5189, 0x28c4, 0x5462, 0x2a31, 0x1518, 0x4a8c, 0x6546, + 0x32a3, 0x5951, 0x2ca8, 0x5654, 0x6b2a, 0x3595, 0x1aca, 0x0d65, 0x06b2, 0x0359, + 0x01ac, 0x40d6, 0x206b, 0x5035, 0x281a, 0x140d, 0x0a06, 0x0503, 0x4281, 0x2140, + 0x50a0, 0x6850, 0x7428, 0x7a14, 0x7d0a, 0x3e85, 0x1f42, 0x0fa1, 0x07d0, 0x43e8, + 0x61f4, 0x70fa, 0x387d, 0x1c3e, 0x0e1f, 0x470f, 0x6387, 0x71c3, 0x78e1, 0x3c70, + 0x5e38, 0x6f1c, 0x778e, 0x3bc7, 0x5de3, 0x6ef1, 0x3778, 0x5bbc, 0x6dde, 0x36ef, + 0x5b77, 0x6dbb, 0x76dd, 0x3b6e, 0x1db7, 0x4edb, 0x676d, 0x33b6, 0x19db, 0x4ced, + 0x2676, 0x133b, 0x499d, 0x24ce, 0x1267, 0x4933, 0x6499, 0x324c, 0x5926, 0x2c93, + 0x5649, 0x2b24, 0x5592, 0x2ac9, 0x1564, 0x4ab2, 0x2559, 0x12ac, 0x4956, 0x24ab, + 0x5255, 0x292a, 0x1495, 0x0a4a, 0x0525, 0x0292, 0x0149, 0x00a4, 0x4052, 0x2029, + 0x1014, 0x480a, 0x2405, 0x1202, 0x0901, 0x0480, 0x4240, 0x6120, 0x7090, 0x7848, + 0x7c24, 0x7e12, 0x3f09, 0x1f84, 0x4fc2, 0x27e1, 0x13f0, 0x49f8, 0x64fc, 0x727e, + 0x393f, 0x5c9f, 0x6e4f, 0x7727, 0x7b93, 0x7dc9, 0x3ee4, 0x5f72, 0x2fb9, 0x17dc, + 0x4bee, 0x25f7, 0x52fb, 0x697d, 0x34be, 0x1a5f, 0x4d2f, 0x6697, 0x734b, 0x79a5, + 0x3cd2, 0x1e69, 0x0f34, 0x479a, 0x23cd, 0x11e6, 0x08f3, 0x4479, 0x223c, 0x511e, + 0x288f, 0x5447, 0x6a23, 0x7511, 0x3a88, 0x5d44, 0x6ea2, 0x3751, 0x1ba8, 0x4dd4, + 0x66ea, 0x3375, 0x19ba, 0x0cdd, 0x066e, 0x0337, 0x419b, 0x60cd, 0x3066, 0x1833, + 0x4c19, 0x260c, 0x5306, 0x2983, 0x54c1, 0x2a60, 0x5530, 0x6a98, 0x754c, 0x7aa6, + 0x3d53, 0x5ea9, 0x2f54, 0x57aa, 0x2bd5, 0x15ea, 0x0af5, 0x057a, 0x02bd, 0x015e, + 0x00af, 0x4057, 0x602b, 0x7015, 0x380a, 0x1c05, 0x0e02, 0x0701, 0x0380, 0x41c0, + 0x60e0, 0x7070, 0x7838, 0x7c1c, 0x7e0e, 0x3f07, 0x5f83, 0x6fc1, 0x37e0, 0x5bf0, + 0x6df8, 0x76fc, 0x7b7e, 0x3dbf, 0x5edf, 0x6f6f, 0x77b7, 0x7bdb, 0x7ded, 0x3ef6, + 0x1f7b, 0x4fbd, 0x27de, 0x13ef, 0x49f7, 0x64fb, 0x727d, 0x393e, 0x1c9f, 0x4e4f, + 0x6727, 0x7393, 0x79c9, 0x3ce4, 0x5e72, 0x2f39, 0x179c, 0x4bce, 0x25e7, 0x52f3, + 0x6979, 0x34bc, 0x5a5e, 0x2d2f, 0x5697, 0x6b4b, 0x75a5, 0x3ad2, 0x1d69, 0x0eb4, + 0x475a, 0x23ad, 0x11d6, 0x08eb, 0x4475, 0x223a, 0x111d, 0x088e, 0x0447, 0x4223, + 0x6111, 0x3088, 0x5844, 0x6c22, 0x3611, 0x1b08, 0x4d84, 0x66c2, 0x3361, 0x19b0, + 0x4cd8, 0x666c, 0x7336, 0x399b, 0x5ccd, 0x2e66, 0x1733, 0x4b99, 0x25cc, 0x52e6, + 0x2973, 0x54b9, 0x2a5c, 0x552e, 0x2a97, 0x554b, 0x6aa5, 0x3552, 0x1aa9, 0x0d54, + 0x46aa, 0x2355, 0x11aa, 0x08d5, 0x046a, 0x0235, 0x011a, 0x008d, 0x0046, 0x0023, + 0x4011, 0x2008, 0x5004, 0x6802, 0x3401, 0x1a00, 0x4d00, 0x6680, 0x7340, 0x79a0, + 0x7cd0, 0x7e68, 0x7f34, 0x7f9a, 0x3fcd, 0x1fe6, 0x0ff3, 0x47f9, 0x23fc, 0x51fe, + 0x28ff, 0x547f, 0x6a3f, 0x751f, 0x7a8f, 0x7d47, 0x7ea3, 0x7f51, 0x3fa8, 0x5fd4, + 0x6fea, 0x37f5, 0x1bfa, 0x0dfd, 0x06fe, 0x037f, 0x41bf, 0x60df, 0x706f, 0x7837, + 0x7c1b, 0x7e0d, 0x3f06, 0x1f83, 0x4fc1, 0x27e0, 0x53f0, 0x69f8, 0x74fc, 0x7a7e, + 0x3d3f, 0x5e9f, 0x6f4f, 0x77a7, 0x7bd3, 0x7de9, 0x3ef4, 0x5f7a, 0x2fbd, 0x17de, + 0x0bef, 0x45f7, 0x62fb, 0x717d, 0x38be, 0x1c5f, 0x4e2f, 0x6717, 0x738b, 0x79c5, + 0x3ce2, 0x1e71, 0x0f38, 0x479c, 0x63ce, 0x31e7, 0x58f3, 0x6c79, 0x363c, 0x5b1e, + 0x2d8f, 0x56c7, 0x6b63, 0x75b1, 0x3ad8, 0x5d6c, 0x6eb6, 0x375b, 0x5bad, 0x2dd6, + 0x16eb, 0x4b75, 0x25ba, 0x12dd, 0x096e, 0x04b7, 0x425b, 0x612d, 0x3096, 0x184b, + 0x4c25, 0x2612, 0x1309, 0x0984, 0x44c2, 0x2261, 0x1130, 0x4898, 0x644c, 0x7226, + 0x3913, 0x5c89, 0x2e44, 0x5722, 0x2b91, 0x15c8, 0x4ae4, 0x6572, 0x32b9, 0x195c, + 0x4cae, 0x2657, 0x532b, 0x6995, 0x34ca, 0x1a65, 0x0d32, 0x0699, 0x034c, 0x41a6, + 0x20d3, 0x5069, 0x2834, 0x541a, 0x2a0d, 0x1506, 0x0a83, 0x4541, 0x22a0, 0x5150, + 0x68a8, 0x7454, 0x7a2a, 0x3d15, 0x1e8a, 0x0f45, 0x07a2, 0x03d1, 0x01e8, 0x40f4, + 0x607a, 0x303d, 0x181e, 0x0c0f, 0x4607, 0x6303, 0x7181, 0x38c0, 0x5c60, 0x6e30, + 0x7718, 0x7b8c, 0x7dc6, 0x3ee3, 0x5f71, 0x2fb8, 0x57dc, 0x6bee, 0x35f7, 0x5afb, + 0x6d7d, 0x36be, 0x1b5f, 0x4daf, 0x66d7, 0x736b, 0x79b5, 0x3cda, 0x1e6d, 0x0f36, + 0x079b, 0x43cd, 0x21e6, 0x10f3, 0x4879, 0x243c, 0x521e, 0x290f, 0x5487, 0x6a43, + 0x7521, 0x3a90, 0x5d48, 0x6ea4, 0x7752, 0x3ba9, 0x1dd4, 0x4eea, 0x2775, 0x13ba, + 0x09dd, 0x04ee, 0x0277, 0x413b, 0x609d, 0x304e, 0x1827, 0x4c13, 0x6609, 0x3304, + 0x5982, 0x2cc1, 0x1660, 0x4b30, 0x6598, 0x72cc, 0x7966, 0x3cb3, 0x5e59, 0x2f2c, + 0x5796, 0x2bcb, 0x55e5, 0x2af2, 0x1579, 0x0abc, 0x455e, 0x22af, 0x5157, 0x68ab, + 0x7455, 0x3a2a, 0x1d15, 0x0e8a, 0x0745, 0x03a2, 0x01d1, 0x00e8, 0x4074, 0x603a, + 0x301d, 0x180e, 0x0c07, 0x4603, 0x6301, 0x3180, 0x58c0, 0x6c60, 0x7630, 0x7b18, + 0x7d8c, 0x7ec6, 0x3f63, 0x5fb1, 0x2fd8, 0x57ec, 0x6bf6, 0x35fb, 0x5afd, 0x2d7e, + 0x16bf, 0x4b5f, 0x65af, 0x72d7, 0x796b, 0x7cb5, 0x3e5a, 0x1f2d, 0x0f96, 0x07cb, + 0x43e5, 0x21f2, 0x10f9, 0x087c, 0x443e, 0x221f, 0x510f, 0x6887, 0x7443, 0x7a21, + 0x3d10, 0x5e88, 0x6f44, 0x77a2, 0x3bd1, 0x1de8, 0x4ef4, 0x677a, 0x33bd, 0x19de, + 0x0cef, 0x4677, 0x633b, 0x719d, 0x38ce, 0x1c67, 0x4e33, 0x6719, 0x338c, 0x59c6, + 0x2ce3, 0x5671, 0x2b38, 0x559c, 0x6ace, 0x3567, 0x5ab3, 0x6d59, 0x36ac, 0x5b56, + 0x2dab, 0x56d5, 0x2b6a, 0x15b5, 0x0ada, 0x056d, 0x02b6, 0x015b, 0x40ad, 0x2056, + 0x102b, 0x4815, 0x240a, 0x1205, 0x0902, 0x0481, 0x0240, 0x4120, 0x6090, 0x7048, + 0x7824, 0x7c12, 0x3e09, 0x1f04, 0x4f82, 0x27c1, 0x13e0, 0x49f0, 0x64f8, 0x727c, + 0x793e, 0x3c9f, 0x5e4f, 0x6f27, 0x7793, 0x7bc9, 0x3de4, 0x5ef2, 0x2f79, 0x17bc, + 0x4bde, 0x25ef, 0x52f7, 0x697b, 0x74bd, 0x3a5e, 0x1d2f, 0x4e97, 0x674b, 0x73a5, + 0x39d2, 0x1ce9, 0x0e74, 0x473a, 0x239d, 0x11ce, 0x08e7, 0x4473, 0x6239, 0x311c, + 0x588e, 0x2c47, 0x5623, 0x6b11, 0x3588, 0x5ac4, 0x6d62, 0x36b1, 0x1b58, 0x4dac, + 0x66d6, 0x336b, 0x59b5, 0x2cda, 0x166d, 0x0b36, 0x059b, 0x42cd, 0x2166, 0x10b3, + 0x4859, 0x242c, 0x5216, 0x290b, 0x5485, 0x2a42, 0x1521, 0x0a90, 0x4548, 0x62a4, + 0x7152, 0x38a9, 0x1c54, 0x4e2a, 0x2715, 0x138a, 0x09c5, 0x04e2, 0x0271, 0x0138, + 0x409c, 0x604e, 0x3027, 0x5813, 0x6c09, 0x3604, 0x5b02, 0x2d81, 0x16c0, 0x4b60, + 0x65b0, 0x72d8, 0x796c, 0x7cb6, 0x3e5b, 0x5f2d, 0x2f96, 0x17cb, 0x4be5, 0x25f2, + 0x12f9, 0x097c, 0x44be, 0x225f, 0x512f, 0x6897, 0x744b, 0x7a25, 0x3d12, 0x1e89, + 0x0f44, 0x47a2, 0x23d1, 0x11e8, 0x48f4, 0x647a, 0x323d, 0x191e, 0x0c8f, 0x4647, + 0x6323, 0x7191, 0x38c8, 0x5c64, 0x6e32, 0x3719, 0x1b8c, 0x4dc6, 0x26e3, 0x5371, + 0x29b8, 0x54dc, 0x6a6e, 0x3537, 0x5a9b, 0x6d4d, 0x36a6, 0x1b53, 0x4da9, 0x26d4, + 0x536a, 0x29b5, 0x14da, 0x0a6d, 0x0536, 0x029b, 0x414d, 0x20a6, 0x1053, 0x4829, + 0x2414, 0x520a, 0x2905, 0x1482, 0x0a41, 0x0520, 0x4290, 0x6148, 0x70a4, 0x7852, + 0x3c29, 0x1e14, 0x4f0a, 0x2785, 0x13c2, 0x09e1, 0x04f0, 0x4278, 0x613c, 0x709e, + 0x384f, 0x5c27, 0x6e13, 0x7709, 0x3b84, 0x5dc2, 0x2ee1, 0x1770, 0x4bb8, 0x65dc, + 0x72ee, 0x3977, 0x5cbb, 0x6e5d, 0x372e, 0x1b97, 0x4dcb, 0x66e5, 0x3372, 0x19b9, + 0x0cdc, 0x466e, 0x2337, 0x519b, 0x68cd, 0x3466, 0x1a33, 0x4d19, 0x268c, 0x5346, + 0x29a3, 0x54d1, 0x2a68, 0x5534, 0x6a9a, 0x354d, 0x1aa6, 0x0d53, 0x46a9, 0x2354, + 0x51aa, 0x28d5, 0x146a, 0x0a35, 0x051a, 0x028d, 0x0146, 0x00a3, 0x4051, 0x2028, + 0x5014, 0x680a, 0x3405, 0x1a02, 0x0d01, 0x0680, 0x4340, 0x61a0, 0x70d0, 0x7868, + 0x7c34, 0x7e1a, 0x3f0d, 0x1f86, 0x0fc3, 0x47e1, 0x23f0, 0x51f8, 0x68fc, 0x747e, + 0x3a3f, 0x5d1f, 0x6e8f, 0x7747, 0x7ba3, 0x7dd1, 0x3ee8, 0x5f74, 0x6fba, 0x37dd, + 0x1bee, 0x0df7, 0x46fb, 0x637d, 0x31be, 0x18df, 0x4c6f, 0x6637, 0x731b, 0x798d, + 0x3cc6, 0x1e63, 0x4f31, 0x2798, 0x53cc, 0x69e6, 0x34f3, 0x5a79, 0x2d3c, 0x569e, + 0x2b4f, 0x55a7, 0x6ad3, 0x7569, 0x3ab4, 0x5d5a, 0x2ead, 0x1756, 0x0bab, 0x45d5, + 0x22ea, 0x1175, 0x08ba, 0x045d, 0x022e, 0x0117, 0x408b, 0x6045, 0x3022, 0x1811, + 0x0c08, 0x4604, 0x6302, 0x3181, 0x18c0, 0x4c60, 0x6630, 0x7318, 0x798c, 0x7cc6, + 0x3e63, 0x5f31, 0x2f98, 0x57cc, 0x6be6, 0x35f3, 0x5af9, 0x2d7c, 0x56be, 0x2b5f, + 0x55af, 0x6ad7, 0x756b, 0x7ab5, 0x3d5a, 0x1ead, 0x0f56, 0x07ab, 0x43d5, 0x21ea, + 0x10f5, 0x087a, 0x043d, 0x021e, 0x010f, 0x4087, 0x6043, 0x7021, 0x3810, 0x5c08, + 0x6e04, 0x7702, 0x3b81, 0x1dc0, 0x4ee0, 0x6770, 0x73b8, 0x79dc, 0x7cee, 0x3e77, + 0x5f3b, 0x6f9d, 0x37ce, 0x1be7, 0x4df3, 0x66f9, 0x337c, 0x59be, 0x2cdf, 0x566f, + 0x6b37, 0x759b, 0x7acd, 0x3d66, 0x1eb3, 0x4f59, 0x27ac, 0x53d6, 0x29eb, 0x54f5, + 0x2a7a, 0x153d, 0x0a9e, 0x054f, 0x42a7, 0x6153, 0x70a9, 0x3854, 0x5c2a, 0x2e15, + 0x170a, 0x0b85, 0x05c2, 0x02e1, 0x0170, 0x40b8, 0x605c, 0x702e, 0x3817, 0x5c0b, + 0x6e05, 0x3702, 0x1b81, 0x0dc0, 0x46e0, 0x6370, 0x71b8, 0x78dc, 0x7c6e, 0x3e37, + 0x5f1b, 0x6f8d, 0x37c6, 0x1be3, 0x4df1, 0x26f8, 0x537c, 0x69be, 0x34df, 0x5a6f, + 0x6d37, 0x769b, 0x7b4d, 0x3da6, 0x1ed3, 0x4f69, 0x27b4, 0x53da, 0x29ed, 0x14f6, + 0x0a7b, 0x453d, 0x229e, 0x114f, 0x48a7, 0x6453, 0x7229, 0x3914, 0x5c8a, 0x2e45, + 0x1722, 0x0b91, 0x05c8, 0x42e4, 0x6172, 0x30b9, 0x185c, 0x4c2e, 0x2617, 0x530b, + 0x6985, 0x34c2, 0x1a61, 0x0d30, 0x4698, 0x634c, 0x71a6, 0x38d3, 0x5c69, 0x2e34, + 0x571a, 0x2b8d, 0x15c6, 0x0ae3, 0x4571, 0x22b8, 0x515c, 0x68ae, 0x3457, 0x5a2b, + 0x6d15, 0x368a, 0x1b45, 0x0da2, 0x06d1, 0x0368, 0x41b4, 0x60da, 0x306d, 0x1836, + 0x0c1b, 0x460d, 0x2306, 0x1183, 0x48c1, 0x2460, 0x5230, 0x6918, 0x748c, 0x7a46, + 0x3d23, 0x5e91, 0x2f48, 0x57a4, 0x6bd2, 0x35e9, 0x1af4, 0x4d7a, 0x26bd, 0x135e, + 0x09af, 0x44d7, 0x626b, 0x7135, 0x389a, 0x1c4d, 0x0e26, 0x0713, 0x4389, 0x21c4, + 0x50e2, 0x2871, 0x1438, 0x4a1c, 0x650e, 0x3287, 0x5943, 0x6ca1, 0x3650, 0x5b28, + 0x6d94, 0x76ca, 0x3b65, 0x1db2, 0x0ed9, 0x076c, 0x43b6, 0x21db, 0x50ed, 0x2876, + 0x143b, 0x4a1d, 0x250e, 0x1287, 0x4943, 0x64a1, 0x3250, 0x5928, 0x6c94, 0x764a, + 0x3b25, 0x1d92, 0x0ec9, 0x0764, 0x43b2, 0x21d9, 0x10ec, 0x4876, 0x243b, 0x521d, + 0x290e, 0x1487, 0x4a43, 0x6521, 0x3290, 0x5948, 0x6ca4, 0x7652, 0x3b29, 0x1d94, + 0x4eca, 0x2765, 0x13b2, 0x09d9, 0x04ec, 0x4276, 0x213b, 0x509d, 0x284e, 0x1427, + 0x4a13, 0x6509, 0x3284, 0x5942, 0x2ca1, 0x1650, 0x4b28, 0x6594, 0x72ca, 0x3965, + 0x1cb2, 0x0e59, 0x072c, 0x4396, 0x21cb, 0x50e5, 0x2872, 0x1439, 0x0a1c, 0x450e, + 0x2287, 0x5143, 0x68a1, 0x3450, 0x5a28, 0x6d14, 0x768a, 0x3b45, 0x1da2, 0x0ed1, + 0x0768, 0x43b4, 0x61da, 0x30ed, 0x1876, 0x0c3b, 0x461d, 0x230e, 0x1187, 0x48c3, + 0x6461, 0x3230, 0x5918, 0x6c8c, 0x7646, 0x3b23, 0x5d91, 0x2ec8, 0x5764, 0x6bb2, + 0x35d9, 0x1aec, 0x4d76, 0x26bb, 0x535d, 0x29ae, 0x14d7, 0x4a6b, 0x6535, 0x329a, + 0x194d, 0x0ca6, 0x0653, 0x4329, 0x2194, 0x50ca, 0x2865, 0x1432, 0x0a19, 0x050c, + 0x4286, 0x2143, 0x50a1, 0x2850, 0x5428, 0x6a14, 0x750a, 0x3a85, 0x1d42, 0x0ea1, + 0x0750, 0x43a8, 0x61d4, 0x70ea, 0x3875, 0x1c3a, 0x0e1d, 0x070e, 0x0387, 0x41c3, + 0x60e1, 0x3070, 0x5838, 0x6c1c, 0x760e, 0x3b07, 0x5d83, 0x6ec1, 0x3760, 0x5bb0, + 0x6dd8, 0x76ec, 0x7b76, 0x3dbb, 0x5edd, 0x2f6e, 0x17b7, 0x4bdb, 0x65ed, 0x32f6, + 0x197b, 0x4cbd, 0x265e, 0x132f, 0x4997, 0x64cb, 0x7265, 0x3932, 0x1c99, 0x0e4c, + 0x4726, 0x2393, 0x51c9, 0x28e4, 0x5472, 0x2a39, 0x151c, 0x4a8e, 0x2547, 0x52a3, + 0x6951, 0x34a8, 0x5a54, 0x6d2a, 0x3695, 0x1b4a, 0x0da5, 0x06d2, 0x0369, 0x01b4, + 0x40da, 0x206d, 0x1036, 0x081b, 0x440d, 0x2206, 0x1103, 0x4881, 0x2440, 0x5220, + 0x6910, 0x7488, 0x7a44, 0x7d22, 0x3e91, 0x1f48, 0x4fa4, 0x67d2, 0x33e9, 0x19f4, + 0x4cfa, 0x267d, 0x133e, 0x099f, 0x44cf, 0x6267, 0x7133, 0x7899, 0x3c4c, 0x5e26, + 0x2f13, 0x5789, 0x2bc4, 0x55e2, 0x2af1, 0x1578, 0x4abc, 0x655e, 0x32af, 0x5957, + 0x6cab, 0x7655, 0x3b2a, 0x1d95, 0x0eca, 0x0765, 0x03b2, 0x01d9, 0x00ec, 0x4076, + 0x203b, 0x501d, 0x280e, 0x1407, 0x4a03, 0x6501, 0x3280, 0x5940, 0x6ca0, 0x7650, + 0x7b28, 0x7d94, 0x7eca, 0x3f65, 0x1fb2, 0x0fd9, 0x07ec, 0x43f6, 0x21fb, 0x50fd, + 0x287e, 0x143f, 0x4a1f, 0x650f, 0x7287, 0x7943, 0x7ca1, 0x3e50, 0x5f28, 0x6f94, + 0x77ca, 0x3be5, 0x1df2, 0x0ef9, 0x077c, 0x43be, 0x21df, 0x50ef, 0x6877, 0x743b, + 0x7a1d, 0x3d0e, 0x1e87, 0x4f43, 0x67a1, 0x33d0, 0x59e8, 0x6cf4, 0x767a, 0x3b3d, + 0x1d9e, 0x0ecf, 0x4767, 0x63b3, 0x71d9, 0x38ec, 0x5c76, 0x2e3b, 0x571d, 0x2b8e, + 0x15c7, 0x4ae3, 0x6571, 0x32b8, 0x595c, 0x6cae, 0x3657, 0x5b2b, 0x6d95, 0x36ca, + 0x1b65, 0x0db2, 0x06d9, 0x036c, 0x41b6, 0x20db, 0x506d, 0x2836, 0x141b, 0x4a0d, + 0x2506, 0x1283, 0x4941, 0x24a0, 0x5250, 0x6928, 0x7494, 0x7a4a, 0x3d25, 0x1e92, + 0x0f49, 0x07a4, 0x43d2, 0x21e9, 0x10f4, 0x487a, 0x243d, 0x121e, 0x090f, 0x4487, + 0x6243, 0x7121, 0x3890, 0x5c48, 0x6e24, 0x7712, 0x3b89, 0x1dc4, 0x4ee2, 0x2771, + 0x13b8, 0x49dc, 0x64ee, 0x3277, 0x593b, 0x6c9d, 0x364e, 0x1b27, 0x4d93, 0x66c9, + 0x3364, 0x59b2, 0x2cd9, 0x166c, 0x4b36, 0x259b, 0x52cd, 0x2966, 0x14b3, 0x4a59, + 0x252c, 0x5296, 0x294b, 0x54a5, 0x2a52, 0x1529, 0x0a94, 0x454a, 0x22a5, 0x1152, + 0x08a9, 0x0454, 0x422a, 0x2115, 0x108a, 0x0845, 0x0422, 0x0211, 0x0108, 0x4084, + 0x6042, 0x3021, 0x1810, 0x4c08, 0x6604, 0x7302, 0x3981, 0x1cc0, 0x4e60, 0x6730, + 0x7398, 0x79cc, 0x7ce6, 0x3e73, 0x5f39, 0x2f9c, 0x57ce, 0x2be7, 0x55f3, 0x6af9, + 0x357c, 0x5abe, 0x2d5f, 0x56af, 0x6b57, 0x75ab, 0x7ad5, 0x3d6a, 0x1eb5, 0x0f5a, + 0x07ad, 0x03d6, 0x01eb, 0x40f5, 0x207a, 0x103d, 0x081e, 0x040f, 0x4207, 0x6103, + 0x7081, 0x3840, 0x5c20, 0x6e10, 0x7708, 0x7b84, 0x7dc2, 0x3ee1, 0x1f70, 0x4fb8, + 0x67dc, 0x73ee, 0x39f7, 0x5cfb, 0x6e7d, 0x373e, 0x1b9f, 0x4dcf, 0x66e7, 0x7373, + 0x79b9, 0x3cdc, 0x5e6e, 0x2f37, 0x579b, 0x6bcd, 0x35e6, 0x1af3, 0x4d79, 0x26bc, + 0x535e, 0x29af, 0x54d7, 0x6a6b, 0x7535, 0x3a9a, 0x1d4d, 0x0ea6, 0x0753, 0x43a9, + 0x21d4, 0x50ea, 0x2875, 0x143a, 0x0a1d, 0x050e, 0x0287, 0x4143, 0x60a1, 0x3050, + 0x5828, 0x6c14, 0x760a, 0x3b05, 0x1d82, 0x0ec1, 0x0760, 0x43b0, 0x61d8, 0x70ec, + 0x7876, 0x3c3b, 0x5e1d, 0x2f0e, 0x1787, 0x4bc3, 0x65e1, 0x32f0, 0x5978, 0x6cbc, + 0x765e, 0x3b2f, 0x5d97, 0x6ecb, 0x7765, 0x3bb2, 0x1dd9, 0x0eec, 0x4776, 0x23bb, + 0x51dd, 0x28ee, 0x1477, 0x4a3b, 0x651d, 0x328e, 0x1947, 0x4ca3, 0x6651, 0x3328, + 0x5994, 0x6cca, 0x3665, 0x1b32, 0x0d99, 0x06cc, 0x4366, 0x21b3, 0x50d9, 0x286c, + 0x5436, 0x2a1b, 0x550d, 0x2a86, 0x1543, 0x4aa1, 0x2550, 0x52a8, 0x6954, 0x74aa, + 0x3a55, 0x1d2a, 0x0e95, 0x074a, 0x03a5, 0x01d2, 0x00e9, 0x0074, 0x403a, 0x201d, + 0x100e, 0x0807, 0x4403, 0x6201, 0x3100, 0x5880, 0x6c40, 0x7620, 0x7b10, 0x7d88, + 0x7ec4, 0x7f62, 0x3fb1, 0x1fd8, 0x4fec, 0x67f6, 0x33fb, 0x59fd, 0x2cfe, 0x167f, + 0x4b3f, 0x659f, 0x72cf, 0x7967, 0x7cb3, 0x7e59, 0x3f2c, 0x5f96, 0x2fcb, 0x57e5, + 0x2bf2, 0x15f9, 0x0afc, 0x457e, 0x22bf, 0x515f, 0x68af, 0x7457, 0x7a2b, 0x7d15, + 0x3e8a, 0x1f45, 0x0fa2, 0x07d1, 0x03e8, 0x41f4, 0x60fa, 0x307d, 0x183e, 0x0c1f, + 0x460f, 0x6307, 0x7183, 0x78c1, 0x3c60, 0x5e30, 0x6f18, 0x778c, 0x7bc6, 0x3de3, + 0x5ef1, 0x2f78, 0x57bc, 0x6bde, 0x35ef, 0x5af7, 0x6d7b, 0x76bd, 0x3b5e, 0x1daf, + 0x4ed7, 0x676b, 0x73b5, 0x39da, 0x1ced, 0x0e76, 0x073b, 0x439d, 0x21ce, 0x10e7, + 0x4873, 0x6439, 0x321c, 0x590e, 0x2c87, 0x5643, 0x6b21, 0x3590, 0x5ac8, 0x6d64, + 0x76b2, 0x3b59, 0x1dac, 0x4ed6, 0x276b, 0x53b5, 0x29da, 0x14ed, 0x0a76, 0x053b, + 0x429d, 0x214e, 0x10a7, 0x4853, 0x6429, 0x3214, 0x590a, 0x2c85, 0x1642, 0x0b21, + 0x0590, 0x42c8, 0x6164, 0x70b2, 0x3859, 0x1c2c, 0x4e16, 0x270b, 0x5385, 0x29c2, + 0x14e1, 0x0a70, 0x4538, 0x629c, 0x714e, 0x38a7, 0x5c53, 0x6e29, 0x3714, 0x5b8a, + 0x2dc5, 0x16e2, 0x0b71, 0x05b8, 0x42dc, 0x616e, 0x30b7, 0x585b, 0x6c2d, 0x3616, + 0x1b0b, 0x4d85, 0x26c2, 0x1361, 0x09b0, 0x44d8, 0x626c, 0x7136, 0x389b, 0x5c4d, + 0x2e26, 0x1713, 0x4b89, 0x25c4, 0x52e2, 0x2971, 0x14b8, 0x4a5c, 0x652e, 0x3297, + 0x594b, 0x6ca5, 0x3652, 0x1b29, 0x0d94, 0x46ca, 0x2365, 0x11b2, 0x08d9, 0x046c, + 0x4236, 0x211b, 0x508d, 0x2846, 0x1423, 0x4a11, 0x2508, 0x5284, 0x6942, 0x34a1, + 0x1a50, 0x4d28, 0x6694, 0x734a, 0x39a5, 0x1cd2, 0x0e69, 0x0734, 0x439a, 0x21cd, + 0x10e6, 0x0873, 0x4439, 0x221c, 0x510e, 0x2887, 0x5443, 0x6a21, 0x3510, 0x5a88, + 0x6d44, 0x76a2, 0x3b51, 0x1da8, 0x4ed4, 0x676a, 0x33b5, 0x19da, 0x0ced, 0x0676, + 0x033b, 0x419d, 0x20ce, 0x1067, 0x4833, 0x6419, 0x320c, 0x5906, 0x2c83, 0x5641, + 0x2b20, 0x5590, 0x6ac8, 0x7564, 0x7ab2, 0x3d59, 0x1eac, 0x4f56, 0x27ab, 0x53d5, + 0x29ea, 0x14f5, 0x0a7a, 0x053d, 0x029e, 0x014f, 0x40a7, 0x6053, 0x7029, 0x3814, + 0x5c0a, 0x2e05, 0x1702, 0x0b81, 0x05c0, 0x42e0, 0x6170, 0x70b8, 0x785c, 0x7c2e, + 0x3e17, 0x5f0b, 0x6f85, 0x37c2, 0x1be1, 0x0df0, 0x46f8, 0x637c, 0x71be, 0x38df, + 0x5c6f, 0x6e37, 0x771b, 0x7b8d, 0x3dc6, 0x1ee3, 0x4f71, 0x27b8, 0x53dc, 0x69ee, + 0x34f7, 0x5a7b, 0x6d3d, 0x369e, 0x1b4f, 0x4da7, 0x66d3, 0x7369, 0x39b4, 0x5cda, + 0x2e6d, 0x1736, 0x0b9b, 0x45cd, 0x22e6, 0x1173, 0x48b9, 0x245c, 0x522e, 0x2917, + 0x548b, 0x6a45, 0x3522, 0x1a91, 0x0d48, 0x46a4, 0x6352, 0x31a9, 0x18d4, 0x4c6a, + 0x2635, 0x131a, 0x098d, 0x04c6, 0x0263, 0x4131, 0x2098, 0x504c, 0x6826, 0x3413, + 0x5a09, 0x2d04, 0x5682, 0x2b41, 0x15a0, 0x4ad0, 0x6568, 0x72b4, 0x795a, 0x3cad, + 0x1e56, 0x0f2b, 0x4795, 0x23ca, 0x11e5, 0x08f2, 0x0479, 0x023c, 0x411e, 0x208f, + 0x5047, 0x6823, 0x7411, 0x3a08, 0x5d04, 0x6e82, 0x3741, 0x1ba0, 0x4dd0, 0x66e8, + 0x7374, 0x79ba, 0x3cdd, 0x1e6e, 0x0f37, 0x479b, 0x63cd, 0x31e6, 0x18f3, 0x4c79, + 0x263c, 0x531e, 0x298f, 0x54c7, 0x6a63, 0x7531, 0x3a98, 0x5d4c, 0x6ea6, 0x3753, + 0x5ba9, 0x2dd4, 0x56ea, 0x2b75, 0x15ba, 0x0add, 0x056e, 0x02b7, 0x415b, 0x60ad, + 0x3056, 0x182b, 0x4c15, 0x260a, 0x1305, 0x0982, 0x04c1, 0x0260, 0x4130, 0x6098, + 0x704c, 0x7826, 0x3c13, 0x5e09, 0x2f04, 0x5782, 0x2bc1, 0x15e0, 0x4af0, 0x6578, + 0x72bc, 0x795e, 0x3caf, 0x5e57, 0x6f2b, 0x7795, 0x3bca, 0x1de5, 0x0ef2, 0x0779, + 0x03bc, 0x41de, 0x20ef, 0x5077, 0x683b, 0x741d, 0x3a0e, 0x1d07, 0x4e83, 0x6741, + 0x33a0, 0x59d0, 0x6ce8, 0x7674, 0x7b3a, 0x3d9d, 0x1ece, 0x0f67, 0x47b3, 0x63d9, + 0x31ec, 0x58f6, 0x2c7b, 0x563d, 0x2b1e, 0x158f, 0x4ac7, 0x6563, 0x72b1, 0x3958, + 0x5cac, 0x6e56, 0x372b, 0x5b95, 0x2dca, 0x16e5, 0x0b72, 0x05b9, 0x02dc, 0x416e, + 0x20b7, 0x505b, 0x682d, 0x3416, 0x1a0b, 0x4d05, 0x2682, 0x1341, 0x09a0, 0x44d0, + 0x6268, 0x7134, 0x789a, 0x3c4d, 0x1e26, 0x0f13, 0x4789, 0x23c4, 0x51e2, 0x28f1, + 0x1478, 0x4a3c, 0x651e, 0x328f, 0x5947, 0x6ca3, 0x7651, 0x3b28, 0x5d94, 0x6eca, + 0x3765, 0x1bb2, 0x0dd9, 0x06ec, 0x4376, 0x21bb, 0x50dd, 0x286e, 0x1437, 0x4a1b, + 0x650d, 0x3286, 0x1943, 0x4ca1, 0x2650, 0x5328, 0x6994, 0x74ca, 0x3a65, 0x1d32, + 0x0e99, 0x074c, 0x43a6, 0x21d3, 0x50e9, 0x2874, 0x543a, 0x2a1d, 0x150e, 0x0a87, + 0x4543, 0x62a1, 0x3150, 0x58a8, 0x6c54, 0x762a, 0x3b15, 0x1d8a, 0x0ec5, 0x0762, + 0x03b1, 0x01d8, 0x40ec, 0x6076, 0x303b, 0x581d, 0x2c0e, 0x1607, 0x4b03, 0x6581, + 0x32c0, 0x5960, 0x6cb0, 0x7658, 0x7b2c, 0x7d96, 0x3ecb, 0x5f65, 0x2fb2, 0x17d9, + 0x0bec, 0x45f6, 0x22fb, 0x517d, 0x28be, 0x145f, 0x4a2f, 0x6517, 0x728b, 0x7945, + 0x3ca2, 0x1e51, 0x0f28, 0x4794, 0x63ca, 0x31e5, 0x18f2, 0x0c79, 0x063c, 0x431e, + 0x218f, 0x50c7, 0x6863, 0x7431, 0x3a18, 0x5d0c, 0x6e86, 0x3743, 0x5ba1, 0x2dd0, + 0x56e8, 0x6b74, 0x75ba, 0x3add, 0x1d6e, 0x0eb7, 0x475b, 0x63ad, 0x31d6, 0x18eb, + 0x4c75, 0x263a, 0x131d, 0x098e, 0x04c7, 0x4263, 0x6131, 0x3098, 0x584c, 0x6c26, + 0x3613, 0x5b09, 0x2d84, 0x56c2, 0x2b61, 0x15b0, 0x4ad8, 0x656c, 0x72b6, 0x395b, + 0x5cad, 0x2e56, 0x172b, 0x4b95, 0x25ca, 0x12e5, 0x0972, 0x04b9, 0x025c, 0x412e, + 0x2097, 0x504b, 0x6825, 0x3412, 0x1a09, 0x0d04, 0x4682, 0x2341, 0x11a0, 0x48d0, + 0x6468, 0x7234, 0x791a, 0x3c8d, 0x1e46, 0x0f23, 0x4791, 0x23c8, 0x51e4, 0x68f2, + 0x3479, 0x1a3c, 0x4d1e, 0x268f, 0x5347, 0x69a3, 0x74d1, 0x3a68, 0x5d34, 0x6e9a, + 0x374d, 0x1ba6, 0x0dd3, 0x46e9, 0x2374, 0x51ba, 0x28dd, 0x146e, 0x0a37, 0x451b, + 0x628d, 0x3146, 0x18a3, 0x4c51, 0x2628, 0x5314, 0x698a, 0x34c5, 0x1a62, 0x0d31, + 0x0698, 0x434c, 0x61a6, 0x30d3, 0x5869, 0x2c34, 0x561a, 0x2b0d, 0x1586, 0x0ac3, + 0x4561, 0x22b0, 0x5158, 0x68ac, 0x7456, 0x3a2b, 0x5d15, 0x2e8a, 0x1745, 0x0ba2, + 0x05d1, 0x02e8, 0x4174, 0x60ba, 0x305d, 0x182e, 0x0c17, 0x460b, 0x6305, 0x3182, + 0x18c1, 0x0c60, 0x4630, 0x6318, 0x718c, 0x78c6, 0x3c63, 0x5e31, 0x2f18, 0x578c, + 0x6bc6, 0x35e3, 0x5af1, 0x2d78, 0x56bc, 0x6b5e, 0x35af, 0x5ad7, 0x6d6b, 0x76b5, + 0x3b5a, 0x1dad, 0x0ed6, 0x076b, 0x43b5, 0x21da, 0x10ed, 0x0876, 0x043b, 0x421d, + 0x210e, 0x1087, 0x4843, 0x6421, 0x3210, 0x5908, 0x6c84, 0x7642, 0x3b21, 0x1d90, + 0x4ec8, 0x6764, 0x73b2, 0x39d9, 0x1cec, 0x4e76, 0x273b, 0x539d, 0x29ce, 0x14e7, + 0x4a73, 0x6539, 0x329c, 0x594e, 0x2ca7, 0x5653, 0x6b29, 0x3594, 0x5aca, 0x2d65, + 0x16b2, 0x0b59, 0x05ac, 0x42d6, 0x216b, 0x50b5, 0x285a, 0x142d, 0x0a16, 0x050b, + 0x4285, 0x2142, 0x10a1, 0x0850, 0x4428, 0x6214, 0x710a, 0x3885, 0x1c42, 0x0e21, + 0x0710, 0x4388, 0x61c4, 0x70e2, 0x3871, 0x1c38, 0x4e1c, 0x670e, 0x3387, 0x59c3, + 0x6ce1, 0x3670, 0x5b38, 0x6d9c, 0x76ce, 0x3b67, 0x5db3, 0x6ed9, 0x376c, 0x5bb6, + 0x2ddb, 0x56ed, 0x2b76, 0x15bb, 0x4add, 0x256e, 0x12b7, 0x495b, 0x64ad, 0x3256, + 0x192b, 0x4c95, 0x264a, 0x1325, 0x0992, 0x04c9, 0x0264, 0x4132, 0x2099, 0x104c, + 0x4826, 0x2413, 0x5209, 0x2904, 0x5482, 0x2a41, 0x1520, 0x4a90, 0x6548, 0x72a4, + 0x7952, 0x3ca9, 0x1e54, 0x4f2a, 0x2795, 0x13ca, 0x09e5, 0x04f2, 0x0279, 0x013c, + 0x409e, 0x204f, 0x5027, 0x6813, 0x7409, 0x3a04, 0x5d02, 0x2e81, 0x1740, 0x4ba0, + 0x65d0, 0x72e8, 0x7974, 0x7cba, 0x3e5d, 0x1f2e, 0x0f97, 0x47cb, 0x63e5, 0x31f2, + 0x18f9, 0x0c7c, 0x463e, 0x231f, 0x518f, 0x68c7, 0x7463, 0x7a31, 0x3d18, 0x5e8c, + 0x6f46, 0x37a3, 0x5bd1, 0x2de8, 0x56f4, 0x6b7a, 0x35bd, 0x1ade, 0x0d6f, 0x46b7, + 0x635b, 0x71ad, 0x38d6, 0x1c6b, 0x4e35, 0x271a, 0x138d, 0x09c6, 0x04e3, 0x4271, + 0x2138, 0x509c, 0x684e, 0x3427, 0x5a13, 0x6d09, 0x3684, 0x5b42, 0x2da1, 0x16d0, + 0x4b68, 0x65b4, 0x72da, 0x396d, 0x1cb6, 0x0e5b, 0x472d, 0x2396, 0x11cb, 0x48e5, + 0x2472, 0x1239, 0x091c, 0x448e, 0x2247, 0x5123, 0x6891, 0x3448, 0x5a24, 0x6d12, + 0x3689, 0x1b44, 0x4da2, 0x26d1, 0x1368, 0x49b4, 0x64da, 0x326d, 0x1936, 0x0c9b, + 0x464d, 0x2326, 0x1193, 0x48c9, 0x2464, 0x5232, 0x2919, 0x148c, 0x4a46, 0x2523, + 0x5291, 0x2948, 0x54a4, 0x6a52, 0x3529, 0x1a94, 0x4d4a, 0x26a5, 0x1352, 0x09a9, + 0x04d4, 0x426a, 0x2135, 0x109a, 0x084d, 0x0426, 0x0213, 0x4109, 0x2084, 0x5042, + 0x2821, 0x1410, 0x4a08, 0x6504, 0x7282, 0x3941, 0x1ca0, 0x4e50, 0x6728, 0x7394, + 0x79ca, 0x3ce5, 0x1e72, 0x0f39, 0x079c, 0x43ce, 0x21e7, 0x50f3, 0x6879, 0x343c, + 0x5a1e, 0x2d0f, 0x5687, 0x6b43, 0x75a1, 0x3ad0, 0x5d68, 0x6eb4, 0x775a, 0x3bad, + 0x1dd6, 0x0eeb, 0x4775, 0x23ba, 0x11dd, 0x08ee, 0x0477, 0x423b, 0x611d, 0x308e, + 0x1847, 0x4c23, 0x6611, 0x3308, 0x5984, 0x6cc2, 0x3661, 0x1b30, 0x4d98, 0x66cc, + 0x7366, 0x39b3, 0x5cd9, 0x2e6c, 0x5736, 0x2b9b, 0x55cd, 0x2ae6, 0x1573, 0x4ab9, + 0x255c, 0x52ae, 0x2957, 0x54ab, 0x6a55, 0x352a, 0x1a95, 0x0d4a, 0x06a5, 0x0352, + 0x01a9, 0x00d4, 0x406a, 0x2035, 0x101a, 0x080d, 0x0406, 0x0203, 0x4101, 0x2080, + 0x5040, 0x6820, 0x7410, 0x7a08, 0x7d04, 0x7e82, 0x3f41, 0x1fa0, 0x4fd0, 0x67e8, + 0x73f4, 0x79fa, 0x3cfd, 0x1e7e, 0x0f3f, 0x479f, 0x63cf, 0x71e7, 0x78f3, 0x7c79, + 0x3e3c, 0x5f1e, 0x2f8f, 0x57c7, 0x6be3, 0x75f1, 0x3af8, 0x5d7c, 0x6ebe, 0x375f, + 0x5baf, 0x6dd7, 0x76eb, 0x7b75, 0x3dba, 0x1edd, 0x0f6e, 0x07b7, 0x43db, 0x61ed, + 0x30f6, 0x187b, 0x4c3d, 0x261e, 0x130f, 0x4987, 0x64c3, 0x7261, 0x3930, 0x5c98, + 0x6e4c, 0x7726, 0x3b93, 0x5dc9, 0x2ee4, 0x5772, 0x2bb9, 0x15dc, 0x4aee, 0x2577, + 0x52bb, 0x695d, 0x34ae, 0x1a57, 0x4d2b, 0x6695, 0x334a, 0x19a5, 0x0cd2, 0x0669, + 0x0334, 0x419a, 0x20cd, 0x1066, 0x0833, 0x4419, 0x220c, 0x5106, 0x2883, 0x5441, + 0x2a20, 0x5510, 0x6a88, 0x7544, 0x7aa2, 0x3d51, 0x1ea8, 0x4f54, 0x67aa, 0x33d5, + 0x19ea, 0x0cf5, 0x067a, 0x033d, 0x019e, 0x00cf, 0x4067, 0x6033, 0x7019, 0x380c, + 0x5c06, 0x2e03, 0x5701, 0x2b80, 0x55c0, 0x6ae0, 0x7570, 0x7ab8, 0x7d5c, 0x7eae, + 0x3f57, 0x5fab, 0x6fd5, 0x37ea, 0x1bf5, 0x0dfa, 0x06fd, 0x037e, 0x01bf, 0x40df, + 0x606f, 0x7037, 0x781b, 0x7c0d, 0x3e06, 0x1f03, 0x4f81, 0x27c0, 0x53e0, 0x69f0, + 0x74f8, 0x7a7c, 0x7d3e, 0x3e9f, 0x5f4f, 0x6fa7, 0x77d3, 0x7be9, 0x3df4, 0x5efa, + 0x2f7d, 0x17be, 0x0bdf, 0x45ef, 0x62f7, 0x717b, 0x78bd, 0x3c5e, 0x1e2f, 0x4f17, + 0x678b, 0x73c5, 0x39e2, 0x1cf1, 0x0e78, 0x473c, 0x639e, 0x31cf, 0x58e7, 0x6c73, + 0x7639, 0x3b1c, 0x5d8e, 0x2ec7, 0x5763, 0x6bb1, 0x35d8, 0x5aec, 0x6d76, 0x36bb, + 0x5b5d, 0x2dae, 0x16d7, 0x4b6b, 0x65b5, 0x32da, 0x196d, 0x0cb6, 0x065b, 0x432d, + 0x2196, 0x10cb, 0x4865, 0x2432, 0x1219, 0x090c, 0x4486, 0x2243, 0x5121, 0x2890, + 0x5448, 0x6a24, 0x7512, 0x3a89, 0x1d44, 0x4ea2, 0x2751, 0x13a8, 0x49d4, 0x64ea, + 0x3275, 0x193a, 0x0c9d, 0x064e, 0x0327, 0x4193, 0x60c9, 0x3064, 0x5832, 0x2c19, + 0x160c, 0x4b06, 0x2583, 0x52c1, 0x2960, 0x54b0, 0x6a58, 0x752c, 0x7a96, 0x3d4b, + 0x5ea5, 0x2f52, 0x17a9, 0x0bd4, 0x45ea, 0x22f5, 0x117a, 0x08bd, 0x045e, 0x022f, + 0x4117, 0x608b, 0x7045, 0x3822, 0x1c11, 0x0e08, 0x4704, 0x6382, 0x31c1, 0x18e0, + 0x4c70, 0x6638, 0x731c, 0x798e, 0x3cc7, 0x5e63, 0x6f31, 0x3798, 0x5bcc, 0x6de6, + 0x36f3, 0x5b79, 0x2dbc, 0x56de, 0x2b6f, 0x55b7, 0x6adb, 0x756d, 0x3ab6, 0x1d5b, + 0x4ead, 0x2756, 0x13ab, 0x49d5, 0x24ea, 0x1275, 0x093a, 0x049d, 0x024e, 0x0127, + 0x4093, 0x6049, 0x3024, 0x5812, 0x2c09, 0x1604, 0x4b02, 0x2581, 0x12c0, 0x4960, + 0x64b0, 0x7258, 0x792c, 0x7c96, 0x3e4b, 0x5f25, 0x2f92, 0x17c9, 0x0be4, 0x45f2, + 0x22f9, 0x117c, 0x48be, 0x245f, 0x522f, 0x6917, 0x748b, 0x7a45, 0x3d22, 0x1e91, + 0x0f48, 0x47a4, 0x63d2, 0x31e9, 0x18f4, 0x4c7a, 0x263d, 0x131e, 0x098f, 0x44c7, + 0x6263, 0x7131, 0x3898, 0x5c4c, 0x6e26, 0x3713, 0x5b89, 0x2dc4, 0x56e2, 0x2b71, + 0x15b8, 0x4adc, 0x656e, 0x32b7, 0x595b, 0x6cad, 0x3656, 0x1b2b, 0x4d95, 0x26ca, + 0x1365, 0x09b2, 0x04d9, 0x026c, 0x4136, 0x209b, 0x504d, 0x2826, 0x1413, 0x4a09, + 0x2504, 0x5282, 0x2941, 0x14a0, 0x4a50, 0x6528, 0x7294, 0x794a, 0x3ca5, 0x1e52, + 0x0f29, 0x0794, 0x43ca, 0x21e5, 0x10f2, 0x0879, 0x043c, 0x421e, 0x210f, 0x5087, + 0x6843, 0x7421, 0x3a10, 0x5d08, 0x6e84, 0x7742, 0x3ba1, 0x1dd0, 0x4ee8, 0x6774, + 0x73ba, 0x39dd, 0x1cee, 0x0e77, 0x473b, 0x639d, 0x31ce, 0x18e7, 0x4c73, 0x6639, + 0x331c, 0x598e, 0x2cc7, 0x5663, 0x6b31, 0x3598, 0x5acc, 0x6d66, 0x36b3, 0x5b59, + 0x2dac, 0x56d6, 0x2b6b, 0x55b5, 0x2ada, 0x156d, 0x0ab6, 0x055b, 0x42ad, 0x2156, + 0x10ab, 0x4855, 0x242a, 0x1215, 0x090a, 0x0485, 0x0242, 0x0121, 0x0090, 0x4048, + 0x6024, 0x7012, 0x3809, 0x1c04, 0x4e02, 0x2701, 0x1380, 0x49c0, 0x64e0, 0x7270, + 0x7938, 0x7c9c, 0x7e4e, 0x3f27, 0x5f93, 0x6fc9, 0x37e4, 0x5bf2, 0x2df9, 0x16fc, + 0x4b7e, 0x25bf, 0x52df, 0x696f, 0x74b7, 0x7a5b, 0x7d2d, 0x3e96, 0x1f4b, 0x4fa5, + 0x27d2, 0x13e9, 0x09f4, 0x44fa, 0x227d, 0x113e, 0x089f, 0x444f, 0x6227, 0x7113, + 0x7889, 0x3c44, 0x5e22, 0x2f11, 0x1788, 0x4bc4, 0x65e2, 0x32f1, 0x1978, 0x4cbc, + 0x665e, 0x332f, 0x5997, 0x6ccb, 0x7665, 0x3b32, 0x1d99, 0x0ecc, 0x4766, 0x23b3, + 0x51d9, 0x28ec, 0x5476, 0x2a3b, 0x551d, 0x2a8e, 0x1547, 0x4aa3, 0x6551, 0x32a8, + 0x5954, 0x6caa, 0x3655, 0x1b2a, 0x0d95, 0x06ca, 0x0365, 0x01b2, 0x00d9, 0x006c, + 0x4036, 0x201b, 0x500d, 0x2806, 0x1403, 0x4a01, 0x2500, 0x5280, 0x6940, 0x74a0, + 0x7a50, 0x7d28, 0x7e94, 0x7f4a, 0x3fa5, 0x1fd2, 0x0fe9, 0x07f4, 0x43fa, 0x21fd, + 0x10fe, 0x087f, 0x443f, 0x621f, 0x710f, 0x7887, 0x7c43, 0x7e21, 0x3f10, 0x5f88, + 0x6fc4, 0x77e2, 0x3bf1, 0x1df8, 0x4efc, 0x677e, 0x33bf, 0x59df, 0x6cef, 0x7677, + 0x7b3b, 0x7d9d, 0x3ece, 0x1f67, 0x4fb3, 0x67d9, 0x33ec, 0x59f6, 0x2cfb, 0x567d, + 0x2b3e, 0x159f, 0x4acf, 0x6567, 0x72b3, 0x7959, 0x3cac, 0x5e56, 0x2f2b, 0x5795, + 0x2bca, 0x15e5, 0x0af2, 0x0579, 0x02bc, 0x415e, 0x20af, 0x5057, 0x682b, 0x7415, + 0x3a0a, 0x1d05, 0x0e82, 0x0741, 0x03a0, 0x41d0, 0x60e8, 0x7074, 0x783a, 0x3c1d, + 0x1e0e, 0x0f07, 0x4783, 0x63c1, 0x31e0, 0x58f0, 0x6c78, 0x763c, 0x7b1e, 0x3d8f, + 0x5ec7, 0x6f63, 0x77b1, 0x3bd8, 0x5dec, 0x6ef6, 0x377b, 0x5bbd, 0x2dde, 0x16ef, + 0x4b77, 0x65bb, 0x72dd, 0x396e, 0x1cb7, 0x4e5b, 0x672d, 0x3396, 0x19cb, 0x4ce5, + 0x2672, 0x1339, 0x099c, 0x44ce, 0x2267, 0x5133, 0x6899, 0x344c, 0x5a26, 0x2d13, + 0x5689, 0x2b44, 0x55a2, 0x2ad1, 0x1568, 0x4ab4, 0x655a, 0x32ad, 0x1956, 0x0cab, + 0x4655, 0x232a, 0x1195, 0x08ca, 0x0465, 0x0232, 0x0119, 0x008c, 0x4046, 0x2023, + 0x5011, 0x2808, 0x5404, 0x6a02, 0x3501, 0x1a80, 0x4d40, 0x66a0, 0x7350, 0x79a8, + 0x7cd4, 0x7e6a, 0x3f35, 0x1f9a, 0x0fcd, 0x07e6, 0x03f3, 0x41f9, 0x20fc, 0x507e, + 0x283f, 0x541f, 0x6a0f, 0x7507, 0x7a83, 0x7d41, 0x3ea0, 0x5f50, 0x6fa8, 0x77d4, + 0x7bea, 0x3df5, 0x1efa, 0x0f7d, 0x07be, 0x03df, 0x41ef, 0x60f7, 0x707b, 0x783d, + 0x3c1e, 0x1e0f, 0x4f07, 0x6783, 0x73c1, 0x39e0, 0x5cf0, 0x6e78, 0x773c, 0x7b9e, + 0x3dcf, 0x5ee7, 0x6f73, 0x77b9, 0x3bdc, 0x5dee, 0x2ef7, 0x577b, 0x6bbd, 0x35de, + 0x1aef, 0x4d77, 0x66bb, 0x735d, 0x39ae, 0x1cd7, 0x4e6b, 0x6735, 0x339a, 0x19cd, + 0x0ce6, 0x0673, 0x4339, 0x219c, 0x50ce, 0x2867, 0x5433, 0x6a19, 0x350c, 0x5a86, + 0x2d43, 0x56a1, 0x2b50, 0x55a8, 0x6ad4, 0x756a, 0x3ab5, 0x1d5a, 0x0ead, 0x0756, + 0x03ab, 0x41d5, 0x20ea, 0x1075, 0x083a, 0x041d, 0x020e, 0x0107, 0x4083, 0x6041, + 0x3020, 0x5810, 0x6c08, 0x7604, 0x7b02, 0x3d81, 0x1ec0, 0x4f60, 0x67b0, 0x73d8, + 0x79ec, 0x7cf6, 0x3e7b, 0x5f3d, 0x2f9e, 0x17cf, 0x4be7, 0x65f3, 0x72f9, 0x397c, + 0x5cbe, 0x2e5f, 0x572f, 0x6b97, 0x75cb, 0x7ae5, 0x3d72, 0x1eb9, 0x0f5c, 0x47ae, + 0x23d7, 0x51eb, 0x68f5, 0x347a, 0x1a3d, 0x0d1e, 0x068f, 0x4347, 0x61a3, 0x70d1, + 0x3868, 0x5c34, 0x6e1a, 0x370d, 0x1b86, 0x0dc3, 0x46e1, 0x2370, 0x51b8, 0x68dc, + 0x746e, 0x3a37, 0x5d1b, 0x6e8d, 0x3746, 0x1ba3, 0x4dd1, 0x26e8, 0x5374, 0x69ba, + 0x34dd, 0x1a6e, 0x0d37, 0x469b, 0x634d, 0x31a6, 0x18d3, 0x4c69, 0x2634, 0x531a, + 0x298d, 0x14c6, 0x0a63, 0x4531, 0x2298, 0x514c, 0x68a6, 0x3453, 0x5a29, 0x2d14, + 0x568a, 0x2b45, 0x15a2, 0x0ad1, 0x0568, 0x42b4, 0x615a, 0x30ad, 0x1856, 0x0c2b, + 0x4615, 0x230a, 0x1185, 0x08c2, 0x0461, 0x0230, 0x4118, 0x608c, 0x7046, 0x3823, + 0x5c11, 0x2e08, 0x5704, 0x6b82, 0x35c1, 0x1ae0, 0x4d70, 0x66b8, 0x735c, 0x79ae, + 0x3cd7, 0x5e6b, 0x6f35, 0x379a, 0x1bcd, 0x0de6, 0x06f3, 0x4379, 0x21bc, 0x50de, + 0x286f, 0x5437, 0x6a1b, 0x750d, 0x3a86, 0x1d43, 0x4ea1, 0x2750, 0x53a8, 0x69d4, + 0x74ea, 0x3a75, 0x1d3a, 0x0e9d, 0x074e, 0x03a7, 0x41d3, 0x60e9, 0x3074, 0x583a, + 0x2c1d, 0x160e, 0x0b07, 0x4583, 0x62c1, 0x3160, 0x58b0, 0x6c58, 0x762c, 0x7b16, + 0x3d8b, 0x5ec5, 0x2f62, 0x17b1, 0x0bd8, 0x45ec, 0x62f6, 0x317b, 0x58bd, 0x2c5e, + 0x162f, 0x4b17, 0x658b, 0x72c5, 0x3962, 0x1cb1, 0x0e58, 0x472c, 0x6396, 0x31cb, + 0x58e5, 0x2c72, 0x1639, 0x0b1c, 0x458e, 0x22c7, 0x5163, 0x68b1, 0x3458, 0x5a2c, + 0x6d16, 0x368b, 0x5b45, 0x2da2, 0x16d1, 0x0b68, 0x45b4, 0x62da, 0x316d, 0x18b6, + 0x0c5b, 0x462d, 0x2316, 0x118b, 0x48c5, 0x2462, 0x1231, 0x0918, 0x448c, 0x6246, + 0x3123, 0x5891, 0x2c48, 0x5624, 0x6b12, 0x3589, 0x1ac4, 0x4d62, 0x26b1, 0x1358, + 0x49ac, 0x64d6, 0x326b, 0x5935, 0x2c9a, 0x164d, 0x0b26, 0x0593, 0x42c9, 0x2164, + 0x50b2, 0x2859, 0x142c, 0x4a16, 0x250b, 0x5285, 0x2942, 0x14a1, 0x0a50, 0x4528, + 0x6294, 0x714a, 0x38a5, 0x1c52, 0x0e29, 0x0714, 0x438a, 0x21c5, 0x10e2, 0x0871, + 0x0438, 0x421c, 0x610e, 0x3087, 0x5843, 0x6c21, 0x3610, 0x5b08, 0x6d84, 0x76c2, + 0x3b61, 0x1db0, 0x4ed8, 0x676c, 0x73b6, 0x39db, 0x5ced, 0x2e76, 0x173b, 0x4b9d, + 0x25ce, 0x12e7, 0x4973, 0x64b9, 0x325c, 0x592e, 0x2c97, 0x564b, 0x6b25, 0x3592, + 0x1ac9, 0x0d64, 0x46b2, 0x2359, 0x11ac, 0x48d6, 0x246b, 0x5235, 0x291a, 0x148d, + 0x0a46, 0x0523, 0x4291, 0x2148, 0x50a4, 0x6852, 0x3429, 0x1a14, 0x4d0a, 0x2685, + 0x1342, 0x09a1, 0x04d0, 0x4268, 0x6134, 0x709a, 0x384d, 0x1c26, 0x0e13, 0x4709, + 0x2384, 0x51c2, 0x28e1, 0x1470, 0x4a38, 0x651c, 0x728e, 0x3947, 0x5ca3, 0x6e51, + 0x3728, 0x5b94, 0x6dca, 0x36e5, 0x1b72, 0x0db9, 0x06dc, 0x436e, 0x21b7, 0x50db, + 0x686d, 0x3436, 0x1a1b, 0x4d0d, 0x2686, 0x1343, 0x49a1, 0x24d0, 0x5268, 0x6934, + 0x749a, 0x3a4d, 0x1d26, 0x0e93, 0x4749, 0x23a4, 0x51d2, 0x28e9, 0x1474, 0x4a3a, + 0x251d, 0x128e, 0x0947, 0x44a3, 0x6251, 0x3128, 0x5894, 0x6c4a, 0x3625, 0x1b12, + 0x0d89, 0x06c4, 0x4362, 0x21b1, 0x10d8, 0x486c, 0x6436, 0x321b, 0x590d, 0x2c86, + 0x1643, 0x4b21, 0x2590, 0x52c8, 0x6964, 0x74b2, 0x3a59, 0x1d2c, 0x4e96, 0x274b, + 0x53a5, 0x29d2, 0x14e9, 0x0a74, 0x453a, 0x229d, 0x114e, 0x08a7, 0x4453, 0x6229, + 0x3114, 0x588a, 0x2c45, 0x1622, 0x0b11, 0x0588, 0x42c4, 0x6162, 0x30b1, 0x1858, + 0x4c2c, 0x6616, 0x330b, 0x5985, 0x2cc2, 0x1661, 0x0b30, 0x4598, 0x62cc, 0x7166, + 0x38b3, 0x5c59, 0x2e2c, 0x5716, 0x2b8b, 0x55c5, 0x2ae2, 0x1571, 0x0ab8, 0x455c, + 0x62ae, 0x3157, 0x58ab, 0x6c55, 0x362a, 0x1b15, 0x0d8a, 0x06c5, 0x0362, 0x01b1, + 0x00d8, 0x406c, 0x6036, 0x301b, 0x580d, 0x2c06, 0x1603, 0x4b01, 0x2580, 0x52c0, + 0x6960, 0x74b0, 0x7a58, 0x7d2c, 0x7e96, 0x3f4b, 0x5fa5, 0x2fd2, 0x17e9, 0x0bf4, + 0x45fa, 0x22fd, 0x117e, 0x08bf, 0x445f, 0x622f, 0x7117, 0x788b, 0x7c45, 0x3e22, + 0x1f11, 0x0f88, 0x47c4, 0x63e2, 0x31f1, 0x18f8, 0x4c7c, 0x663e, 0x331f, 0x598f, + 0x6cc7, 0x7663, 0x7b31, 0x3d98, 0x5ecc, 0x6f66, 0x37b3, 0x5bd9, 0x2dec, 0x56f6, + 0x2b7b, 0x55bd, 0x2ade, 0x156f, 0x4ab7, 0x655b, 0x72ad, 0x3956, 0x1cab, 0x4e55, + 0x272a, 0x1395, 0x09ca, 0x04e5, 0x0272, 0x0139, 0x009c, 0x404e, 0x2027, 0x5013, + 0x6809, 0x3404, 0x5a02, 0x2d01, 0x1680, 0x4b40, 0x65a0, 0x72d0, 0x7968, 0x7cb4, + 0x7e5a, 0x3f2d, 0x1f96, 0x0fcb, 0x47e5, 0x23f2, 0x11f9, 0x08fc, 0x447e, 0x223f, + 0x511f, 0x688f, 0x7447, 0x7a23, 0x7d11, 0x3e88, 0x5f44, 0x6fa2, 0x37d1, 0x1be8, + 0x4df4, 0x66fa, 0x337d, 0x19be, 0x0cdf, 0x466f, 0x6337, 0x719b, 0x78cd, 0x3c66, + 0x1e33, 0x4f19, 0x278c, 0x53c6, 0x29e3, 0x54f1, 0x2a78, 0x553c, 0x6a9e, 0x354f, + 0x5aa7, 0x6d53, 0x76a9, 0x3b54, 0x5daa, 0x2ed5, 0x176a, 0x0bb5, 0x05da, 0x02ed, + 0x0176, 0x00bb, 0x405d, 0x202e, 0x1017, 0x480b, 0x6405, 0x3202, 0x1901, 0x0c80, + 0x4640, 0x6320, 0x7190, 0x78c8, 0x7c64, 0x7e32, 0x3f19, 0x1f8c, 0x4fc6, 0x27e3, + 0x53f1, 0x29f8, 0x54fc, 0x6a7e, 0x353f, 0x5a9f, 0x6d4f, 0x76a7, 0x7b53, 0x7da9, + 0x3ed4, 0x5f6a, 0x2fb5, 0x17da, 0x0bed, 0x05f6, 0x02fb, 0x417d, 0x20be, 0x105f, + 0x482f, 0x6417, 0x720b, 0x7905, 0x3c82, 0x1e41, 0x0f20, 0x4790, 0x63c8, 0x71e4, + 0x78f2, 0x3c79, 0x1e3c, 0x4f1e, 0x278f, 0x53c7, 0x69e3, 0x74f1, 0x3a78, 0x5d3c, + 0x6e9e, 0x374f, 0x5ba7, 0x6dd3, 0x76e9, 0x3b74, 0x5dba, 0x2edd, 0x176e, 0x0bb7, + 0x45db, 0x62ed, 0x3176, 0x18bb, 0x4c5d, 0x262e, 0x1317, 0x498b, 0x64c5, 0x3262, + 0x1931, 0x0c98, 0x464c, 0x6326, 0x3193, 0x58c9, 0x2c64, 0x5632, 0x2b19, 0x158c, + 0x4ac6, 0x2563, 0x52b1, 0x2958, 0x54ac, 0x6a56, 0x352b, 0x5a95, 0x2d4a, 0x16a5, + 0x0b52, 0x05a9, 0x02d4, 0x416a, 0x20b5, 0x105a, 0x082d, 0x0416, 0x020b, 0x4105, + 0x2082, 0x1041, 0x0820, 0x4410, 0x6208, 0x7104, 0x7882, 0x3c41, 0x1e20, 0x4f10, + 0x6788, 0x73c4, 0x79e2, 0x3cf1, 0x1e78, 0x4f3c, 0x679e, 0x33cf, 0x59e7, 0x6cf3, + 0x7679, 0x3b3c, 0x5d9e, 0x2ecf, 0x5767, 0x6bb3, 0x75d9, 0x3aec, 0x5d76, 0x2ebb, + 0x575d, 0x2bae, 0x15d7, 0x4aeb, 0x6575, 0x32ba, 0x195d, 0x0cae, 0x0657, 0x432b, + 0x6195, 0x30ca, 0x1865, 0x0c32, 0x0619, 0x030c, 0x4186, 0x20c3, 0x5061, 0x2830, + 0x5418, 0x6a0c, 0x7506, 0x3a83, 0x5d41, 0x2ea0, 0x5750, 0x6ba8, 0x75d4, 0x7aea, + 0x3d75, 0x1eba, 0x0f5d, 0x07ae, 0x03d7, 0x41eb, 0x60f5, 0x307a, 0x183d, 0x0c1e, + 0x060f, 0x4307, 0x6183, 0x70c1, 0x3860, 0x5c30, 0x6e18, 0x770c, 0x7b86, 0x3dc3, + 0x5ee1, 0x2f70, 0x57b8, 0x6bdc, 0x75ee, 0x3af7, 0x5d7b, 0x6ebd, 0x375e, 0x1baf, + 0x4dd7, 0x66eb, 0x7375, 0x39ba, 0x1cdd, 0x0e6e, 0x0737, 0x439b, 0x61cd, 0x30e6, + 0x1873, 0x4c39, 0x261c, 0x530e, 0x2987, 0x54c3, 0x6a61, 0x3530, 0x5a98, 0x6d4c, + 0x76a6, 0x3b53, 0x5da9, 0x2ed4, 0x576a, 0x2bb5, 0x15da, 0x0aed, 0x0576, 0x02bb, + 0x415d, 0x20ae, 0x1057, 0x482b, 0x6415, 0x320a, 0x1905, 0x0c82, 0x0641, 0x0320, + 0x4190, 0x60c8, 0x7064, 0x7832, 0x3c19, 0x1e0c, 0x4f06, 0x2783, 0x53c1, 0x29e0, + 0x54f0, 0x6a78, 0x753c, 0x7a9e, 0x3d4f, 0x5ea7, 0x6f53, 0x77a9, 0x3bd4, 0x5dea, + 0x2ef5, 0x177a, 0x0bbd, 0x05de, 0x02ef, 0x4177, 0x60bb, 0x705d, 0x382e, 0x1c17, + 0x4e0b, 0x6705, 0x3382, 0x19c1, 0x0ce0, 0x4670, 0x6338, 0x719c, 0x78ce, 0x3c67, + 0x5e33, 0x6f19, 0x378c, 0x5bc6, 0x2de3, 0x56f1, 0x2b78, 0x55bc, 0x6ade, 0x356f, + 0x5ab7, 0x6d5b, 0x76ad, 0x3b56, 0x1dab, 0x4ed5, 0x276a, 0x13b5, 0x09da, 0x04ed, + 0x0276, 0x013b, 0x409d, 0x204e, 0x1027, 0x4813, 0x6409, 0x3204, 0x5902, 0x2c81, + 0x1640, 0x4b20, 0x6590, 0x72c8, 0x7964, 0x7cb2, 0x3e59, 0x1f2c, 0x4f96, 0x27cb, + 0x53e5, 0x29f2, 0x14f9, 0x0a7c, 0x453e, 0x229f, 0x514f, 0x68a7, 0x7453, 0x7a29, + 0x3d14, 0x5e8a, 0x2f45, 0x17a2, 0x0bd1, 0x05e8, 0x42f4, 0x617a, 0x30bd, 0x185e, + 0x0c2f, 0x4617, 0x630b, 0x7185, 0x38c2, 0x1c61, 0x0e30, 0x4718, 0x638c, 0x71c6, + 0x38e3, 0x5c71, 0x2e38, 0x571c, 0x6b8e, 0x35c7, 0x5ae3, 0x6d71, 0x36b8, 0x5b5c, + 0x6dae, 0x36d7, 0x5b6b, 0x6db5, 0x36da, 0x1b6d, 0x0db6, 0x06db, 0x436d, 0x21b6, + 0x10db, 0x486d, 0x2436, 0x121b, 0x490d, 0x2486, 0x1243, 0x4921, 0x2490, 0x5248, + 0x6924, 0x7492, 0x3a49, 0x1d24, 0x4e92, 0x2749, 0x13a4, 0x49d2, 0x24e9, 0x1274, + 0x493a, 0x249d, 0x124e, 0x0927, 0x4493, 0x6249, 0x3124, 0x5892, 0x2c49, 0x1624, + 0x4b12, 0x2589, 0x12c4, 0x4962, 0x24b1, 0x1258, 0x492c, 0x6496, 0x324b, 0x5925, + 0x2c92, 0x1649, 0x0b24, 0x4592, 0x22c9, 0x1164, 0x48b2, 0x2459, 0x122c, 0x4916, + 0x248b, 0x5245, 0x2922, 0x1491, 0x0a48, 0x4524, 0x6292, 0x3149, 0x18a4, 0x4c52, + 0x2629, 0x1314, 0x498a, 0x24c5, 0x1262, 0x0931, 0x0498, 0x424c, 0x6126, 0x3093, + 0x5849, 0x2c24, 0x5612, 0x2b09, 0x1584, 0x4ac2, 0x2561, 0x12b0, 0x4958, 0x64ac, + 0x7256, 0x392b, 0x5c95, 0x2e4a, 0x1725, 0x0b92, 0x05c9, 0x02e4, 0x4172, 0x20b9, + 0x105c, 0x482e, 0x2417, 0x520b, 0x6905, 0x3482, 0x1a41, 0x0d20, 0x4690, 0x6348, + 0x71a4, 0x78d2, 0x3c69, 0x1e34, 0x4f1a, 0x278d, 0x13c6, 0x09e3, 0x44f1, 0x2278, + 0x513c, 0x689e, 0x344f, 0x5a27, 0x6d13, 0x7689, 0x3b44, 0x5da2, 0x2ed1, 0x1768, + 0x4bb4, 0x65da, 0x32ed, 0x1976, 0x0cbb, 0x465d, 0x232e, 0x1197, 0x48cb, 0x6465, + 0x3232, 0x1919, 0x0c8c, 0x4646, 0x2323, 0x5191, 0x28c8, 0x5464, 0x6a32, 0x3519, + 0x1a8c, 0x4d46, 0x26a3, 0x5351, 0x29a8, 0x54d4, 0x6a6a, 0x3535, 0x1a9a, 0x0d4d, + 0x06a6, 0x0353, 0x41a9, 0x20d4, 0x506a, 0x2835, 0x141a, 0x0a0d, 0x0506, 0x0283, + 0x4141, 0x20a0, 0x5050, 0x6828, 0x7414, 0x7a0a, 0x3d05, 0x1e82, 0x0f41, 0x07a0, + 0x43d0, 0x61e8, 0x70f4, 0x787a, 0x3c3d, 0x1e1e, 0x0f0f, 0x4787, 0x63c3, 0x71e1, + 0x38f0, 0x5c78, 0x6e3c, 0x771e, 0x3b8f, 0x5dc7, 0x6ee3, 0x7771, 0x3bb8, 0x5ddc, + 0x6eee, 0x3777, 0x5bbb, 0x6ddd, 0x36ee, 0x1b77, 0x4dbb, 0x66dd, 0x336e, 0x19b7, + 0x4cdb, 0x666d, 0x3336, 0x199b, 0x4ccd, 0x2666, 0x1333, 0x4999, 0x24cc, 0x5266, + 0x2933, 0x5499, 0x2a4c, 0x5526, 0x2a93, 0x5549, 0x2aa4, 0x5552, 0x2aa9, 0x1554, + 0x4aaa, 0x2555, 0x12aa, 0x0955, 0x04aa, 0x0255, 0x012a, 0x0095, 0x004a, 0x0025, + 0x0012, 0x0009, 0x0004, 0x4002, 0x2001, 0x1000, 0x4800, 0x6400, 0x7200, 0x7900, + 0x7c80, 0x7e40, 0x7f20, 0x7f90, 0x7fc8, 0x7fe4, 0x7ff2, 0x3ff9, 0x1ffc, 0x4ffe, + 0x27ff, 0x53ff, 0x69ff, 0x74ff, 0x7a7f, 0x7d3f, 0x7e9f, 0x7f4f, 0x7fa7, 0x7fd3, + 0x7fe9, 0x3ff4, 0x5ffa, 0x2ffd, 0x17fe, 0x0bff, 0x45ff, 0x62ff, 0x717f, 0x78bf, + 0x7c5f, 0x7e2f, 0x7f17, 0x7f8b, 0x7fc5, 0x3fe2, 0x1ff1, 0x0ff8, 0x47fc, 0x63fe, + 0x31ff, 0x58ff, 0x6c7f, 0x763f, 0x7b1f, 0x7d8f, 0x7ec7, 0x7f63, 0x7fb1, 0x3fd8, + 0x5fec, 0x6ff6, 0x37fb, 0x5bfd, 0x2dfe, 0x16ff, 0x4b7f, 0x65bf, 0x72df, 0x796f, + 0x7cb7, 0x7e5b, 0x7f2d, 0x3f96, 0x1fcb, 0x4fe5, 0x27f2, 0x13f9, 0x09fc, 0x44fe, + 0x227f, 0x513f, 0x689f, 0x744f, 0x7a27, 0x7d13, 0x7e89, 0x3f44, 0x5fa2, 0x2fd1, + 0x17e8, 0x4bf4, 0x65fa, 0x32fd, 0x197e, 0x0cbf, 0x465f, 0x632f, 0x7197, 0x78cb, + 0x7c65, 0x3e32, 0x1f19, 0x0f8c, 0x47c6, 0x23e3, 0x51f1, 0x28f8, 0x547c, 0x6a3e, + 0x351f, 0x5a8f, 0x6d47, 0x76a3, 0x7b51, 0x3da8, 0x5ed4, 0x6f6a, 0x37b5, 0x1bda, + 0x0ded, 0x06f6, 0x037b, 0x41bd, 0x20de, 0x106f, 0x4837, 0x641b, 0x720d, 0x3906, + 0x1c83, 0x4e41, 0x2720, 0x5390, 0x69c8, 0x74e4, 0x7a72, 0x3d39, 0x1e9c, 0x4f4e, + 0x27a7, 0x53d3, 0x69e9, 0x34f4, 0x5a7a, 0x2d3d, 0x169e, 0x0b4f, 0x45a7, 0x62d3, + 0x7169, 0x38b4, 0x5c5a, 0x2e2d, 0x1716, 0x0b8b, 0x45c5, 0x22e2, 0x1171, 0x08b8, + 0x445c, 0x622e, 0x3117, 0x588b, 0x6c45, 0x3622, 0x1b11, 0x0d88, 0x46c4, 0x6362, + 0x31b1, 0x18d8, 0x4c6c, 0x6636, 0x331b, 0x598d, 0x2cc6, 0x1663, 0x4b31, 0x2598, + 0x52cc, 0x6966, 0x34b3, 0x5a59, 0x2d2c, 0x5696, 0x2b4b, 0x55a5, 0x2ad2, 0x1569, + 0x0ab4, 0x455a, 0x22ad, 0x1156, 0x08ab, 0x4455, 0x222a, 0x1115, 0x088a, 0x0445, + 0x0222, 0x0111, 0x0088, 0x4044, 0x6022, 0x3011, 0x1808, 0x4c04, 0x6602, 0x3301, + 0x1980, 0x4cc0, 0x6660, 0x7330, 0x7998, 0x7ccc, 0x7e66, 0x3f33, 0x5f99, 0x2fcc, + 0x57e6, 0x2bf3, 0x55f9, 0x2afc, 0x557e, 0x2abf, 0x555f, 0x6aaf, 0x7557, 0x7aab, + 0x7d55, 0x3eaa, 0x1f55, 0x0faa, 0x07d5, 0x03ea, 0x01f5, 0x00fa, 0x007d, 0x003e, + 0x001f, 0x400f, 0x6007, 0x7003, 0x7801, 0x3c00, 0x5e00, 0x6f00, 0x7780, 0x7bc0, + 0x7de0, 0x7ef0, 0x7f78, 0x7fbc, 0x7fde, 0x3fef, 0x5ff7, 0x6ffb, 0x77fd, 0x3bfe, + 0x1dff, 0x4eff, 0x677f, 0x73bf, 0x79df, 0x7cef, 0x7e77, 0x7f3b, 0x7f9d, 0x3fce, + 0x1fe7, 0x4ff3, 0x67f9, 0x33fc, 0x59fe, 0x2cff, 0x567f, 0x6b3f, 0x759f, 0x7acf, + 0x7d67, 0x7eb3, 0x7f59, 0x3fac, 0x5fd6, 0x2feb, 0x57f5, 0x2bfa, 0x15fd, 0x0afe, + 0x057f, 0x42bf, 0x615f, 0x70af, 0x7857, 0x7c2b, 0x7e15, 0x3f0a, 0x1f85, 0x0fc2, + 0x07e1, 0x03f0, 0x41f8, 0x60fc, 0x707e, 0x383f, 0x5c1f, 0x6e0f, 0x7707, 0x7b83, + 0x7dc1, 0x3ee0, 0x5f70, 0x6fb8, 0x77dc, 0x7bee, 0x3df7, 0x5efb, 0x6f7d, 0x37be, + 0x1bdf, 0x4def, 0x66f7, 0x737b, 0x79bd, 0x3cde, 0x1e6f, 0x4f37, 0x679b, 0x73cd, + 0x39e6, 0x1cf3, 0x4e79, 0x273c, 0x539e, 0x29cf, 0x54e7, 0x6a73, 0x7539, 0x3a9c, + 0x5d4e, 0x2ea7, 0x5753, 0x6ba9, 0x35d4, 0x5aea, 0x2d75, 0x16ba, 0x0b5d, 0x05ae, + 0x02d7, 0x416b, 0x60b5, 0x305a, 0x182d, 0x0c16, 0x060b, 0x4305, 0x2182, 0x10c1, + 0x0860, 0x4430, 0x6218, 0x710c, 0x7886, 0x3c43, 0x5e21, 0x2f10, 0x5788, 0x6bc4, + 0x75e2, 0x3af1, 0x1d78, 0x4ebc, 0x675e, 0x33af, 0x59d7, 0x6ceb, 0x7675, 0x3b3a, + 0x1d9d, 0x0ece, 0x0767, 0x43b3, 0x61d9, 0x30ec, 0x5876, 0x2c3b, 0x561d, 0x2b0e, + 0x1587, 0x4ac3, 0x6561, 0x32b0, 0x5958, 0x6cac, 0x7656, 0x3b2b, 0x5d95, 0x2eca, + 0x1765, 0x0bb2, 0x05d9, 0x02ec, 0x4176, 0x20bb, 0x505d, 0x282e, 0x1417, 0x4a0b, + 0x6505, 0x3282, 0x1941, 0x0ca0, 0x4650, 0x6328, 0x7194, 0x78ca, 0x3c65, 0x1e32, + 0x0f19, 0x078c, 0x43c6, 0x21e3, 0x50f1, 0x2878, 0x543c, 0x6a1e, 0x350f, 0x5a87, + 0x6d43, 0x76a1, 0x3b50, 0x5da8, 0x6ed4, 0x776a, 0x3bb5, 0x1dda, 0x0eed, 0x0776, + 0x03bb, 0x41dd, 0x20ee, 0x1077, 0x483b, 0x641d, 0x320e, 0x1907, 0x4c83, 0x6641, + 0x3320, 0x5990, 0x6cc8, 0x7664, 0x7b32, 0x3d99, 0x1ecc, 0x4f66, 0x27b3, 0x53d9, + 0x29ec, 0x54f6, 0x2a7b, 0x553d, 0x2a9e, 0x154f, 0x4aa7, 0x6553, 0x72a9, 0x3954, + 0x5caa, 0x2e55, 0x172a, 0x0b95, 0x05ca, 0x02e5, 0x0172, 0x00b9, 0x005c, 0x402e, + 0x2017, 0x500b, 0x6805, 0x3402, 0x1a01, 0x0d00, 0x4680, 0x6340, 0x71a0, 0x78d0, + 0x7c68, 0x7e34, 0x7f1a, 0x3f8d, 0x1fc6, 0x0fe3, 0x47f1, 0x23f8, 0x51fc, 0x68fe, + 0x347f, 0x5a3f, 0x6d1f, 0x768f, 0x7b47, 0x7da3, 0x7ed1, 0x3f68, 0x5fb4, 0x6fda, + 0x37ed, 0x1bf6, 0x0dfb, 0x46fd, 0x237e, 0x11bf, 0x48df, 0x646f, 0x7237, 0x791b, + 0x7c8d, 0x3e46, 0x1f23, 0x4f91, 0x27c8, 0x53e4, 0x69f2, 0x34f9, 0x1a7c, 0x4d3e, + 0x269f, 0x534f, 0x69a7, 0x74d3, 0x7a69, 0x3d34, 0x5e9a, 0x2f4d, 0x17a6, 0x0bd3, + 0x45e9, 0x22f4, 0x517a, 0x28bd, 0x145e, 0x0a2f, 0x4517, 0x628b, 0x7145, 0x38a2, + 0x1c51, 0x0e28, 0x4714, 0x638a, 0x31c5, 0x18e2, 0x0c71, 0x0638, 0x431c, 0x618e, + 0x30c7, 0x5863, 0x6c31, 0x3618, 0x5b0c, 0x6d86, 0x36c3, 0x5b61, 0x2db0, 0x56d8, + 0x6b6c, 0x75b6, 0x3adb, 0x5d6d, 0x2eb6, 0x175b, 0x4bad, 0x25d6, 0x12eb, 0x4975, + 0x24ba, 0x125d, 0x092e, 0x0497, 0x424b, 0x6125, 0x3092, 0x1849, 0x0c24, 0x4612, + 0x2309, 0x1184, 0x48c2, 0x2461, 0x1230, 0x4918, 0x648c, 0x7246, 0x3923, 0x5c91, + 0x2e48, 0x5724, 0x6b92, 0x35c9, 0x1ae4, 0x4d72, 0x26b9, 0x135c, 0x49ae, 0x24d7, + 0x526b, 0x6935, 0x349a, 0x1a4d, 0x0d26, 0x0693, 0x4349, 0x21a4, 0x50d2, 0x2869, + 0x1434, 0x4a1a, 0x250d, 0x1286, 0x0943, 0x44a1, 0x2250, 0x5128, 0x6894, 0x744a, + 0x3a25, 0x1d12, 0x0e89, 0x0744, 0x43a2, 0x21d1, 0x10e8, 0x4874, 0x643a, 0x321d, + 0x190e, 0x0c87, 0x4643, 0x6321, 0x3190, 0x58c8, 0x6c64, 0x7632, 0x3b19, 0x1d8c, + 0x4ec6, 0x2763, 0x53b1, 0x29d8, 0x54ec, 0x6a76, 0x353b, 0x5a9d, 0x2d4e, 0x16a7, + 0x4b53, 0x65a9, 0x32d4, 0x596a, 0x2cb5, 0x165a, 0x0b2d, 0x0596, 0x02cb, 0x4165, + 0x20b2, 0x1059, 0x082c, 0x4416, 0x220b, 0x5105, 0x2882, 0x1441, 0x0a20, 0x4510, + 0x6288, 0x7144, 0x78a2, 0x3c51, 0x1e28, 0x4f14, 0x678a, 0x33c5, 0x19e2, 0x0cf1, + 0x0678, 0x433c, 0x619e, 0x30cf, 0x5867, 0x6c33, 0x7619, 0x3b0c, 0x5d86, 0x2ec3, + 0x5761, 0x2bb0, 0x55d8, 0x6aec, 0x7576, 0x3abb, 0x5d5d, 0x2eae, 0x1757, 0x4bab, + 0x65d5, 0x32ea, 0x1975, 0x0cba, 0x065d, 0x032e, 0x0197, 0x40cb, 0x6065, 0x3032, + 0x1819, 0x0c0c, 0x4606, 0x2303, 0x5181, 0x28c0, 0x5460, 0x6a30, 0x7518, 0x7a8c, + 0x7d46, 0x3ea3, 0x5f51, 0x2fa8, 0x57d4, 0x6bea, 0x35f5, 0x1afa, 0x0d7d, 0x06be, + 0x035f, 0x41af, 0x60d7, 0x706b, 0x7835, 0x3c1a, 0x1e0d, 0x0f06, 0x0783, 0x43c1, + 0x21e0, 0x50f0, 0x6878, 0x743c, 0x7a1e, 0x3d0f, 0x5e87, 0x6f43, 0x77a1, 0x3bd0, + 0x5de8, 0x6ef4, 0x777a, 0x3bbd, 0x1dde, 0x0eef, 0x4777, 0x63bb, 0x71dd, 0x38ee, + 0x1c77, 0x4e3b, 0x671d, 0x338e, 0x19c7, 0x4ce3, 0x6671, 0x3338, 0x599c, 0x6cce, + 0x3667, 0x5b33, 0x6d99, 0x36cc, 0x5b66, 0x2db3, 0x56d9, 0x2b6c, 0x55b6, 0x2adb, + 0x556d, 0x2ab6, 0x155b, 0x4aad, 0x2556, 0x12ab, 0x4955, 0x24aa, 0x1255, 0x092a, + 0x0495, 0x024a, 0x0125, 0x0092, 0x0049, 0x0024, 0x4012, 0x2009, 0x1004, 0x4802, + 0x2401, 0x1200, 0x4900, 0x6480, 0x7240, 0x7920, 0x7c90, 0x7e48, 0x7f24, 0x7f92, + 0x3fc9, 0x1fe4, 0x4ff2, 0x27f9, 0x13fc, 0x49fe, 0x24ff, 0x527f, 0x693f, 0x749f, + 0x7a4f, 0x7d27, 0x7e93, 0x7f49, 0x3fa4, 0x5fd2, 0x2fe9, 0x17f4, 0x4bfa, 0x25fd, + 0x12fe, 0x097f, 0x44bf, 0x625f, 0x712f, 0x7897, 0x7c4b, 0x7e25, 0x3f12, 0x1f89, + 0x0fc4, 0x47e2, 0x23f1, 0x11f8, 0x48fc, 0x647e, 0x323f, 0x591f, 0x6c8f, 0x7647, + 0x7b23, 0x7d91, 0x3ec8, 0x5f64, 0x6fb2, 0x37d9, 0x1bec, 0x4df6, 0x26fb, 0x537d, + 0x29be, 0x14df, 0x4a6f, 0x6537, 0x729b, 0x794d, 0x3ca6, 0x1e53, 0x4f29, 0x2794, + 0x53ca, 0x29e5, 0x14f2, 0x0a79, 0x053c, 0x429e, 0x214f, 0x50a7, 0x6853, 0x7429, + 0x3a14, 0x5d0a, 0x2e85, 0x1742, 0x0ba1, 0x05d0, 0x42e8, 0x6174, 0x70ba, 0x385d, + 0x1c2e, 0x0e17, 0x470b, 0x6385, 0x31c2, 0x18e1, 0x0c70, 0x4638, 0x631c, 0x718e, + 0x38c7, 0x5c63, 0x6e31, 0x3718, 0x5b8c, 0x6dc6, 0x36e3, 0x5b71, 0x2db8, 0x56dc, + 0x6b6e, 0x35b7, 0x5adb, 0x6d6d, 0x36b6, 0x1b5b, 0x4dad, 0x26d6, 0x136b, 0x49b5, + 0x24da, 0x126d, 0x0936, 0x049b, 0x424d, 0x2126, 0x1093, 0x4849, 0x2424, 0x5212, + 0x2909, 0x1484, 0x4a42, 0x2521, 0x1290, 0x4948, 0x64a4, 0x7252, 0x3929, 0x1c94, + 0x4e4a, 0x2725, 0x1392, 0x09c9, 0x04e4, 0x4272, 0x2139, 0x109c, 0x484e, 0x2427, + 0x5213, 0x6909, 0x3484, 0x5a42, 0x2d21, 0x1690, 0x4b48, 0x65a4, 0x72d2, 0x3969, + 0x1cb4, 0x4e5a, 0x272d, 0x1396, 0x09cb, 0x44e5, 0x2272, 0x1139, 0x089c, 0x444e, + 0x2227, 0x5113, 0x6889, 0x3444, 0x5a22, 0x2d11, 0x1688, 0x4b44, 0x65a2, 0x32d1, + 0x1968, 0x4cb4, 0x665a, 0x332d, 0x1996, 0x0ccb, 0x4665, 0x2332, 0x1199, 0x08cc, + 0x4466, 0x2233, 0x5119, 0x288c, 0x5446, 0x2a23, 0x5511, 0x2a88, 0x5544, 0x6aa2, + 0x3551, 0x1aa8, 0x4d54, 0x66aa, 0x3355, 0x19aa, 0x0cd5, 0x066a, 0x0335, 0x019a, + 0x00cd, 0x0066, 0x0033, 0x4019, 0x200c, 0x5006, 0x2803, 0x5401, 0x2a00, 0x5500, + 0x6a80, 0x7540, 0x7aa0, 0x7d50, 0x7ea8, 0x7f54, 0x7faa, 0x3fd5, 0x1fea, 0x0ff5, + 0x07fa, 0x03fd, 0x01fe, 0x00ff, 0x407f, 0x603f, 0x701f, 0x780f, 0x7c07, 0x7e03, + 0x7f01, 0x3f80, 0x5fc0, 0x6fe0, 0x77f0, 0x7bf8, 0x7dfc, 0x7efe, 0x3f7f, 0x5fbf, + 0x6fdf, 0x77ef, 0x7bf7, 0x7dfb, 0x7efd, 0x3f7e, 0x1fbf, 0x4fdf, 0x67ef, 0x73f7, + 0x79fb, 0x7cfd, 0x3e7e, 0x1f3f, 0x4f9f, 0x67cf, 0x73e7, 0x79f3, 0x7cf9, 0x3e7c, + 0x5f3e, 0x2f9f, 0x57cf, 0x6be7, 0x75f3, 0x7af9, 0x3d7c, 0x5ebe, 0x2f5f, 0x57af, + 0x6bd7, 0x75eb, 0x7af5, 0x3d7a, 0x1ebd, 0x0f5e, 0x07af, 0x43d7, 0x61eb, 0x70f5, + 0x387a, 0x1c3d, 0x0e1e, 0x070f, 0x4387, 0x61c3, 0x70e1, 0x3870, 0x5c38, 0x6e1c, + 0x770e, 0x3b87, 0x5dc3, 0x6ee1, 0x3770, 0x5bb8, 0x6ddc, 0x76ee, 0x3b77, 0x5dbb, + 0x6edd, 0x376e, 0x1bb7, 0x4ddb, 0x66ed, 0x3376, 0x19bb, 0x4cdd, 0x266e, 0x1337, + 0x499b, 0x64cd, 0x3266, 0x1933, 0x4c99, 0x264c, 0x5326, 0x2993, 0x54c9, 0x2a64, + 0x5532, 0x2a99, 0x154c, 0x4aa6, 0x2553, 0x52a9, 0x2954, 0x54aa, 0x2a55, 0x152a, + 0x0a95, 0x054a, 0x02a5, 0x0152, 0x00a9, 0x0054, 0x402a, 0x2015, 0x100a, 0x0805, + 0x0402, 0x0201, 0x0100, 0x4080, 0x6040, 0x7020, 0x7810, 0x7c08, 0x7e04, 0x7f02, + 0x3f81, 0x1fc0, 0x4fe0, 0x67f0, 0x73f8, 0x79fc, 0x7cfe, 0x3e7f, 0x5f3f, 0x6f9f, + 0x77cf, 0x7be7, 0x7df3, 0x7ef9, 0x3f7c, 0x5fbe, 0x2fdf, 0x57ef, 0x6bf7, 0x75fb, + 0x7afd, 0x3d7e, 0x1ebf, 0x4f5f, 0x67af, 0x73d7, 0x79eb, 0x7cf5, 0x3e7a, 0x1f3d, + 0x0f9e, 0x07cf, 0x43e7, 0x61f3, 0x70f9, 0x387c, 0x5c3e, 0x2e1f, 0x570f, 0x6b87, + 0x75c3, 0x7ae1, 0x3d70, 0x5eb8, 0x6f5c, 0x77ae, 0x3bd7, 0x5deb, 0x6ef5, 0x377a, + 0x1bbd, 0x0dde, 0x06ef, 0x4377, 0x61bb, 0x70dd, 0x386e, 0x1c37, 0x4e1b, 0x670d, + 0x3386, 0x19c3, 0x4ce1, 0x2670, 0x5338, 0x699c, 0x74ce, 0x3a67, 0x5d33, 0x6e99, + 0x374c, 0x5ba6, 0x2dd3, 0x56e9, 0x2b74, 0x55ba, 0x2add, 0x156e, 0x0ab7, 0x455b, + 0x62ad, 0x3156, 0x18ab, 0x4c55, 0x262a, 0x1315, 0x098a, 0x04c5, 0x0262, 0x0131, + 0x0098, 0x404c, 0x6026, 0x3013, 0x5809, 0x2c04, 0x5602, 0x2b01, 0x1580, 0x4ac0, + 0x6560, 0x72b0, 0x7958, 0x7cac, 0x7e56, 0x3f2b, 0x5f95, 0x2fca, 0x17e5, 0x0bf2, + 0x05f9, 0x02fc, 0x417e, 0x20bf, 0x505f, 0x682f, 0x7417, 0x7a0b, 0x7d05, 0x3e82, + 0x1f41, 0x0fa0, 0x47d0, 0x63e8, 0x71f4, 0x78fa, 0x3c7d, 0x1e3e, 0x0f1f, 0x478f, + 0x63c7, 0x71e3, 0x78f1, 0x3c78, 0x5e3c, 0x6f1e, 0x378f, 0x5bc7, 0x6de3, 0x76f1, + 0x3b78, 0x5dbc, 0x6ede, 0x376f, 0x5bb7, 0x6ddb, 0x76ed, 0x3b76, 0x1dbb, 0x4edd, + 0x276e, 0x13b7, 0x49db, 0x64ed, 0x3276, 0x193b, 0x4c9d, 0x264e, 0x1327, 0x4993, + 0x64c9, 0x3264, 0x5932, 0x2c99, 0x164c, 0x4b26, 0x2593, 0x52c9, 0x2964, 0x54b2, + 0x2a59, 0x152c, 0x4a96, 0x254b, 0x52a5, 0x2952, 0x14a9, 0x0a54, 0x452a, 0x2295, + 0x114a, 0x08a5, 0x0452, 0x0229, 0x0114, 0x408a, 0x2045, 0x1022, 0x0811, 0x0408, + 0x4204, 0x6102, 0x3081, 0x1840, 0x4c20, 0x6610, 0x7308, 0x7984, 0x7cc2, 0x3e61, + 0x1f30, 0x4f98, 0x67cc, 0x73e6, 0x39f3, 0x5cf9, 0x2e7c, 0x573e, 0x2b9f, 0x55cf, + 0x6ae7, 0x7573, 0x7ab9, 0x3d5c, 0x5eae, 0x2f57, 0x57ab, 0x6bd5, 0x35ea, 0x1af5, + 0x0d7a, 0x06bd, 0x035e, 0x01af, 0x40d7, 0x606b, 0x7035, 0x381a, 0x1c0d, 0x0e06, + 0x0703, 0x4381, 0x21c0, 0x50e0, 0x6870, 0x7438, 0x7a1c, 0x7d0e, 0x3e87, 0x5f43, + 0x6fa1, 0x37d0, 0x5be8, 0x6df4, 0x76fa, 0x3b7d, 0x1dbe, 0x0edf, 0x476f, 0x63b7, + 0x71db, 0x78ed, 0x3c76, 0x1e3b, 0x4f1d, 0x278e, 0x13c7, 0x49e3, 0x64f1, 0x3278, + 0x593c, 0x6c9e, 0x364f, 0x5b27, 0x6d93, 0x76c9, 0x3b64, 0x5db2, 0x2ed9, 0x176c, + 0x4bb6, 0x25db, 0x52ed, 0x2976, 0x14bb, 0x4a5d, 0x252e, 0x1297, 0x494b, 0x64a5, + 0x3252, 0x1929, 0x0c94, 0x464a, 0x2325, 0x1192, 0x08c9, 0x0464, 0x4232, 0x2119, + 0x108c, 0x4846, 0x2423, 0x5211, 0x2908, 0x5484, 0x6a42, 0x3521, 0x1a90, 0x4d48, + 0x66a4, 0x7352, 0x39a9, 0x1cd4, 0x4e6a, 0x2735, 0x139a, 0x09cd, 0x04e6, 0x0273, + 0x4139, 0x209c, 0x504e, 0x2827, 0x5413, 0x6a09, 0x3504, 0x5a82, 0x2d41, 0x16a0, + 0x4b50, 0x65a8, 0x72d4, 0x796a, 0x3cb5, 0x1e5a, 0x0f2d, 0x0796, 0x03cb, 0x41e5, + 0x20f2, 0x1079, 0x083c, 0x441e, 0x220f, 0x5107, 0x6883, 0x7441, 0x3a20, 0x5d10, + 0x6e88, 0x7744, 0x7ba2, 0x3dd1, 0x1ee8, 0x4f74, 0x67ba, 0x33dd, 0x19ee, 0x0cf7, + 0x467b, 0x633d, 0x319e, 0x18cf, 0x4c67, 0x6633, 0x7319, 0x398c, 0x5cc6, 0x2e63, + 0x5731, 0x2b98, 0x55cc, 0x6ae6, 0x3573, 0x5ab9, 0x2d5c, 0x56ae, 0x2b57, 0x55ab, + 0x6ad5, 0x356a, 0x1ab5, 0x0d5a, 0x06ad, 0x0356, 0x01ab, 0x40d5, 0x206a, 0x1035, + 0x081a, 0x040d, 0x0206, 0x0103, 0x4081, 0x2040, 0x5020, 0x6810, 0x7408, 0x7a04, + 0x7d02, 0x3e81, 0x1f40, 0x4fa0, 0x67d0, 0x73e8, 0x79f4, 0x7cfa, 0x3e7d, 0x1f3e, + 0x0f9f, 0x47cf, 0x63e7, 0x71f3, 0x78f9, 0x3c7c, 0x5e3e, 0x2f1f, 0x578f, 0x6bc7, + 0x75e3, 0x7af1, 0x3d78, 0x5ebc, 0x6f5e, 0x37af, 0x5bd7, 0x6deb, 0x76f5, 0x3b7a, + 0x1dbd, 0x0ede, 0x076f, 0x43b7, 0x61db, 0x70ed, 0x3876, 0x1c3b, 0x4e1d, 0x270e, + 0x1387, 0x49c3, 0x64e1, 0x3270, 0x5938, 0x6c9c, 0x764e, 0x3b27, 0x5d93, 0x6ec9, + 0x3764, 0x5bb2, 0x2dd9, 0x16ec, 0x4b76, 0x25bb, 0x52dd, 0x296e, 0x14b7, 0x4a5b, + 0x652d, 0x3296, 0x194b, 0x4ca5, 0x2652, 0x1329, 0x0994, 0x44ca, 0x2265, 0x1132, + 0x0899, 0x044c, 0x4226, 0x2113, 0x5089, 0x2844, 0x5422, 0x2a11, 0x1508, 0x4a84, + 0x6542, 0x32a1, 0x1950, 0x4ca8, 0x6654, 0x732a, 0x3995, 0x1cca, 0x0e65, 0x0732, + 0x0399, 0x01cc, 0x40e6, 0x2073, 0x5039, 0x281c, 0x540e, 0x2a07, 0x5503, 0x6a81, + 0x3540, 0x5aa0, 0x6d50, 0x76a8, 0x7b54, 0x7daa, 0x3ed5, 0x1f6a, 0x0fb5, 0x07da, + 0x03ed, 0x01f6, 0x00fb, 0x407d, 0x203e, 0x101f, 0x480f, 0x6407, 0x7203, 0x7901, + 0x3c80, 0x5e40, 0x6f20, 0x7790, 0x7bc8, 0x7de4, 0x7ef2, 0x3f79, 0x1fbc, 0x4fde, + 0x27ef, 0x53f7, 0x69fb, 0x74fd, 0x3a7e, 0x1d3f, 0x4e9f, 0x674f, 0x73a7, 0x79d3, + 0x7ce9, 0x3e74, 0x5f3a, 0x2f9d, 0x17ce, 0x0be7, 0x45f3, 0x62f9, 0x317c, 0x58be, + 0x2c5f, 0x562f, 0x6b17, 0x758b, 0x7ac5, 0x3d62, 0x1eb1, 0x0f58, 0x47ac, 0x63d6, + 0x31eb, 0x58f5, 0x2c7a, 0x163d, 0x0b1e, 0x058f, 0x42c7, 0x6163, 0x70b1, 0x3858, + 0x5c2c, 0x6e16, 0x370b, 0x5b85, 0x2dc2, 0x16e1, 0x0b70, 0x45b8, 0x62dc, 0x716e, + 0x38b7, 0x5c5b, 0x6e2d, 0x3716, 0x1b8b, 0x4dc5, 0x26e2, 0x1371, 0x09b8, 0x44dc, + 0x626e, 0x3137, 0x589b, 0x6c4d, 0x3626, 0x1b13, 0x4d89, 0x26c4, 0x5362, 0x29b1, + 0x14d8, 0x4a6c, 0x6536, 0x329b, 0x594d, 0x2ca6, 0x1653, 0x4b29, 0x2594, 0x52ca, + 0x2965, 0x14b2, 0x0a59, 0x052c, 0x4296, 0x214b, 0x50a5, 0x2852, 0x1429, 0x0a14, + 0x450a, 0x2285, 0x1142, 0x08a1, 0x0450, 0x4228, 0x6114, 0x708a, 0x3845, 0x1c22, + 0x0e11, 0x0708, 0x4384, 0x61c2, 0x30e1, 0x1870, 0x4c38, 0x661c, 0x730e, 0x3987, + 0x5cc3, 0x6e61, 0x3730, 0x5b98, 0x6dcc, 0x76e6, 0x3b73, 0x5db9, 0x2edc, 0x576e, + 0x2bb7, 0x55db, 0x6aed, 0x3576, 0x1abb, 0x4d5d, 0x26ae, 0x1357, 0x49ab, 0x64d5, + 0x326a, 0x1935, 0x0c9a, 0x064d, 0x0326, 0x0193, 0x40c9, 0x2064, 0x5032, 0x2819, + 0x140c, 0x4a06, 0x2503, 0x5281, 0x2940, 0x54a0, 0x6a50, 0x7528, 0x7a94, 0x7d4a, + 0x3ea5, 0x1f52, 0x0fa9, 0x07d4, 0x43ea, 0x21f5, 0x10fa, 0x087d, 0x043e, 0x021f, + 0x410f, 0x6087, 0x7043, 0x7821, 0x3c10, 0x5e08, 0x6f04, 0x7782, 0x3bc1, 0x1de0, + 0x4ef0, 0x6778, 0x73bc, 0x79de, 0x3cef, 0x5e77, 0x6f3b, 0x779d, 0x3bce, 0x1de7, + 0x4ef3, 0x6779, 0x33bc, 0x59de, 0x2cef, 0x5677, 0x6b3b, 0x759d, 0x3ace, 0x1d67, + 0x4eb3, 0x6759, 0x33ac, 0x59d6, 0x2ceb, 0x5675, 0x2b3a, 0x159d, 0x0ace, 0x0567, + 0x42b3, 0x6159, 0x30ac, 0x5856, 0x2c2b, 0x5615, 0x2b0a, 0x1585, 0x0ac2, 0x0561, + 0x02b0, 0x4158, 0x60ac, 0x7056, 0x382b, 0x5c15, 0x2e0a, 0x1705, 0x0b82, 0x05c1, + 0x02e0, 0x4170, 0x60b8, 0x705c, 0x782e, 0x3c17, 0x5e0b, 0x6f05, 0x3782, 0x1bc1, + 0x0de0, 0x46f0, 0x6378, 0x71bc, 0x78de, 0x3c6f, 0x5e37, 0x6f1b, 0x778d, 0x3bc6, + 0x1de3, 0x4ef1, 0x2778, 0x53bc, 0x69de, 0x34ef, 0x5a77, 0x6d3b, 0x769d, 0x3b4e, + 0x1da7, 0x4ed3, 0x6769, 0x33b4, 0x59da, 0x2ced, 0x1676, 0x0b3b, 0x459d, 0x22ce, + 0x1167, 0x48b3, 0x6459, 0x322c, 0x5916, 0x2c8b, 0x5645, 0x2b22, 0x1591, 0x0ac8, + 0x4564, 0x62b2, 0x3159, 0x18ac, 0x4c56, 0x262b, 0x5315, 0x298a, 0x14c5, 0x0a62, + 0x0531, 0x0298, 0x414c, 0x60a6, 0x3053, 0x5829, 0x2c14, 0x560a, 0x2b05, 0x1582, + 0x0ac1, 0x0560, 0x42b0, 0x6158, 0x70ac, 0x7856, 0x3c2b, 0x5e15, 0x2f0a, 0x1785, + 0x0bc2, 0x05e1, 0x02f0, 0x4178, 0x60bc, 0x705e, 0x382f, 0x5c17, 0x6e0b, 0x7705, + 0x3b82, 0x1dc1, 0x0ee0, 0x4770, 0x63b8, 0x71dc, 0x78ee, 0x3c77, 0x5e3b, 0x6f1d, + 0x378e, 0x1bc7, 0x4de3, 0x66f1, 0x3378, 0x59bc, 0x6cde, 0x366f, 0x5b37, 0x6d9b, + 0x76cd, 0x3b66, 0x1db3, 0x4ed9, 0x276c, 0x53b6, 0x29db, 0x54ed, 0x2a76, 0x153b, + 0x4a9d, 0x254e, 0x12a7, 0x4953, 0x64a9, 0x3254, 0x592a, 0x2c95, 0x164a, 0x0b25, + 0x0592, 0x02c9, 0x0164, 0x40b2, 0x2059, 0x102c, 0x4816, 0x240b, 0x5205, 0x2902, + 0x1481, 0x0a40, 0x4520, 0x6290, 0x7148, 0x78a4, 0x7c52, 0x3e29, 0x1f14, 0x4f8a, + 0x27c5, 0x13e2, 0x09f1, 0x04f8, 0x427c, 0x613e, 0x309f, 0x584f, 0x6c27, 0x7613, + 0x7b09, 0x3d84, 0x5ec2, 0x2f61, 0x17b0, 0x4bd8, 0x65ec, 0x72f6, 0x397b, 0x5cbd, + 0x2e5e, 0x172f, 0x4b97, 0x65cb, 0x72e5, 0x3972, 0x1cb9, 0x0e5c, 0x472e, 0x2397, + 0x51cb, 0x68e5, 0x3472, 0x1a39, 0x0d1c, 0x468e, 0x2347, 0x51a3, 0x68d1, 0x3468, + 0x5a34, 0x6d1a, 0x368d, 0x1b46, 0x0da3, 0x46d1, 0x2368, 0x51b4, 0x68da, 0x346d, + 0x1a36, 0x0d1b, 0x468d, 0x2346, 0x11a3, 0x48d1, 0x2468, 0x5234, 0x691a, 0x348d, + 0x1a46, 0x0d23, 0x4691, 0x2348, 0x51a4, 0x68d2, 0x3469, 0x1a34, 0x4d1a, 0x268d, + 0x1346, 0x09a3, 0x44d1, 0x2268, 0x5134, 0x689a, 0x344d, 0x1a26, 0x0d13, 0x4689, + 0x2344, 0x51a2, 0x28d1, 0x1468, 0x4a34, 0x651a, 0x328d, 0x1946, 0x0ca3, 0x4651, + 0x2328, 0x5194, 0x68ca, 0x3465, 0x1a32, 0x0d19, 0x068c, 0x4346, 0x21a3, 0x50d1, + 0x2868, 0x5434, 0x6a1a, 0x350d, 0x1a86, 0x0d43, 0x46a1, 0x2350, 0x51a8, 0x68d4, + 0x746a, 0x3a35, 0x1d1a, 0x0e8d, 0x0746, 0x03a3, 0x41d1, 0x20e8, 0x5074, 0x683a, + 0x341d, 0x1a0e, 0x0d07, 0x4683, 0x6341, 0x31a0, 0x58d0, 0x6c68, 0x7634, 0x7b1a, + 0x3d8d, 0x1ec6, 0x0f63, 0x47b1, 0x23d8, 0x51ec, 0x68f6, 0x347b, 0x5a3d, 0x2d1e, + 0x168f, 0x4b47, 0x65a3, 0x72d1, 0x3968, 0x5cb4, 0x6e5a, 0x372d, 0x1b96, 0x0dcb, + 0x46e5, 0x2372, 0x11b9, 0x08dc, 0x446e, 0x2237, 0x511b, 0x688d, 0x3446, 0x1a23, + 0x4d11, 0x2688, 0x5344, 0x69a2, 0x34d1, 0x1a68, 0x4d34, 0x669a, 0x334d, 0x19a6, + 0x0cd3, 0x4669, 0x2334, 0x519a, 0x28cd, 0x1466, 0x0a33, 0x4519, 0x228c, 0x5146, + 0x28a3, 0x5451, 0x2a28, 0x5514, 0x6a8a, 0x3545, 0x1aa2, 0x0d51, 0x06a8, 0x4354, + 0x61aa, 0x30d5, 0x186a, 0x0c35, 0x061a, 0x030d, 0x0186, 0x00c3, 0x4061, 0x2030, + 0x5018, 0x680c, 0x7406, 0x3a03, 0x5d01, 0x2e80, 0x5740, 0x6ba0, 0x75d0, 0x7ae8, + 0x7d74, 0x7eba, 0x3f5d, 0x1fae, 0x0fd7, 0x47eb, 0x63f5, 0x31fa, 0x18fd, 0x0c7e, + 0x063f, 0x431f, 0x618f, 0x70c7, 0x7863, 0x7c31, 0x3e18, 0x5f0c, 0x6f86, 0x37c3, + 0x5be1, 0x2df0, 0x56f8, 0x6b7c, 0x75be, 0x3adf, 0x5d6f, 0x6eb7, 0x775b, 0x7bad, + 0x3dd6, 0x1eeb, 0x4f75, 0x27ba, 0x13dd, 0x09ee, 0x04f7, 0x427b, 0x613d, 0x309e, + 0x184f, 0x4c27, 0x6613, 0x7309, 0x3984, 0x5cc2, 0x2e61, 0x1730, 0x4b98, 0x65cc, + 0x72e6, 0x3973, 0x5cb9, 0x2e5c, 0x572e, 0x2b97, 0x55cb, 0x6ae5, 0x3572, 0x1ab9, + 0x0d5c, 0x46ae, 0x2357, 0x51ab, 0x68d5, 0x346a, 0x1a35, 0x0d1a, 0x068d, 0x0346, + 0x01a3, 0x40d1, 0x2068, 0x5034, 0x681a, 0x340d, 0x1a06, 0x0d03, 0x4681, 0x2340, + 0x51a0, 0x68d0, 0x7468, 0x7a34, 0x7d1a, 0x3e8d, 0x1f46, 0x0fa3, 0x47d1, 0x23e8, + 0x51f4, 0x68fa, 0x347d, 0x1a3e, 0x0d1f, 0x468f, 0x6347, 0x71a3, 0x78d1, 0x3c68, + 0x5e34, 0x6f1a, 0x378d, 0x1bc6, 0x0de3, 0x46f1, 0x2378, 0x51bc, 0x68de, 0x346f, + 0x5a37, 0x6d1b, 0x768d, 0x3b46, 0x1da3, 0x4ed1, 0x2768, 0x53b4, 0x69da, 0x34ed, + 0x1a76, 0x0d3b, 0x469d, 0x234e, 0x11a7, 0x48d3, 0x6469, 0x3234, 0x591a, 0x2c8d, + 0x1646, 0x0b23, 0x4591, 0x22c8, 0x5164, 0x68b2, 0x3459, 0x1a2c, 0x4d16, 0x268b, + 0x5345, 0x29a2, 0x14d1, 0x0a68, 0x4534, 0x629a, 0x314d, 0x18a6, 0x0c53, 0x4629, + 0x2314, 0x518a, 0x28c5, 0x1462, 0x0a31, 0x0518, 0x428c, 0x6146, 0x30a3, 0x5851, + 0x2c28, 0x5614, 0x6b0a, 0x3585, 0x1ac2, 0x0d61, 0x06b0, 0x4358, 0x61ac, 0x70d6, + 0x386b, 0x5c35, 0x2e1a, 0x170d, 0x0b86, 0x05c3, 0x42e1, 0x2170, 0x50b8, 0x685c, + 0x742e, 0x3a17, 0x5d0b, 0x6e85, 0x3742, 0x1ba1, 0x0dd0, 0x46e8, 0x6374, 0x71ba, + 0x38dd, 0x1c6e, 0x0e37, 0x471b, 0x638d, 0x31c6, 0x18e3, 0x4c71, 0x2638, 0x531c, + 0x698e, 0x34c7, 0x5a63, 0x6d31, 0x3698, 0x5b4c, 0x6da6, 0x36d3, 0x5b69, 0x2db4, + 0x56da, 0x2b6d, 0x15b6, 0x0adb, 0x456d, 0x22b6, 0x115b, 0x48ad, 0x2456, 0x122b, + 0x4915, 0x248a, 0x1245, 0x0922, 0x0491, 0x0248, 0x4124, 0x6092, 0x3049, 0x1824, + 0x4c12, 0x2609, 0x1304, 0x4982, 0x24c1, 0x1260, 0x4930, 0x6498, 0x724c, 0x7926, + 0x3c93, 0x5e49, 0x2f24, 0x5792, 0x2bc9, 0x15e4, 0x4af2, 0x2579, 0x12bc, 0x495e, + 0x24af, 0x5257, 0x692b, 0x7495, 0x3a4a, 0x1d25, 0x0e92, 0x0749, 0x03a4, 0x41d2, + 0x20e9, 0x1074, 0x483a, 0x241d, 0x120e, 0x0907, 0x4483, 0x6241, 0x3120, 0x5890, + 0x6c48, 0x7624, 0x7b12, 0x3d89, 0x1ec4, 0x4f62, 0x27b1, 0x13d8, 0x49ec, 0x64f6, + 0x327b, 0x593d, 0x2c9e, 0x164f, 0x4b27, 0x6593, 0x72c9, 0x3964, 0x5cb2, 0x2e59, + 0x172c, 0x4b96, 0x25cb, 0x52e5, 0x2972, 0x14b9, 0x0a5c, 0x452e, 0x2297, 0x514b, + 0x68a5, 0x3452, 0x1a29, 0x0d14, 0x468a, 0x2345, 0x11a2, 0x08d1, 0x0468, 0x4234, + 0x611a, 0x308d, 0x1846, 0x0c23, 0x4611, 0x2308, 0x5184, 0x68c2, 0x3461, 0x1a30, + 0x4d18, 0x668c, 0x7346, 0x39a3, 0x5cd1, 0x2e68, 0x5734, 0x6b9a, 0x35cd, 0x1ae6, + 0x0d73, 0x46b9, 0x235c, 0x51ae, 0x28d7, 0x546b, 0x6a35, 0x351a, 0x1a8d, 0x0d46, + 0x06a3, 0x4351, 0x21a8, 0x50d4, 0x686a, 0x3435, 0x1a1a, 0x0d0d, 0x0686, 0x0343, + 0x41a1, 0x20d0, 0x5068, 0x6834, 0x741a, 0x3a0d, 0x1d06, 0x0e83, 0x4741, 0x23a0, + 0x51d0, 0x68e8, 0x7474, 0x7a3a, 0x3d1d, 0x1e8e, 0x0f47, 0x47a3, 0x63d1, 0x31e8, + 0x58f4, 0x6c7a, 0x363d, 0x1b1e, 0x0d8f, 0x46c7, 0x6363, 0x71b1, 0x38d8, 0x5c6c, + 0x6e36, 0x371b, 0x5b8d, 0x2dc6, 0x16e3, 0x4b71, 0x25b8, 0x52dc, 0x696e, 0x34b7, + 0x5a5b, 0x6d2d, 0x3696, 0x1b4b, 0x4da5, 0x26d2, 0x1369, 0x09b4, 0x44da, 0x226d, + 0x1136, 0x089b, 0x444d, 0x2226, 0x1113, 0x4889, 0x2444, 0x5222, 0x2911, 0x1488, + 0x4a44, 0x6522, 0x3291, 0x1948, 0x4ca4, 0x6652, 0x3329, 0x1994, 0x4cca, 0x2665, + 0x1332, 0x0999, 0x04cc, 0x4266, 0x2133, 0x5099, 0x284c, 0x5426, 0x2a13, 0x5509, + 0x2a84, 0x5542, 0x2aa1, 0x1550, 0x4aa8, 0x6554, 0x72aa, 0x3955, 0x1caa, 0x0e55, + 0x072a, 0x0395, 0x01ca, 0x00e5, 0x0072, 0x0039, 0x001c, 0x400e, 0x2007, 0x5003, + 0x6801, 0x3400, 0x5a00, 0x6d00, 0x7680, 0x7b40, 0x7da0, 0x7ed0, 0x7f68, 0x7fb4, + 0x7fda, 0x3fed, 0x1ff6, 0x0ffb, 0x47fd, 0x23fe, 0x11ff, 0x48ff, 0x647f, 0x723f, + 0x791f, 0x7c8f, 0x7e47, 0x7f23, 0x7f91, 0x3fc8, 0x5fe4, 0x6ff2, 0x37f9, 0x1bfc, + 0x4dfe, 0x26ff, 0x537f, 0x69bf, 0x74df, 0x7a6f, 0x7d37, 0x7e9b, 0x7f4d, 0x3fa6, + 0x1fd3, 0x4fe9, 0x27f4, 0x53fa, 0x29fd, 0x14fe, 0x0a7f, 0x453f, 0x629f, 0x714f, + 0x78a7, 0x7c53, 0x7e29, 0x3f14, 0x5f8a, 0x2fc5, 0x17e2, 0x0bf1, 0x05f8, 0x42fc, + 0x617e, 0x30bf, 0x585f, 0x6c2f, 0x7617, 0x7b0b, 0x7d85, 0x3ec2, 0x1f61, 0x0fb0, + 0x47d8, 0x63ec, 0x71f6, 0x38fb, 0x5c7d, 0x2e3e, 0x171f, 0x4b8f, 0x65c7, 0x72e3, + 0x7971, 0x3cb8, 0x5e5c, 0x6f2e, 0x3797, 0x5bcb, 0x6de5, 0x36f2, 0x1b79, 0x0dbc, + 0x46de, 0x236f, 0x51b7, 0x68db, 0x746d, 0x3a36, 0x1d1b, 0x4e8d, 0x2746, 0x13a3, + 0x49d1, 0x24e8, 0x5274, 0x693a, 0x349d, 0x1a4e, 0x0d27, 0x4693, 0x6349, 0x31a4, + 0x58d2, 0x2c69, 0x1634, 0x4b1a, 0x258d, 0x12c6, 0x0963, 0x44b1, 0x2258, 0x512c, + 0x6896, 0x344b, 0x5a25, 0x2d12, 0x1689, 0x0b44, 0x45a2, 0x22d1, 0x1168, 0x48b4, + 0x645a, 0x322d, 0x1916, 0x0c8b, 0x4645, 0x2322, 0x1191, 0x08c8, 0x4464, 0x6232, + 0x3119, 0x188c, 0x4c46, 0x2623, 0x5311, 0x2988, 0x54c4, 0x6a62, 0x3531, 0x1a98, + 0x4d4c, 0x66a6, 0x3353, 0x59a9, 0x2cd4, 0x566a, 0x2b35, 0x159a, 0x0acd, 0x0566, + 0x02b3, 0x4159, 0x20ac, 0x5056, 0x282b, 0x5415, 0x2a0a, 0x1505, 0x0a82, 0x0541, + 0x02a0, 0x4150, 0x60a8, 0x7054, 0x782a, 0x3c15, 0x1e0a, 0x0f05, 0x0782, 0x03c1, + 0x01e0, 0x40f0, 0x6078, 0x703c, 0x781e, 0x3c0f, 0x5e07, 0x6f03, 0x7781, 0x3bc0, + 0x5de0, 0x6ef0, 0x7778, 0x7bbc, 0x7dde, 0x3eef, 0x5f77, 0x6fbb, 0x77dd, 0x3bee, + 0x1df7, 0x4efb, 0x677d, 0x33be, 0x19df, 0x4cef, 0x6677, 0x733b, 0x799d, 0x3cce, + 0x1e67, 0x4f33, 0x6799, 0x33cc, 0x59e6, 0x2cf3, 0x5679, 0x2b3c, 0x559e, 0x2acf, + 0x5567, 0x6ab3, 0x7559, 0x3aac, 0x5d56, 0x2eab, 0x5755, 0x2baa, 0x15d5, 0x0aea, + 0x0575, 0x02ba, 0x015d, 0x00ae, 0x0057, 0x402b, 0x6015, 0x300a, 0x1805, 0x0c02, + 0x0601, 0x0300, 0x4180, 0x60c0, 0x7060, 0x7830, 0x7c18, 0x7e0c, 0x7f06, 0x3f83, + 0x5fc1, 0x2fe0, 0x57f0, 0x6bf8, 0x75fc, 0x7afe, 0x3d7f, 0x5ebf, 0x6f5f, 0x77af, + 0x7bd7, 0x7deb, 0x7ef5, 0x3f7a, 0x1fbd, 0x0fde, 0x07ef, 0x43f7, 0x61fb, 0x70fd, + 0x387e, 0x1c3f, 0x4e1f, 0x670f, 0x7387, 0x79c3, 0x7ce1, 0x3e70, 0x5f38, 0x6f9c, + 0x77ce, 0x3be7, 0x5df3, 0x6ef9, 0x377c, 0x5bbe, 0x2ddf, 0x56ef, 0x6b77, 0x75bb, + 0x7add, 0x3d6e, 0x1eb7, 0x4f5b, 0x67ad, 0x33d6, 0x19eb, 0x4cf5, 0x267a, 0x133d, + 0x099e, 0x04cf, 0x4267, 0x6133, 0x7099, 0x384c, 0x5c26, 0x2e13, 0x5709, 0x2b84, + 0x55c2, 0x2ae1, 0x1570, 0x4ab8, 0x655c, 0x72ae, 0x3957, 0x5cab, 0x6e55, 0x372a, + 0x1b95, 0x0dca, 0x06e5, 0x0372, 0x01b9, 0x00dc, 0x406e, 0x2037, 0x501b, 0x680d, + 0x3406, 0x1a03, 0x4d01, 0x2680, 0x5340, 0x69a0, 0x74d0, 0x7a68, 0x7d34, 0x7e9a, + 0x3f4d, 0x1fa6, 0x0fd3, 0x47e9, 0x23f4, 0x51fa, 0x28fd, 0x147e, 0x0a3f, 0x451f, + 0x628f, 0x7147, 0x78a3, 0x7c51, 0x3e28, 0x5f14, 0x6f8a, 0x37c5, 0x1be2, 0x0df1, + 0x06f8, 0x437c, 0x61be, 0x30df, 0x586f, 0x6c37, 0x761b, 0x7b0d, 0x3d86, 0x1ec3, + 0x4f61, 0x27b0, 0x53d8, 0x69ec, 0x74f6, 0x3a7b, 0x5d3d, 0x2e9e, 0x174f, 0x4ba7, + 0x65d3, 0x72e9, 0x3974, 0x5cba, 0x2e5d, 0x172e, 0x0b97, 0x45cb, 0x62e5, 0x3172, + 0x18b9, 0x0c5c, 0x462e, 0x2317, 0x518b, 0x68c5, 0x3462, 0x1a31, 0x0d18, 0x468c, + 0x6346, 0x31a3, 0x58d1, 0x2c68, 0x5634, 0x6b1a, 0x358d, 0x1ac6, 0x0d63, 0x46b1, + 0x2358, 0x51ac, 0x68d6, 0x346b, 0x5a35, 0x2d1a, 0x168d, 0x0b46, 0x05a3, 0x42d1, + 0x2168, 0x50b4, 0x685a, 0x342d, 0x1a16, 0x0d0b, 0x4685, 0x2342, 0x11a1, 0x08d0, + 0x4468, 0x6234, 0x711a, 0x388d, 0x1c46, 0x0e23, 0x4711, 0x2388, 0x51c4, 0x68e2, + 0x3471, 0x1a38, 0x4d1c, 0x668e, 0x3347, 0x59a3, 0x6cd1, 0x3668, 0x5b34, 0x6d9a, + 0x36cd, 0x1b66, 0x0db3, 0x46d9, 0x236c, 0x51b6, 0x28db, 0x546d, 0x2a36, 0x151b, + 0x4a8d, 0x2546, 0x12a3, 0x4951, 0x24a8, 0x5254, 0x692a, 0x3495, 0x1a4a, 0x0d25, + 0x0692, 0x0349, 0x01a4, 0x40d2, 0x2069, 0x1034, 0x481a, 0x240d, 0x1206, 0x0903, + 0x4481, 0x2240, 0x5120, 0x6890, 0x7448, 0x7a24, 0x7d12, 0x3e89, 0x1f44, 0x4fa2, + 0x27d1, 0x13e8, 0x49f4, 0x64fa, 0x327d, 0x193e, 0x0c9f, 0x464f, 0x6327, 0x7193, + 0x78c9, 0x3c64, 0x5e32, 0x2f19, 0x178c, 0x4bc6, 0x25e3, 0x52f1, 0x2978, 0x54bc, + 0x6a5e, 0x352f, 0x5a97, 0x6d4b, 0x76a5, 0x3b52, 0x1da9, 0x0ed4, 0x476a, 0x23b5, + 0x11da, 0x08ed, 0x0476, 0x023b, 0x411d, 0x208e, 0x1047, 0x4823, 0x6411, 0x3208, + 0x5904, 0x6c82, 0x3641, 0x1b20, 0x4d90, 0x66c8, 0x7364, 0x79b2, 0x3cd9, 0x1e6c, + 0x4f36, 0x279b, 0x53cd, 0x29e6, 0x14f3, 0x4a79, 0x253c, 0x529e, 0x294f, 0x54a7, + 0x6a53, 0x7529, 0x3a94, 0x5d4a, 0x2ea5, 0x1752, 0x0ba9, 0x05d4, 0x42ea, 0x2175, + 0x10ba, 0x085d, 0x042e, 0x0217, 0x410b, 0x6085, 0x3042, 0x1821, 0x0c10, 0x4608, + 0x6304, 0x7182, 0x38c1, 0x1c60, 0x4e30, 0x6718, 0x738c, 0x79c6, 0x3ce3, 0x5e71, + 0x2f38, 0x579c, 0x6bce, 0x35e7, 0x5af3, 0x6d79, 0x36bc, 0x5b5e, 0x2daf, 0x56d7, + 0x6b6b, 0x75b5, 0x3ada, 0x1d6d, 0x0eb6, 0x075b, 0x43ad, 0x21d6, 0x10eb, 0x4875, + 0x243a, 0x121d, 0x090e, 0x0487, 0x4243, 0x6121, 0x3090, 0x5848, 0x6c24, 0x7612, + 0x3b09, 0x1d84, 0x4ec2, 0x2761, 0x13b0, 0x49d8, 0x64ec, 0x7276, 0x393b, 0x5c9d, + 0x2e4e, 0x1727, 0x4b93, 0x65c9, 0x32e4, 0x5972, 0x2cb9, 0x165c, 0x4b2e, 0x2597, + 0x52cb, 0x6965, 0x34b2, 0x1a59, 0x0d2c, 0x4696, 0x234b, 0x51a5, 0x28d2, 0x1469, + 0x0a34, 0x451a, 0x228d, 0x1146, 0x08a3, 0x4451, 0x2228, 0x5114, 0x688a, 0x3445, + 0x1a22, 0x0d11, 0x0688, 0x4344, 0x61a2, 0x30d1, 0x1868, 0x4c34, 0x661a, 0x330d, + 0x1986, 0x0cc3, 0x4661, 0x2330, 0x5198, 0x68cc, 0x7466, 0x3a33, 0x5d19, 0x2e8c, + 0x5746, 0x2ba3, 0x55d1, 0x2ae8, 0x5574, 0x6aba, 0x355d, 0x1aae, 0x0d57, 0x46ab, + 0x6355, 0x31aa, 0x18d5, 0x0c6a, 0x0635, 0x031a, 0x018d, 0x00c6, 0x0063, 0x4031, + 0x2018, 0x500c, 0x6806, 0x3403, 0x5a01, 0x2d00, 0x5680, 0x6b40, 0x75a0, 0x7ad0, + 0x7d68, 0x7eb4, 0x7f5a, 0x3fad, 0x1fd6, 0x0feb, 0x47f5, 0x23fa, 0x11fd, 0x08fe, + 0x047f, 0x423f, 0x611f, 0x708f, 0x7847, 0x7c23, 0x7e11, 0x3f08, 0x5f84, 0x6fc2, + 0x37e1, 0x1bf0, 0x4df8, 0x66fc, 0x737e, 0x39bf, 0x5cdf, 0x6e6f, 0x7737, 0x7b9b, + 0x7dcd, 0x3ee6, 0x1f73, 0x4fb9, 0x27dc, 0x53ee, 0x29f7, 0x54fb, 0x6a7d, 0x353e, + 0x1a9f, 0x4d4f, 0x66a7, 0x7353, 0x79a9, 0x3cd4, 0x5e6a, 0x2f35, 0x179a, 0x0bcd, + 0x05e6, 0x02f3, 0x4179, 0x20bc, 0x505e, 0x282f, 0x5417, 0x6a0b, 0x7505, 0x3a82, + 0x1d41, 0x0ea0, 0x4750, 0x63a8, 0x71d4, 0x78ea, 0x3c75, 0x1e3a, 0x0f1d, 0x078e, + 0x03c7, 0x41e3, 0x60f1, 0x3078, 0x583c, 0x6c1e, 0x360f, 0x5b07, 0x6d83, 0x76c1, + 0x3b60, 0x5db0, 0x6ed8, 0x776c, 0x7bb6, 0x3ddb, 0x5eed, 0x2f76, 0x17bb, 0x4bdd, + 0x25ee, 0x12f7, 0x497b, 0x64bd, 0x325e, 0x192f, 0x4c97, 0x664b, 0x7325, 0x3992, + 0x1cc9, 0x0e64, 0x4732, 0x2399, 0x11cc, 0x48e6, 0x2473, 0x5239, 0x291c, 0x548e, + 0x2a47, 0x5523, 0x6a91, 0x3548, 0x5aa4, 0x6d52, 0x36a9, 0x1b54, 0x4daa, 0x26d5, + 0x136a, 0x09b5, 0x04da, 0x026d, 0x0136, 0x009b, 0x404d, 0x2026, 0x1013, 0x4809, + 0x2404, 0x5202, 0x2901, 0x1480, 0x4a40, 0x6520, 0x7290, 0x7948, 0x7ca4, 0x7e52, + 0x3f29, 0x1f94, 0x4fca, 0x27e5, 0x13f2, 0x09f9, 0x04fc, 0x427e, 0x213f, 0x509f, + 0x684f, 0x7427, 0x7a13, 0x7d09, 0x3e84, 0x5f42, 0x2fa1, 0x17d0, 0x4be8, 0x65f4, + 0x72fa, 0x397d, 0x1cbe, 0x0e5f, 0x472f, 0x6397, 0x71cb, 0x78e5, 0x3c72, 0x1e39, + 0x0f1c, 0x478e, 0x23c7, 0x51e3, 0x68f1, 0x3478, 0x5a3c, 0x6d1e, 0x368f, 0x5b47, + 0x6da3, 0x76d1, 0x3b68, 0x5db4, 0x6eda, 0x376d, 0x1bb6, 0x0ddb, 0x46ed, 0x2376, + 0x11bb, 0x48dd, 0x246e, 0x1237, 0x491b, 0x648d, 0x3246, 0x1923, 0x4c91, 0x2648, + 0x5324, 0x6992, 0x34c9, 0x1a64, 0x4d32, 0x2699, 0x134c, 0x49a6, 0x24d3, 0x5269, + 0x2934, 0x549a, 0x2a4d, 0x1526, 0x0a93, 0x4549, 0x22a4, 0x5152, 0x28a9, 0x1454, + 0x4a2a, 0x2515, 0x128a, 0x0945, 0x04a2, 0x0251, 0x0128, 0x4094, 0x604a, 0x3025, + 0x1812, 0x0c09, 0x0604, 0x4302, 0x2181, 0x10c0, 0x4860, 0x6430, 0x7218, 0x790c, + 0x7c86, 0x3e43, 0x5f21, 0x2f90, 0x57c8, 0x6be4, 0x75f2, 0x3af9, 0x1d7c, 0x4ebe, + 0x275f, 0x53af, 0x69d7, 0x74eb, 0x7a75, 0x3d3a, 0x1e9d, 0x0f4e, 0x07a7, 0x43d3, + 0x61e9, 0x30f4, 0x587a, 0x2c3d, 0x161e, 0x0b0f, 0x4587, 0x62c3, 0x7161, 0x38b0, + 0x5c58, 0x6e2c, 0x7716, 0x3b8b, 0x5dc5, 0x2ee2, 0x1771, 0x0bb8, 0x45dc, 0x62ee, + 0x3177, 0x58bb, 0x6c5d, 0x362e, 0x1b17, 0x4d8b, 0x66c5, 0x3362, 0x19b1, 0x0cd8, + 0x466c, 0x6336, 0x319b, 0x58cd, 0x2c66, 0x1633, 0x4b19, 0x258c, 0x52c6, 0x2963, + 0x54b1, 0x2a58, 0x552c, 0x6a96, 0x354b, 0x5aa5, 0x2d52, 0x16a9, 0x0b54, 0x45aa, + 0x22d5, 0x116a, 0x08b5, 0x045a, 0x022d, 0x0116, 0x008b, 0x4045, 0x2022, 0x1011, + 0x0808, 0x4404, 0x6202, 0x3101, 0x1880, 0x4c40, 0x6620, 0x7310, 0x7988, 0x7cc4, + 0x7e62, 0x3f31, 0x1f98, 0x4fcc, 0x67e6, 0x33f3, 0x59f9, 0x2cfc, 0x567e, 0x2b3f, + 0x559f, 0x6acf, 0x7567, 0x7ab3, 0x7d59, 0x3eac, 0x5f56, 0x2fab, 0x57d5, 0x2bea, + 0x15f5, 0x0afa, 0x057d, 0x02be, 0x015f, 0x40af, 0x6057, 0x702b, 0x7815, 0x3c0a, + 0x1e05, 0x0f02, 0x0781, 0x03c0, 0x41e0, 0x60f0, 0x7078, 0x783c, 0x7c1e, 0x3e0f, + 0x5f07, 0x6f83, 0x77c1, 0x3be0, 0x5df0, 0x6ef8, 0x777c, 0x7bbe, 0x3ddf, 0x5eef, + 0x6f77, 0x77bb, 0x7bdd, 0x3dee, 0x1ef7, 0x4f7b, 0x67bd, 0x33de, 0x19ef, 0x4cf7, + 0x667b, 0x733d, 0x399e, 0x1ccf, 0x4e67, 0x6733, 0x7399, 0x39cc, 0x5ce6, 0x2e73, + 0x5739, 0x2b9c, 0x55ce, 0x2ae7, 0x5573, 0x6ab9, 0x355c, 0x5aae, 0x2d57, 0x56ab, + 0x6b55, 0x35aa, 0x1ad5, 0x0d6a, 0x06b5, 0x035a, 0x01ad, 0x00d6, 0x006b, 0x4035, + 0x201a, 0x100d, 0x0806, 0x0403, 0x4201, 0x2100, 0x5080, 0x6840, 0x7420, 0x7a10, + 0x7d08, 0x7e84, 0x7f42, 0x3fa1, 0x1fd0, 0x4fe8, 0x67f4, 0x73fa, 0x39fd, 0x1cfe, + 0x0e7f, 0x473f, 0x639f, 0x71cf, 0x78e7, 0x7c73, 0x7e39, 0x3f1c, 0x5f8e, 0x2fc7, + 0x57e3, 0x6bf1, 0x35f8, 0x5afc, 0x6d7e, 0x36bf, 0x5b5f, 0x6daf, 0x76d7, 0x7b6b, + 0x7db5, 0x3eda, 0x1f6d, 0x0fb6, 0x07db, 0x43ed, 0x21f6, 0x10fb, 0x487d, 0x243e, + 0x121f, 0x490f, 0x6487, 0x7243, 0x7921, 0x3c90, 0x5e48, 0x6f24, 0x7792, 0x3bc9, + 0x1de4, 0x4ef2, 0x2779, 0x13bc, 0x49de, 0x24ef, 0x5277, 0x693b, 0x749d, 0x3a4e, + 0x1d27, 0x4e93, 0x6749, 0x33a4, 0x59d2, 0x2ce9, 0x1674, 0x4b3a, 0x259d, 0x12ce, + 0x0967, 0x44b3, 0x6259, 0x312c, 0x5896, 0x2c4b, 0x5625, 0x2b12, 0x1589, 0x0ac4, + 0x4562, 0x22b1, 0x1158, 0x48ac, 0x6456, 0x322b, 0x5915, 0x2c8a, 0x1645, 0x0b22, + 0x0591, 0x02c8, 0x4164, 0x60b2, 0x3059, 0x182c, 0x4c16, 0x260b, 0x5305, 0x2982, + 0x14c1, 0x0a60, 0x4530, 0x6298, 0x714c, 0x78a6, 0x3c53, 0x5e29, 0x2f14, 0x578a, + 0x2bc5, 0x15e2, 0x0af1, 0x0578, 0x42bc, 0x615e, 0x30af, 0x5857, 0x6c2b, 0x7615, + 0x3b0a, 0x1d85, 0x0ec2, 0x0761, 0x03b0, 0x41d8, 0x60ec, 0x7076, 0x383b, 0x5c1d, + 0x2e0e, 0x1707, 0x4b83, 0x65c1, 0x32e0, 0x5970, 0x6cb8, 0x765c, 0x7b2e, 0x3d97, + 0x5ecb, 0x6f65, 0x37b2, 0x1bd9, 0x0dec, 0x46f6, 0x237b, 0x51bd, 0x28de, 0x146f, + 0x4a37, 0x651b, 0x728d, 0x3946, 0x1ca3, 0x4e51, 0x2728, 0x5394, 0x69ca, 0x34e5, + 0x1a72, 0x0d39, 0x069c, 0x434e, 0x21a7, 0x50d3, 0x6869, 0x3434, 0x5a1a, 0x2d0d, + 0x1686, 0x0b43, 0x45a1, 0x22d0, 0x5168, 0x68b4, 0x745a, 0x3a2d, 0x1d16, 0x0e8b, + 0x4745, 0x23a2, 0x11d1, 0x08e8, 0x4474, 0x623a, 0x311d, 0x188e, 0x0c47, 0x4623, + 0x6311, 0x3188, 0x58c4, 0x6c62, 0x3631, 0x1b18, 0x4d8c, 0x66c6, 0x3363, 0x59b1, + 0x2cd8, 0x566c, 0x6b36, 0x359b, 0x5acd, 0x2d66, 0x16b3, 0x4b59, 0x25ac, 0x52d6, + 0x296b, 0x54b5, 0x2a5a, 0x152d, 0x0a96, 0x054b, 0x42a5, 0x2152, 0x10a9, 0x0854, + 0x442a, 0x2215, 0x110a, 0x0885, 0x0442, 0x0221, 0x0110, 0x4088, 0x6044, 0x7022, + 0x3811, 0x1c08, 0x4e04, 0x6702, 0x3381, 0x19c0, 0x4ce0, 0x6670, 0x7338, 0x799c, + 0x7cce, 0x3e67, 0x5f33, 0x6f99, 0x37cc, 0x5be6, 0x2df3, 0x56f9, 0x2b7c, 0x55be, + 0x2adf, 0x556f, 0x6ab7, 0x755b, 0x7aad, 0x3d56, 0x1eab, 0x4f55, 0x27aa, 0x13d5, + 0x09ea, 0x04f5, 0x027a, 0x013d, 0x009e, 0x004f, 0x4027, 0x6013, 0x7009, 0x3804, + 0x5c02, 0x2e01, 0x1700, 0x4b80, 0x65c0, 0x72e0, 0x7970, 0x7cb8, 0x7e5c, 0x7f2e, + 0x3f97, 0x5fcb, 0x6fe5, 0x37f2, 0x1bf9, 0x0dfc, 0x46fe, 0x237f, 0x51bf, 0x68df, + 0x746f, 0x7a37, 0x7d1b, 0x7e8d, 0x3f46, 0x1fa3, 0x4fd1, 0x27e8, 0x53f4, 0x69fa, + 0x34fd, 0x1a7e, 0x0d3f, 0x469f, 0x634f, 0x71a7, 0x78d3, 0x7c69, 0x3e34, 0x5f1a, + 0x2f8d, 0x17c6, 0x0be3, 0x45f1, 0x22f8, 0x517c, 0x68be, 0x345f, 0x5a2f, 0x6d17, + 0x768b, 0x7b45, 0x3da2, 0x1ed1, 0x0f68, 0x47b4, 0x63da, 0x31ed, 0x18f6, 0x0c7b, + 0x463d, 0x231e, 0x118f, 0x48c7, 0x6463, 0x7231, 0x3918, 0x5c8c, 0x6e46, 0x3723, + 0x5b91, 0x2dc8, 0x56e4, 0x6b72, 0x35b9, 0x1adc, 0x4d6e, 0x26b7, 0x535b, 0x69ad, + 0x34d6, 0x1a6b, 0x4d35, 0x269a, 0x134d, 0x09a6, 0x04d3, 0x4269, 0x2134, 0x509a, + 0x284d, 0x1426, 0x0a13, 0x4509, 0x2284, 0x5142, 0x28a1, 0x1450, 0x4a28, 0x6514, + 0x728a, 0x3945, 0x1ca2, 0x0e51, 0x0728, 0x4394, 0x61ca, 0x30e5, 0x1872, 0x0c39, + 0x061c, 0x430e, 0x2187, 0x50c3, 0x6861, 0x3430, 0x5a18, 0x6d0c, 0x7686, 0x3b43, + 0x5da1, 0x2ed0, 0x5768, 0x6bb4, 0x75da, 0x3aed, 0x1d76, 0x0ebb, 0x475d, 0x23ae, + 0x11d7, 0x48eb, 0x6475, 0x323a, 0x191d, 0x0c8e, 0x0647, 0x4323, 0x6191, 0x30c8, + 0x5864, 0x6c32, 0x3619, 0x1b0c, 0x4d86, 0x26c3, 0x5361, 0x29b0, 0x54d8, 0x6a6c, + 0x7536, 0x3a9b, 0x5d4d, 0x2ea6, 0x1753, 0x4ba9, 0x25d4, 0x52ea, 0x2975, 0x14ba, + 0x0a5d, 0x052e, 0x0297, 0x414b, 0x60a5, 0x3052, 0x1829, 0x0c14, 0x460a, 0x2305, + 0x1182, 0x08c1, 0x0460, 0x4230, 0x6118, 0x708c, 0x7846, 0x3c23, 0x5e11, 0x2f08, + 0x5784, 0x6bc2, 0x35e1, 0x1af0, 0x4d78, 0x66bc, 0x735e, 0x39af, 0x5cd7, 0x6e6b, + 0x7735, 0x3b9a, 0x1dcd, 0x0ee6, 0x0773, 0x43b9, 0x21dc, 0x50ee, 0x2877, 0x543b, + 0x6a1d, 0x350e, 0x1a87, 0x4d43, 0x66a1, 0x3350, 0x59a8, 0x6cd4, 0x766a, 0x3b35, + 0x1d9a, 0x0ecd, 0x0766, 0x03b3, 0x41d9, 0x20ec, 0x5076, 0x283b, 0x541d, 0x2a0e, + 0x1507, 0x4a83, 0x6541, 0x32a0, 0x5950, 0x6ca8, 0x7654, 0x7b2a, 0x3d95, 0x1eca, + 0x0f65, 0x07b2, 0x03d9, 0x01ec, 0x40f6, 0x207b, 0x503d, 0x281e, 0x140f, 0x4a07, + 0x6503, 0x7281, 0x3940, 0x5ca0, 0x6e50, 0x7728, 0x7b94, 0x7dca, 0x3ee5, 0x1f72, + 0x0fb9, 0x07dc, 0x43ee, 0x21f7, 0x50fb, 0x687d, 0x343e, 0x1a1f, 0x4d0f, 0x6687, + 0x7343, 0x79a1, 0x3cd0, 0x5e68, 0x6f34, 0x779a, 0x3bcd, 0x1de6, 0x0ef3, 0x4779, + 0x23bc, 0x51de, 0x28ef, 0x5477, 0x6a3b, 0x751d, 0x3a8e, 0x1d47, 0x4ea3, 0x6751, + 0x33a8, 0x59d4, 0x6cea, 0x3675, 0x1b3a, 0x0d9d, 0x06ce, 0x0367, 0x41b3, 0x60d9, + 0x306c, 0x5836, 0x2c1b, 0x560d, 0x2b06, 0x1583, 0x4ac1, 0x2560, 0x52b0, 0x6958, + 0x74ac, 0x7a56, 0x3d2b, 0x5e95, 0x2f4a, 0x17a5, 0x0bd2, 0x05e9, 0x02f4, 0x417a, + 0x20bd, 0x105e, 0x082f, 0x4417, 0x620b, 0x7105, 0x3882, 0x1c41, 0x0e20, 0x4710, + 0x6388, 0x71c4, 0x78e2, 0x3c71, 0x1e38, 0x4f1c, 0x678e, 0x33c7, 0x59e3, 0x6cf1, + 0x3678, 0x5b3c, 0x6d9e, 0x36cf, 0x5b67, 0x6db3, 0x76d9, 0x3b6c, 0x5db6, 0x2edb, + 0x576d, 0x2bb6, 0x15db, 0x4aed, 0x2576, 0x12bb, 0x495d, 0x24ae, 0x1257, 0x492b, + 0x6495, 0x324a, 0x1925, 0x0c92, 0x0649, 0x0324, 0x4192, 0x20c9, 0x1064, 0x4832, + 0x2419, 0x120c, 0x4906, 0x2483, 0x5241, 0x2920, 0x5490, 0x6a48, 0x7524, 0x7a92, + 0x3d49, 0x1ea4, 0x4f52, 0x27a9, 0x13d4, 0x49ea, 0x24f5, 0x127a, 0x093d, 0x049e, + 0x024f, 0x4127, 0x6093, 0x7049, 0x3824, 0x5c12, 0x2e09, 0x1704, 0x4b82, 0x25c1, + 0x12e0, 0x4970, 0x64b8, 0x725c, 0x792e, 0x3c97, 0x5e4b, 0x6f25, 0x3792, 0x1bc9, + 0x0de4, 0x46f2, 0x2379, 0x11bc, 0x48de, 0x246f, 0x5237, 0x691b, 0x748d, 0x3a46, + 0x1d23, 0x4e91, 0x2748, 0x53a4, 0x69d2, 0x34e9, 0x1a74, 0x4d3a, 0x269d, 0x134e, + 0x09a7, 0x44d3, 0x6269, 0x3134, 0x589a, 0x2c4d, 0x1626, 0x0b13, 0x4589, 0x22c4, + 0x5162, 0x28b1, 0x1458, 0x4a2c, 0x6516, 0x328b, 0x5945, 0x2ca2, 0x1651, 0x0b28, + 0x4594, 0x62ca, 0x3165, 0x18b2, 0x0c59, 0x062c, 0x4316, 0x218b, 0x50c5, 0x2862, + 0x1431, 0x0a18, 0x450c, 0x6286, 0x3143, 0x58a1, 0x2c50, 0x5628, 0x6b14, 0x758a, + 0x3ac5, 0x1d62, 0x0eb1, 0x0758, 0x43ac, 0x61d6, 0x30eb, 0x5875, 0x2c3a, 0x161d, + 0x0b0e, 0x0587, 0x42c3, 0x6161, 0x30b0, 0x5858, 0x6c2c, 0x7616, 0x3b0b, 0x5d85, + 0x2ec2, 0x1761, 0x0bb0, 0x45d8, 0x62ec, 0x7176, 0x38bb, 0x5c5d, 0x2e2e, 0x1717, + 0x4b8b, 0x65c5, 0x32e2, 0x1971, 0x0cb8, 0x465c, 0x632e, 0x3197, 0x58cb, 0x6c65, + 0x3632, 0x1b19, 0x0d8c, 0x46c6, 0x2363, 0x51b1, 0x28d8, 0x546c, 0x6a36, 0x351b, + 0x5a8d, 0x2d46, 0x16a3, 0x4b51, 0x25a8, 0x52d4, 0x696a, 0x34b5, 0x1a5a, 0x0d2d, + 0x0696, 0x034b, 0x41a5, 0x20d2, 0x1069, 0x0834, 0x441a, 0x220d, 0x1106, 0x0883, + 0x4441, 0x2220, 0x5110, 0x6888, 0x7444, 0x7a22, 0x3d11, 0x1e88, 0x4f44, 0x67a2, + 0x33d1, 0x19e8, 0x4cf4, 0x667a, 0x333d, 0x199e, 0x0ccf, 0x4667, 0x6333, 0x7199, + 0x38cc, 0x5c66, 0x2e33, 0x5719, 0x2b8c, 0x55c6, 0x2ae3, 0x5571, 0x2ab8, 0x555c, + 0x6aae, 0x3557, 0x5aab, 0x6d55, 0x36aa, 0x1b55, 0x0daa, 0x06d5, 0x036a, 0x01b5, + 0x00da, 0x006d, 0x0036, 0x001b, 0x400d, 0x2006, 0x1003, 0x4801, 0x2400, 0x5200, + 0x6900, 0x7480, 0x7a40, 0x7d20, 0x7e90, 0x7f48, 0x7fa4, 0x7fd2, 0x3fe9, 0x1ff4, + 0x4ffa, 0x27fd, 0x13fe, 0x09ff, 0x44ff, 0x627f, 0x713f, 0x789f, 0x7c4f, 0x7e27, + 0x7f13, 0x7f89, 0x3fc4, 0x5fe2, 0x2ff1, 0x17f8, 0x4bfc, 0x65fe, 0x32ff, 0x597f, + 0x6cbf, 0x765f, 0x7b2f, 0x7d97, 0x7ecb, 0x7f65, 0x3fb2, 0x1fd9, 0x0fec, 0x47f6, + 0x23fb, 0x51fd, 0x28fe, 0x147f, 0x4a3f, 0x651f, 0x728f, 0x7947, 0x7ca3, 0x7e51, + 0x3f28, 0x5f94, 0x6fca, 0x37e5, 0x1bf2, 0x0df9, 0x06fc, 0x437e, 0x21bf, 0x50df, + 0x686f, 0x7437, 0x7a1b, 0x7d0d, 0x3e86, 0x1f43, 0x4fa1, 0x27d0, 0x53e8, 0x69f4, + 0x74fa, 0x3a7d, 0x1d3e, 0x0e9f, 0x474f, 0x63a7, 0x71d3, 0x78e9, 0x3c74, 0x5e3a, + 0x2f1d, 0x178e, 0x0bc7, 0x45e3, 0x62f1, 0x3178, 0x58bc, 0x6c5e, 0x362f, 0x5b17, + 0x6d8b, 0x76c5, 0x3b62, 0x1db1, 0x0ed8, 0x476c, 0x63b6, 0x31db, 0x58ed, 0x2c76, + 0x163b, 0x4b1d, 0x258e, 0x12c7, 0x4963, 0x64b1, 0x3258, 0x592c, 0x6c96, 0x364b, + 0x5b25, 0x2d92, 0x16c9, 0x0b64, 0x45b2, 0x22d9, 0x116c, 0x48b6, 0x245b, 0x522d, + 0x2916, 0x148b, 0x4a45, 0x2522, 0x1291, 0x0948, 0x44a4, 0x6252, 0x3129, 0x1894, + 0x4c4a, 0x2625, 0x1312, 0x0989, 0x04c4, 0x4262, 0x2131, 0x1098, 0x484c, 0x6426, + 0x3213, 0x5909, 0x2c84, 0x5642, 0x2b21, 0x1590, 0x4ac8, 0x6564, 0x72b2, 0x3959, + 0x1cac, 0x4e56, 0x272b, 0x5395, 0x29ca, 0x14e5, 0x0a72, 0x0539, 0x029c, 0x414e, + 0x20a7, 0x5053, 0x6829, 0x3414, 0x5a0a, 0x2d05, 0x1682, 0x0b41, 0x05a0, 0x42d0, + 0x6168, 0x70b4, 0x785a, 0x3c2d, 0x1e16, 0x0f0b, 0x4785, 0x23c2, 0x11e1, 0x08f0, + 0x4478, 0x623c, 0x711e, 0x388f, 0x5c47, 0x6e23, 0x7711, 0x3b88, 0x5dc4, 0x6ee2, + 0x3771, 0x1bb8, 0x4ddc, 0x66ee, 0x3377, 0x59bb, 0x6cdd, 0x366e, 0x1b37, 0x4d9b, + 0x66cd, 0x3366, 0x19b3, 0x4cd9, 0x266c, 0x5336, 0x299b, 0x54cd, 0x2a66, 0x1533, + 0x4a99, 0x254c, 0x52a6, 0x2953, 0x54a9, 0x2a54, 0x552a, 0x2a95, 0x154a, 0x0aa5, + 0x0552, 0x02a9, 0x0154, 0x40aa, 0x2055, 0x102a, 0x0815, 0x040a, 0x0205, 0x0102, + 0x0081, 0x0040, 0x4020, 0x6010, 0x7008, 0x7804, 0x7c02, 0x3e01, 0x1f00, 0x4f80, + 0x67c0, 0x73e0, 0x79f0, 0x7cf8, 0x7e7c, 0x7f3e, 0x3f9f, 0x5fcf, 0x6fe7, 0x77f3, + 0x7bf9, 0x3dfc, 0x5efe, 0x2f7f, 0x57bf, 0x6bdf, 0x75ef, 0x7af7, 0x7d7b, 0x7ebd, + 0x3f5e, 0x1faf, 0x4fd7, 0x67eb, 0x73f5, 0x39fa, 0x1cfd, 0x0e7e, 0x073f, 0x439f, + 0x61cf, 0x70e7, 0x7873, 0x7c39, 0x3e1c, 0x5f0e, 0x2f87, 0x57c3, 0x6be1, 0x35f0, + 0x5af8, 0x6d7c, 0x76be, 0x3b5f, 0x5daf, 0x6ed7, 0x776b, 0x7bb5, 0x3dda, 0x1eed, + 0x0f76, 0x07bb, 0x43dd, 0x21ee, 0x10f7, 0x487b, 0x643d, 0x321e, 0x190f, 0x4c87, + 0x6643, 0x7321, 0x3990, 0x5cc8, 0x6e64, 0x7732, 0x3b99, 0x1dcc, 0x4ee6, 0x2773, + 0x53b9, 0x29dc, 0x54ee, 0x2a77, 0x553b, 0x6a9d, 0x354e, 0x1aa7, 0x4d53, 0x66a9, + 0x3354, 0x59aa, 0x2cd5, 0x166a, 0x0b35, 0x059a, 0x02cd, 0x0166, 0x00b3, 0x4059, + 0x202c, 0x5016, 0x280b, 0x5405, 0x2a02, 0x1501, 0x0a80, 0x4540, 0x62a0, 0x7150, + 0x78a8, 0x7c54, 0x7e2a, 0x3f15, 0x1f8a, 0x0fc5, 0x07e2, 0x03f1, 0x01f8, 0x40fc, + 0x607e, 0x303f, 0x581f, 0x6c0f, 0x7607, 0x7b03, 0x7d81, 0x3ec0, 0x5f60, 0x6fb0, + 0x77d8, 0x7bec, 0x7df6, 0x3efb, 0x5f7d, 0x2fbe, 0x17df, 0x4bef, 0x65f7, 0x72fb, + 0x797d, 0x3cbe, 0x1e5f, 0x4f2f, 0x6797, 0x73cb, 0x79e5, 0x3cf2, 0x1e79, 0x0f3c, + 0x479e, 0x23cf, 0x51e7, 0x68f3, 0x7479, 0x3a3c, 0x5d1e, 0x2e8f, 0x5747, 0x6ba3, + 0x75d1, 0x3ae8, 0x5d74, 0x6eba, 0x375d, 0x1bae, 0x0dd7, 0x46eb, 0x6375, 0x31ba, + 0x18dd, 0x0c6e, 0x0637, 0x431b, 0x618d, 0x30c6, 0x1863, 0x4c31, 0x2618, 0x530c, + 0x6986, 0x34c3, 0x5a61, 0x2d30, 0x5698, 0x6b4c, 0x75a6, 0x3ad3, 0x5d69, 0x2eb4, + 0x575a, 0x2bad, 0x15d6, 0x0aeb, 0x4575, 0x22ba, 0x115d, 0x08ae, 0x0457, 0x422b, + 0x6115, 0x308a, 0x1845, 0x0c22, 0x0611, 0x0308, 0x4184, 0x60c2, 0x3061, 0x1830, + 0x4c18, 0x660c, 0x7306, 0x3983, 0x5cc1, 0x2e60, 0x5730, 0x6b98, 0x75cc, 0x7ae6, + 0x3d73, 0x5eb9, 0x2f5c, 0x57ae, 0x2bd7, 0x55eb, 0x6af5, 0x357a, 0x1abd, 0x0d5e, + 0x06af, 0x4357, 0x61ab, 0x70d5, 0x386a, 0x1c35, 0x0e1a, 0x070d, 0x0386, 0x01c3, + 0x40e1, 0x2070, 0x5038, 0x681c, 0x740e, 0x3a07, 0x5d03, 0x6e81, 0x3740, 0x5ba0, + 0x6dd0, 0x76e8, 0x7b74, 0x7dba, 0x3edd, 0x1f6e, 0x0fb7, 0x47db, 0x63ed, 0x31f6, + 0x18fb, 0x4c7d, 0x263e, 0x131f, 0x498f, 0x64c7, 0x7263, 0x7931, 0x3c98, 0x5e4c, + 0x6f26, 0x3793, 0x5bc9, 0x2de4, 0x56f2, 0x2b79, 0x15bc, 0x4ade, 0x256f, 0x52b7, + 0x695b, 0x74ad, 0x3a56, 0x1d2b, 0x4e95, 0x274a, 0x13a5, 0x09d2, 0x04e9, 0x0274, + 0x413a, 0x209d, 0x104e, 0x0827, 0x4413, 0x6209, 0x3104, 0x5882, 0x2c41, 0x1620, + 0x4b10, 0x6588, 0x72c4, 0x7962, 0x3cb1, 0x1e58, 0x4f2c, 0x6796, 0x33cb, 0x59e5, + 0x2cf2, 0x1679, 0x0b3c, 0x459e, 0x22cf, 0x5167, 0x68b3, 0x7459, 0x3a2c, 0x5d16, + 0x2e8b, 0x5745, 0x2ba2, 0x15d1, 0x0ae8, 0x4574, 0x62ba, 0x315d, 0x18ae, 0x0c57, + 0x462b, 0x6315, 0x318a, 0x18c5, 0x0c62, 0x0631, 0x0318, 0x418c, 0x60c6, 0x3063, + 0x5831, 0x2c18, 0x560c, 0x6b06, 0x3583, 0x5ac1, 0x2d60, 0x56b0, 0x6b58, 0x75ac, + 0x7ad6, 0x3d6b, 0x5eb5, 0x2f5a, 0x17ad, 0x0bd6, 0x05eb, 0x42f5, 0x217a, 0x10bd, + 0x085e, 0x042f, 0x4217, 0x610b, 0x7085, 0x3842, 0x1c21, 0x0e10, 0x4708, 0x6384, + 0x71c2, 0x38e1, 0x1c70, 0x4e38, 0x671c, 0x738e, 0x39c7, 0x5ce3, 0x6e71, 0x3738, + 0x5b9c, 0x6dce, 0x36e7, 0x5b73, 0x6db9, 0x36dc, 0x5b6e, 0x2db7, 0x56db, 0x6b6d, + 0x35b6, 0x1adb, 0x4d6d, 0x26b6, 0x135b, 0x49ad, 0x24d6, 0x126b, 0x4935, 0x249a, + 0x124d, 0x0926, 0x0493, 0x4249, 0x2124, 0x5092, 0x2849, 0x1424, 0x4a12, 0x2509, + 0x1284, 0x4942, 0x24a1, 0x1250, 0x4928, 0x6494, 0x724a, 0x3925, 0x1c92, 0x0e49, + 0x0724, 0x4392, 0x21c9, 0x10e4, 0x4872, 0x2439, 0x121c, 0x490e, 0x2487, 0x5243, + 0x6921, 0x3490, 0x5a48, 0x6d24, 0x7692, 0x3b49, 0x1da4, 0x4ed2, 0x2769, 0x13b4, + 0x49da, 0x24ed, 0x1276, 0x093b, 0x449d, 0x224e, 0x1127, 0x4893, 0x6449, 0x3224, + 0x5912, 0x2c89, 0x1644, 0x4b22, 0x2591, 0x12c8, 0x4964, 0x64b2, 0x3259, 0x192c, + 0x4c96, 0x264b, 0x5325, 0x2992, 0x14c9, 0x0a64, 0x4532, 0x2299, 0x114c, 0x48a6, + 0x2453, 0x5229, 0x2914, 0x548a, 0x2a45, 0x1522, 0x0a91, 0x0548, 0x42a4, 0x6152, + 0x30a9, 0x1854, 0x4c2a, 0x2615, 0x130a, 0x0985, 0x04c2, 0x0261, 0x0130, 0x4098, + 0x604c, 0x7026, 0x3813, 0x5c09, 0x2e04, 0x5702, 0x2b81, 0x15c0, 0x4ae0, 0x6570, + 0x72b8, 0x795c, 0x7cae, 0x3e57, 0x5f2b, 0x6f95, 0x37ca, 0x1be5, 0x0df2, 0x06f9, + 0x037c, 0x41be, 0x20df, 0x506f, 0x6837, 0x741b, 0x7a0d, 0x3d06, 0x1e83, 0x4f41, + 0x27a0, 0x53d0, 0x69e8, 0x74f4, 0x7a7a, 0x3d3d, 0x1e9e, 0x0f4f, 0x47a7, 0x63d3, + 0x71e9, 0x38f4, 0x5c7a, 0x2e3d, 0x171e, 0x0b8f, 0x45c7, 0x62e3, 0x7171, 0x38b8, + 0x5c5c, 0x6e2e, 0x3717, 0x5b8b, 0x6dc5, 0x36e2, 0x1b71, 0x0db8, 0x46dc, 0x636e, + 0x31b7, 0x58db, 0x6c6d, 0x3636, 0x1b1b, 0x4d8d, 0x26c6, 0x1363, 0x49b1, 0x24d8, + 0x526c, 0x6936, 0x349b, 0x5a4d, 0x2d26, 0x1693, 0x4b49, 0x25a4, 0x52d2, 0x2969, + 0x14b4, 0x4a5a, 0x252d, 0x1296, 0x094b, 0x44a5, 0x2252, 0x1129, 0x0894, 0x444a, + 0x2225, 0x1112, 0x0889, 0x0444, 0x4222, 0x2111, 0x1088, 0x4844, 0x6422, 0x3211, + 0x1908, 0x4c84, 0x6642, 0x3321, 0x1990, 0x4cc8, 0x6664, 0x7332, 0x3999, 0x1ccc, + 0x4e66, 0x2733, 0x5399, 0x29cc, 0x54e6, 0x2a73, 0x5539, 0x2a9c, 0x554e, 0x2aa7, + 0x5553, 0x6aa9, 0x3554, 0x5aaa, 0x2d55, 0x16aa, 0x0b55, 0x05aa, 0x02d5, 0x016a, + 0x00b5, 0x005a, 0x002d, 0x0016, 0x000b, 0x4005, 0x2002, 0x1001, 0x0800, 0x4400, + 0x6200, 0x7100, 0x7880, 0x7c40, 0x7e20, 0x7f10, 0x7f88, 0x7fc4, 0x7fe2, 0x3ff1, + 0x1ff8, 0x4ffc, 0x67fe, 0x33ff, 0x59ff, 0x6cff, 0x767f, 0x7b3f, 0x7d9f, 0x7ecf, + 0x7f67, 0x7fb3, 0x7fd9, 0x3fec, 0x5ff6, 0x2ffb, 0x57fd, 0x2bfe, 0x15ff, 0x4aff, + 0x657f, 0x72bf, 0x795f, 0x7caf, 0x7e57, 0x7f2b, 0x7f95, 0x3fca, 0x1fe5, 0x0ff2, + 0x07f9, 0x03fc, 0x41fe, 0x20ff, 0x507f, 0x683f, 0x741f, 0x7a0f, 0x7d07, 0x7e83, + 0x7f41, 0x3fa0, 0x5fd0, 0x6fe8, 0x77f4, 0x7bfa, 0x3dfd, 0x1efe, 0x0f7f, 0x47bf, + 0x63df, 0x71ef, 0x78f7, 0x7c7b, 0x7e3d, 0x3f1e, 0x1f8f, 0x4fc7, 0x67e3, 0x73f1, + 0x39f8, 0x5cfc, 0x6e7e, 0x373f, 0x5b9f, 0x6dcf, 0x76e7, 0x7b73, 0x7db9, 0x3edc, + 0x5f6e, 0x2fb7, 0x57db, 0x6bed, 0x35f6, 0x1afb, 0x4d7d, 0x26be, 0x135f, 0x49af, + 0x64d7, 0x726b, 0x7935, 0x3c9a, 0x1e4d, 0x0f26, 0x0793, 0x43c9, 0x21e4, 0x50f2, + 0x2879, 0x143c, 0x4a1e, 0x250f, 0x5287, 0x6943, 0x74a1, 0x3a50, 0x5d28, 0x6e94, + 0x774a, 0x3ba5, 0x1dd2, 0x0ee9, 0x0774, 0x43ba, 0x21dd, 0x10ee, 0x0877, 0x443b, + 0x621d, 0x310e, 0x1887, 0x4c43, 0x6621, 0x3310, 0x5988, 0x6cc4, 0x7662, 0x3b31, + 0x1d98, 0x4ecc, 0x6766, 0x33b3, 0x59d9, 0x2cec, 0x5676, 0x2b3b, 0x559d, 0x2ace, + 0x1567, 0x4ab3, 0x6559, 0x32ac, 0x5956, 0x2cab, 0x5655, 0x2b2a, 0x1595, 0x0aca, + 0x0565, 0x02b2, 0x0159, 0x00ac, 0x4056, 0x202b, 0x5015, 0x280a, 0x1405, 0x0a02, + 0x0501, 0x0280, 0x4140, 0x60a0, 0x7050, 0x7828, 0x7c14, 0x7e0a, 0x3f05, 0x1f82, + 0x0fc1, 0x07e0, 0x43f0, 0x61f8, 0x70fc, 0x787e, 0x3c3f, 0x5e1f, 0x6f0f, 0x7787, + 0x7bc3, 0x7de1, 0x3ef0, 0x5f78, 0x6fbc, 0x77de, 0x3bef, 0x5df7, 0x6efb, 0x777d, + 0x3bbe, 0x1ddf, 0x4eef, 0x6777, 0x73bb, 0x79dd, 0x3cee, 0x1e77, 0x4f3b, 0x679d, + 0x33ce, 0x19e7, 0x4cf3, 0x6679, 0x333c, 0x599e, 0x2ccf, 0x5667, 0x6b33, 0x7599, + 0x3acc, 0x5d66, 0x2eb3, 0x5759, 0x2bac, 0x55d6, 0x2aeb, 0x5575, 0x2aba, 0x155d, + 0x0aae, 0x0557, 0x42ab, 0x6155, 0x30aa, 0x1855, 0x0c2a, 0x0615, 0x030a, 0x0185, + 0x00c2, 0x0061, 0x0030, 0x4018, 0x600c, 0x7006, 0x3803, 0x5c01, 0x2e00, 0x5700, + 0x6b80, 0x75c0, 0x7ae0, 0x7d70, 0x7eb8, 0x7f5c, 0x7fae, 0x3fd7, 0x5feb, 0x6ff5, + 0x37fa, 0x1bfd, 0x0dfe, 0x06ff, 0x437f, 0x61bf, 0x70df, 0x786f, 0x7c37, 0x7e1b, + 0x7f0d, 0x3f86, 0x1fc3, 0x4fe1, 0x27f0, 0x53f8, 0x69fc, 0x74fe, 0x3a7f, 0x5d3f, + 0x6e9f, 0x774f, 0x7ba7, 0x7dd3, 0x7ee9, 0x3f74, 0x5fba, 0x2fdd, 0x17ee, 0x0bf7, + 0x45fb, 0x62fd, 0x317e, 0x18bf, 0x4c5f, 0x662f, 0x7317, 0x798b, 0x7cc5, 0x3e62, + 0x1f31, 0x0f98, 0x47cc, 0x63e6, 0x31f3, 0x58f9, 0x2c7c, 0x563e, 0x2b1f, 0x558f, + 0x6ac7, 0x7563, 0x7ab1, 0x3d58, 0x5eac, 0x6f56, 0x37ab, 0x5bd5, 0x2dea, 0x16f5, + 0x0b7a, 0x05bd, 0x02de, 0x016f, 0x40b7, 0x605b, 0x702d, 0x3816, 0x1c0b, 0x4e05, + 0x2702, 0x1381, 0x09c0, 0x44e0, 0x6270, 0x7138, 0x789c, 0x7c4e, 0x3e27, 0x5f13, + 0x6f89, 0x37c4, 0x5be2, 0x2df1, 0x16f8, 0x4b7c, 0x65be, 0x32df, 0x596f, 0x6cb7, + 0x765b, 0x7b2d, 0x3d96, 0x1ecb, 0x4f65, 0x27b2, 0x13d9, 0x09ec, 0x44f6, 0x227b, + 0x513d, 0x289e, 0x144f, 0x4a27, 0x6513, 0x7289, 0x3944, 0x5ca2, 0x2e51, 0x1728, + 0x4b94, 0x65ca, 0x32e5, 0x1972, 0x0cb9, 0x065c, 0x432e, 0x2197, 0x50cb, 0x6865, + 0x3432, 0x1a19, 0x0d0c, 0x4686, 0x2343, 0x51a1, 0x28d0, 0x5468, 0x6a34, 0x751a, + 0x3a8d, 0x1d46, 0x0ea3, 0x4751, 0x23a8, 0x51d4, 0x68ea, 0x3475, 0x1a3a, 0x0d1d, + 0x068e, 0x0347, 0x41a3, 0x60d1, 0x3068, 0x5834, 0x6c1a, 0x360d, 0x1b06, 0x0d83, + 0x46c1, 0x2360, 0x51b0, 0x68d8, 0x746c, 0x7a36, 0x3d1b, 0x5e8d, 0x2f46, 0x17a3, + 0x4bd1, 0x25e8, 0x52f4, 0x697a, 0x34bd, 0x1a5e, 0x0d2f, 0x4697, 0x634b, 0x71a5, + 0x38d2, 0x1c69, 0x0e34, 0x471a, 0x238d, 0x11c6, 0x08e3, 0x4471, 0x2238, 0x511c, + 0x688e, 0x3447, 0x5a23, 0x6d11, 0x3688, 0x5b44, 0x6da2, 0x36d1, 0x1b68, 0x4db4, + 0x66da, 0x336d, 0x19b6, 0x0cdb, 0x466d, 0x2336, 0x119b, 0x48cd, 0x2466, 0x1233, + 0x4919, 0x248c, 0x5246, 0x2923, 0x5491, 0x2a48, 0x5524, 0x6a92, 0x3549, 0x1aa4, + 0x4d52, 0x26a9, 0x1354, 0x49aa, 0x24d5, 0x126a, 0x0935, 0x049a, 0x024d, 0x0126, + 0x0093, 0x4049, 0x2024, 0x5012, 0x2809, 0x1404, 0x4a02, 0x2501, 0x1280, 0x4940, + 0x64a0, 0x7250, 0x7928, 0x7c94, 0x7e4a, 0x3f25, 0x1f92, 0x0fc9, 0x07e4, 0x43f2, + 0x21f9, 0x10fc, 0x487e, 0x243f, 0x521f, 0x690f, 0x7487, 0x7a43, 0x7d21, 0x3e90, + 0x5f48, 0x6fa4, 0x77d2, 0x3be9, 0x1df4, 0x4efa, 0x277d, 0x13be, 0x09df, 0x44ef, + 0x6277, 0x713b, 0x789d, 0x3c4e, 0x1e27, 0x4f13, 0x6789, 0x33c4, 0x59e2, 0x2cf1, + 0x1678, 0x4b3c, 0x659e, 0x32cf, 0x5967, 0x6cb3, 0x7659, 0x3b2c, 0x5d96, 0x2ecb, + 0x5765, 0x2bb2, 0x15d9, 0x0aec, 0x4576, 0x22bb, 0x515d, 0x28ae, 0x1457, 0x4a2b, + 0x6515, 0x328a, 0x1945, 0x0ca2, 0x0651, 0x0328, 0x4194, 0x60ca, 0x3065, 0x1832, + 0x0c19, 0x060c, 0x4306, 0x2183, 0x50c1, 0x2860, 0x5430, 0x6a18, 0x750c, 0x7a86, + 0x3d43, 0x5ea1, 0x2f50, 0x57a8, 0x6bd4, 0x75ea, 0x3af5, 0x1d7a, 0x0ebd, 0x075e, + 0x03af, 0x41d7, 0x60eb, 0x7075, 0x383a, 0x1c1d, 0x0e0e, 0x0707, 0x4383, 0x61c1, + 0x30e0, 0x5870, 0x6c38, 0x761c, 0x7b0e, 0x3d87, 0x5ec3, 0x6f61, 0x37b0, 0x5bd8, + 0x6dec, 0x76f6, 0x3b7b, 0x5dbd, 0x2ede, 0x176f, 0x4bb7, 0x65db, 0x72ed, 0x3976, + 0x1cbb, 0x4e5d, 0x272e, 0x1397, 0x49cb, 0x64e5, 0x3272, 0x1939, 0x0c9c, 0x464e, + 0x2327, 0x5193, 0x68c9, 0x3464, 0x5a32, 0x2d19, 0x168c, 0x4b46, 0x25a3, 0x52d1, + 0x2968, 0x54b4, 0x6a5a, 0x352d, 0x1a96, 0x0d4b, 0x46a5, 0x2352, 0x11a9, 0x08d4, + 0x446a, 0x2235, 0x111a, 0x088d, 0x0446, 0x0223, 0x4111, 0x2088, 0x5044, 0x6822, + 0x3411, 0x1a08, 0x4d04, 0x6682, 0x3341, 0x19a0, 0x4cd0, 0x6668, 0x7334, 0x799a, + 0x3ccd, 0x1e66, 0x0f33, 0x4799, 0x23cc, 0x51e6, 0x28f3, 0x5479, 0x2a3c, 0x551e, + 0x2a8f, 0x5547, 0x6aa3, 0x7551, 0x3aa8, 0x5d54, 0x6eaa, 0x3755, 0x1baa, 0x0dd5, + 0x06ea, 0x0375, 0x01ba, 0x00dd, 0x006e, 0x0037, 0x401b, 0x600d, 0x3006, 0x1803, + 0x4c01, 0x2600, 0x5300, 0x6980, 0x74c0, 0x7a60, 0x7d30, 0x7e98, 0x7f4c, 0x7fa6, + 0x3fd3, 0x5fe9, 0x2ff4, 0x57fa, 0x2bfd, 0x15fe, 0x0aff, 0x457f, 0x62bf, 0x715f, + 0x78af, 0x7c57, 0x7e2b, 0x7f15, 0x3f8a, 0x1fc5, 0x0fe2, 0x07f1, 0x03f8, 0x41fc, + 0x60fe, 0x307f, 0x583f, 0x6c1f, 0x760f, 0x7b07, 0x7d83, 0x7ec1, 0x3f60, 0x5fb0, + 0x6fd8, 0x77ec, 0x7bf6, 0x3dfb, 0x5efd, 0x2f7e, 0x17bf, 0x4bdf, 0x65ef, 0x72f7, + 0x797b, 0x7cbd, 0x3e5e, 0x1f2f, 0x4f97, 0x67cb, 0x73e5, 0x39f2, 0x1cf9, 0x0e7c, + 0x473e, 0x239f, 0x51cf, 0x68e7, 0x7473, 0x7a39, 0x3d1c, 0x5e8e, 0x2f47, 0x57a3, + 0x6bd1, 0x35e8, 0x5af4, 0x6d7a, 0x36bd, 0x1b5e, 0x0daf, 0x46d7, 0x636b, 0x71b5, + 0x38da, 0x1c6d, 0x0e36, 0x071b, 0x438d, 0x21c6, 0x10e3, 0x4871, 0x2438, 0x521c, + 0x690e, 0x3487, 0x5a43, 0x6d21, 0x3690, 0x5b48, 0x6da4, 0x76d2, 0x3b69, 0x1db4, + 0x4eda, 0x276d, 0x13b6, 0x09db, 0x44ed, 0x2276, 0x113b, 0x489d, 0x244e, 0x1227, + 0x4913, 0x6489, 0x3244, 0x5922, 0x2c91, 0x1648, 0x4b24, 0x6592, 0x32c9, 0x1964, + 0x4cb2, 0x2659, 0x132c, 0x4996, 0x24cb, 0x5265, 0x2932, 0x1499, 0x0a4c, 0x4526, + 0x2293, 0x5149, 0x28a4, 0x5452, 0x2a29, 0x1514, 0x4a8a, 0x2545, 0x12a2, 0x0951, + 0x04a8, 0x4254, 0x612a, 0x3095, 0x184a, 0x0c25, 0x0612, 0x0309, 0x0184, 0x40c2, + 0x2061, 0x1030, 0x4818, 0x640c, 0x7206, 0x3903, 0x5c81, 0x2e40, 0x5720, 0x6b90, + 0x75c8, 0x7ae4, 0x7d72, 0x3eb9, 0x1f5c, 0x4fae, 0x27d7, 0x53eb, 0x69f5, 0x34fa, + 0x1a7d, 0x0d3e, 0x069f, 0x434f, 0x61a7, 0x70d3, 0x7869, 0x3c34, 0x5e1a, 0x2f0d, + 0x1786, 0x0bc3, 0x45e1, 0x22f0, 0x5178, 0x68bc, 0x745e, 0x3a2f, 0x5d17, 0x6e8b, + 0x7745, 0x3ba2, 0x1dd1, 0x0ee8, 0x4774, 0x63ba, 0x31dd, 0x18ee, 0x0c77, 0x463b, + 0x631d, 0x318e, 0x18c7, 0x4c63, 0x6631, 0x3318, 0x598c, 0x6cc6, 0x3663, 0x5b31, + 0x2d98, 0x56cc, 0x6b66, 0x35b3, 0x5ad9, 0x2d6c, 0x56b6, 0x2b5b, 0x55ad, 0x2ad6, + 0x156b, 0x4ab5, 0x255a, 0x12ad, 0x0956, 0x04ab, 0x4255, 0x212a, 0x1095, 0x084a, + 0x0425, 0x0212, 0x0109, 0x0084, 0x4042, 0x2021, 0x1010, 0x4808, 0x6404, 0x7202, + 0x3901, 0x1c80, 0x4e40, 0x6720, 0x7390, 0x79c8, 0x7ce4, 0x7e72, 0x3f39, 0x1f9c, + 0x4fce, 0x27e7, 0x53f3, 0x69f9, 0x34fc, 0x5a7e, 0x2d3f, 0x569f, 0x6b4f, 0x75a7, + 0x7ad3, 0x7d69, 0x3eb4, 0x5f5a, 0x2fad, 0x17d6, 0x0beb, 0x45f5, 0x22fa, 0x117d, + 0x08be, 0x045f, 0x422f, 0x6117, 0x708b, 0x7845, 0x3c22, 0x1e11, 0x0f08, 0x4784, + 0x63c2, 0x31e1, 0x18f0, 0x4c78, 0x663c, 0x731e, 0x398f, 0x5cc7, 0x6e63, 0x7731, + 0x3b98, 0x5dcc, 0x6ee6, 0x3773, 0x5bb9, 0x2ddc, 0x56ee, 0x2b77, 0x55bb, 0x6add, + 0x356e, 0x1ab7, 0x4d5b, 0x66ad, 0x3356, 0x19ab, 0x4cd5, 0x266a, 0x1335, 0x099a, + 0x04cd, 0x0266, 0x0133, 0x4099, 0x204c, 0x5026, 0x2813, 0x5409, 0x2a04, 0x5502, + 0x2a81, 0x1540, 0x4aa0, 0x6550, 0x72a8, 0x7954, 0x7caa, 0x3e55, 0x1f2a, 0x0f95, + 0x07ca, 0x03e5, 0x01f2, 0x00f9, 0x007c, 0x403e, 0x201f, 0x500f, 0x6807, 0x7403, + 0x7a01, 0x3d00, 0x5e80, 0x6f40, 0x77a0, 0x7bd0, 0x7de8, 0x7ef4, 0x7f7a, 0x3fbd, + 0x1fde, 0x0fef, 0x47f7, 0x63fb, 0x71fd, 0x38fe, 0x1c7f, 0x4e3f, 0x671f, 0x738f, + 0x79c7, 0x7ce3, 0x7e71, 0x3f38, 0x5f9c, 0x6fce, 0x37e7, 0x5bf3, 0x6df9, 0x36fc, + 0x5b7e, 0x2dbf, 0x56df, 0x6b6f, 0x75b7, 0x7adb, 0x7d6d, 0x3eb6, 0x1f5b, 0x4fad, + 0x27d6, 0x13eb, 0x49f5, 0x24fa, 0x127d, 0x093e, 0x049f, 0x424f, 0x6127, 0x7093, + 0x7849, 0x3c24, 0x5e12, 0x2f09, 0x1784, 0x4bc2, 0x25e1, 0x12f0, 0x4978, 0x64bc, + 0x725e, 0x392f, 0x5c97, 0x6e4b, 0x7725, 0x3b92, 0x1dc9, 0x0ee4, 0x4772, 0x23b9, + 0x11dc, 0x48ee, 0x2477, 0x523b, 0x691d, 0x348e, 0x1a47, 0x4d23, 0x6691, 0x3348, + 0x59a4, 0x6cd2, 0x3669, 0x1b34, 0x4d9a, 0x26cd, 0x1366, 0x09b3, 0x44d9, 0x226c, + 0x5136, 0x289b, 0x544d, 0x2a26, 0x1513, 0x4a89, 0x2544, 0x52a2, 0x2951, 0x14a8, + 0x4a54, 0x652a, 0x3295, 0x194a, 0x0ca5, 0x0652, 0x0329, 0x0194, 0x40ca, 0x2065, + 0x1032, 0x0819, 0x040c, 0x4206, 0x2103, 0x5081, 0x2840, 0x5420, 0x6a10, 0x7508, + 0x7a84, 0x7d42, 0x3ea1, 0x1f50, 0x4fa8, 0x67d4, 0x73ea, 0x39f5, 0x1cfa, 0x0e7d, + 0x073e, 0x039f, 0x41cf, 0x60e7, 0x7073, 0x7839, 0x3c1c, 0x5e0e, 0x2f07, 0x5783, + 0x6bc1, 0x35e0, 0x5af0, 0x6d78, 0x76bc, 0x7b5e, 0x3daf, 0x5ed7, 0x6f6b, 0x77b5, + 0x3bda, 0x1ded, 0x0ef6, 0x077b, 0x43bd, 0x21de, 0x10ef, 0x4877, 0x643b, 0x721d, + 0x390e, 0x1c87, 0x4e43, 0x6721, 0x3390, 0x59c8, 0x6ce4, 0x7672, 0x3b39, 0x1d9c, + 0x4ece, 0x2767, 0x53b3, 0x69d9, 0x34ec, 0x5a76, 0x2d3b, 0x569d, 0x2b4e, 0x15a7, + 0x4ad3, 0x6569, 0x32b4, 0x595a, 0x2cad, 0x1656, 0x0b2b, 0x4595, 0x22ca, 0x1165, + 0x08b2, 0x0459, 0x022c, 0x4116, 0x208b, 0x5045, 0x2822, 0x1411, 0x0a08, 0x4504, + 0x6282, 0x3141, 0x18a0, 0x4c50, 0x6628, 0x7314, 0x798a, 0x3cc5, 0x1e62, 0x0f31, + 0x0798, 0x43cc, 0x61e6, 0x30f3, 0x5879, 0x2c3c, 0x561e, 0x2b0f, 0x5587, 0x6ac3, + 0x7561, 0x3ab0, 0x5d58, 0x6eac, 0x7756, 0x3bab, 0x5dd5, 0x2eea, 0x1775, 0x0bba, + 0x05dd, 0x02ee, 0x0177, 0x40bb, 0x605d, 0x302e, 0x1817, 0x4c0b, 0x6605, 0x3302, + 0x1981, 0x0cc0, 0x4660, 0x6330, 0x7198, 0x78cc, 0x7c66, 0x3e33, 0x5f19, 0x2f8c, + 0x57c6, 0x2be3, 0x55f1, 0x2af8, 0x557c, 0x6abe, 0x355f, 0x5aaf, 0x6d57, 0x76ab, + 0x7b55, 0x3daa, 0x1ed5, 0x0f6a, 0x07b5, 0x03da, 0x01ed, 0x00f6, 0x007b, 0x403d, + 0x201e, 0x100f, 0x4807, 0x6403, 0x7201, 0x3900, 0x5c80, 0x6e40, 0x7720, 0x7b90, + 0x7dc8, 0x7ee4, 0x7f72, 0x3fb9, 0x1fdc, 0x4fee, 0x27f7, 0x53fb, 0x69fd, 0x34fe, + 0x1a7f, 0x4d3f, 0x669f, 0x734f, 0x79a7, 0x7cd3, 0x7e69, 0x3f34, 0x5f9a, 0x2fcd, + 0x17e6, 0x0bf3, 0x45f9, 0x22fc, 0x517e, 0x28bf, 0x545f, 0x6a2f, 0x7517, 0x7a8b, + 0x7d45, 0x3ea2, 0x1f51, 0x0fa8, 0x47d4, 0x63ea, 0x31f5, 0x18fa, 0x0c7d, 0x063e, + 0x031f, 0x418f, 0x60c7, 0x7063, 0x7831, 0x3c18, 0x5e0c, 0x6f06, 0x3783, 0x5bc1, + 0x2de0, 0x56f0, 0x6b78, 0x75bc, 0x7ade, 0x3d6f, 0x5eb7, 0x6f5b, 0x77ad, 0x3bd6, + 0x1deb, 0x4ef5, 0x277a, 0x13bd, 0x09de, 0x04ef, 0x4277, 0x613b, 0x709d, 0x384e, + 0x1c27, 0x4e13, 0x6709, 0x3384, 0x59c2, 0x2ce1, 0x1670, 0x4b38, 0x659c, 0x72ce, + 0x3967, 0x5cb3, 0x6e59, 0x372c, 0x5b96, 0x2dcb, 0x56e5, 0x2b72, 0x15b9, 0x0adc, + 0x456e, 0x22b7, 0x515b, 0x68ad, 0x3456, 0x1a2b, 0x4d15, 0x268a, 0x1345, 0x09a2, + 0x04d1, 0x0268, 0x4134, 0x609a, 0x304d, 0x1826, 0x0c13, 0x4609, 0x2304, 0x5182, + 0x28c1, 0x1460, 0x4a30, 0x6518, 0x728c, 0x7946, 0x3ca3, 0x5e51, 0x2f28, 0x5794, + 0x6bca, 0x35e5, 0x1af2, 0x0d79, 0x06bc, 0x435e, 0x21af, 0x50d7, 0x686b, 0x7435, + 0x3a1a, 0x1d0d, 0x0e86, 0x0743, 0x43a1, 0x21d0, 0x50e8, 0x6874, 0x743a, 0x3a1d, + 0x1d0e, 0x0e87, 0x4743, 0x63a1, 0x31d0, 0x58e8, 0x6c74, 0x763a, 0x3b1d, 0x1d8e, + 0x0ec7, 0x4763, 0x63b1, 0x31d8, 0x58ec, 0x6c76, 0x363b, 0x5b1d, 0x2d8e, 0x16c7, + 0x4b63, 0x65b1, 0x32d8, 0x596c, 0x6cb6, 0x365b, 0x5b2d, 0x2d96, 0x16cb, 0x4b65, + 0x25b2, 0x12d9, 0x096c, 0x44b6, 0x225b, 0x512d, 0x2896, 0x144b, 0x4a25, 0x2512, + 0x1289, 0x0944, 0x44a2, 0x2251, 0x1128, 0x4894, 0x644a, 0x3225, 0x1912, 0x0c89, + 0x0644, 0x4322, 0x2191, 0x10c8, 0x4864, 0x6432, 0x3219, 0x190c, 0x4c86, 0x2643, + 0x5321, 0x2990, 0x54c8, 0x6a64, 0x7532, 0x3a99, 0x1d4c, 0x4ea6, 0x2753, 0x53a9, + 0x29d4, 0x54ea, 0x2a75, 0x153a, 0x0a9d, 0x054e, 0x02a7, 0x4153, 0x60a9, 0x3054, + 0x582a, 0x2c15, 0x160a, 0x0b05, 0x0582, 0x02c1, 0x0160, 0x40b0, 0x6058, 0x702c, + 0x7816, 0x3c0b, 0x5e05, 0x2f02, 0x1781, 0x0bc0, 0x45e0, 0x62f0, 0x7178, 0x78bc, + 0x7c5e, 0x3e2f, 0x5f17, 0x6f8b, 0x77c5, 0x3be2, 0x1df1, 0x0ef8, 0x477c, 0x63be, + 0x31df, 0x58ef, 0x6c77, 0x763b, 0x7b1d, 0x3d8e, 0x1ec7, 0x4f63, 0x67b1, 0x33d8, + 0x59ec, 0x6cf6, 0x367b, 0x5b3d, 0x2d9e, 0x16cf, 0x4b67, 0x65b3, 0x72d9, 0x396c, + 0x5cb6, 0x2e5b, 0x572d, 0x2b96, 0x15cb, 0x4ae5, 0x2572, 0x12b9, 0x095c, 0x44ae, + 0x2257, 0x512b, 0x6895, 0x344a, 0x1a25, 0x0d12, 0x0689, 0x0344, 0x41a2, 0x20d1, + 0x1068, 0x4834, 0x641a, 0x320d, 0x1906, 0x0c83, 0x4641, 0x2320, 0x5190, 0x68c8, + 0x7464, 0x7a32, 0x3d19, 0x1e8c, 0x4f46, 0x27a3, 0x53d1, 0x29e8, 0x54f4, 0x6a7a, + 0x353d, 0x1a9e, 0x0d4f, 0x46a7, 0x6353, 0x71a9, 0x38d4, 0x5c6a, 0x2e35, 0x171a, + 0x0b8d, 0x05c6, 0x02e3, 0x4171, 0x20b8, 0x505c, 0x682e, 0x3417, 0x5a0b, 0x6d05, + 0x3682, 0x1b41, 0x0da0, 0x46d0, 0x6368, 0x71b4, 0x78da, 0x3c6d, 0x1e36, 0x0f1b, + 0x478d, 0x23c6, 0x11e3, 0x48f1, 0x2478, 0x523c, 0x691e, 0x348f, 0x5a47, 0x6d23, + 0x7691, 0x3b48, 0x5da4, 0x6ed2, 0x3769, 0x1bb4, 0x4dda, 0x26ed, 0x1376, 0x09bb, + 0x44dd, 0x226e, 0x1137, 0x489b, 0x644d, 0x3226, 0x1913, 0x4c89, 0x2644, 0x5322, + 0x2991, 0x14c8, 0x4a64, 0x6532, 0x3299, 0x194c, 0x4ca6, 0x2653, 0x5329, 0x2994, + 0x54ca, 0x2a65, 0x1532, 0x0a99, 0x054c, 0x42a6, 0x2153, 0x50a9, 0x2854, 0x542a, + 0x2a15, 0x150a, 0x0a85, 0x0542, 0x02a1, 0x0150, 0x40a8, 0x6054, 0x702a, 0x3815, + 0x1c0a, 0x0e05, 0x0702, 0x0381, 0x01c0, 0x40e0, 0x6070, 0x7038, 0x781c, 0x7c0e, + 0x3e07, 0x5f03, 0x6f81, 0x37c0, 0x5be0, 0x6df0, 0x76f8, 0x7b7c, 0x7dbe, 0x3edf, + 0x5f6f, 0x6fb7, 0x77db, 0x7bed, 0x3df6, 0x1efb, 0x4f7d, 0x27be, 0x13df, 0x49ef, + 0x64f7, 0x727b, 0x793d, 0x3c9e, 0x1e4f, 0x4f27, 0x6793, 0x73c9, 0x39e4, 0x5cf2, + 0x2e79, 0x173c, 0x4b9e, 0x25cf, 0x52e7, 0x6973, 0x74b9, 0x3a5c, 0x5d2e, 0x2e97, + 0x574b, 0x6ba5, 0x35d2, 0x1ae9, 0x0d74, 0x46ba, 0x235d, 0x11ae, 0x08d7, 0x446b, + 0x6235, 0x311a, 0x188d, 0x0c46, 0x0623, 0x4311, 0x2188, 0x50c4, 0x6862, 0x3431, + 0x1a18, 0x4d0c, 0x6686, 0x3343, 0x59a1, 0x2cd0, 0x5668, 0x6b34, 0x759a, 0x3acd, + 0x1d66, 0x0eb3, 0x4759, 0x23ac, 0x51d6, 0x28eb, 0x5475, 0x2a3a, 0x151d, 0x0a8e, + 0x0547, 0x42a3, 0x6151, 0x30a8, 0x5854, 0x6c2a, 0x3615, 0x1b0a, 0x0d85, 0x06c2, + 0x0361, 0x01b0, 0x40d8, 0x606c, 0x7036, 0x381b, 0x5c0d, 0x2e06, 0x1703, 0x4b81, + 0x25c0, 0x52e0, 0x6970, 0x74b8, 0x7a5c, 0x7d2e, 0x3e97, 0x5f4b, 0x6fa5, 0x37d2, + 0x1be9, 0x0df4, 0x46fa, 0x237d, 0x11be, 0x08df, 0x446f, 0x6237, 0x711b, 0x788d, + 0x3c46, 0x1e23, 0x4f11, 0x2788, 0x53c4, 0x69e2, 0x34f1, 0x1a78, 0x4d3c, 0x669e, + 0x334f, 0x59a7, 0x6cd3, 0x7669, 0x3b34, 0x5d9a, 0x2ecd, 0x1766, 0x0bb3, 0x45d9, + 0x22ec, 0x5176, 0x28bb, 0x545d, 0x2a2e, 0x1517, 0x4a8b, 0x6545, 0x32a2, 0x1951, + 0x0ca8, 0x4654, 0x632a, 0x3195, 0x18ca, 0x0c65, 0x0632, 0x0319, 0x018c, 0x40c6, + 0x2063, 0x5031, 0x2818, 0x540c, 0x6a06, 0x3503, 0x5a81, 0x2d40, 0x56a0, 0x6b50, + 0x75a8, 0x7ad4, 0x7d6a, 0x3eb5, 0x1f5a, 0x0fad, 0x07d6, 0x03eb, 0x41f5, 0x20fa, + 0x107d, 0x083e, 0x041f, 0x420f, 0x6107, 0x7083, 0x7841, 0x3c20, 0x5e10, 0x6f08, + 0x7784, 0x7bc2, 0x3de1, 0x1ef0, 0x4f78, 0x67bc, 0x73de, 0x39ef, 0x5cf7, 0x6e7b, + 0x773d, 0x3b9e, 0x1dcf, 0x4ee7, 0x6773, 0x73b9, 0x39dc, 0x5cee, 0x2e77, 0x573b, + 0x6b9d, 0x35ce, 0x1ae7, 0x4d73, 0x66b9, 0x335c, 0x59ae, 0x2cd7, 0x566b, 0x6b35, + 0x359a, 0x1acd, 0x0d66, 0x06b3, 0x4359, 0x21ac, 0x50d6, 0x286b, 0x5435, 0x2a1a, + 0x150d, 0x0a86, 0x0543, 0x42a1, 0x2150, 0x50a8, 0x6854, 0x742a, 0x3a15, 0x1d0a, + 0x0e85, 0x0742, 0x03a1, 0x01d0, 0x40e8, 0x6074, 0x703a, 0x381d, 0x1c0e, 0x0e07, + 0x4703, 0x6381, 0x31c0, 0x58e0, 0x6c70, 0x7638, 0x7b1c, 0x7d8e, 0x3ec7, 0x5f63, + 0x6fb1, 0x37d8, 0x5bec, 0x6df6, 0x36fb, 0x5b7d, 0x2dbe, 0x16df, 0x4b6f, 0x65b7, + 0x72db, 0x796d, 0x3cb6, 0x1e5b, 0x4f2d, 0x2796, 0x13cb, 0x49e5, 0x24f2, 0x1279, + 0x093c, 0x449e, 0x224f, 0x5127, 0x6893, 0x7449, 0x3a24, 0x5d12, 0x2e89, 0x1744, + 0x4ba2, 0x25d1, 0x12e8, 0x4974, 0x64ba, 0x325d, 0x192e, 0x0c97, 0x464b, 0x6325, + 0x3192, 0x18c9, 0x0c64, 0x4632, 0x2319, 0x118c, 0x48c6, 0x2463, 0x5231, 0x2918, + 0x548c, 0x6a46, 0x3523, 0x5a91, 0x2d48, 0x56a4, 0x6b52, 0x35a9, 0x1ad4, 0x4d6a, + 0x26b5, 0x135a, 0x09ad, 0x04d6, 0x026b, 0x4135, 0x209a, 0x104d, 0x0826, 0x0413, + 0x4209, 0x2104, 0x5082, 0x2841, 0x1420, 0x4a10, 0x6508, 0x7284, 0x7942, 0x3ca1, + 0x1e50, 0x4f28, 0x6794, 0x73ca, 0x39e5, 0x1cf2, 0x0e79, 0x073c, 0x439e, 0x21cf, + 0x50e7, 0x6873, 0x7439, 0x3a1c, 0x5d0e, 0x2e87, 0x5743, 0x6ba1, 0x35d0, 0x5ae8, + 0x6d74, 0x76ba, 0x3b5d, 0x1dae, 0x0ed7, 0x476b, 0x63b5, 0x31da, 0x18ed, 0x0c76, + 0x063b, 0x431d, 0x218e, 0x10c7, 0x4863, 0x6431, 0x3218, 0x590c, 0x6c86, 0x3643, + 0x5b21, 0x2d90, 0x56c8, 0x6b64, 0x75b2, 0x3ad9, 0x1d6c, 0x4eb6, 0x275b, 0x53ad, + 0x29d6, 0x14eb, 0x4a75, 0x253a, 0x129d, 0x094e, 0x04a7, 0x4253, 0x6129, 0x3094, + 0x584a, 0x2c25, 0x1612, 0x0b09, 0x0584, 0x42c2, 0x2161, 0x10b0, 0x4858, 0x642c, + 0x7216, 0x390b, 0x5c85, 0x2e42, 0x1721, 0x0b90, 0x45c8, 0x62e4, 0x7172, 0x38b9, + 0x1c5c, 0x4e2e, 0x2717, 0x538b, 0x69c5, 0x34e2, 0x1a71, 0x0d38, 0x469c, 0x634e, + 0x31a7, 0x58d3, 0x6c69, 0x3634, 0x5b1a, 0x2d8d, 0x16c6, 0x0b63, 0x45b1, 0x22d8, + 0x516c, 0x68b6, 0x345b, 0x5a2d, 0x2d16, 0x168b, 0x4b45, 0x25a2, 0x12d1, 0x0968, + 0x44b4, 0x625a, 0x312d, 0x1896, 0x0c4b, 0x4625, 0x2312, 0x1189, 0x08c4, 0x4462, + 0x2231, 0x1118, 0x488c, 0x6446, 0x3223, 0x5911, 0x2c88, 0x5644, 0x6b22, 0x3591, + 0x1ac8, 0x4d64, 0x66b2, 0x3359, 0x19ac, 0x4cd6, 0x266b, 0x5335, 0x299a, 0x14cd, + 0x0a66, 0x0533, 0x4299, 0x214c, 0x50a6, 0x2853, 0x5429, 0x2a14, 0x550a, 0x2a85, + 0x1542, 0x0aa1, 0x0550, 0x42a8, 0x6154, 0x70aa, 0x3855, 0x1c2a, 0x0e15, 0x070a, + 0x0385, 0x01c2, 0x00e1, 0x0070, 0x4038, 0x601c, 0x700e, 0x3807, 0x5c03, 0x6e01, + 0x3700, 0x5b80, 0x6dc0, 0x76e0, 0x7b70, 0x7db8, 0x7edc, 0x7f6e, 0x3fb7, 0x5fdb, + 0x6fed, 0x37f6, 0x1bfb, 0x4dfd, 0x26fe, 0x137f, 0x49bf, 0x64df, 0x726f, 0x7937, + 0x7c9b, 0x7e4d, 0x3f26, 0x1f93, 0x4fc9, 0x27e4, 0x53f2, 0x29f9, 0x14fc, 0x4a7e, + 0x253f, 0x529f, 0x694f, 0x74a7, 0x7a53, 0x7d29, 0x3e94, 0x5f4a, 0x2fa5, 0x17d2, + 0x0be9, 0x05f4, 0x42fa, 0x217d, 0x10be, 0x085f, 0x442f, 0x6217, 0x710b, 0x7885, + 0x3c42, 0x1e21, 0x0f10, 0x4788, 0x63c4, 0x71e2, 0x38f1, 0x1c78, 0x4e3c, 0x671e, + 0x338f, 0x59c7, 0x6ce3, 0x7671, 0x3b38, 0x5d9c, 0x6ece, 0x3767, 0x5bb3, 0x6dd9, + 0x36ec, 0x5b76, 0x2dbb, 0x56dd, 0x2b6e, 0x15b7, 0x4adb, 0x656d, 0x32b6, 0x195b, + 0x4cad, 0x2656, 0x132b, 0x4995, 0x24ca, 0x1265, 0x0932, 0x0499, 0x024c, 0x4126, + 0x2093, 0x5049, 0x2824, 0x5412, 0x2a09, 0x1504, 0x4a82, 0x2541, 0x12a0, 0x4950, + 0x64a8, 0x7254, 0x792a, 0x3c95, 0x1e4a, 0x0f25, 0x0792, 0x03c9, 0x01e4, 0x40f2, + 0x2079, 0x103c, 0x481e, 0x240f, 0x5207, 0x6903, 0x7481, 0x3a40, 0x5d20, 0x6e90, + 0x7748, 0x7ba4, 0x7dd2, 0x3ee9, 0x1f74, 0x4fba, 0x27dd, 0x13ee, 0x09f7, 0x44fb, + 0x627d, 0x313e, 0x189f, 0x4c4f, 0x6627, 0x7313, 0x7989, 0x3cc4, 0x5e62, 0x2f31, + 0x1798, 0x4bcc, 0x65e6, 0x32f3, 0x5979, 0x2cbc, 0x565e, 0x2b2f, 0x5597, 0x6acb, + 0x7565, 0x3ab2, 0x1d59, 0x0eac, 0x4756, 0x23ab, 0x51d5, 0x28ea, 0x1475, 0x0a3a, + 0x051d, 0x028e, 0x0147, 0x40a3, 0x6051, 0x3028, 0x5814, 0x6c0a, 0x3605, 0x1b02, + 0x0d81, 0x06c0, 0x4360, 0x61b0, 0x70d8, 0x786c, 0x7c36, 0x3e1b, 0x5f0d, 0x2f86, + 0x17c3, 0x4be1, 0x25f0, 0x52f8, 0x697c, 0x74be, 0x3a5f, 0x5d2f, 0x6e97, 0x774b, + 0x7ba5, 0x3dd2, 0x1ee9, 0x0f74, 0x47ba, 0x23dd, 0x11ee, 0x08f7, 0x447b, 0x623d, + 0x311e, 0x188f, 0x4c47, 0x6623, 0x7311, 0x3988, 0x5cc4, 0x6e62, 0x3731, 0x1b98, + 0x4dcc, 0x66e6, 0x3373, 0x59b9, 0x2cdc, 0x566e, 0x2b37, 0x559b, 0x6acd, 0x3566, + 0x1ab3, 0x4d59, 0x26ac, 0x5356, 0x29ab, 0x54d5, 0x2a6a, 0x1535, 0x0a9a, 0x054d, + 0x02a6, 0x0153, 0x40a9, 0x2054, 0x502a, 0x2815, 0x140a, 0x0a05, 0x0502, 0x0281, + 0x0140, 0x40a0, 0x6050, 0x7028, 0x7814, 0x7c0a, 0x3e05, 0x1f02, 0x0f81, 0x07c0, + 0x43e0, 0x61f0, 0x70f8, 0x787c, 0x7c3e, 0x3e1f, 0x5f0f, 0x6f87, 0x77c3, 0x7be1, + 0x3df0, 0x5ef8, 0x6f7c, 0x77be, 0x3bdf, 0x5def, 0x6ef7, 0x777b, 0x7bbd, 0x3dde, + 0x1eef, 0x4f77, 0x67bb, 0x73dd, 0x39ee, 0x1cf7, 0x4e7b, 0x673d, 0x339e, 0x19cf, + 0x4ce7, 0x6673, 0x7339, 0x399c, 0x5cce, 0x2e67, 0x5733, 0x6b99, 0x35cc, 0x5ae6, + 0x2d73, 0x56b9, 0x2b5c, 0x55ae, 0x2ad7, 0x556b, 0x6ab5, 0x355a, 0x1aad, 0x0d56, + 0x06ab, 0x4355, 0x21aa, 0x10d5, 0x086a, 0x0435, 0x021a, 0x010d, 0x0086, 0x0043, + 0x4021, 0x2010, 0x5008, 0x6804, 0x7402, 0x3a01, 0x1d00, 0x4e80, 0x6740, 0x73a0, + 0x79d0, 0x7ce8, 0x7e74, 0x7f3a, 0x3f9d, 0x1fce, 0x0fe7, 0x47f3, 0x63f9, 0x31fc, + 0x58fe, 0x2c7f, 0x563f, 0x6b1f, 0x758f, 0x7ac7, 0x7d63, 0x7eb1, 0x3f58, 0x5fac, + 0x6fd6, 0x37eb, 0x5bf5, 0x2dfa, 0x16fd, 0x0b7e, 0x05bf, 0x42df, 0x616f, 0x70b7, + 0x785b, 0x7c2d, 0x3e16, 0x1f0b, 0x4f85, 0x27c2, 0x13e1, 0x09f0, 0x44f8, 0x627c, + 0x713e, 0x389f, 0x5c4f, 0x6e27, 0x7713, 0x7b89, 0x3dc4, 0x5ee2, 0x2f71, 0x17b8, + 0x4bdc, 0x65ee, 0x32f7, 0x597b, 0x6cbd, 0x365e, 0x1b2f, 0x4d97, 0x66cb, 0x7365, + 0x39b2, 0x1cd9, 0x0e6c, 0x4736, 0x239b, 0x51cd, 0x28e6, 0x1473, 0x4a39, 0x251c, + 0x528e, 0x2947, 0x54a3, 0x6a51, 0x3528, 0x5a94, 0x6d4a, 0x36a5, 0x1b52, 0x0da9, + 0x06d4, 0x436a, 0x21b5, 0x10da, 0x086d, 0x0436, 0x021b, 0x410d, 0x2086, 0x1043, + 0x4821, 0x2410, 0x5208, 0x6904, 0x7482, 0x3a41, 0x1d20, 0x4e90, 0x6748, 0x73a4, + 0x79d2, 0x3ce9, 0x1e74, 0x4f3a, 0x279d, 0x13ce, 0x09e7, 0x44f3, 0x6279, 0x313c, + 0x589e, 0x2c4f, 0x5627, 0x6b13, 0x7589, 0x3ac4, 0x5d62, 0x2eb1, 0x1758, 0x4bac, + 0x65d6, 0x32eb, 0x5975, 0x2cba, 0x165d, 0x0b2e, 0x0597, 0x42cb, 0x6165, 0x30b2, + 0x1859, 0x0c2c, 0x4616, 0x230b, 0x5185, 0x28c2, 0x1461, 0x0a30, 0x4518, 0x628c, + 0x7146, 0x38a3, 0x5c51, 0x2e28, 0x5714, 0x6b8a, 0x35c5, 0x1ae2, 0x0d71, 0x06b8, + 0x435c, 0x61ae, 0x30d7, 0x586b, 0x6c35, 0x361a, 0x1b0d, 0x0d86, 0x06c3, 0x4361, + 0x21b0, 0x50d8, 0x686c, 0x7436, 0x3a1b, 0x5d0d, 0x2e86, 0x1743, 0x4ba1, 0x25d0, + 0x52e8, 0x6974, 0x74ba, 0x3a5d, 0x1d2e, 0x0e97, 0x474b, 0x63a5, 0x31d2, 0x18e9, + 0x0c74, 0x463a, 0x231d, 0x118e, 0x08c7, 0x4463, 0x6231, 0x3118, 0x588c, 0x6c46, + 0x3623, 0x5b11, 0x2d88, 0x56c4, 0x6b62, 0x35b1, 0x1ad8, 0x4d6c, 0x66b6, 0x335b, + 0x59ad, 0x2cd6, 0x166b, 0x4b35, 0x259a, 0x12cd, 0x0966, 0x04b3, 0x4259, 0x212c, + 0x5096, 0x284b, 0x5425, 0x2a12, 0x1509, 0x0a84, 0x4542, 0x22a1, 0x1150, 0x48a8, + 0x6454, 0x722a, 0x3915, 0x1c8a, 0x0e45, 0x0722, 0x0391, 0x01c8, 0x40e4, 0x6072, + 0x3039, 0x181c, 0x4c0e, 0x2607, 0x5303, 0x6981, 0x34c0, 0x5a60, 0x6d30, 0x7698, + 0x7b4c, 0x7da6, 0x3ed3, 0x5f69, 0x2fb4, 0x57da, 0x2bed, 0x15f6, 0x0afb, 0x457d, + 0x22be, 0x115f, 0x48af, 0x6457, 0x722b, 0x7915, 0x3c8a, 0x1e45, 0x0f22, 0x0791, + 0x03c8, 0x41e4, 0x60f2, 0x3079, 0x183c, 0x4c1e, 0x260f, 0x5307, 0x6983, 0x74c1, + 0x3a60, 0x5d30, 0x6e98, 0x774c, 0x7ba6, 0x3dd3, 0x5ee9, 0x2f74, 0x57ba, 0x2bdd, + 0x15ee, 0x0af7, 0x457b, 0x62bd, 0x315e, 0x18af, 0x4c57, 0x662b, 0x7315, 0x398a, + 0x1cc5, 0x0e62, 0x0731, 0x0398, 0x41cc, 0x60e6, 0x3073, 0x5839, 0x2c1c, 0x560e, + 0x2b07, 0x5583, 0x6ac1, 0x3560, 0x5ab0, 0x6d58, 0x76ac, 0x7b56, 0x3dab, 0x5ed5, + 0x2f6a, 0x17b5, 0x0bda, 0x05ed, 0x02f6, 0x017b, 0x40bd, 0x205e, 0x102f, 0x4817, + 0x640b, 0x7205, 0x3902, 0x1c81, 0x0e40, 0x4720, 0x6390, 0x71c8, 0x78e4, 0x7c72, + 0x3e39, 0x1f1c, 0x4f8e, 0x27c7, 0x53e3, 0x69f1, 0x34f8, 0x5a7c, 0x6d3e, 0x369f, + 0x5b4f, 0x6da7, 0x76d3, 0x7b69, 0x3db4, 0x5eda, 0x2f6d, 0x17b6, 0x0bdb, 0x45ed, + 0x22f6, 0x117b, 0x48bd, 0x245e, 0x122f, 0x4917, 0x648b, 0x7245, 0x3922, 0x1c91, + 0x0e48, 0x4724, 0x6392, 0x31c9, 0x18e4, 0x4c72, 0x2639, 0x131c, 0x498e, 0x24c7, + 0x5263, 0x6931, 0x3498, 0x5a4c, 0x6d26, 0x3693, 0x5b49, 0x2da4, 0x56d2, 0x2b69, + 0x15b4, 0x4ada, 0x256d, 0x12b6, 0x095b, 0x44ad, 0x2256, 0x112b, 0x4895, 0x244a, + 0x1225, 0x0912, 0x0489, 0x0244, 0x4122, 0x2091, 0x1048, 0x4824, 0x6412, 0x3209, + 0x1904, 0x4c82, 0x2641, 0x1320, 0x4990, 0x64c8, 0x7264, 0x7932, 0x3c99, 0x1e4c, + 0x4f26, 0x2793, 0x53c9, 0x29e4, 0x54f2, 0x2a79, 0x153c, 0x4a9e, 0x254f, 0x52a7, + 0x6953, 0x74a9, 0x3a54, 0x5d2a, 0x2e95, 0x174a, 0x0ba5, 0x05d2, 0x02e9, 0x0174, + 0x40ba, 0x205d, 0x102e, 0x0817, 0x440b, 0x6205, 0x3102, 0x1881, 0x0c40, 0x4620, + 0x6310, 0x7188, 0x78c4, 0x7c62, 0x3e31, 0x1f18, 0x4f8c, 0x67c6, 0x33e3, 0x59f1, + 0x2cf8, 0x567c, 0x6b3e, 0x359f, 0x5acf, 0x6d67, 0x76b3, 0x7b59, 0x3dac, 0x5ed6, + 0x2f6b, 0x57b5, 0x2bda, 0x15ed, 0x0af6, 0x057b, 0x42bd, 0x215e, 0x10af, 0x4857, + 0x642b, 0x7215, 0x390a, 0x1c85, 0x0e42, 0x0721, 0x0390, 0x41c8, 0x60e4, 0x7072, + 0x3839, 0x1c1c, 0x4e0e, 0x2707, 0x5383, 0x69c1, 0x34e0, 0x5a70, 0x6d38, 0x769c, + 0x7b4e, 0x3da7, 0x5ed3, 0x6f69, 0x37b4, 0x5bda, 0x2ded, 0x16f6, 0x0b7b, 0x45bd, + 0x22de, 0x116f, 0x48b7, 0x645b, 0x722d, 0x3916, 0x1c8b, 0x4e45, 0x2722, 0x1391, + 0x09c8, 0x44e4, 0x6272, 0x3139, 0x189c, 0x4c4e, 0x2627, 0x5313, 0x6989, 0x34c4, + 0x5a62, 0x2d31, 0x1698, 0x4b4c, 0x65a6, 0x32d3, 0x5969, 0x2cb4, 0x565a, 0x2b2d, + 0x1596, 0x0acb, 0x4565, 0x22b2, 0x1159, 0x08ac, 0x4456, 0x222b, 0x5115, 0x288a, + 0x1445, 0x0a22, 0x0511, 0x0288, 0x4144, 0x60a2, 0x3051, 0x1828, 0x4c14, 0x660a, + 0x3305, 0x1982, 0x0cc1, 0x0660, 0x4330, 0x6198, 0x70cc, 0x7866, 0x3c33, 0x5e19, + 0x2f0c, 0x5786, 0x2bc3, 0x55e1, 0x2af0, 0x5578, 0x6abc, 0x755e, 0x3aaf, 0x5d57, + 0x6eab, 0x7755, 0x3baa, 0x1dd5, 0x0eea, 0x0775, 0x03ba, 0x01dd, 0x00ee, 0x0077, + 0x403b, 0x601d, 0x300e, 0x1807, 0x4c03, 0x6601, 0x3300, 0x5980, 0x6cc0, 0x7660, + 0x7b30, 0x7d98, 0x7ecc, 0x7f66, 0x3fb3, 0x5fd9, 0x2fec, 0x57f6, 0x2bfb, 0x55fd, + 0x2afe, 0x157f, 0x4abf, 0x655f, 0x72af, 0x7957, 0x7cab, 0x7e55, 0x3f2a, 0x1f95, + 0x0fca, 0x07e5, 0x03f2, 0x01f9, 0x00fc, 0x407e, 0x203f, 0x501f, 0x680f, 0x7407, + 0x7a03, 0x7d01, 0x3e80, 0x5f40, 0x6fa0, 0x77d0, 0x7be8, 0x7df4, 0x7efa, 0x3f7d, + 0x1fbe, 0x0fdf, 0x47ef, 0x63f7, 0x71fb, 0x78fd, 0x3c7e, 0x1e3f, 0x4f1f, 0x678f, + 0x73c7, 0x79e3, 0x7cf1, 0x3e78, 0x5f3c, 0x6f9e, 0x37cf, 0x5be7, 0x6df3, 0x76f9, + 0x3b7c, 0x5dbe, 0x2edf, 0x576f, 0x6bb7, 0x75db, 0x7aed, 0x3d76, 0x1ebb, 0x4f5d, + 0x27ae, 0x13d7, 0x49eb, 0x64f5, 0x327a, 0x193d, 0x0c9e, 0x064f, 0x4327, 0x6193, + 0x70c9, 0x3864, 0x5c32, 0x2e19, 0x170c, 0x4b86, 0x25c3, 0x52e1, 0x2970, 0x54b8, + 0x6a5c, 0x752e, 0x3a97, 0x5d4b, 0x6ea5, 0x3752, 0x1ba9, 0x0dd4, 0x46ea, 0x2375, + 0x11ba, 0x08dd, 0x046e, 0x0237, 0x411b, 0x608d, 0x3046, 0x1823, 0x4c11, 0x2608, + 0x5304, 0x6982, 0x34c1, 0x1a60, 0x4d30, 0x6698, 0x734c, 0x79a6, 0x3cd3, 0x5e69, + 0x2f34, 0x579a, 0x2bcd, 0x15e6, 0x0af3, 0x4579, 0x22bc, 0x515e, 0x28af, 0x5457, + 0x6a2b, 0x7515, 0x3a8a, 0x1d45, 0x0ea2, 0x0751, 0x03a8, 0x41d4, 0x60ea, 0x3075, + 0x183a, 0x0c1d, 0x060e, 0x0307, 0x4183, 0x60c1, 0x3060, 0x5830, 0x6c18, 0x760c, + 0x7b06, 0x3d83, 0x5ec1, 0x2f60, 0x57b0, 0x6bd8, 0x75ec, 0x7af6, 0x3d7b, 0x5ebd, + 0x2f5e, 0x17af, 0x4bd7, 0x65eb, 0x72f5, 0x397a, 0x1cbd, 0x0e5e, 0x072f, 0x4397, + 0x61cb, 0x70e5, 0x3872, 0x1c39, 0x0e1c, 0x470e, 0x2387, 0x51c3, 0x68e1, 0x3470, + 0x5a38, 0x6d1c, 0x768e, 0x3b47, 0x5da3, 0x6ed1, 0x3768, 0x5bb4, 0x6dda, 0x36ed, + 0x1b76, 0x0dbb, 0x46dd, 0x236e, 0x11b7, 0x48db, 0x646d, 0x3236, 0x191b, 0x4c8d, + 0x2646, 0x1323, 0x4991, 0x24c8, 0x5264, 0x6932, 0x3499, 0x1a4c, 0x4d26, 0x2693, + 0x5349, 0x29a4, 0x54d2, 0x2a69, 0x1534, 0x4a9a, 0x254d, 0x12a6, 0x0953, 0x44a9, + 0x2254, 0x512a, 0x2895, 0x144a, 0x0a25, 0x0512, 0x0289, 0x0144, 0x40a2, 0x2051, + 0x1028, 0x4814, 0x640a, 0x3205, 0x1902, 0x0c81, 0x0640, 0x4320, 0x6190, 0x70c8, + 0x7864, 0x7c32, 0x3e19, 0x1f0c, 0x4f86, 0x27c3, 0x53e1, 0x29f0, 0x54f8, 0x6a7c, + 0x753e, 0x3a9f, 0x5d4f, 0x6ea7, 0x7753, 0x7ba9, 0x3dd4, 0x5eea, 0x2f75, 0x17ba, + 0x0bdd, 0x05ee, 0x02f7, 0x417b, 0x60bd, 0x305e, 0x182f, 0x4c17, 0x660b, 0x7305, + 0x3982, 0x1cc1, 0x0e60, 0x4730, 0x6398, 0x71cc, 0x78e6, 0x3c73, 0x5e39, 0x2f1c, + 0x578e, 0x2bc7, 0x55e3, 0x6af1, 0x3578, 0x5abc, 0x6d5e, 0x36af, 0x5b57, 0x6dab, + 0x76d5, 0x3b6a, 0x1db5, 0x0eda, 0x076d, 0x03b6, 0x01db, 0x40ed, 0x2076, 0x103b, + 0x481d, 0x240e, 0x1207, 0x4903, 0x6481, 0x3240, 0x5920, 0x6c90, 0x7648, 0x7b24, + 0x7d92, 0x3ec9, 0x1f64, 0x4fb2, 0x27d9, 0x13ec, 0x49f6, 0x24fb, 0x527d, 0x293e, + 0x149f, 0x4a4f, 0x6527, 0x7293, 0x7949, 0x3ca4, 0x5e52, 0x2f29, 0x1794, 0x4bca, + 0x25e5, 0x12f2, 0x0979, 0x04bc, 0x425e, 0x212f, 0x5097, 0x684b, 0x7425, 0x3a12, + 0x1d09, 0x0e84, 0x4742, 0x23a1, 0x11d0, 0x48e8, 0x6474, 0x723a, 0x391d, 0x1c8e, + 0x0e47, 0x4723, 0x6391, 0x31c8, 0x58e4, 0x6c72, 0x3639, 0x1b1c, 0x4d8e, 0x26c7, + 0x5363, 0x69b1, 0x34d8, 0x5a6c, 0x6d36, 0x369b, 0x5b4d, 0x2da6, 0x16d3, 0x4b69, + 0x25b4, 0x52da, 0x296d, 0x14b6, 0x0a5b, 0x452d, 0x2296, 0x114b, 0x48a5, 0x2452, + 0x1229, 0x0914, 0x448a, 0x2245, 0x1122, 0x0891, 0x0448, 0x4224, 0x6112, 0x3089, + 0x1844, 0x4c22, 0x2611, 0x1308, 0x4984, 0x64c2, 0x3261, 0x1930, 0x4c98, 0x664c, + 0x7326, 0x3993, 0x5cc9, 0x2e64, 0x5732, 0x2b99, 0x15cc, 0x4ae6, 0x2573, 0x52b9, + 0x295c, 0x54ae, 0x2a57, 0x552b, 0x6a95, 0x354a, 0x1aa5, 0x0d52, 0x06a9, 0x0354, + 0x41aa, 0x20d5, 0x106a, 0x0835, 0x041a, 0x020d, 0x0106, 0x0083, 0x4041, 0x2020, + 0x5010, 0x6808, 0x7404, 0x7a02, 0x3d01, 0x1e80, 0x4f40, 0x67a0, 0x73d0, 0x79e8, + 0x7cf4, 0x7e7a, 0x3f3d, 0x1f9e, 0x0fcf, 0x47e7, 0x63f3, 0x71f9, 0x38fc, 0x5c7e, + 0x2e3f, 0x571f, 0x6b8f, 0x75c7, 0x7ae3, 0x7d71, 0x3eb8, 0x5f5c, 0x6fae, 0x37d7, + 0x5beb, 0x6df5, 0x36fa, 0x1b7d, 0x0dbe, 0x06df, 0x436f, 0x61b7, 0x70db, 0x786d, + 0x3c36, 0x1e1b, 0x4f0d, 0x2786, 0x13c3, 0x49e1, 0x24f0, 0x5278, 0x693c, 0x749e, + 0x3a4f, 0x5d27, 0x6e93, 0x7749, 0x3ba4, 0x5dd2, 0x2ee9, 0x1774, 0x4bba, 0x25dd, + 0x12ee, 0x0977, 0x44bb, 0x625d, 0x312e, 0x1897, 0x4c4b, 0x6625, 0x3312, 0x1989, + 0x0cc4, 0x4662, 0x2331, 0x1198, 0x48cc, 0x6466, 0x3233, 0x5919, 0x2c8c, 0x5646, + 0x2b23, 0x5591, 0x2ac8, 0x5564, 0x6ab2, 0x3559, 0x1aac, 0x4d56, 0x26ab, 0x5355, + 0x29aa, 0x14d5, 0x0a6a, 0x0535, 0x029a, 0x014d, 0x00a6, 0x0053, 0x4029, 0x2014, + 0x500a, 0x2805, 0x1402, 0x0a01, 0x0500, 0x4280, 0x6140, 0x70a0, 0x7850, 0x7c28, + 0x7e14, 0x7f0a, 0x3f85, 0x1fc2, 0x0fe1, 0x07f0, 0x43f8, 0x61fc, 0x70fe, 0x387f, + 0x5c3f, 0x6e1f, 0x770f, 0x7b87, 0x7dc3, 0x7ee1, 0x3f70, 0x5fb8, 0x6fdc, 0x77ee, + 0x3bf7, 0x5dfb, 0x6efd, 0x377e, 0x1bbf, 0x4ddf, 0x66ef, 0x7377, 0x79bb, 0x7cdd, + 0x3e6e, 0x1f37, 0x4f9b, 0x67cd, 0x33e6, 0x19f3, 0x4cf9, 0x267c, 0x533e, 0x299f, + 0x54cf, 0x6a67, 0x7533, 0x7a99, 0x3d4c, 0x5ea6, 0x2f53, 0x57a9, 0x2bd4, 0x55ea, + 0x2af5, 0x157a, 0x0abd, 0x055e, 0x02af, 0x4157, 0x60ab, 0x7055, 0x382a, 0x1c15, + 0x0e0a, 0x0705, 0x0382, 0x01c1, 0x00e0, 0x4070, 0x6038, 0x701c, 0x780e, 0x3c07, + 0x5e03, 0x6f01, 0x3780, 0x5bc0, 0x6de0, 0x76f0, 0x7b78, 0x7dbc, 0x7ede, 0x3f6f, + 0x5fb7, 0x6fdb, 0x77ed, 0x3bf6, 0x1dfb, 0x4efd, 0x277e, 0x13bf, 0x49df, 0x64ef, + 0x7277, 0x793b, 0x7c9d, 0x3e4e, 0x1f27, 0x4f93, 0x67c9, 0x33e4, 0x59f2, 0x2cf9, + 0x167c, 0x4b3e, 0x259f, 0x52cf, 0x6967, 0x74b3, 0x7a59, 0x3d2c, 0x5e96, 0x2f4b, + 0x57a5, 0x2bd2, 0x15e9, 0x0af4, 0x457a, 0x22bd, 0x115e, 0x08af, 0x4457, 0x622b, + 0x7115, 0x388a, 0x1c45, 0x0e22, 0x0711, 0x0388, 0x41c4, 0x60e2, 0x3071, 0x1838, + 0x4c1c, 0x660e, 0x3307, 0x5983, 0x6cc1, 0x3660, 0x5b30, 0x6d98, 0x76cc, 0x7b66, + 0x3db3, 0x5ed9, 0x2f6c, 0x57b6, 0x2bdb, 0x55ed, 0x2af6, 0x157b, 0x4abd, 0x255e, + 0x12af, 0x4957, 0x64ab, 0x7255, 0x392a, 0x1c95, 0x0e4a, 0x0725, 0x0392, 0x01c9, + 0x00e4, 0x4072, 0x2039, 0x101c, 0x480e, 0x2407, 0x5203, 0x6901, 0x3480, 0x5a40, + 0x6d20, 0x7690, 0x7b48, 0x7da4, 0x7ed2, 0x3f69, 0x1fb4, 0x4fda, 0x27ed, 0x13f6, + 0x09fb, 0x44fd, 0x227e, 0x113f, 0x489f, 0x644f, 0x7227, 0x7913, 0x7c89, 0x3e44, + 0x5f22, 0x2f91, 0x17c8, 0x4be4, 0x65f2, 0x32f9, 0x197c, 0x4cbe, 0x265f, 0x532f, + 0x6997, 0x74cb, 0x7a65, 0x3d32, 0x1e99, 0x0f4c, 0x47a6, 0x23d3, 0x51e9, 0x28f4, + 0x547a, 0x2a3d, 0x151e, 0x0a8f, 0x4547, 0x62a3, 0x7151, 0x38a8, 0x5c54, 0x6e2a, + 0x3715, 0x1b8a, 0x0dc5, 0x06e2, 0x0371, 0x01b8, 0x40dc, 0x606e, 0x3037, 0x581b, + 0x6c0d, 0x3606, 0x1b03, 0x4d81, 0x26c0, 0x5360, 0x69b0, 0x74d8, 0x7a6c, 0x7d36, + 0x3e9b, 0x5f4d, 0x2fa6, 0x17d3, 0x4be9, 0x25f4, 0x52fa, 0x297d, 0x14be, 0x0a5f, + 0x452f, 0x6297, 0x714b, 0x78a5, 0x3c52, 0x1e29, 0x0f14, 0x478a, 0x23c5, 0x11e2, + 0x08f1, 0x0478, 0x423c, 0x611e, 0x308f, 0x5847, 0x6c23, 0x7611, 0x3b08, 0x5d84, + 0x6ec2, 0x3761, 0x1bb0, 0x4dd8, 0x66ec, 0x7376, 0x39bb, 0x5cdd, 0x2e6e, 0x1737, + 0x4b9b, 0x65cd, 0x32e6, 0x1973, 0x4cb9, 0x265c, 0x532e, 0x2997, 0x54cb, 0x6a65, + 0x3532, 0x1a99, 0x0d4c, 0x46a6, 0x2353, 0x51a9, 0x28d4, 0x546a, 0x2a35, 0x151a, + 0x0a8d, 0x0546, 0x02a3, 0x4151, 0x20a8, 0x5054, 0x682a, 0x3415, 0x1a0a, 0x0d05, + 0x0682, 0x0341, 0x01a0, 0x40d0, 0x6068, 0x7034, 0x781a, 0x3c0d, 0x1e06, 0x0f03, + 0x4781, 0x23c0, 0x51e0, 0x68f0, 0x7478, 0x7a3c, 0x7d1e, 0x3e8f, 0x5f47, 0x6fa3, + 0x77d1, 0x3be8, 0x5df4, 0x6efa, 0x377d, 0x1bbe, 0x0ddf, 0x46ef, 0x6377, 0x71bb, + 0x78dd, 0x3c6e, 0x1e37, 0x4f1b, 0x678d, 0x33c6, 0x19e3, 0x4cf1, 0x2678, 0x533c, + 0x699e, 0x34cf, 0x5a67, 0x6d33, 0x7699, 0x3b4c, 0x5da6, 0x2ed3, 0x5769, 0x2bb4, + 0x55da, 0x2aed, 0x1576, 0x0abb, 0x455d, 0x22ae, 0x1157, 0x48ab, 0x6455, 0x322a, + 0x1915, 0x0c8a, 0x0645, 0x0322, 0x0191, 0x00c8, 0x4064, 0x6032, 0x3019, 0x180c, + 0x4c06, 0x2603, 0x5301, 0x2980, 0x54c0, 0x6a60, 0x7530, 0x7a98, 0x7d4c, 0x7ea6, + 0x3f53, 0x5fa9, 0x2fd4, 0x57ea, 0x2bf5, 0x15fa, 0x0afd, 0x057e, 0x02bf, 0x415f, + 0x60af, 0x7057, 0x782b, 0x7c15, 0x3e0a, 0x1f05, 0x0f82, 0x07c1, 0x03e0, 0x41f0, + 0x60f8, 0x707c, 0x783e, 0x3c1f, 0x5e0f, 0x6f07, 0x7783, 0x7bc1, 0x3de0, 0x5ef0, + 0x6f78, 0x77bc, 0x7bde, 0x3def, 0x5ef7, 0x6f7b, 0x77bd, 0x3bde, 0x1def, 0x4ef7, + 0x677b, 0x73bd, 0x39de, 0x1cef, 0x4e77, 0x673b, 0x739d, 0x39ce, 0x1ce7, 0x4e73, + 0x6739, 0x339c, 0x59ce, 0x2ce7, 0x5673, 0x6b39, 0x359c, 0x5ace, 0x2d67, 0x56b3, + 0x6b59, 0x35ac, 0x5ad6, 0x2d6b, 0x56b5, 0x2b5a, 0x15ad, 0x0ad6, 0x056b, 0x42b5, + 0x215a, 0x10ad, 0x0856, 0x042b, 0x4215, 0x210a, 0x1085, 0x0842, 0x0421, 0x0210, + 0x4108, 0x6084, 0x7042, 0x3821, 0x1c10, 0x4e08, 0x6704, 0x7382, 0x39c1, 0x1ce0, + 0x4e70, 0x6738, 0x739c, 0x79ce, 0x3ce7, 0x5e73, 0x6f39, 0x379c, 0x5bce, 0x2de7, + 0x56f3, 0x6b79, 0x35bc, 0x5ade, 0x2d6f, 0x56b7, 0x6b5b, 0x75ad, 0x3ad6, 0x1d6b, + 0x4eb5, 0x275a, 0x13ad, 0x09d6, 0x04eb, 0x4275, 0x213a, 0x109d, 0x084e, 0x0427, + 0x4213, 0x6109, 0x3084, 0x5842, 0x2c21, 0x1610, 0x4b08, 0x6584, 0x72c2, 0x3961, + 0x1cb0, 0x4e58, 0x672c, 0x7396, 0x39cb, 0x5ce5, 0x2e72, 0x1739, 0x0b9c, 0x45ce, + 0x22e7, 0x5173, 0x68b9, 0x345c, 0x5a2e, 0x2d17, 0x568b, 0x6b45, 0x35a2, 0x1ad1, + 0x0d68, 0x46b4, 0x635a, 0x31ad, 0x18d6, 0x0c6b, 0x4635, 0x231a, 0x118d, 0x08c6, + 0x0463, 0x4231, 0x2118, 0x508c, 0x6846, 0x3423, 0x5a11, 0x2d08, 0x5684, 0x6b42, + 0x35a1, 0x1ad0, 0x4d68, 0x66b4, 0x735a, 0x39ad, 0x1cd6, 0x0e6b, 0x4735, 0x239a, + 0x11cd, 0x08e6, 0x0473, 0x4239, 0x211c, 0x508e, 0x2847, 0x5423, 0x6a11, 0x3508, + 0x5a84, 0x6d42, 0x36a1, 0x1b50, 0x4da8, 0x66d4, 0x736a, 0x39b5, 0x1cda, 0x0e6d, + 0x0736, 0x039b, 0x41cd, 0x20e6, 0x1073, 0x4839, 0x241c, 0x520e, 0x2907, 0x5483, + 0x6a41, 0x3520, 0x5a90, 0x6d48, 0x76a4, 0x7b52, 0x3da9, 0x1ed4, 0x4f6a, 0x27b5, + 0x13da, 0x09ed, 0x04f6, 0x027b, 0x413d, 0x209e, 0x104f, 0x4827, 0x6413, 0x7209, + 0x3904, 0x5c82, 0x2e41, 0x1720, 0x4b90, 0x65c8, 0x72e4, 0x7972, 0x3cb9, 0x1e5c, + 0x4f2e, 0x2797, 0x53cb, 0x69e5, 0x34f2, 0x1a79, 0x0d3c, 0x469e, 0x234f, 0x51a7, + 0x68d3, 0x7469, 0x3a34, 0x5d1a, 0x2e8d, 0x1746, 0x0ba3, 0x45d1, 0x22e8, 0x5174, + 0x68ba, 0x345d, 0x1a2e, 0x0d17, 0x468b, 0x6345, 0x31a2, 0x18d1, 0x0c68, 0x4634, + 0x631a, 0x318d, 0x18c6, 0x0c63, 0x4631, 0x2318, 0x518c, 0x68c6, 0x3463, 0x5a31, + 0x2d18, 0x568c, 0x6b46, 0x35a3, 0x5ad1, 0x2d68, 0x56b4, 0x6b5a, 0x35ad, 0x1ad6, + 0x0d6b, 0x46b5, 0x235a, 0x11ad, 0x08d6, 0x046b, 0x4235, 0x211a, 0x108d, 0x0846, + 0x0423, 0x4211, 0x2108, 0x5084, 0x6842, 0x3421, 0x1a10, 0x4d08, 0x6684, 0x7342, + 0x39a1, 0x1cd0, 0x4e68, 0x6734, 0x739a, 0x39cd, 0x1ce6, 0x0e73, 0x4739, 0x239c, + 0x51ce, 0x28e7, 0x5473, 0x6a39, 0x351c, 0x5a8e, 0x2d47, 0x56a3, 0x6b51, 0x35a8, + 0x5ad4, 0x6d6a, 0x36b5, 0x1b5a, 0x0dad, 0x06d6, 0x036b, 0x41b5, 0x20da, 0x106d, + 0x0836, 0x041b, 0x420d, 0x2106, 0x1083, 0x4841, 0x2420, 0x5210, 0x6908, 0x7484, + 0x7a42, 0x3d21, 0x1e90, 0x4f48, 0x67a4, 0x73d2, 0x39e9, 0x1cf4, 0x4e7a, 0x273d, + 0x139e, 0x09cf, 0x44e7, 0x6273, 0x7139, 0x389c, 0x5c4e, 0x2e27, 0x5713, 0x6b89, + 0x35c4, 0x5ae2, 0x2d71, 0x16b8, 0x4b5c, 0x65ae, 0x32d7, 0x596b, 0x6cb5, 0x365a, + 0x1b2d, 0x0d96, 0x06cb, 0x4365, 0x21b2, 0x10d9, 0x086c, 0x4436, 0x221b, 0x510d, + 0x2886, 0x1443, 0x4a21, 0x2510, 0x5288, 0x6944, 0x74a2, 0x3a51, 0x1d28, 0x4e94, + 0x674a, 0x33a5, 0x19d2, 0x0ce9, 0x0674, 0x433a, 0x219d, 0x10ce, 0x0867, 0x4433, + 0x6219, 0x310c, 0x5886, 0x2c43, 0x5621, 0x2b10, 0x5588, 0x6ac4, 0x7562, 0x3ab1, + 0x1d58, 0x4eac, 0x6756, 0x33ab, 0x59d5, 0x2cea, 0x1675, 0x0b3a, 0x059d, 0x02ce, + 0x0167, 0x40b3, 0x6059, 0x302c, 0x5816, 0x2c0b, 0x5605, 0x2b02, 0x1581, 0x0ac0, + 0x4560, 0x62b0, 0x7158, 0x78ac, 0x7c56, 0x3e2b, 0x5f15, 0x2f8a, 0x17c5, 0x0be2, + 0x05f1, 0x02f8, 0x417c, 0x60be, 0x305f, 0x582f, 0x6c17, 0x760b, 0x7b05, 0x3d82, + 0x1ec1, 0x0f60, 0x47b0, 0x63d8, 0x71ec, 0x78f6, 0x3c7b, 0x5e3d, 0x2f1e, 0x178f, + 0x4bc7, 0x65e3, 0x72f1, 0x3978, 0x5cbc, 0x6e5e, 0x372f, 0x5b97, 0x6dcb, 0x76e5, + 0x3b72, 0x1db9, 0x0edc, 0x476e, 0x23b7, 0x51db, 0x68ed, 0x3476, 0x1a3b, 0x4d1d, + 0x268e, 0x1347, 0x49a3, 0x64d1, 0x3268, 0x5934, 0x6c9a, 0x364d, 0x1b26, 0x0d93, + 0x46c9, 0x2364, 0x51b2, 0x28d9, 0x146c, 0x4a36, 0x251b, 0x528d, 0x2946, 0x14a3, + 0x4a51, 0x2528, 0x5294, 0x694a, 0x34a5, 0x1a52, 0x0d29, 0x0694, 0x434a, 0x21a5, + 0x10d2, 0x0869, 0x0434, 0x421a, 0x210d, 0x1086, 0x0843, 0x4421, 0x2210, 0x5108, + 0x6884, 0x7442, 0x3a21, 0x1d10, 0x4e88, 0x6744, 0x73a2, 0x39d1, 0x1ce8, 0x4e74, + 0x673a, 0x339d, 0x19ce, 0x0ce7, 0x4673, 0x6339, 0x319c, 0x58ce, 0x2c67, 0x5633, + 0x6b19, 0x358c, 0x5ac6, 0x2d63, 0x56b1, 0x2b58, 0x55ac, 0x6ad6, 0x356b, 0x5ab5, + 0x2d5a, 0x16ad, 0x0b56, 0x05ab, 0x42d5, 0x216a, 0x10b5, 0x085a, 0x042d, 0x0216, + 0x010b, 0x4085, 0x2042, 0x1021, 0x0810, 0x4408, 0x6204, 0x7102, 0x3881, 0x1c40, + 0x4e20, 0x6710, 0x7388, 0x79c4, 0x7ce2, 0x3e71, 0x1f38, 0x4f9c, 0x67ce, 0x33e7, + 0x59f3, 0x6cf9, 0x367c, 0x5b3e, 0x2d9f, 0x56cf, 0x6b67, 0x75b3, 0x7ad9, 0x3d6c, + 0x5eb6, 0x2f5b, 0x57ad, 0x2bd6, 0x15eb, 0x4af5, 0x257a, 0x12bd, 0x095e, 0x04af, + 0x4257, 0x612b, 0x7095, 0x384a, 0x1c25, 0x0e12, 0x0709, 0x0384, 0x41c2, 0x20e1, + 0x1070, 0x4838, 0x641c, 0x720e, 0x3907, 0x5c83, 0x6e41, 0x3720, 0x5b90, 0x6dc8, + 0x76e4, 0x7b72, 0x3db9, 0x1edc, 0x4f6e, 0x27b7, 0x53db, 0x69ed, 0x34f6, 0x1a7b, + 0x4d3d, 0x269e, 0x134f, 0x49a7, 0x64d3, 0x7269, 0x3934, 0x5c9a, 0x2e4d, 0x1726, + 0x0b93, 0x45c9, 0x22e4, 0x5172, 0x28b9, 0x145c, 0x4a2e, 0x2517, 0x528b, 0x6945, + 0x34a2, 0x1a51, 0x0d28, 0x4694, 0x634a, 0x31a5, 0x18d2, 0x0c69, 0x0634, 0x431a, + 0x218d, 0x10c6, 0x0863, 0x4431, 0x2218, 0x510c, 0x6886, 0x3443, 0x5a21, 0x2d10, + 0x5688, 0x6b44, 0x75a2, 0x3ad1, 0x1d68, 0x4eb4, 0x675a, 0x33ad, 0x19d6, 0x0ceb, + 0x4675, 0x233a, 0x119d, 0x08ce, 0x0467, 0x4233, 0x6119, 0x308c, 0x5846, 0x2c23, + 0x5611, 0x2b08, 0x5584, 0x6ac2, 0x3561, 0x1ab0, 0x4d58, 0x66ac, 0x7356, 0x39ab, + 0x5cd5, 0x2e6a, 0x1735, 0x0b9a, 0x05cd, 0x02e6, 0x0173, 0x40b9, 0x205c, 0x502e, + 0x2817, 0x540b, 0x6a05, 0x3502, 0x1a81, 0x0d40, 0x46a0, 0x6350, 0x71a8, 0x78d4, + 0x7c6a, 0x3e35, 0x1f1a, 0x0f8d, 0x07c6, 0x03e3, 0x41f1, 0x20f8, 0x507c, 0x683e, + 0x341f, 0x5a0f, 0x6d07, 0x7683, 0x7b41, 0x3da0, 0x5ed0, 0x6f68, 0x77b4, 0x7bda, + 0x3ded, 0x1ef6, 0x0f7b, 0x47bd, 0x23de, 0x11ef, 0x48f7, 0x647b, 0x723d, 0x391e, + 0x1c8f, 0x4e47, 0x6723, 0x7391, 0x39c8, 0x5ce4, 0x6e72, 0x3739, 0x1b9c, 0x4dce, + 0x26e7, 0x5373, 0x69b9, 0x34dc, 0x5a6e, 0x2d37, 0x569b, 0x6b4d, 0x35a6, 0x1ad3, + 0x4d69, 0x26b4, 0x535a, 0x29ad, 0x14d6, 0x0a6b, 0x4535, 0x229a, 0x114d, 0x08a6, + 0x0453, 0x4229, 0x2114, 0x508a, 0x2845, 0x1422, 0x0a11, 0x0508, 0x4284, 0x6142, + 0x30a1, 0x1850, 0x4c28, 0x6614, 0x730a, 0x3985, 0x1cc2, 0x0e61, 0x0730, 0x4398, + 0x61cc, 0x70e6, 0x3873, 0x5c39, 0x2e1c, 0x570e, 0x2b87, 0x55c3, 0x6ae1, 0x3570, + 0x5ab8, 0x6d5c, 0x76ae, 0x3b57, 0x5dab, 0x6ed5, 0x376a, 0x1bb5, 0x0dda, 0x06ed, + 0x0376, 0x01bb, 0x40dd, 0x206e, 0x1037, 0x481b, 0x640d, 0x3206, 0x1903, 0x4c81, + 0x2640, 0x5320, 0x6990, 0x74c8, 0x7a64, 0x7d32, 0x3e99, 0x1f4c, 0x4fa6, 0x27d3, + 0x53e9, 0x29f4, 0x54fa, 0x2a7d, 0x153e, 0x0a9f, 0x454f, 0x62a7, 0x7153, 0x78a9, + 0x3c54, 0x5e2a, 0x2f15, 0x178a, 0x0bc5, 0x05e2, 0x02f1, 0x0178, 0x40bc, 0x605e, + 0x302f, 0x5817, 0x6c0b, 0x7605, 0x3b02, 0x1d81, 0x0ec0, 0x4760, 0x63b0, 0x71d8, + 0x78ec, 0x7c76, 0x3e3b, 0x5f1d, 0x2f8e, 0x17c7, 0x4be3, 0x65f1, 0x32f8, 0x597c, + 0x6cbe, 0x365f, 0x5b2f, 0x6d97, 0x76cb, 0x7b65, 0x3db2, 0x1ed9, 0x0f6c, 0x47b6, + 0x23db, 0x51ed, 0x28f6, 0x147b, 0x4a3d, 0x251e, 0x128f, 0x4947, 0x64a3, 0x7251, + 0x3928, 0x5c94, 0x6e4a, 0x3725, 0x1b92, 0x0dc9, 0x06e4, 0x4372, 0x21b9, 0x10dc, + 0x486e, 0x2437, 0x521b, 0x690d, 0x3486, 0x1a43, 0x4d21, 0x2690, 0x5348, 0x69a4, + 0x74d2, 0x3a69, 0x1d34, 0x4e9a, 0x274d, 0x13a6, 0x09d3, 0x44e9, 0x2274, 0x513a, + 0x289d, 0x144e, 0x0a27, 0x4513, 0x6289, 0x3144, 0x58a2, 0x2c51, 0x1628, 0x4b14, + 0x658a, 0x32c5, 0x1962, 0x0cb1, 0x0658, 0x432c, 0x6196, 0x30cb, 0x5865, 0x2c32, + 0x1619, 0x0b0c, 0x4586, 0x22c3, 0x5161, 0x28b0, 0x5458, 0x6a2c, 0x7516, 0x3a8b, + 0x5d45, 0x2ea2, 0x1751, 0x0ba8, 0x45d4, 0x62ea, 0x3175, 0x18ba, 0x0c5d, 0x062e, + 0x0317, 0x418b, 0x60c5, 0x3062, 0x1831, 0x0c18, 0x460c, 0x6306, 0x3183, 0x58c1, + 0x2c60, 0x5630, 0x6b18, 0x758c, 0x7ac6, 0x3d63, 0x5eb1, 0x2f58, 0x57ac, 0x6bd6, + 0x35eb, 0x5af5, 0x2d7a, 0x16bd, 0x0b5e, 0x05af, 0x42d7, 0x616b, 0x70b5, 0x385a, + 0x1c2d, 0x0e16, 0x070b, 0x4385, 0x21c2, 0x10e1, 0x0870, 0x4438, 0x621c, 0x710e, + 0x3887, 0x5c43, 0x6e21, 0x3710, 0x5b88, 0x6dc4, 0x76e2, 0x3b71, 0x1db8, 0x4edc, + 0x676e, 0x33b7, 0x59db, 0x6ced, 0x3676, 0x1b3b, 0x4d9d, 0x26ce, 0x1367, 0x49b3, + 0x64d9, 0x326c, 0x5936, 0x2c9b, 0x564d, 0x2b26, 0x1593, 0x4ac9, 0x2564, 0x52b2, + 0x2959, 0x14ac, 0x4a56, 0x252b, 0x5295, 0x294a, 0x14a5, 0x0a52, 0x0529, 0x0294, + 0x414a, 0x20a5, 0x1052, 0x0829, 0x0414, 0x420a, 0x2105, 0x1082, 0x0841, 0x0420, + 0x4210, 0x6108, 0x7084, 0x7842, 0x3c21, 0x1e10, 0x4f08, 0x6784, 0x73c2, 0x39e1, + 0x1cf0, 0x4e78, 0x673c, 0x739e, 0x39cf, 0x5ce7, 0x6e73, 0x7739, 0x3b9c, 0x5dce, + 0x2ee7, 0x5773, 0x6bb9, 0x35dc, 0x5aee, 0x2d77, 0x56bb, 0x6b5d, 0x35ae, 0x1ad7, + 0x4d6b, 0x66b5, 0x335a, 0x19ad, 0x0cd6, 0x066b, 0x4335, 0x219a, 0x10cd, 0x0866, + 0x0433, 0x4219, 0x210c, 0x5086, 0x2843, 0x5421, 0x2a10, 0x5508, 0x6a84, 0x7542, + 0x3aa1, 0x1d50, 0x4ea8, 0x6754, 0x73aa, 0x39d5, 0x1cea, 0x0e75, 0x073a, 0x039d, + 0x01ce, 0x00e7, 0x4073, 0x6039, 0x301c, 0x580e, 0x2c07, 0x5603, 0x6b01, 0x3580, + 0x5ac0, 0x6d60, 0x76b0, 0x7b58, 0x7dac, 0x7ed6, 0x3f6b, 0x5fb5, 0x2fda, 0x17ed, + 0x0bf6, 0x05fb, 0x42fd, 0x217e, 0x10bf, 0x485f, 0x642f, 0x7217, 0x790b, 0x7c85, + 0x3e42, 0x1f21, 0x0f90, 0x47c8, 0x63e4, 0x71f2, 0x38f9, 0x1c7c, 0x4e3e, 0x271f, + 0x538f, 0x69c7, 0x74e3, 0x7a71, 0x3d38, 0x5e9c, 0x6f4e, 0x37a7, 0x5bd3, 0x6de9, + 0x36f4, 0x5b7a, 0x2dbd, 0x16de, 0x0b6f, 0x45b7, 0x62db, 0x716d, 0x38b6, 0x1c5b, + 0x4e2d, 0x2716, 0x138b, 0x49c5, 0x24e2, 0x1271, 0x0938, 0x449c, 0x624e, 0x3127, + 0x5893, 0x6c49, 0x3624, 0x5b12, 0x2d89, 0x16c4, 0x4b62, 0x25b1, 0x12d8, 0x496c, + 0x64b6, 0x325b, 0x592d, 0x2c96, 0x164b, 0x4b25, 0x2592, 0x12c9, 0x0964, 0x44b2, + 0x2259, 0x112c, 0x4896, 0x244b, 0x5225, 0x2912, 0x1489, 0x0a44, 0x4522, 0x2291, + 0x1148, 0x48a4, 0x6452, 0x3229, 0x1914, 0x4c8a, 0x2645, 0x1322, 0x0991, 0x04c8, + 0x4264, 0x6132, 0x3099, 0x184c, 0x4c26, 0x2613, 0x5309, 0x2984, 0x54c2, 0x2a61, + 0x1530, 0x4a98, 0x654c, 0x72a6, 0x3953, 0x5ca9, 0x2e54, 0x572a, 0x2b95, 0x15ca, + 0x0ae5, 0x0572, 0x02b9, 0x015c, 0x40ae, 0x2057, 0x502b, 0x6815, 0x340a, 0x1a05, + 0x0d02, 0x0681, 0x0340, 0x41a0, 0x60d0, 0x7068, 0x7834, 0x7c1a, 0x3e0d, 0x1f06, + 0x0f83, 0x47c1, 0x23e0, 0x51f0, 0x68f8, 0x747c, 0x7a3e, 0x3d1f, 0x5e8f, 0x6f47, + 0x77a3, 0x7bd1, 0x3de8, 0x5ef4, 0x6f7a, 0x37bd, 0x1bde, 0x0def, 0x46f7, 0x637b, + 0x71bd, 0x38de, 0x1c6f, 0x4e37, 0x671b, 0x738d, 0x39c6, 0x1ce3, 0x4e71, 0x2738, + 0x539c, 0x69ce, 0x34e7, 0x5a73, 0x6d39, 0x369c, 0x5b4e, 0x2da7, 0x56d3, 0x6b69, + 0x35b4, 0x5ada, 0x2d6d, 0x16b6, 0x0b5b, 0x45ad, 0x22d6, 0x116b, 0x48b5, 0x245a, + 0x122d, 0x0916, 0x048b, 0x4245, 0x2122, 0x1091, 0x0848, 0x4424, 0x6212, 0x3109, + 0x1884, 0x4c42, 0x2621, 0x1310, 0x4988, 0x64c4, 0x7262, 0x3931, 0x1c98, 0x4e4c, + 0x6726, 0x3393, 0x59c9, 0x2ce4, 0x5672, 0x2b39, 0x159c, 0x4ace, 0x2567, 0x52b3, + 0x6959, 0x34ac, 0x5a56, 0x2d2b, 0x5695, 0x2b4a, 0x15a5, 0x0ad2, 0x0569, 0x02b4, + 0x415a, 0x20ad, 0x1056, 0x082b, 0x4415, 0x220a, 0x1105, 0x0882, 0x0441, 0x0220, + 0x4110, 0x6088, 0x7044, 0x7822, 0x3c11, 0x1e08, 0x4f04, 0x6782, 0x33c1, 0x19e0, + 0x4cf0, 0x6678, 0x733c, 0x799e, 0x3ccf, 0x5e67, 0x6f33, 0x7799, 0x3bcc, 0x5de6, + 0x2ef3, 0x5779, 0x2bbc, 0x55de, 0x2aef, 0x5577, 0x6abb, 0x755d, 0x3aae, 0x1d57, + 0x4eab, 0x6755, 0x33aa, 0x19d5, 0x0cea, 0x0675, 0x033a, 0x019d, 0x00ce, 0x0067, + 0x4033, 0x6019, 0x300c, 0x5806, 0x2c03, 0x5601, 0x2b00, 0x5580, 0x6ac0, 0x7560, + 0x7ab0, 0x7d58, 0x7eac, 0x7f56, 0x3fab, 0x5fd5, 0x2fea, 0x17f5, 0x0bfa, 0x05fd, + 0x02fe, 0x017f, 0x40bf, 0x605f, 0x702f, 0x7817, 0x7c0b, 0x7e05, 0x3f02, 0x1f81, + 0x0fc0, 0x47e0, 0x63f0, 0x71f8, 0x78fc, 0x7c7e, 0x3e3f, 0x5f1f, 0x6f8f, 0x77c7, + 0x7be3, 0x7df1, 0x3ef8, 0x5f7c, 0x6fbe, 0x37df, 0x5bef, 0x6df7, 0x76fb, 0x7b7d, + 0x3dbe, 0x1edf, 0x4f6f, 0x67b7, 0x73db, 0x79ed, 0x3cf6, 0x1e7b, 0x4f3d, 0x279e, + 0x13cf, 0x49e7, 0x64f3, 0x7279, 0x393c, 0x5c9e, 0x2e4f, 0x5727, 0x6b93, 0x75c9, + 0x3ae4, 0x5d72, 0x2eb9, 0x175c, 0x4bae, 0x25d7, 0x52eb, 0x6975, 0x34ba, 0x1a5d, + 0x0d2e, 0x0697, 0x434b, 0x61a5, 0x30d2, 0x1869, 0x0c34, 0x461a, 0x230d, 0x1186, + 0x08c3, 0x4461, 0x2230, 0x5118, 0x688c, 0x7446, 0x3a23, 0x5d11, 0x2e88, 0x5744, + 0x6ba2, 0x35d1, 0x1ae8, 0x4d74, 0x66ba, 0x335d, 0x19ae, 0x0cd7, 0x466b, 0x6335, + 0x319a, 0x18cd, 0x0c66, 0x0633, 0x4319, 0x218c, 0x50c6, 0x2863, 0x5431, 0x2a18, + 0x550c, 0x6a86, 0x3543, 0x5aa1, 0x2d50, 0x56a8, 0x6b54, 0x75aa, 0x3ad5, 0x1d6a, + 0x0eb5, 0x075a, 0x03ad, 0x01d6, 0x00eb, 0x4075, 0x203a, 0x101d, 0x080e, 0x0407, + 0x4203, 0x6101, 0x3080, 0x5840, 0x6c20, 0x7610, 0x7b08, 0x7d84, 0x7ec2, 0x3f61, + 0x1fb0, 0x4fd8, 0x67ec, 0x73f6, 0x39fb, 0x5cfd, 0x2e7e, 0x173f, 0x4b9f, 0x65cf, + 0x72e7, 0x7973, 0x7cb9, 0x3e5c, 0x5f2e, 0x2f97, 0x57cb, 0x6be5, 0x35f2, 0x1af9, + 0x0d7c, 0x46be, 0x235f, 0x51af, 0x68d7, 0x746b, 0x7a35, 0x3d1a, 0x1e8d, 0x0f46, + 0x07a3, 0x43d1, 0x21e8, 0x50f4, 0x687a, 0x343d, 0x1a1e, 0x0d0f, 0x4687, 0x6343, + 0x71a1, 0x38d0, 0x5c68, 0x6e34, 0x771a, 0x3b8d, 0x1dc6, 0x0ee3, 0x4771, 0x23b8, + 0x51dc, 0x68ee, 0x3477, 0x5a3b, 0x6d1d, 0x368e, 0x1b47, 0x4da3, 0x66d1, 0x3368, + 0x59b4, 0x6cda, 0x366d, 0x1b36, 0x0d9b, 0x46cd, 0x2366, 0x11b3, 0x48d9, 0x246c, + 0x5236, 0x291b, 0x548d, 0x2a46, 0x1523, 0x4a91, 0x2548, 0x52a4, 0x6952, 0x34a9, + 0x1a54, 0x4d2a, 0x2695, 0x134a, 0x09a5, 0x04d2, 0x0269, 0x0134, 0x409a, 0x204d, + 0x1026, 0x0813, 0x4409, 0x2204, 0x5102, 0x2881, 0x1440, 0x4a20, 0x6510, 0x7288, + 0x7944, 0x7ca2, 0x3e51, 0x1f28, 0x4f94, 0x67ca, 0x33e5, 0x19f2, 0x0cf9, 0x067c, + 0x433e, 0x219f, 0x50cf, 0x6867, 0x7433, 0x7a19, 0x3d0c, 0x5e86, 0x2f43, 0x57a1, + 0x2bd0, 0x55e8, 0x6af4, 0x757a, 0x3abd, 0x1d5e, 0x0eaf, 0x4757, 0x63ab, 0x71d5, + 0x38ea, 0x1c75, 0x0e3a, 0x071d, 0x038e, 0x01c7, 0x40e3, 0x6071, 0x3038, 0x581c, + 0x6c0e, 0x3607, 0x5b03, 0x6d81, 0x36c0, 0x5b60, 0x6db0, 0x76d8, 0x7b6c, 0x7db6, + 0x3edb, 0x5f6d, 0x2fb6, 0x17db, 0x4bed, 0x25f6, 0x12fb, 0x497d, 0x24be, 0x125f, + 0x492f, 0x6497, 0x724b, 0x7925, 0x3c92, 0x1e49, 0x0f24, 0x4792, 0x23c9, 0x11e4, + 0x48f2, 0x2479, 0x123c, 0x491e, 0x248f, 0x5247, 0x6923, 0x7491, 0x3a48, 0x5d24, + 0x6e92, 0x3749, 0x1ba4, 0x4dd2, 0x26e9, 0x1374, 0x49ba, 0x24dd, 0x126e, 0x0937, + 0x449b, 0x624d, 0x3126, 0x1893, 0x4c49, 0x2624, 0x5312, 0x2989, 0x14c4, 0x4a62, + 0x2531, 0x1298, 0x494c, 0x64a6, 0x3253, 0x5929, 0x2c94, 0x564a, 0x2b25, 0x1592, + 0x0ac9, 0x0564, 0x42b2, 0x2159, 0x10ac, 0x4856, 0x242b, 0x5215, 0x290a, 0x1485, + 0x0a42, 0x0521, 0x0290, 0x4148, 0x60a4, 0x7052, 0x3829, 0x1c14, 0x4e0a, 0x2705, + 0x1382, 0x09c1, 0x04e0, 0x4270, 0x6138, 0x709c, 0x784e, 0x3c27, 0x5e13, 0x6f09, + 0x3784, 0x5bc2, 0x2de1, 0x16f0, 0x4b78, 0x65bc, 0x72de, 0x396f, 0x5cb7, 0x6e5b, + 0x772d, 0x3b96, 0x1dcb, 0x4ee5, 0x2772, 0x13b9, 0x09dc, 0x44ee, 0x2277, 0x513b, + 0x689d, 0x344e, 0x1a27, 0x4d13, 0x6689, 0x3344, 0x59a2, 0x2cd1, 0x1668, 0x4b34, + 0x659a, 0x32cd, 0x1966, 0x0cb3, 0x4659, 0x232c, 0x5196, 0x28cb, 0x5465, 0x2a32, + 0x1519, 0x0a8c, 0x4546, 0x22a3, 0x5151, 0x28a8, 0x5454, 0x6a2a, 0x3515, 0x1a8a, + 0x0d45, 0x06a2, 0x0351, 0x01a8, 0x40d4, 0x606a, 0x3035, 0x181a, 0x0c0d, 0x0606, + 0x0303, 0x4181, 0x20c0, 0x5060, 0x6830, 0x7418, 0x7a0c, 0x7d06, 0x3e83, 0x5f41, + 0x2fa0, 0x57d0, 0x6be8, 0x75f4, 0x7afa, 0x3d7d, 0x1ebe, 0x0f5f, 0x47af, 0x63d7, + 0x71eb, 0x78f5, 0x3c7a, 0x1e3d, 0x0f1e, 0x078f, 0x43c7, 0x61e3, 0x70f1, 0x3878, + 0x5c3c, 0x6e1e, 0x370f, 0x5b87, 0x6dc3, 0x76e1, 0x3b70, 0x5db8, 0x6edc, 0x776e, + 0x3bb7, 0x5ddb, 0x6eed, 0x3776, 0x1bbb, 0x4ddd, 0x26ee, 0x1377, 0x49bb, 0x64dd, + 0x326e, 0x1937, 0x4c9b, 0x664d, 0x3326, 0x1993, 0x4cc9, 0x2664, 0x5332, 0x2999, + 0x14cc, 0x4a66, 0x2533, 0x5299, 0x294c, 0x54a6, 0x2a53, 0x5529, 0x2a94, 0x554a, + 0x2aa5, 0x1552, 0x0aa9, 0x0554, 0x42aa, 0x2155, 0x10aa, 0x0855, 0x042a, 0x0215, + 0x010a, 0x0085, 0x0042, 0x0021, 0x0010, 0x4008, 0x6004, 0x7002, 0x3801, 0x1c00, + 0x4e00, 0x6700, 0x7380, 0x79c0, 0x7ce0, 0x7e70, 0x7f38, 0x7f9c, 0x7fce, 0x3fe7, + 0x5ff3, 0x6ff9, 0x37fc, 0x5bfe, 0x2dff, 0x56ff, 0x6b7f, 0x75bf, 0x7adf, 0x7d6f, + 0x7eb7, 0x7f5b, 0x7fad, 0x3fd6, 0x1feb, 0x4ff5, 0x27fa, 0x13fd, 0x09fe, 0x04ff, + 0x427f, 0x613f, 0x709f, 0x784f, 0x7c27, 0x7e13, 0x7f09, 0x3f84, 0x5fc2, 0x2fe1, + 0x17f0, 0x4bf8, 0x65fc, 0x72fe, 0x397f, 0x5cbf, 0x6e5f, 0x772f, 0x7b97, 0x7dcb, + 0x7ee5, 0x3f72, 0x1fb9, 0x0fdc, 0x47ee, 0x23f7, 0x51fb, 0x68fd, 0x347e, 0x1a3f, + 0x4d1f, 0x668f, 0x7347, 0x79a3, 0x7cd1, 0x3e68, 0x5f34, 0x6f9a, 0x37cd, 0x1be6, + 0x0df3, 0x46f9, 0x237c, 0x51be, 0x28df, 0x546f, 0x6a37, 0x751b, 0x7a8d, 0x3d46, + 0x1ea3, 0x4f51, 0x27a8, 0x53d4, 0x69ea, 0x34f5, 0x1a7a, 0x0d3d, 0x069e, 0x034f, + 0x41a7, 0x60d3, 0x7069, 0x3834, 0x5c1a, 0x2e0d, 0x1706, 0x0b83, 0x45c1, 0x22e0, + 0x5170, 0x68b8, 0x745c, 0x7a2e, 0x3d17, 0x5e8b, 0x6f45, 0x37a2, 0x1bd1, 0x0de8, + 0x46f4, 0x637a, 0x31bd, 0x18de, 0x0c6f, 0x4637, 0x631b, 0x718d, 0x38c6, 0x1c63, + 0x4e31, 0x2718, 0x538c, 0x69c6, 0x34e3, 0x5a71, 0x2d38, 0x569c, 0x6b4e, 0x35a7, + 0x5ad3, 0x6d69, 0x36b4, 0x5b5a, 0x2dad, 0x16d6, 0x0b6b, 0x45b5, 0x22da, 0x116d, + 0x08b6, 0x045b, 0x422d, 0x2116, 0x108b, 0x4845, 0x2422, 0x1211, 0x0908, 0x4484, + 0x6242, 0x3121, 0x1890, 0x4c48, 0x6624, 0x7312, 0x3989, 0x1cc4, 0x4e62, 0x2731, + 0x1398, 0x49cc, 0x64e6, 0x3273, 0x5939, 0x2c9c, 0x564e, 0x2b27, 0x5593, 0x6ac9, + 0x3564, 0x5ab2, 0x2d59, 0x16ac, 0x4b56, 0x25ab, 0x52d5, 0x296a, 0x14b5, 0x0a5a, + 0x052d, 0x0296, 0x014b, 0x40a5, 0x2052, 0x1029, 0x0814, 0x440a, 0x2205, 0x1102, + 0x0881, 0x0440, 0x4220, 0x6110, 0x7088, 0x7844, 0x7c22, 0x3e11, 0x1f08, 0x4f84, + 0x67c2, 0x33e1, 0x19f0, 0x4cf8, 0x667c, 0x733e, 0x399f, 0x5ccf, 0x6e67, 0x7733, + 0x7b99, 0x3dcc, 0x5ee6, 0x2f73, 0x57b9, 0x2bdc, 0x55ee, 0x2af7, 0x557b, 0x6abd, + 0x355e, 0x1aaf, 0x4d57, 0x66ab, 0x7355, 0x39aa, 0x1cd5, 0x0e6a, 0x0735, 0x039a, + 0x01cd, 0x00e6, 0x0073, 0x4039, 0x201c, 0x500e, 0x2807, 0x5403, 0x6a01, 0x3500, + 0x5a80, 0x6d40, 0x76a0, 0x7b50, 0x7da8, 0x7ed4, 0x7f6a, 0x3fb5, 0x1fda, 0x0fed, + 0x07f6, 0x03fb, 0x41fd, 0x20fe, 0x107f, 0x483f, 0x641f, 0x720f, 0x7907, 0x7c83, + 0x7e41, 0x3f20, 0x5f90, 0x6fc8, 0x77e4, 0x7bf2, 0x3df9, 0x1efc, 0x4f7e, 0x27bf, + 0x53df, 0x69ef, 0x74f7, 0x7a7b, 0x7d3d, 0x3e9e, 0x1f4f, 0x4fa7, 0x67d3, 0x73e9, + 0x39f4, 0x5cfa, 0x2e7d, 0x173e, 0x0b9f, 0x45cf, 0x62e7, 0x7173, 0x78b9, 0x3c5c, + 0x5e2e, 0x2f17, 0x578b, 0x6bc5, 0x35e2, 0x1af1, 0x0d78, 0x46bc, 0x635e, 0x31af, + 0x58d7, 0x6c6b, 0x7635, 0x3b1a, 0x1d8d, 0x0ec6, 0x0763, 0x43b1, 0x21d8, 0x50ec, + 0x6876, 0x343b, 0x5a1d, 0x2d0e, 0x1687, 0x4b43, 0x65a1, 0x32d0, 0x5968, 0x6cb4, + 0x765a, 0x3b2d, 0x1d96, 0x0ecb, 0x4765, 0x23b2, 0x11d9, 0x08ec, 0x4476, 0x223b, + 0x511d, 0x288e, 0x1447, 0x4a23, 0x6511, 0x3288, 0x5944, 0x6ca2, 0x3651, 0x1b28, + 0x4d94, 0x66ca, 0x3365, 0x19b2, 0x0cd9, 0x066c, 0x4336, 0x219b, 0x50cd, 0x2866, + 0x1433, 0x4a19, 0x250c, 0x5286, 0x2943, 0x54a1, 0x2a50, 0x5528, 0x6a94, 0x754a, + 0x3aa5, 0x1d52, 0x0ea9, 0x0754, 0x43aa, 0x21d5, 0x10ea, 0x0875, 0x043a, 0x021d, + 0x010e, 0x0087, 0x4043, 0x6021, 0x3010, 0x5808, 0x6c04, 0x7602, 0x3b01, 0x1d80, + 0x4ec0, 0x6760, 0x73b0, 0x79d8, 0x7cec, 0x7e76, 0x3f3b, 0x5f9d, 0x2fce, 0x17e7, + 0x4bf3, 0x65f9, 0x32fc, 0x597e, 0x2cbf, 0x565f, 0x6b2f, 0x7597, 0x7acb, 0x7d65, + 0x3eb2, 0x1f59, 0x0fac, 0x47d6, 0x23eb, 0x51f5, 0x28fa, 0x147d, 0x0a3e, 0x051f, + 0x428f, 0x6147, 0x70a3, 0x7851, 0x3c28, 0x5e14, 0x6f0a, 0x3785, 0x1bc2, 0x0de1, + 0x06f0, 0x4378, 0x61bc, 0x70de, 0x386f, 0x5c37, 0x6e1b, 0x770d, 0x3b86, 0x1dc3, + 0x4ee1, 0x2770, 0x53b8, 0x69dc, 0x74ee, 0x3a77, 0x5d3b, 0x6e9d, 0x374e, 0x1ba7, + 0x4dd3, 0x66e9, 0x3374, 0x59ba, 0x2cdd, 0x166e, 0x0b37, 0x459b, 0x62cd, 0x3166, + 0x18b3, 0x4c59, 0x262c, 0x5316, 0x298b, 0x54c5, 0x2a62, 0x1531, 0x0a98, 0x454c, + 0x62a6, 0x3153, 0x58a9, 0x2c54, 0x562a, 0x2b15, 0x158a, 0x0ac5, 0x0562, 0x02b1, + 0x0158, 0x40ac, 0x6056, 0x302b, 0x5815, 0x2c0a, 0x1605, 0x0b02, 0x0581, 0x02c0, + 0x4160, 0x60b0, 0x7058, 0x782c, 0x7c16, 0x3e0b, 0x5f05, 0x2f82, 0x17c1, 0x0be0, + 0x45f0, 0x62f8, 0x717c, 0x78be, 0x3c5f, 0x5e2f, 0x6f17, 0x778b, 0x7bc5, 0x3de2, + 0x1ef1, 0x0f78, 0x47bc, 0x63de, 0x31ef, 0x58f7, 0x6c7b, 0x763d, 0x3b1e, 0x1d8f, + 0x4ec7, 0x6763, 0x73b1, 0x39d8, 0x5cec, 0x6e76, 0x373b, 0x5b9d, 0x2dce, 0x16e7, + 0x4b73, 0x65b9, 0x32dc, 0x596e, 0x2cb7, 0x565b, 0x6b2d, 0x3596, 0x1acb, 0x4d65, + 0x26b2, 0x1359, 0x09ac, 0x44d6, 0x226b, 0x5135, 0x289a, 0x144d, 0x0a26, 0x0513, + 0x4289, 0x2144, 0x50a2, 0x2851, 0x1428, 0x4a14, 0x650a, 0x3285, 0x1942, 0x0ca1, + 0x0650, 0x4328, 0x6194, 0x70ca, 0x3865, 0x1c32, 0x0e19, 0x070c, 0x4386, 0x21c3, + 0x50e1, 0x2870, 0x5438, 0x6a1c, 0x750e, 0x3a87, 0x5d43, 0x6ea1, 0x3750, 0x5ba8, + 0x6dd4, 0x76ea, 0x3b75, 0x1dba, 0x0edd, 0x076e, 0x03b7, 0x41db, 0x60ed, 0x3076, + 0x183b, 0x4c1d, 0x260e, 0x1307, 0x4983, 0x64c1, 0x3260, 0x5930, 0x6c98, 0x764c, + 0x7b26, 0x3d93, 0x5ec9, 0x2f64, 0x57b2, 0x2bd9, 0x15ec, 0x4af6, 0x257b, 0x52bd, + 0x295e, 0x14af, 0x4a57, 0x652b, 0x7295, 0x394a, 0x1ca5, 0x0e52, 0x0729, 0x0394, + 0x41ca, 0x20e5, 0x1072, 0x0839, 0x041c, 0x420e, 0x2107, 0x5083, 0x6841, 0x3420, + 0x5a10, 0x6d08, 0x7684, 0x7b42, 0x3da1, 0x1ed0, 0x4f68, 0x67b4, 0x73da, 0x39ed, + 0x1cf6, 0x0e7b, 0x473d, 0x239e, 0x11cf, 0x48e7, 0x6473, 0x7239, 0x391c, 0x5c8e, + 0x2e47, 0x5723, 0x6b91, 0x35c8, 0x5ae4, 0x6d72, 0x36b9, 0x1b5c, 0x4dae, 0x26d7, + 0x536b, 0x69b5, 0x34da, 0x1a6d, 0x0d36, 0x069b, 0x434d, 0x21a6, 0x10d3, 0x4869, + 0x2434, 0x521a, 0x290d, 0x1486, 0x0a43, 0x4521, 0x2290, 0x5148, 0x68a4, 0x7452, + 0x3a29, 0x1d14, 0x4e8a, 0x2745, 0x13a2, 0x09d1, 0x04e8, 0x4274, 0x613a, 0x309d, + 0x184e, 0x0c27, 0x4613, 0x6309, 0x3184, 0x58c2, 0x2c61, 0x1630, 0x4b18, 0x658c, + 0x72c6, 0x3963, 0x5cb1, 0x2e58, 0x572c, 0x6b96, 0x35cb, 0x5ae5, 0x2d72, 0x16b9, + 0x0b5c, 0x45ae, 0x22d7, 0x516b, 0x68b5, 0x345a, 0x1a2d, 0x0d16, 0x068b, 0x4345, + 0x21a2, 0x10d1, 0x0868, 0x4434, 0x621a, 0x310d, 0x1886, 0x0c43, 0x4621, 0x2310, + 0x5188, 0x68c4, 0x7462, 0x3a31, 0x1d18, 0x4e8c, 0x6746, 0x33a3, 0x59d1, 0x2ce8, + 0x5674, 0x6b3a, 0x359d, 0x1ace, 0x0d67, 0x46b3, 0x6359, 0x31ac, 0x58d6, 0x2c6b, + 0x5635, 0x2b1a, 0x158d, 0x0ac6, 0x0563, 0x42b1, 0x2158, 0x50ac, 0x6856, 0x342b, + 0x5a15, 0x2d0a, 0x1685, 0x0b42, 0x05a1, 0x02d0, 0x4168, 0x60b4, 0x705a, 0x382d, + 0x1c16, 0x0e0b, 0x4705, 0x2382, 0x11c1, 0x08e0, 0x4470, 0x6238, 0x711c, 0x788e, + 0x3c47, 0x5e23, 0x6f11, 0x3788, 0x5bc4, 0x6de2, 0x36f1, 0x1b78, 0x4dbc, 0x66de, + 0x336f, 0x59b7, 0x6cdb, 0x766d, 0x3b36, 0x1d9b, 0x4ecd, 0x2766, 0x13b3, 0x49d9, + 0x24ec, 0x5276, 0x293b, 0x549d, 0x2a4e, 0x1527, 0x4a93, 0x6549, 0x32a4, 0x5952, + 0x2ca9, 0x1654, 0x4b2a, 0x2595, 0x12ca, 0x0965, 0x04b2, 0x0259, 0x012c, 0x4096, + 0x204b, 0x5025, 0x2812, 0x1409, 0x0a04, 0x4502, 0x2281, 0x1140, 0x48a0, 0x6450, + 0x7228, 0x7914, 0x7c8a, 0x3e45, 0x1f22, 0x0f91, 0x07c8, 0x43e4, 0x61f2, 0x30f9, + 0x187c, 0x4c3e, 0x261f, 0x530f, 0x6987, 0x74c3, 0x7a61, 0x3d30, 0x5e98, 0x6f4c, + 0x77a6, 0x3bd3, 0x5de9, 0x2ef4, 0x577a, 0x2bbd, 0x15de, 0x0aef, 0x4577, 0x62bb, + 0x715d, 0x38ae, 0x1c57, 0x4e2b, 0x6715, 0x338a, 0x19c5, 0x0ce2, 0x0671, 0x0338, + 0x419c, 0x60ce, 0x3067, 0x5833, 0x6c19, 0x360c, 0x5b06, 0x2d83, 0x56c1, 0x2b60, + 0x55b0, 0x6ad8, 0x756c, 0x7ab6, 0x3d5b, 0x5ead, 0x2f56, 0x17ab, 0x4bd5, 0x25ea, + 0x12f5, 0x097a, 0x04bd, 0x025e, 0x012f, 0x4097, 0x604b, 0x7025, 0x3812, 0x1c09, + 0x0e04, 0x4702, 0x2381, 0x11c0, 0x48e0, 0x6470, 0x7238, 0x791c, 0x7c8e, 0x3e47, + 0x5f23, 0x6f91, 0x37c8, 0x5be4, 0x6df2, 0x36f9, 0x1b7c, 0x4dbe, 0x26df, 0x536f, + 0x69b7, 0x74db, 0x7a6d, 0x3d36, 0x1e9b, 0x4f4d, 0x27a6, 0x13d3, 0x49e9, 0x24f4, + 0x527a, 0x293d, 0x149e, 0x0a4f, 0x4527, 0x6293, 0x7149, 0x38a4, 0x5c52, 0x2e29, + 0x1714, 0x4b8a, 0x25c5, 0x12e2, 0x0971, 0x04b8, 0x425c, 0x612e, 0x3097, 0x584b, + 0x6c25, 0x3612, 0x1b09, 0x0d84, 0x46c2, 0x2361, 0x11b0, 0x48d8, 0x646c, 0x7236, + 0x391b, 0x5c8d, 0x2e46, 0x1723, 0x4b91, 0x25c8, 0x52e4, 0x6972, 0x34b9, 0x1a5c, + 0x4d2e, 0x2697, 0x534b, 0x69a5, 0x34d2, 0x1a69, 0x0d34, 0x469a, 0x234d, 0x11a6, + 0x08d3, 0x4469, 0x2234, 0x511a, 0x288d, 0x1446, 0x0a23, 0x4511, 0x2288, 0x5144, + 0x68a2, 0x3451, 0x1a28, 0x4d14, 0x668a, 0x3345, 0x19a2, 0x0cd1, 0x0668, 0x4334, + 0x619a, 0x30cd, 0x1866, 0x0c33, 0x4619, 0x230c, 0x5186, 0x28c3, 0x5461, 0x2a30, + 0x5518, 0x6a8c, 0x7546, 0x3aa3, 0x5d51, 0x2ea8, 0x5754, 0x6baa, 0x35d5, 0x1aea, + 0x0d75, 0x06ba, 0x035d, 0x01ae, 0x00d7, 0x406b, 0x6035, 0x301a, 0x180d, 0x0c06, + 0x0603, 0x4301, 0x2180, 0x50c0, 0x6860, 0x7430, 0x7a18, 0x7d0c, 0x7e86, 0x3f43, + 0x5fa1, 0x2fd0, 0x57e8, 0x6bf4, 0x75fa, 0x3afd, 0x1d7e, 0x0ebf, 0x475f, 0x63af, + 0x71d7, 0x78eb, 0x7c75, 0x3e3a, 0x1f1d, 0x0f8e, 0x07c7, 0x43e3, 0x61f1, 0x30f8, + 0x587c, 0x6c3e, 0x361f, 0x5b0f, 0x6d87, 0x76c3, 0x7b61, 0x3db0, 0x5ed8, 0x6f6c, + 0x77b6, 0x3bdb, 0x5ded, 0x2ef6, 0x177b, 0x4bbd, 0x25de, 0x12ef, 0x4977, 0x64bb, + 0x725d, 0x392e, 0x1c97, 0x4e4b, 0x6725, 0x3392, 0x19c9, 0x0ce4, 0x4672, 0x2339, + 0x119c, 0x48ce, 0x2467, 0x5233, 0x6919, 0x348c, 0x5a46, 0x2d23, 0x5691, 0x2b48, + 0x55a4, 0x6ad2, 0x3569, 0x1ab4, 0x4d5a, 0x26ad, 0x1356, 0x09ab, 0x44d5, 0x226a, + 0x1135, 0x089a, 0x044d, 0x0226, 0x0113, 0x4089, 0x2044, 0x5022, 0x2811, 0x1408, + 0x4a04, 0x6502, 0x3281, 0x1940, 0x4ca0, 0x6650, 0x7328, 0x7994, 0x7cca, 0x3e65, + 0x1f32, 0x0f99, 0x07cc, 0x43e6, 0x21f3, 0x50f9, 0x287c, 0x543e, 0x2a1f, 0x550f, + 0x6a87, 0x7543, 0x7aa1, 0x3d50, 0x5ea8, 0x6f54, 0x77aa, 0x3bd5, 0x1dea, 0x0ef5, + 0x077a, 0x03bd, 0x01de, 0x00ef, 0x4077, 0x603b, 0x701d, 0x380e, 0x1c07, 0x4e03, + 0x6701, 0x3380, 0x59c0, 0x6ce0, 0x7670, 0x7b38, 0x7d9c, 0x7ece, 0x3f67, 0x5fb3, + 0x6fd9, 0x37ec, 0x5bf6, 0x2dfb, 0x56fd, 0x2b7e, 0x15bf, 0x4adf, 0x656f, 0x72b7, + 0x795b, 0x7cad, 0x3e56, 0x1f2b, 0x4f95, 0x27ca, 0x13e5, 0x09f2, 0x04f9, 0x027c, + 0x413e, 0x209f, 0x504f, 0x6827, 0x7413, 0x7a09, 0x3d04, 0x5e82, 0x2f41, 0x17a0, + 0x4bd0, 0x65e8, 0x72f4, 0x797a, 0x3cbd, 0x1e5e, 0x0f2f, 0x4797, 0x63cb, 0x71e5, + 0x38f2, 0x1c79, 0x0e3c, 0x471e, 0x238f, 0x51c7, 0x68e3, 0x7471, 0x3a38, 0x5d1c, + 0x6e8e, 0x3747, 0x5ba3, 0x6dd1, 0x36e8, 0x5b74, 0x6dba, 0x36dd, 0x1b6e, 0x0db7, + 0x46db, 0x636d, 0x31b6, 0x18db, 0x4c6d, 0x2636, 0x131b, 0x498d, 0x24c6, 0x1263, + 0x4931, 0x2498, 0x524c, 0x6926, 0x3493, 0x5a49, 0x2d24, 0x5692, 0x2b49, 0x15a4, + 0x4ad2, 0x2569, 0x12b4, 0x495a, 0x24ad, 0x1256, 0x092b, 0x4495, 0x224a, 0x1125, + 0x0892, 0x0449, 0x0224, 0x4112, 0x2089, 0x1044, 0x4822, 0x2411, 0x1208, 0x4904, + 0x6482, 0x3241, 0x1920, 0x4c90, 0x6648, 0x7324, 0x7992, 0x3cc9, 0x1e64, 0x4f32, + 0x2799, 0x13cc, 0x49e6, 0x24f3, 0x5279, 0x293c, 0x549e, 0x2a4f, 0x5527, 0x6a93, + 0x7549, 0x3aa4, 0x5d52, 0x2ea9, 0x1754, 0x4baa, 0x25d5, 0x12ea, 0x0975, 0x04ba, + 0x025d, 0x012e, 0x0097, 0x404b, 0x6025, 0x3012, 0x1809, 0x0c04, 0x4602, 0x2301, + 0x1180, 0x48c0, 0x6460, 0x7230, 0x7918, 0x7c8c, 0x7e46, 0x3f23, 0x5f91, 0x2fc8, + 0x57e4, 0x6bf2, 0x35f9, 0x1afc, 0x4d7e, 0x26bf, 0x535f, 0x69af, 0x74d7, 0x7a6b, + 0x7d35, 0x3e9a, 0x1f4d, 0x0fa6, 0x07d3, 0x43e9, 0x21f4, 0x50fa, 0x287d, 0x143e, + 0x0a1f, 0x450f, 0x6287, 0x7143, 0x78a1, 0x3c50, 0x5e28, 0x6f14, 0x778a, 0x3bc5, + 0x1de2, 0x0ef1, 0x0778, 0x43bc, 0x61de, 0x30ef, 0x5877, 0x6c3b, 0x761d, 0x3b0e, + 0x1d87, 0x4ec3, 0x6761, 0x33b0, 0x59d8, 0x6cec, 0x7676, 0x3b3b, 0x5d9d, 0x2ece, + 0x1767, 0x4bb3, 0x65d9, 0x32ec, 0x5976, 0x2cbb, 0x565d, 0x2b2e, 0x1597, 0x4acb, + 0x6565, 0x32b2, 0x1959, 0x0cac, 0x4656, 0x232b, 0x5195, 0x28ca, 0x1465, 0x0a32, + 0x0519, 0x028c, 0x4146, 0x20a3, 0x5051, 0x2828, 0x5414, 0x6a0a, 0x3505, 0x1a82, + 0x0d41, 0x06a0, 0x4350, 0x61a8, 0x70d4, 0x786a, 0x3c35, 0x1e1a, 0x0f0d, 0x0786, + 0x03c3, 0x41e1, 0x20f0, 0x5078, 0x683c, 0x741e, 0x3a0f, 0x5d07, 0x6e83, 0x7741, + 0x3ba0, 0x5dd0, 0x6ee8, 0x7774, 0x7bba, 0x3ddd, 0x1eee, 0x0f77, 0x47bb, 0x63dd, + 0x31ee, 0x18f7, 0x4c7b, 0x663d, 0x331e, 0x198f, 0x4cc7, 0x6663, 0x7331, 0x3998, + 0x5ccc, 0x6e66, 0x3733, 0x5b99, 0x2dcc, 0x56e6, 0x2b73, 0x55b9, 0x2adc, 0x556e, + 0x2ab7, 0x555b, 0x6aad, 0x3556, 0x1aab, 0x4d55, 0x26aa, 0x1355, 0x09aa, 0x04d5, + 0x026a, 0x0135, 0x009a, 0x004d, 0x0026, 0x0013, 0x4009, 0x2004, 0x5002, 0x2801, + 0x1400, 0x4a00, 0x6500, 0x7280, 0x7940, 0x7ca0, 0x7e50, 0x7f28, 0x7f94, 0x7fca, + 0x3fe5, 0x1ff2, 0x0ff9, 0x07fc, 0x43fe, 0x21ff, 0x50ff, 0x687f, 0x743f, 0x7a1f, + 0x7d0f, 0x7e87, 0x7f43, 0x7fa1, 0x3fd0, 0x5fe8, 0x6ff4, 0x77fa, 0x3bfd, 0x1dfe, + 0x0eff, 0x477f, 0x63bf, 0x71df, 0x78ef, 0x7c77, 0x7e3b, 0x7f1d, 0x3f8e, 0x1fc7, + 0x4fe3, 0x67f1, 0x33f8, 0x59fc, 0x6cfe, 0x367f, 0x5b3f, 0x6d9f, 0x76cf, 0x7b67, + 0x7db3, 0x7ed9, 0x3f6c, 0x5fb6, 0x2fdb, 0x57ed, 0x2bf6, 0x15fb, 0x4afd, 0x257e, + 0x12bf, 0x495f, 0x64af, 0x7257, 0x792b, 0x7c95, 0x3e4a, 0x1f25, 0x0f92, 0x07c9, + 0x03e4, 0x41f2, 0x20f9, 0x107c, 0x483e, 0x241f, 0x520f, 0x6907, 0x7483, 0x7a41, + 0x3d20, 0x5e90, 0x6f48, 0x77a4, 0x7bd2, 0x3de9, 0x1ef4, 0x4f7a, 0x27bd, 0x13de, + 0x09ef, 0x44f7, 0x627b, 0x713d, 0x389e, 0x1c4f, 0x4e27, 0x6713, 0x7389, 0x39c4, + 0x5ce2, 0x2e71, 0x1738, 0x4b9c, 0x65ce, 0x32e7, 0x5973, 0x6cb9, 0x365c, 0x5b2e, + 0x2d97, 0x56cb, 0x6b65, 0x35b2, 0x1ad9, 0x0d6c, 0x46b6, 0x235b, 0x51ad, 0x28d6, + 0x146b, 0x4a35, 0x251a, 0x128d, 0x0946, 0x04a3, 0x4251, 0x2128, 0x5094, 0x684a, + 0x3425, 0x1a12, 0x0d09, 0x0684, 0x4342, 0x21a1, 0x10d0, 0x4868, 0x6434, 0x721a, + 0x390d, 0x1c86, 0x0e43, 0x4721, 0x2390, 0x51c8, 0x68e4, 0x7472, 0x3a39, 0x1d1c, + 0x4e8e, 0x2747, 0x53a3, 0x69d1, 0x34e8, 0x5a74, 0x6d3a, 0x369d, 0x1b4e, 0x0da7, + 0x46d3, 0x6369, 0x31b4, 0x58da, 0x2c6d, 0x1636, 0x0b1b, 0x458d, 0x22c6, 0x1163, + 0x48b1, 0x2458, 0x522c, 0x6916, 0x348b, 0x5a45, 0x2d22, 0x1691, 0x0b48, 0x45a4, + 0x62d2, 0x3169, 0x18b4, 0x4c5a, 0x262d, 0x1316, 0x098b, 0x44c5, 0x2262, 0x1131, + 0x0898, 0x444c, 0x6226, 0x3113, 0x5889, 0x2c44, 0x5622, 0x2b11, 0x1588, 0x4ac4, + 0x6562, 0x32b1, 0x1958, 0x4cac, 0x6656, 0x332b, 0x5995, 0x2cca, 0x1665, 0x0b32, + 0x0599, 0x02cc, 0x4166, 0x20b3, 0x5059, 0x282c, 0x5416, 0x2a0b, 0x5505, 0x2a82, + 0x1541, 0x0aa0, 0x4550, 0x62a8, 0x7154, 0x78aa, 0x3c55, 0x1e2a, 0x0f15, 0x078a, + 0x03c5, 0x01e2, 0x00f1, 0x0078, 0x403c, 0x601e, 0x300f, 0x5807, 0x6c03, 0x7601, + 0x3b00, 0x5d80, 0x6ec0, 0x7760, 0x7bb0, 0x7dd8, 0x7eec, 0x7f76, 0x3fbb, 0x5fdd, + 0x2fee, 0x17f7, 0x4bfb, 0x65fd, 0x32fe, 0x197f, 0x4cbf, 0x665f, 0x732f, 0x7997, + 0x7ccb, 0x7e65, 0x3f32, 0x1f99, 0x0fcc, 0x47e6, 0x23f3, 0x51f9, 0x28fc, 0x547e, + 0x2a3f, 0x551f, 0x6a8f, 0x7547, 0x7aa3, 0x7d51, 0x3ea8, 0x5f54, 0x6faa, 0x37d5, + 0x1bea, 0x0df5, 0x06fa, 0x037d, 0x01be, 0x00df, 0x406f, 0x6037, 0x701b, 0x780d, + 0x3c06, 0x1e03, 0x4f01, 0x2780, 0x53c0, 0x69e0, 0x74f0, 0x7a78, 0x7d3c, 0x7e9e, + 0x3f4f, 0x5fa7, 0x6fd3, 0x77e9, 0x3bf4, 0x5dfa, 0x2efd, 0x177e, 0x0bbf, 0x45df, + 0x62ef, 0x7177, 0x78bb, 0x7c5d, 0x3e2e, 0x1f17, 0x4f8b, 0x67c5, 0x33e2, 0x19f1, + 0x0cf8, 0x467c, 0x633e, 0x319f, 0x58cf, 0x6c67, 0x7633, 0x7b19, 0x3d8c, 0x5ec6, + 0x2f63, 0x57b1, 0x2bd8, 0x55ec, 0x6af6, 0x357b, 0x5abd, 0x2d5e, 0x16af, 0x4b57, + 0x65ab, 0x72d5, 0x396a, 0x1cb5, 0x0e5a, 0x072d, 0x0396, 0x01cb, 0x40e5, 0x2072, + 0x1039, 0x081c, 0x440e, 0x2207, 0x5103, 0x6881, 0x3440, 0x5a20, 0x6d10, 0x7688, + 0x7b44, 0x7da2, 0x3ed1, 0x1f68, 0x4fb4, 0x67da, 0x33ed, 0x19f6, 0x0cfb, 0x467d, + 0x233e, 0x119f, 0x48cf, 0x6467, 0x7233, 0x7919, 0x3c8c, 0x5e46, 0x2f23, 0x5791, + 0x2bc8, 0x55e4, 0x6af2, 0x3579, 0x1abc, 0x4d5e, 0x26af, 0x5357, 0x69ab, 0x74d5, + 0x3a6a, 0x1d35, 0x0e9a, 0x074d, 0x03a6, 0x01d3, 0x40e9, 0x2074, 0x503a, 0x281d, + 0x140e, 0x0a07, 0x4503, 0x6281, 0x3140, 0x58a0, 0x6c50, 0x7628, 0x7b14, 0x7d8a, + 0x3ec5, 0x1f62, 0x0fb1, 0x07d8, 0x43ec, 0x61f6, 0x30fb, 0x587d, 0x2c3e, 0x161f, + 0x4b0f, 0x6587, 0x72c3, 0x7961, 0x3cb0, 0x5e58, 0x6f2c, 0x7796, 0x3bcb, 0x5de5, + 0x2ef2, 0x1779, 0x0bbc, 0x45de, 0x22ef, 0x5177, 0x68bb, 0x745d, 0x3a2e, 0x1d17, + 0x4e8b, 0x6745, 0x33a2, 0x19d1, 0x0ce8, 0x4674, 0x633a, 0x319d, 0x18ce, 0x0c67, + 0x4633, 0x6319, 0x318c, 0x58c6, 0x2c63, 0x5631, 0x2b18, 0x558c, 0x6ac6, 0x3563, + 0x5ab1, 0x2d58, 0x56ac, 0x6b56, 0x35ab, 0x5ad5, 0x2d6a, 0x16b5, 0x0b5a, 0x05ad, + 0x02d6, 0x016b, 0x40b5, 0x205a, 0x102d, 0x0816, 0x040b, 0x4205, 0x2102, 0x1081, + 0x0840, 0x4420, 0x6210, 0x7108, 0x7884, 0x7c42, 0x3e21, 0x1f10, 0x4f88, 0x67c4, + 0x73e2, 0x39f1, 0x1cf8, 0x4e7c, 0x673e, 0x339f, 0x59cf, 0x6ce7, 0x7673, 0x7b39, + 0x3d9c, 0x5ece, 0x2f67, 0x57b3, 0x6bd9, 0x35ec, 0x5af6, 0x2d7b, 0x56bd, 0x2b5e, + 0x15af, 0x4ad7, 0x656b, 0x72b5, 0x395a, 0x1cad, 0x0e56, 0x072b, 0x4395, 0x21ca, + 0x10e5, 0x0872, 0x0439, 0x021c, 0x410e, 0x2087, 0x5043, 0x6821, 0x3410, 0x5a08, + 0x6d04, 0x7682, 0x3b41, 0x1da0, 0x4ed0, 0x6768, 0x73b4, 0x79da, 0x3ced, 0x1e76, + 0x0f3b, 0x479d, 0x23ce, 0x11e7, 0x48f3, 0x6479, 0x323c, 0x591e, 0x2c8f, 0x5647, + 0x6b23, 0x7591, 0x3ac8, 0x5d64, 0x6eb2, 0x3759, 0x1bac, 0x4dd6, 0x26eb, 0x5375, + 0x29ba, 0x14dd, 0x0a6e, 0x0537, 0x429b, 0x614d, 0x30a6, 0x1853, 0x4c29, 0x2614, + 0x530a, 0x2985, 0x14c2, 0x0a61, 0x0530, 0x4298, 0x614c, 0x70a6, 0x3853, 0x5c29, + 0x2e14, 0x570a, 0x2b85, 0x15c2, 0x0ae1, 0x0570, 0x42b8, 0x615c, 0x70ae, 0x3857, + 0x5c2b, 0x6e15, 0x370a, 0x1b85, 0x0dc2, 0x06e1, 0x0370, 0x41b8, 0x60dc, 0x706e, + 0x3837, 0x5c1b, 0x6e0d, 0x3706, 0x1b83, 0x4dc1, 0x26e0, 0x5370, 0x69b8, 0x74dc, + 0x7a6e, 0x3d37, 0x5e9b, 0x6f4d, 0x37a6, 0x1bd3, 0x4de9, 0x26f4, 0x537a, 0x29bd, + 0x14de, 0x0a6f, 0x4537, 0x629b, 0x714d, 0x38a6, 0x1c53, 0x4e29, 0x2714, 0x538a, + 0x29c5, 0x14e2, 0x0a71, 0x0538, 0x429c, 0x614e, 0x30a7, 0x5853, 0x6c29, 0x3614, + 0x5b0a, 0x2d85, 0x16c2, 0x0b61, 0x05b0, 0x42d8, 0x616c, 0x70b6, 0x385b, 0x5c2d, + 0x2e16, 0x170b, 0x4b85, 0x25c2, 0x12e1, 0x0970, 0x44b8, 0x625c, 0x712e, 0x3897, + 0x5c4b, 0x6e25, 0x3712, 0x1b89, 0x0dc4, 0x46e2, 0x2371, 0x11b8, 0x48dc, 0x646e, + 0x3237, 0x591b, 0x6c8d, 0x3646, 0x1b23, 0x4d91, 0x26c8, 0x5364, 0x69b2, 0x34d9, + 0x1a6c, 0x4d36, 0x269b, 0x534d, 0x29a6, 0x14d3, 0x4a69, 0x2534, 0x529a, 0x294d, + 0x14a6, 0x0a53, 0x4529, 0x2294, 0x514a, 0x28a5, 0x1452, 0x0a29, 0x0514, 0x428a, + 0x2145, 0x10a2, 0x0851, 0x0428, 0x4214, 0x610a, 0x3085, 0x1842, 0x0c21, 0x0610, + 0x4308, 0x6184, 0x70c2, 0x3861, 0x1c30, 0x4e18, 0x670c, 0x7386, 0x39c3, 0x5ce1, + 0x2e70, 0x5738, 0x6b9c, 0x75ce, 0x3ae7, 0x5d73, 0x6eb9, 0x375c, 0x5bae, 0x2dd7, + 0x56eb, 0x6b75, 0x35ba, 0x1add, 0x0d6e, 0x06b7, 0x435b, 0x61ad, 0x30d6, 0x186b, + 0x4c35, 0x261a, 0x130d, 0x0986, 0x04c3, 0x4261, 0x2130, 0x5098, 0x684c, 0x7426, + 0x3a13, 0x5d09, 0x2e84, 0x5742, 0x2ba1, 0x15d0, 0x4ae8, 0x6574, 0x72ba, 0x395d, + 0x1cae, 0x0e57, 0x472b, 0x6395, 0x31ca, 0x18e5, 0x0c72, 0x0639, 0x031c, 0x418e, + 0x20c7, 0x5063, 0x6831, 0x3418, 0x5a0c, 0x6d06, 0x3683, 0x5b41, 0x2da0, 0x56d0, + 0x6b68, 0x75b4, 0x7ada, 0x3d6d, 0x1eb6, 0x0f5b, 0x47ad, 0x23d6, 0x11eb, 0x48f5, + 0x247a, 0x123d, 0x091e, 0x048f, 0x4247, 0x6123, 0x7091, 0x3848, 0x5c24, 0x6e12, + 0x3709, 0x1b84, 0x4dc2, 0x26e1, 0x1370, 0x49b8, 0x64dc, 0x726e, 0x3937, 0x5c9b, + 0x6e4d, 0x3726, 0x1b93, 0x4dc9, 0x26e4, 0x5372, 0x29b9, 0x14dc, 0x4a6e, 0x2537, + 0x529b, 0x694d, 0x34a6, 0x1a53, 0x4d29, 0x2694, 0x534a, 0x29a5, 0x14d2, 0x0a69, + 0x0534, 0x429a, 0x214d, 0x10a6, 0x0853, 0x4429, 0x2214, 0x510a, 0x2885, 0x1442, + 0x0a21, 0x0510, 0x4288, 0x6144, 0x70a2, 0x3851, 0x1c28, 0x4e14, 0x670a, 0x3385, + 0x19c2, 0x0ce1, 0x0670, 0x4338, 0x619c, 0x70ce, 0x3867, 0x5c33, 0x6e19, 0x370c, + 0x5b86, 0x2dc3, 0x56e1, 0x2b70, 0x55b8, 0x6adc, 0x756e, 0x3ab7, 0x5d5b, 0x6ead, + 0x3756, 0x1bab, 0x4dd5, 0x26ea, 0x1375, 0x09ba, 0x04dd, 0x026e, 0x0137, 0x409b, + 0x604d, 0x3026, 0x1813, 0x4c09, 0x2604, 0x5302, 0x2981, 0x14c0, 0x4a60, 0x6530, + 0x7298, 0x794c, 0x7ca6, 0x3e53, 0x5f29, 0x2f94, 0x57ca, 0x2be5, 0x15f2, 0x0af9, + 0x057c, 0x42be, 0x215f, 0x50af, 0x6857, 0x742b, 0x7a15, 0x3d0a, 0x1e85, 0x0f42, + 0x07a1, 0x03d0, 0x41e8, 0x60f4, 0x707a, 0x383d, 0x1c1e, 0x0e0f, 0x4707, 0x6383, + 0x71c1, 0x38e0, 0x5c70, 0x6e38, 0x771c, 0x7b8e, 0x3dc7, 0x5ee3, 0x6f71, 0x37b8, + 0x5bdc, 0x6dee, 0x36f7, 0x5b7b, 0x6dbd, 0x36de, 0x1b6f, 0x4db7, 0x66db, 0x736d, + 0x39b6, 0x1cdb, 0x4e6d, 0x2736, 0x139b, 0x49cd, 0x24e6, 0x1273, 0x4939, 0x249c, + 0x524e, 0x2927, 0x5493, 0x6a49, 0x3524, 0x5a92, 0x2d49, 0x16a4, 0x4b52, 0x25a9, + 0x12d4, 0x496a, 0x24b5, 0x125a, 0x092d, 0x0496, 0x024b, 0x4125, 0x2092, 0x1049, + 0x0824, 0x4412, 0x2209, 0x1104, 0x4882, 0x2441, 0x1220, 0x4910, 0x6488, 0x7244, + 0x7922, 0x3c91, 0x1e48, 0x4f24, 0x6792, 0x33c9, 0x19e4, 0x4cf2, 0x2679, 0x133c, + 0x499e, 0x24cf, 0x5267, 0x6933, 0x7499, 0x3a4c, 0x5d26, 0x2e93, 0x5749, 0x2ba4, + 0x55d2, 0x2ae9, 0x1574, 0x4aba, 0x255d, 0x12ae, 0x0957, 0x44ab, 0x6255, 0x312a, + 0x1895, 0x0c4a, 0x0625, 0x0312, 0x0189, 0x00c4, 0x4062, 0x2031, 0x1018, 0x480c, + 0x6406, 0x3203, 0x5901, 0x2c80, 0x5640, 0x6b20, 0x7590, 0x7ac8, 0x7d64, 0x7eb2, + 0x3f59, 0x1fac, 0x4fd6, 0x27eb, 0x53f5, 0x29fa, 0x14fd, 0x0a7e, 0x053f, 0x429f, + 0x614f, 0x70a7, 0x7853, 0x7c29, 0x3e14, 0x5f0a, 0x2f85, 0x17c2, 0x0be1, 0x05f0, + 0x42f8, 0x617c, 0x70be, 0x385f, 0x5c2f, 0x6e17, 0x770b, 0x7b85, 0x3dc2, 0x1ee1, + 0x0f70, 0x47b8, 0x63dc, 0x71ee, 0x38f7, 0x5c7b, 0x6e3d, 0x371e, 0x1b8f, 0x4dc7, + 0x66e3, 0x7371, 0x39b8, 0x5cdc, 0x6e6e, 0x3737, 0x5b9b, 0x6dcd, 0x36e6, 0x1b73, + 0x4db9, 0x26dc, 0x536e, 0x29b7, 0x54db, 0x6a6d, 0x3536, 0x1a9b, 0x4d4d, 0x26a6, + 0x1353, 0x49a9, 0x24d4, 0x526a, 0x2935, 0x149a, 0x0a4d, 0x0526, 0x0293, 0x4149, + 0x20a4, 0x5052, 0x2829, 0x1414, 0x4a0a, 0x2505, 0x1282, 0x0941, 0x04a0, 0x4250, + 0x6128, 0x7094, 0x784a, 0x3c25, 0x1e12, 0x0f09, 0x0784, 0x43c2, 0x21e1, 0x10f0, + 0x4878, 0x643c, 0x721e, 0x390f, 0x5c87, 0x6e43, 0x7721, 0x3b90, 0x5dc8, 0x6ee4, + 0x7772, 0x3bb9, 0x1ddc, 0x4eee, 0x2777, 0x53bb, 0x69dd, 0x34ee, 0x1a77, 0x4d3b, + 0x669d, 0x334e, 0x19a7, 0x4cd3, 0x6669, 0x3334, 0x599a, 0x2ccd, 0x1666, 0x0b33, + 0x4599, 0x22cc, 0x5166, 0x28b3, 0x5459, 0x2a2c, 0x5516, 0x2a8b, 0x5545, 0x2aa2, + 0x1551, 0x0aa8, 0x4554, 0x62aa, 0x3155, 0x18aa, 0x0c55, 0x062a, 0x0315, 0x018a, + 0x00c5, 0x0062, 0x0031, 0x0018, 0x400c, 0x6006, 0x3003, 0x5801, 0x2c00, 0x5600, + 0x6b00, 0x7580, 0x7ac0, 0x7d60, 0x7eb0, 0x7f58, 0x7fac, 0x7fd6, 0x3feb, 0x5ff5, + 0x2ffa, 0x17fd, 0x0bfe, 0x05ff, 0x42ff, 0x617f, 0x70bf, 0x785f, 0x7c2f, 0x7e17, + 0x7f0b, 0x7f85, 0x3fc2, 0x1fe1, 0x0ff0, 0x47f8, 0x63fc, 0x71fe, 0x38ff, 0x5c7f, + 0x6e3f, 0x771f, 0x7b8f, 0x7dc7, 0x7ee3, 0x7f71, 0x3fb8, 0x5fdc, 0x6fee, 0x37f7, + 0x5bfb, 0x6dfd, 0x36fe, 0x1b7f, 0x4dbf, 0x66df, 0x736f, 0x79b7, 0x7cdb, 0x7e6d, + 0x3f36, 0x1f9b, 0x4fcd, 0x27e6, 0x13f3, 0x49f9, 0x24fc, 0x527e, 0x293f, 0x549f, + 0x6a4f, 0x7527, 0x7a93, 0x7d49, 0x3ea4, 0x5f52, 0x2fa9, 0x17d4, 0x4bea, 0x25f5, + 0x12fa, 0x097d, 0x04be, 0x025f, 0x412f, 0x6097, 0x704b, 0x7825, 0x3c12, 0x1e09, + 0x0f04, 0x4782, 0x23c1, 0x11e0, 0x48f0, 0x6478, 0x723c, 0x791e, 0x3c8f, 0x5e47, + 0x6f23, 0x7791, 0x3bc8, 0x5de4, 0x6ef2, 0x3779, 0x1bbc, 0x4dde, 0x26ef, 0x5377, + 0x69bb, 0x74dd, 0x3a6e, 0x1d37, 0x4e9b, 0x674d, 0x33a6, 0x19d3, 0x4ce9, 0x2674, + 0x533a, 0x299d, 0x14ce, 0x0a67, 0x4533, 0x6299, 0x314c, 0x58a6, 0x2c53, 0x5629, + 0x2b14, 0x558a, 0x2ac5, 0x1562, 0x0ab1, 0x0558, 0x42ac, 0x6156, 0x30ab, 0x5855, + 0x2c2a, 0x1615, 0x0b0a, 0x0585, 0x02c2, 0x0161, 0x00b0, 0x4058, 0x602c, 0x7016, + 0x380b, 0x5c05, 0x2e02, 0x1701, 0x0b80, 0x45c0, 0x62e0, 0x7170, 0x78b8, 0x7c5c, + 0x7e2e, 0x3f17, 0x5f8b, 0x6fc5, 0x37e2, 0x1bf1, 0x0df8, 0x46fc, 0x637e, 0x31bf, + 0x58df, 0x6c6f, 0x7637, 0x7b1b, 0x7d8d, 0x3ec6, 0x1f63, 0x4fb1, 0x27d8, 0x53ec, + 0x69f6, 0x34fb, 0x5a7d, 0x2d3e, 0x169f, 0x4b4f, 0x65a7, 0x72d3, 0x7969, 0x3cb4, + 0x5e5a, 0x2f2d, 0x1796, 0x0bcb, 0x45e5, 0x22f2, 0x1179, 0x08bc, 0x445e, 0x222f, + 0x5117, 0x688b, 0x7445, 0x3a22, 0x1d11, 0x0e88, 0x4744, 0x63a2, 0x31d1, 0x18e8, + 0x4c74, 0x663a, 0x331d, 0x198e, 0x0cc7, 0x4663, 0x6331, 0x3198, 0x58cc, 0x6c66, + 0x3633, 0x5b19, 0x2d8c, 0x56c6, 0x2b63, 0x55b1, 0x2ad8, 0x556c, 0x6ab6, 0x355b, + 0x5aad, 0x2d56, 0x16ab, 0x4b55, 0x25aa, 0x12d5, 0x096a, 0x04b5, 0x025a, 0x012d, + 0x0096, 0x004b, 0x4025, 0x2012, 0x1009, 0x0804, 0x4402, 0x2201, 0x1100, 0x4880, + 0x6440, 0x7220, 0x7910, 0x7c88, 0x7e44, 0x7f22, 0x3f91, 0x1fc8, 0x4fe4, 0x67f2, + 0x33f9, 0x19fc, 0x4cfe, 0x267f, 0x533f, 0x699f, 0x74cf, 0x7a67, 0x7d33, 0x7e99, + 0x3f4c, 0x5fa6, 0x2fd3, 0x57e9, 0x2bf4, 0x55fa, 0x2afd, 0x157e, 0x0abf, 0x455f, + 0x62af, 0x7157, 0x78ab, 0x7c55, 0x3e2a, 0x1f15, 0x0f8a, 0x07c5, 0x03e2, 0x01f1, + 0x00f8, 0x407c, 0x603e, 0x301f, 0x580f, 0x6c07, 0x7603, 0x7b01, 0x3d80, 0x5ec0, + 0x6f60, 0x77b0, 0x7bd8, 0x7dec, 0x7ef6, 0x3f7b, 0x5fbd, 0x2fde, 0x17ef, 0x4bf7, + 0x65fb, 0x72fd, 0x397e, 0x1cbf, 0x4e5f, 0x672f, 0x7397, 0x79cb, 0x7ce5, 0x3e72, + 0x1f39, 0x0f9c, 0x47ce, 0x23e7, 0x51f3, 0x68f9, 0x347c, 0x5a3e, 0x2d1f, 0x568f, + 0x6b47, 0x75a3, 0x7ad1, 0x3d68, 0x5eb4, 0x6f5a, 0x37ad, 0x1bd6, 0x0deb, 0x46f5, + 0x237a, 0x11bd, 0x08de, 0x046f, 0x4237, 0x611b, 0x708d, 0x3846, 0x1c23, 0x4e11, + 0x2708, 0x5384, 0x69c2, 0x34e1, 0x1a70, 0x4d38, 0x669c, 0x734e, 0x39a7, 0x5cd3, + 0x6e69, 0x3734, 0x5b9a, 0x2dcd, 0x16e6, 0x0b73, 0x45b9, 0x22dc, 0x516e, 0x28b7, + 0x545b, 0x6a2d, 0x3516, 0x1a8b, 0x4d45, 0x26a2, 0x1351, 0x09a8, 0x44d4, 0x626a, + 0x3135, 0x189a, 0x0c4d, 0x0626, 0x0313, 0x4189, 0x20c4, 0x5062, 0x2831, 0x1418, + 0x4a0c, 0x6506, 0x3283, 0x5941, 0x2ca0, 0x5650, 0x6b28, 0x7594, 0x7aca, 0x3d65, + 0x1eb2, 0x0f59, 0x07ac, 0x43d6, 0x21eb, 0x50f5, 0x287a, 0x143d, 0x0a1e, 0x050f, + 0x4287, 0x6143, 0x70a1, 0x3850, 0x5c28, 0x6e14, 0x770a, 0x3b85, 0x1dc2, 0x0ee1, + 0x0770, 0x43b8, 0x61dc, 0x70ee, 0x3877, 0x5c3b, 0x6e1d, 0x370e, 0x1b87, 0x4dc3, + 0x66e1, 0x3370, 0x59b8, 0x6cdc, 0x766e, 0x3b37, 0x5d9b, 0x6ecd, 0x3766, 0x1bb3, + 0x4dd9, 0x26ec, 0x5376, 0x29bb, 0x54dd, 0x2a6e, 0x1537, 0x4a9b, 0x654d, 0x32a6, + 0x1953, 0x4ca9, 0x2654, 0x532a, 0x2995, 0x14ca, 0x0a65, 0x0532, 0x0299, 0x014c, + 0x40a6, 0x2053, 0x5029, 0x2814, 0x540a, 0x2a05, 0x1502, 0x0a81, 0x0540, 0x42a0, + 0x6150, 0x70a8, 0x7854, 0x7c2a, 0x3e15, 0x1f0a, 0x0f85, 0x07c2, 0x03e1, 0x01f0, + 0x40f8, 0x607c, 0x703e, 0x381f, 0x5c0f, 0x6e07, 0x7703, 0x7b81, 0x3dc0, 0x5ee0, + 0x6f70, 0x77b8, 0x7bdc, 0x7dee, 0x3ef7, 0x5f7b, 0x6fbd, 0x37de, 0x1bef, 0x4df7, + 0x66fb, 0x737d, 0x39be, 0x1cdf, 0x4e6f, 0x6737, 0x739b, 0x79cd, 0x3ce6, 0x1e73, + 0x4f39, 0x279c, 0x53ce, 0x29e7, 0x54f3, 0x6a79, 0x353c, 0x5a9e, 0x2d4f, 0x56a7, + 0x6b53, 0x75a9, 0x3ad4, 0x5d6a, 0x2eb5, 0x175a, 0x0bad, 0x05d6, 0x02eb, 0x4175, + 0x20ba, 0x105d, 0x082e, 0x0417, 0x420b, 0x6105, 0x3082, 0x1841, 0x0c20, 0x4610, + 0x6308, 0x7184, 0x78c2, 0x3c61, 0x1e30, 0x4f18, 0x678c, 0x73c6, 0x39e3, 0x5cf1, + 0x2e78, 0x573c, 0x6b9e, 0x35cf, 0x5ae7, 0x6d73, 0x76b9, 0x3b5c, 0x5dae, 0x2ed7, + 0x576b, 0x6bb5, 0x35da, 0x1aed, 0x0d76, 0x06bb, 0x435d, 0x21ae, 0x10d7, 0x486b, + 0x6435, 0x321a, 0x190d, 0x0c86, 0x0643, 0x4321, 0x2190, 0x50c8, 0x6864, 0x7432, + 0x3a19, 0x1d0c, 0x4e86, 0x2743, 0x53a1, 0x29d0, 0x54e8, 0x6a74, 0x753a, 0x3a9d, + 0x1d4e, 0x0ea7, 0x4753, 0x63a9, 0x31d4, 0x58ea, 0x2c75, 0x163a, 0x0b1d, 0x058e, + 0x02c7, 0x4163, 0x60b1, 0x3058, 0x582c, 0x6c16, 0x360b, 0x5b05, 0x2d82, 0x16c1, + 0x0b60, 0x45b0, 0x62d8, 0x716c, 0x78b6, 0x3c5b, 0x5e2d, 0x2f16, 0x178b, 0x4bc5, + 0x25e2, 0x12f1, 0x0978, 0x44bc, 0x625e, 0x312f, 0x5897, 0x6c4b, 0x7625, 0x3b12, + 0x1d89, 0x0ec4, 0x4762, 0x23b1, 0x11d8, 0x48ec, 0x6476, 0x323b, 0x591d, 0x2c8e, + 0x1647, 0x4b23, 0x6591, 0x32c8, 0x5964, 0x6cb2, 0x3659, 0x1b2c, 0x4d96, 0x26cb, + 0x5365, 0x29b2, 0x14d9, 0x0a6c, 0x4536, 0x229b, 0x514d, 0x28a6, 0x1453, 0x4a29, + 0x2514, 0x528a, 0x2945, 0x14a2, 0x0a51, 0x0528, 0x4294, 0x614a, 0x30a5, 0x1852, + 0x0c29, 0x0614, 0x430a, 0x2185, 0x10c2, 0x0861, 0x0430, 0x4218, 0x610c, 0x7086, + 0x3843, 0x5c21, 0x2e10, 0x5708, 0x6b84, 0x75c2, 0x3ae1, 0x1d70, 0x4eb8, 0x675c, + 0x73ae, 0x39d7, 0x5ceb, 0x6e75, 0x373a, 0x1b9d, 0x0dce, 0x06e7, 0x4373, 0x61b9, + 0x30dc, 0x586e, 0x2c37, 0x561b, 0x6b0d, 0x3586, 0x1ac3, 0x4d61, 0x26b0, 0x5358, + 0x69ac, 0x74d6, 0x3a6b, 0x5d35, 0x2e9a, 0x174d, 0x0ba6, 0x05d3, 0x42e9, 0x2174, + 0x50ba, 0x285d, 0x142e, 0x0a17, 0x450b, 0x6285, 0x3142, 0x18a1, 0x0c50, 0x4628, + 0x6314, 0x718a, 0x38c5, 0x1c62, 0x0e31, 0x0718, 0x438c, 0x61c6, 0x30e3, 0x5871, + 0x2c38, 0x561c, 0x6b0e, 0x3587, 0x5ac3, 0x6d61, 0x36b0, 0x5b58, 0x6dac, 0x76d6, + 0x3b6b, 0x5db5, 0x2eda, 0x176d, 0x0bb6, 0x05db, 0x42ed, 0x2176, 0x10bb, 0x485d, + 0x242e, 0x1217, 0x490b, 0x6485, 0x3242, 0x1921, 0x0c90, 0x4648, 0x6324, 0x7192, + 0x38c9, 0x1c64, 0x4e32, 0x2719, 0x138c, 0x49c6, 0x24e3, 0x5271, 0x2938, 0x549c, + 0x6a4e, 0x3527, 0x5a93, 0x6d49, 0x36a4, 0x5b52, 0x2da9, 0x16d4, 0x4b6a, 0x25b5, + 0x12da, 0x096d, 0x04b6, 0x025b, 0x412d, 0x2096, 0x104b, 0x4825, 0x2412, 0x1209, + 0x0904, 0x4482, 0x2241, 0x1120, 0x4890, 0x6448, 0x7224, 0x7912, 0x3c89, 0x1e44, + 0x4f22, 0x2791, 0x13c8, 0x49e4, 0x64f2, 0x3279, 0x193c, 0x4c9e, 0x264f, 0x5327, + 0x6993, 0x74c9, 0x3a64, 0x5d32, 0x2e99, 0x174c, 0x4ba6, 0x25d3, 0x52e9, 0x2974, + 0x54ba, 0x2a5d, 0x152e, 0x0a97, 0x454b, 0x62a5, 0x3152, 0x18a9, 0x0c54, 0x462a, + 0x2315, 0x118a, 0x08c5, 0x0462, 0x0231, 0x0118, 0x408c, 0x6046, 0x3023, 0x5811, + 0x2c08, 0x5604, 0x6b02, 0x3581, 0x1ac0, 0x4d60, 0x66b0, 0x7358, 0x79ac, 0x7cd6, + 0x3e6b, 0x5f35, 0x2f9a, 0x17cd, 0x0be6, 0x05f3, 0x42f9, 0x217c, 0x50be, 0x285f, + 0x542f, 0x6a17, 0x750b, 0x7a85, 0x3d42, 0x1ea1, 0x0f50, 0x47a8, 0x63d4, 0x71ea, + 0x38f5, 0x1c7a, 0x0e3d, 0x071e, 0x038f, 0x41c7, 0x60e3, 0x7071, 0x3838, 0x5c1c, + 0x6e0e, 0x3707, 0x5b83, 0x6dc1, 0x36e0, 0x5b70, 0x6db8, 0x76dc, 0x7b6e, 0x3db7, + 0x5edb, 0x6f6d, 0x37b6, 0x1bdb, 0x4ded, 0x26f6, 0x137b, 0x49bd, 0x24de, 0x126f, + 0x4937, 0x649b, 0x724d, 0x3926, 0x1c93, 0x4e49, 0x2724, 0x5392, 0x29c9, 0x14e4, + 0x4a72, 0x2539, 0x129c, 0x494e, 0x24a7, 0x5253, 0x6929, 0x3494, 0x5a4a, 0x2d25, + 0x1692, 0x0b49, 0x05a4, 0x42d2, 0x2169, 0x10b4, 0x485a, 0x242d, 0x1216, 0x090b, + 0x4485, 0x2242, 0x1121, 0x0890, 0x4448, 0x6224, 0x7112, 0x3889, 0x1c44, 0x4e22, + 0x2711, 0x1388, 0x49c4, 0x64e2, 0x3271, 0x1938, 0x4c9c, 0x664e, 0x3327, 0x5993, + 0x6cc9, 0x3664, 0x5b32, 0x2d99, 0x16cc, 0x4b66, 0x25b3, 0x52d9, 0x296c, 0x54b6, + 0x2a5b, 0x552d, 0x2a96, 0x154b, 0x4aa5, 0x2552, 0x12a9, 0x0954, 0x44aa, 0x2255, + 0x112a, 0x0895, 0x044a, 0x0225, 0x0112, 0x0089, 0x0044, 0x4022, 0x2011, 0x1008, + 0x4804, 0x6402, 0x3201, 0x1900, 0x4c80, 0x6640, 0x7320, 0x7990, 0x7cc8, 0x7e64, + 0x7f32, 0x3f99, 0x1fcc, 0x4fe6, 0x27f3, 0x53f9, 0x29fc, 0x54fe, 0x2a7f, 0x553f, + 0x6a9f, 0x754f, 0x7aa7, 0x7d53, 0x7ea9, 0x3f54, 0x5faa, 0x2fd5, 0x17ea, 0x0bf5, + 0x05fa, 0x02fd, 0x017e, 0x00bf, 0x405f, 0x602f, 0x7017, 0x780b, 0x7c05, 0x3e02, + 0x1f01, 0x0f80, 0x47c0, 0x63e0, 0x71f0, 0x78f8, 0x7c7c, 0x7e3e, 0x3f1f, 0x5f8f, + 0x6fc7, 0x77e3, 0x7bf1, 0x3df8, 0x5efc, 0x6f7e, 0x37bf, 0x5bdf, 0x6def, 0x76f7, + 0x7b7b, 0x7dbd, 0x3ede, 0x1f6f, 0x4fb7, 0x67db, 0x73ed, 0x39f6, 0x1cfb, 0x4e7d, + 0x273e, 0x139f, 0x49cf, 0x64e7, 0x7273, 0x7939, 0x3c9c, 0x5e4e, 0x2f27, 0x5793, + 0x6bc9, 0x35e4, 0x5af2, 0x2d79, 0x16bc, 0x4b5e, 0x25af, 0x52d7, 0x696b, 0x74b5, + 0x3a5a, 0x1d2d, 0x0e96, 0x074b, 0x43a5, 0x21d2, 0x10e9, 0x0874, 0x443a, 0x221d, + 0x110e, 0x0887, 0x4443, 0x6221, 0x3110, 0x5888, 0x6c44, 0x7622, 0x3b11, 0x1d88, + 0x4ec4, 0x6762, 0x33b1, 0x19d8, 0x4cec, 0x6676, 0x333b, 0x599d, 0x2cce, 0x1667, + 0x4b33, 0x6599, 0x32cc, 0x5966, 0x2cb3, 0x5659, 0x2b2c, 0x5596, 0x2acb, 0x5565, + 0x2ab2, 0x1559, 0x0aac, 0x4556, 0x22ab, 0x5155, 0x28aa, 0x1455, 0x0a2a, 0x0515, + 0x028a, 0x0145, 0x00a2, 0x0051, 0x0028, 0x4014, 0x600a, 0x3005, 0x1802, 0x0c01, + 0x0600, 0x4300, 0x6180, 0x70c0, 0x7860, 0x7c30, 0x7e18, 0x7f0c, 0x7f86, 0x3fc3, + 0x5fe1, 0x2ff0, 0x57f8, 0x6bfc, 0x75fe, 0x3aff, 0x5d7f, 0x6ebf, 0x775f, 0x7baf, + 0x7dd7, 0x7eeb, 0x7f75, 0x3fba, 0x1fdd, 0x0fee, 0x07f7, 0x43fb, 0x61fd, 0x30fe, + 0x187f, 0x4c3f, 0x661f, 0x730f, 0x7987, 0x7cc3, 0x7e61, 0x3f30, 0x5f98, 0x6fcc, + 0x77e6, 0x3bf3, 0x5df9, 0x2efc, 0x577e, 0x2bbf, 0x55df, 0x6aef, 0x7577, 0x7abb, + 0x7d5d, 0x3eae, 0x1f57, 0x4fab, 0x67d5, 0x33ea, 0x19f5, 0x0cfa, 0x067d, 0x033e, + 0x019f, 0x40cf, 0x6067, 0x7033, 0x7819, 0x3c0c, 0x5e06, 0x2f03, 0x5781, 0x2bc0, + 0x55e0, 0x6af0, 0x7578, 0x7abc, 0x7d5e, 0x3eaf, 0x5f57, 0x6fab, 0x77d5, 0x3bea, + 0x1df5, 0x0efa, 0x077d, 0x03be, 0x01df, 0x40ef, 0x6077, 0x703b, 0x781d, 0x3c0e, + 0x1e07, 0x4f03, 0x6781, 0x33c0, 0x59e0, 0x6cf0, 0x7678, 0x7b3c, 0x7d9e, 0x3ecf, + 0x5f67, 0x6fb3, 0x77d9, 0x3bec, 0x5df6, 0x2efb, 0x577d, 0x2bbe, 0x15df, 0x4aef, + 0x6577, 0x72bb, 0x795d, 0x3cae, 0x1e57, 0x4f2b, 0x6795, 0x33ca, 0x19e5, 0x0cf2, + 0x0679, 0x033c, 0x419e, 0x20cf, 0x5067, 0x6833, 0x7419, 0x3a0c, 0x5d06, 0x2e83, + 0x5741, 0x2ba0, 0x55d0, 0x6ae8, 0x7574, 0x7aba, 0x3d5d, 0x1eae, 0x0f57, 0x47ab, + 0x63d5, 0x31ea, 0x18f5, 0x0c7a, 0x063d, 0x031e, 0x018f, 0x40c7, 0x6063, 0x7031, + 0x3818, 0x5c0c, 0x6e06, 0x3703, 0x5b81, 0x2dc0, 0x56e0, 0x6b70, 0x75b8, 0x7adc, + 0x7d6e, 0x3eb7, 0x5f5b, 0x6fad, 0x37d6, 0x1beb, 0x4df5, 0x26fa, 0x137d, 0x09be, + 0x04df, 0x426f, 0x6137, 0x709b, 0x784d, 0x3c26, 0x1e13, 0x4f09, 0x2784, 0x53c2, + 0x29e1, 0x14f0, 0x4a78, 0x653c, 0x729e, 0x394f, 0x5ca7, 0x6e53, 0x7729, 0x3b94, + 0x5dca, 0x2ee5, 0x1772, 0x0bb9, 0x05dc, 0x42ee, 0x2177, 0x50bb, 0x685d, 0x342e, + 0x1a17, 0x4d0b, 0x6685, 0x3342, 0x19a1, 0x0cd0, 0x4668, 0x6334, 0x719a, 0x38cd, + 0x1c66, 0x0e33, 0x4719, 0x238c, 0x51c6, 0x28e3, 0x5471, 0x2a38, 0x551c, 0x6a8e, + 0x3547, 0x5aa3, 0x6d51, 0x36a8, 0x5b54, 0x6daa, 0x36d5, 0x1b6a, 0x0db5, 0x06da, + 0x036d, 0x01b6, 0x00db, 0x406d, 0x2036, 0x101b, 0x480d, 0x2406, 0x1203, 0x4901, + 0x2480, 0x5240, 0x6920, 0x7490, 0x7a48, 0x7d24, 0x7e92, 0x3f49, 0x1fa4, 0x4fd2, + 0x27e9, 0x13f4, 0x49fa, 0x24fd, 0x127e, 0x093f, 0x449f, 0x624f, 0x7127, 0x7893, + 0x7c49, 0x3e24, 0x5f12, 0x2f89, 0x17c4, 0x4be2, 0x25f1, 0x12f8, 0x497c, 0x64be, + 0x325f, 0x592f, 0x6c97, 0x764b, 0x7b25, 0x3d92, 0x1ec9, 0x0f64, 0x47b2, 0x23d9, + 0x11ec, 0x48f6, 0x247b, 0x523d, 0x291e, 0x148f, 0x4a47, 0x6523, 0x7291, 0x3948, + 0x5ca4, 0x6e52, 0x3729, 0x1b94, 0x4dca, 0x26e5, 0x1372, 0x09b9, 0x04dc, 0x426e, + 0x2137, 0x509b, 0x684d, 0x3426, 0x1a13, 0x4d09, 0x2684, 0x5342, 0x29a1, 0x14d0, + 0x4a68, 0x6534, 0x729a, 0x394d, 0x1ca6, 0x0e53, 0x4729, 0x2394, 0x51ca, 0x28e5, + 0x1472, 0x0a39, 0x051c, 0x428e, 0x2147, 0x50a3, 0x6851, 0x3428, 0x5a14, 0x6d0a, + 0x3685, 0x1b42, 0x0da1, 0x06d0, 0x4368, 0x61b4, 0x70da, 0x386d, 0x1c36, 0x0e1b, + 0x470d, 0x2386, 0x11c3, 0x48e1, 0x2470, 0x5238, 0x691c, 0x748e, 0x3a47, 0x5d23, + 0x6e91, 0x3748, 0x5ba4, 0x6dd2, 0x36e9, 0x1b74, 0x4dba, 0x26dd, 0x136e, 0x09b7, + 0x44db, 0x626d, 0x3136, 0x189b, 0x4c4d, 0x2626, 0x1313, 0x4989, 0x24c4, 0x5262, + 0x2931, 0x1498, 0x4a4c, 0x6526, 0x3293, 0x5949, 0x2ca4, 0x5652, 0x2b29, 0x1594, + 0x4aca, 0x2565, 0x12b2, 0x0959, 0x04ac, 0x4256, 0x212b, 0x5095, 0x284a, 0x1425, + 0x0a12, 0x0509, 0x0284, 0x4142, 0x20a1, 0x1050, 0x4828, 0x6414, 0x720a, 0x3905, + 0x1c82, 0x0e41, 0x0720, 0x4390, 0x61c8, 0x70e4, 0x7872, 0x3c39, 0x1e1c, 0x4f0e, + 0x2787, 0x53c3, 0x69e1, 0x34f0, 0x5a78, 0x6d3c, 0x769e, 0x3b4f, 0x5da7, 0x6ed3, + 0x7769, 0x3bb4, 0x5dda, 0x2eed, 0x1776, 0x0bbb, 0x45dd, 0x22ee, 0x1177, 0x48bb, + 0x645d, 0x322e, 0x1917, 0x4c8b, 0x6645, 0x3322, 0x1991, 0x0cc8, 0x4664, 0x6332, + 0x3199, 0x18cc, 0x4c66, 0x2633, 0x5319, 0x298c, 0x54c6, 0x2a63, 0x5531, 0x2a98, + 0x554c, 0x6aa6, 0x3553, 0x5aa9, 0x2d54, 0x56aa, 0x2b55, 0x15aa, 0x0ad5, 0x056a, + 0x02b5, 0x015a, 0x00ad, 0x0056, 0x002b, 0x4015, 0x200a, 0x1005, 0x0802, 0x0401, + 0x0200, 0x4100, 0x6080, 0x7040, 0x7820, 0x7c10, 0x7e08, 0x7f04, 0x7f82, 0x3fc1, + 0x1fe0, 0x4ff0, 0x67f8, 0x73fc, 0x79fe, 0x3cff, 0x5e7f, 0x6f3f, 0x779f, 0x7bcf, + 0x7de7, 0x7ef3, 0x7f79, 0x3fbc, 0x5fde, 0x2fef, 0x57f7, 0x6bfb, 0x75fd, 0x3afe, + 0x1d7f, 0x4ebf, 0x675f, 0x73af, 0x79d7, 0x7ceb, 0x7e75, 0x3f3a, 0x1f9d, 0x0fce, + 0x07e7, 0x43f3, 0x61f9, 0x30fc, 0x587e, 0x2c3f, 0x561f, 0x6b0f, 0x7587, 0x7ac3, + 0x7d61, 0x3eb0, 0x5f58, 0x6fac, 0x77d6, 0x3beb, 0x5df5, 0x2efa, 0x177d, 0x0bbe, + 0x05df, 0x42ef, 0x6177, 0x70bb, 0x785d, 0x3c2e, 0x1e17, 0x4f0b, 0x6785, 0x33c2, + 0x19e1, 0x0cf0, 0x4678, 0x633c, 0x719e, 0x38cf, 0x5c67, 0x6e33, 0x7719, 0x3b8c, + 0x5dc6, 0x2ee3, 0x5771, 0x2bb8, 0x55dc, 0x6aee, 0x3577, 0x5abb, 0x6d5d, 0x36ae, + 0x1b57, 0x4dab, 0x66d5, 0x336a, 0x19b5, 0x0cda, 0x066d, 0x0336, 0x019b, 0x40cd, + 0x2066, 0x1033, 0x4819, 0x240c, 0x5206, 0x2903, 0x5481, 0x2a40, 0x5520, 0x6a90, + 0x7548, 0x7aa4, 0x7d52, 0x3ea9, 0x1f54, 0x4faa, 0x27d5, 0x13ea, 0x09f5, 0x04fa, + 0x027d, 0x013e, 0x009f, 0x404f, 0x6027, 0x7013, 0x7809, 0x3c04, 0x5e02, 0x2f01, + 0x1780, 0x4bc0, 0x65e0, 0x72f0, 0x7978, 0x7cbc, 0x7e5e, 0x3f2f, 0x5f97, 0x6fcb, + 0x77e5, 0x3bf2, 0x1df9, 0x0efc, 0x477e, 0x23bf, 0x51df, 0x68ef, 0x7477, 0x7a3b, + 0x7d1d, 0x3e8e, 0x1f47, 0x4fa3, 0x67d1, 0x33e8, 0x59f4, 0x6cfa, 0x367d, 0x1b3e, + 0x0d9f, 0x46cf, 0x6367, 0x71b3, 0x78d9, 0x3c6c, 0x5e36, 0x2f1b, 0x578d, 0x2bc6, + 0x15e3, 0x4af1, 0x2578, 0x52bc, 0x695e, 0x34af, 0x5a57, 0x6d2b, 0x7695, 0x3b4a, + 0x1da5, 0x0ed2, 0x0769, 0x03b4, 0x41da, 0x20ed, 0x1076, 0x083b, 0x441d, 0x220e, + 0x1107, 0x4883, 0x6441, 0x3220, 0x5910, 0x6c88, 0x7644, 0x7b22, 0x3d91, 0x1ec8, + 0x4f64, 0x67b2, 0x33d9, 0x19ec, 0x4cf6, 0x267b, 0x533d, 0x299e, 0x14cf, 0x4a67, + 0x6533, 0x7299, 0x394c, 0x5ca6, 0x2e53, 0x5729, 0x2b94, 0x55ca, 0x2ae5, 0x1572, + 0x0ab9, 0x055c, 0x42ae, 0x2157, 0x50ab, 0x6855, 0x342a, 0x1a15, 0x0d0a, 0x0685, + 0x0342, 0x01a1, 0x00d0, 0x4068, 0x6034, 0x701a, 0x380d, 0x1c06, 0x0e03, 0x4701, + 0x2380, 0x51c0, 0x68e0, 0x7470, 0x7a38, 0x7d1c, 0x7e8e, 0x3f47, 0x5fa3, 0x6fd1, + 0x37e8, 0x5bf4, 0x6dfa, 0x36fd, 0x1b7e, 0x0dbf, 0x46df, 0x636f, 0x71b7, 0x78db, + 0x7c6d, 0x3e36, 0x1f1b, 0x4f8d, 0x27c6, 0x13e3, 0x49f1, 0x24f8, 0x527c, 0x693e, + 0x349f, 0x5a4f, 0x6d27, 0x7693, 0x7b49, 0x3da4, 0x5ed2, 0x2f69, 0x17b4, 0x4bda, + 0x25ed, 0x12f6, 0x097b, 0x44bd, 0x225e, 0x112f, 0x4897, 0x644b, 0x7225, 0x3912, + 0x1c89, 0x0e44, 0x4722, 0x2391, 0x11c8, 0x48e4, 0x6472, 0x3239, 0x191c, 0x4c8e, + 0x2647, 0x5323, 0x6991, 0x34c8, 0x5a64, 0x6d32, 0x3699, 0x1b4c, 0x4da6, 0x26d3, + 0x5369, 0x29b4, 0x54da, 0x2a6d, 0x1536, 0x0a9b, 0x454d, 0x22a6, 0x1153, 0x48a9, + 0x2454, 0x522a, 0x2915, 0x148a, 0x0a45, 0x0522, 0x0291, 0x0148, 0x40a4, 0x6052, + 0x3029, 0x1814, 0x4c0a, 0x2605, 0x1302, 0x0981, 0x04c0, 0x4260, 0x6130, 0x7098, + 0x784c, 0x7c26, 0x3e13, 0x5f09, 0x2f84, 0x57c2, 0x2be1, 0x15f0, 0x4af8, 0x657c, + 0x72be, 0x395f, 0x5caf, 0x6e57, 0x772b, 0x7b95, 0x3dca, 0x1ee5, 0x0f72, 0x07b9, + 0x03dc, 0x41ee, 0x20f7, 0x507b, 0x683d, 0x341e, 0x1a0f, 0x4d07, 0x6683, 0x7341, + 0x39a0, 0x5cd0, 0x6e68, 0x7734, 0x7b9a, 0x3dcd, 0x1ee6, 0x0f73, 0x47b9, 0x23dc, + 0x51ee, 0x28f7, 0x547b, 0x6a3d, 0x351e, 0x1a8f, 0x4d47, 0x66a3, 0x7351, 0x39a8, + 0x5cd4, 0x6e6a, 0x3735, 0x1b9a, 0x0dcd, 0x06e6, 0x0373, 0x41b9, 0x20dc, 0x506e, + 0x2837, 0x541b, 0x6a0d, 0x3506, 0x1a83, 0x4d41, 0x26a0, 0x5350, 0x69a8, 0x74d4, + 0x7a6a, 0x3d35, 0x1e9a, 0x0f4d, 0x07a6, 0x03d3, 0x41e9, 0x20f4, 0x507a, 0x283d, + 0x141e, 0x0a0f, 0x4507, 0x6283, 0x7141, 0x38a0, 0x5c50, 0x6e28, 0x7714, 0x7b8a, + 0x3dc5, 0x1ee2, 0x0f71, 0x07b8, 0x43dc, 0x61ee, 0x30f7, 0x587b, 0x6c3d, 0x361e, + 0x1b0f, 0x4d87, 0x66c3, 0x7361, 0x39b0, 0x5cd8, 0x6e6c, 0x7736, 0x3b9b, 0x5dcd, + 0x2ee6, 0x1773, 0x4bb9, 0x25dc, 0x52ee, 0x2977, 0x54bb, 0x6a5d, 0x352e, 0x1a97, + 0x4d4b, 0x66a5, 0x3352, 0x19a9, 0x0cd4, 0x466a, 0x2335, 0x119a, 0x08cd, 0x0466, + 0x0233, 0x4119, 0x208c, 0x5046, 0x2823, 0x5411, 0x2a08, 0x5504, 0x6a82, 0x3541, + 0x1aa0, 0x4d50, 0x66a8, 0x7354, 0x79aa, 0x3cd5, 0x1e6a, 0x0f35, 0x079a, 0x03cd, + 0x01e6, 0x00f3, 0x4079, 0x203c, 0x501e, 0x280f, 0x5407, 0x6a03, 0x7501, 0x3a80, + 0x5d40, 0x6ea0, 0x7750, 0x7ba8, 0x7dd4, 0x7eea, 0x3f75, 0x1fba, 0x0fdd, 0x07ee, + 0x03f7, 0x41fb, 0x60fd, 0x307e, 0x183f, 0x4c1f, 0x660f, 0x7307, 0x7983, 0x7cc1, + 0x3e60, 0x5f30, 0x6f98, 0x77cc, 0x7be6, 0x3df3, 0x5ef9, 0x2f7c, 0x57be, 0x2bdf, + 0x55ef, 0x6af7, 0x757b, 0x7abd, 0x3d5e, 0x1eaf, 0x4f57, 0x67ab, 0x73d5, 0x39ea, + 0x1cf5, 0x0e7a, 0x073d, 0x039e, 0x01cf, 0x40e7, 0x6073, 0x7039, 0x381c, 0x5c0e, + 0x2e07, 0x5703, 0x6b81, 0x35c0, 0x5ae0, 0x6d70, 0x76b8, 0x7b5c, 0x7dae, 0x3ed7, + 0x5f6b, 0x6fb5, 0x37da, 0x1bed, 0x0df6, 0x06fb, 0x437d, 0x21be, 0x10df, 0x486f, + 0x6437, 0x721b, 0x790d, 0x3c86, 0x1e43, 0x4f21, 0x2790, 0x53c8, 0x69e4, 0x74f2, + 0x3a79, 0x1d3c, 0x4e9e, 0x274f, 0x53a7, 0x69d3, 0x74e9, 0x3a74, 0x5d3a, 0x2e9d, + 0x174e, 0x0ba7, 0x45d3, 0x62e9, 0x3174, 0x58ba, 0x2c5d, 0x162e, 0x0b17, 0x458b, + 0x62c5, 0x3162, 0x18b1, 0x0c58, 0x462c, 0x6316, 0x318b, 0x58c5, 0x2c62, 0x1631, + 0x0b18, 0x458c, 0x62c6, 0x3163, 0x58b1, 0x2c58, 0x562c, 0x6b16, 0x358b, 0x5ac5, + 0x2d62, 0x16b1, 0x0b58, 0x45ac, 0x62d6, 0x316b, 0x58b5, 0x2c5a, 0x162d, 0x0b16, + 0x058b, 0x42c5, 0x2162, 0x10b1, 0x0858, 0x442c, 0x6216, 0x310b, 0x5885, 0x2c42, + 0x1621, 0x0b10, 0x4588, 0x62c4, 0x7162, 0x38b1, 0x1c58, 0x4e2c, 0x6716, 0x338b, + 0x59c5, 0x2ce2, 0x1671, 0x0b38, 0x459c, 0x62ce, 0x3167, 0x58b3, 0x6c59, 0x362c, + 0x5b16, 0x2d8b, 0x56c5, 0x2b62, 0x15b1, 0x0ad8, 0x456c, 0x62b6, 0x315b, 0x58ad, + 0x2c56, 0x162b, 0x4b15, 0x258a, 0x12c5, 0x0962, 0x04b1, 0x0258, 0x412c, 0x6096, + 0x304b, 0x5825, 0x2c12, 0x1609, 0x0b04, 0x4582, 0x22c1, 0x1160, 0x48b0, 0x6458, + 0x722c, 0x7916, 0x3c8b, 0x5e45, 0x2f22, 0x1791, 0x0bc8, 0x45e4, 0x62f2, 0x3179, + 0x18bc, 0x4c5e, 0x262f, 0x5317, 0x698b, 0x74c5, 0x3a62, 0x1d31, 0x0e98, 0x474c, + 0x63a6, 0x31d3, 0x58e9, 0x2c74, 0x563a, 0x2b1d, 0x158e, 0x0ac7, 0x4563, 0x62b1, + 0x3158, 0x58ac, 0x6c56, 0x362b, 0x5b15, 0x2d8a, 0x16c5, 0x0b62, 0x05b1, 0x02d8, + 0x416c, 0x60b6, 0x305b, 0x582d, 0x2c16, 0x160b, 0x4b05, 0x2582, 0x12c1, 0x0960, + 0x44b0, 0x6258, 0x712c, 0x7896, 0x3c4b, 0x5e25, 0x2f12, 0x1789, 0x0bc4, 0x45e2, + 0x22f1, 0x1178, 0x48bc, 0x645e, 0x322f, 0x5917, 0x6c8b, 0x7645, 0x3b22, 0x1d91, + 0x0ec8, 0x4764, 0x63b2, 0x31d9, 0x18ec, 0x4c76, 0x263b, 0x531d, 0x298e, 0x14c7, + 0x4a63, 0x6531, 0x3298, 0x594c, 0x6ca6, 0x3653, 0x5b29, 0x2d94, 0x56ca, 0x2b65, + 0x15b2, 0x0ad9, 0x056c, 0x42b6, 0x215b, 0x50ad, 0x2856, 0x142b, 0x4a15, 0x250a, + 0x1285, 0x0942, 0x04a1, 0x0250, 0x4128, 0x6094, 0x704a, 0x3825, 0x1c12, 0x0e09, + 0x0704, 0x4382, 0x21c1, 0x10e0, 0x4870, 0x6438, 0x721c, 0x790e, 0x3c87, 0x5e43, + 0x6f21, 0x3790, 0x5bc8, 0x6de4, 0x76f2, 0x3b79, 0x1dbc, 0x4ede, 0x276f, 0x53b7, + 0x69db, 0x74ed, 0x3a76, 0x1d3b, 0x4e9d, 0x274e, 0x13a7, 0x49d3, 0x64e9, 0x3274, + 0x593a, 0x2c9d, 0x164e, 0x0b27, 0x4593, 0x62c9, 0x3164, 0x58b2, 0x2c59, 0x162c, + 0x4b16, 0x258b, 0x52c5, 0x2962, 0x14b1, 0x0a58, 0x452c, 0x6296, 0x314b, 0x58a5, + 0x2c52, 0x1629, 0x0b14, 0x458a, 0x22c5, 0x1162, 0x08b1, 0x0458, 0x422c, 0x6116, + 0x308b, 0x5845, 0x2c22, 0x1611, 0x0b08, 0x4584, 0x62c2, 0x3161, 0x18b0, 0x4c58, + 0x662c, 0x7316, 0x398b, 0x5cc5, 0x2e62, 0x1731, 0x0b98, 0x45cc, 0x62e6, 0x3173, + 0x58b9, 0x2c5c, 0x562e, 0x2b17, 0x558b, 0x6ac5, 0x3562, 0x1ab1, 0x0d58, 0x46ac, + 0x6356, 0x31ab, 0x58d5, 0x2c6a, 0x1635, 0x0b1a, 0x058d, 0x02c6, 0x0163, 0x40b1, + 0x2058, 0x502c, 0x6816, 0x340b, 0x5a05, 0x2d02, 0x1681, 0x0b40, 0x45a0, 0x62d0, + 0x7168, 0x78b4, 0x7c5a, 0x3e2d, 0x1f16, 0x0f8b, 0x47c5, 0x23e2, 0x11f1, 0x08f8, + 0x447c, 0x623e, 0x311f, 0x588f, 0x6c47, 0x7623, 0x7b11, 0x3d88, 0x5ec4, 0x6f62, + 0x37b1, 0x1bd8, 0x4dec, 0x66f6, 0x337b, 0x59bd, 0x2cde, 0x166f, 0x4b37, 0x659b, + 0x72cd, 0x3966, 0x1cb3, 0x4e59, 0x272c, 0x5396, 0x29cb, 0x54e5, 0x2a72, 0x1539, + 0x0a9c, 0x454e, 0x22a7, 0x5153, 0x68a9, 0x3454, 0x5a2a, 0x2d15, 0x168a, 0x0b45, + 0x05a2, 0x02d1, 0x0168, 0x40b4, 0x605a, 0x302d, 0x1816, 0x0c0b, 0x4605, 0x2302, + 0x1181, 0x08c0, 0x4460, 0x6230, 0x7118, 0x788c, 0x7c46, 0x3e23, 0x5f11, 0x2f88, + 0x57c4, 0x6be2, 0x35f1, 0x1af8, 0x4d7c, 0x66be, 0x335f, 0x59af, 0x6cd7, 0x766b, + 0x7b35, 0x3d9a, 0x1ecd, 0x0f66, 0x07b3, 0x43d9, 0x21ec, 0x50f6, 0x287b, 0x543d, + 0x2a1e, 0x150f, 0x4a87, 0x6543, 0x72a1, 0x3950, 0x5ca8, 0x6e54, 0x772a, 0x3b95, + 0x1dca, 0x0ee5, 0x0772, 0x03b9, 0x01dc, 0x40ee, 0x2077, 0x503b, 0x681d, 0x340e, + 0x1a07, 0x4d03, 0x6681, 0x3340, 0x59a0, 0x6cd0, 0x7668, 0x7b34, 0x7d9a, 0x3ecd, + 0x1f66, 0x0fb3, 0x47d9, 0x23ec, 0x51f6, 0x28fb, 0x547d, 0x2a3e, 0x151f, 0x4a8f, + 0x6547, 0x72a3, 0x7951, 0x3ca8, 0x5e54, 0x6f2a, 0x3795, 0x1bca, 0x0de5, 0x06f2, + 0x0379, 0x01bc, 0x40de, 0x206f, 0x5037, 0x681b, 0x740d, 0x3a06, 0x1d03, 0x4e81, + 0x2740, 0x53a0, 0x69d0, 0x74e8, 0x7a74, 0x7d3a, 0x3e9d, 0x1f4e, 0x0fa7, 0x47d3, + 0x63e9, 0x31f4, 0x58fa, 0x2c7d, 0x163e, 0x0b1f, 0x458f, 0x62c7, 0x7163, 0x78b1, + 0x3c58, 0x5e2c, 0x6f16, 0x378b, 0x5bc5, 0x2de2, 0x16f1, 0x0b78, 0x45bc, 0x62de, + 0x316f, 0x58b7, 0x6c5b, 0x762d, 0x3b16, 0x1d8b, 0x4ec5, 0x2762, 0x13b1, 0x09d8, + 0x44ec, 0x6276, 0x313b, 0x589d, 0x2c4e, 0x1627, 0x4b13, 0x6589, 0x32c4, 0x5962, + 0x2cb1, 0x1658, 0x4b2c, 0x6596, 0x32cb, 0x5965, 0x2cb2, 0x1659, 0x0b2c, 0x4596, + 0x22cb, 0x5165, 0x28b2, 0x1459, 0x0a2c, 0x4516, 0x228b, 0x5145, 0x28a2, 0x1451, + 0x0a28, 0x4514, 0x628a, 0x3145, 0x18a2, 0x0c51, 0x0628, 0x4314, 0x618a, 0x30c5, + 0x1862, 0x0c31, 0x0618, 0x430c, 0x6186, 0x30c3, 0x5861, 0x2c30, 0x5618, 0x6b0c, + 0x7586, 0x3ac3, 0x5d61, 0x2eb0, 0x5758, 0x6bac, 0x75d6, 0x3aeb, 0x5d75, 0x2eba, + 0x175d, 0x0bae, 0x05d7, 0x42eb, 0x6175, 0x30ba, 0x185d, 0x0c2e, 0x0617, 0x430b, + 0x6185, 0x30c2, 0x1861, 0x0c30, 0x4618, 0x630c, 0x7186, 0x38c3, 0x5c61, 0x2e30, + 0x5718, 0x6b8c, 0x75c6, 0x3ae3, 0x5d71, 0x2eb8, 0x575c, 0x6bae, 0x35d7, 0x5aeb, + 0x6d75, 0x36ba, 0x1b5d, 0x0dae, 0x06d7, 0x436b, 0x61b5, 0x30da, 0x186d, 0x0c36, + 0x061b, 0x430d, 0x2186, 0x10c3, 0x4861, 0x2430, 0x5218, 0x690c, 0x7486, 0x3a43, + 0x5d21, 0x2e90, 0x5748, 0x6ba4, 0x75d2, 0x3ae9, 0x1d74, 0x4eba, 0x275d, 0x13ae, + 0x09d7, 0x44eb, 0x6275, 0x313a, 0x189d, 0x0c4e, 0x0627, 0x4313, 0x6189, 0x30c4, + 0x5862, 0x2c31, 0x1618, 0x4b0c, 0x6586, 0x32c3, 0x5961, 0x2cb0, 0x5658, 0x6b2c, + 0x7596, 0x3acb, 0x5d65, 0x2eb2, 0x1759, 0x0bac, 0x45d6, 0x22eb, 0x5175, 0x28ba, + 0x145d, 0x0a2e, 0x0517, 0x428b, 0x6145, 0x30a2, 0x1851, 0x0c28, 0x4614, 0x630a, + 0x3185, 0x18c2, 0x0c61, 0x0630, 0x4318, 0x618c, 0x70c6, 0x3863, 0x5c31, 0x2e18, + 0x570c, 0x6b86, 0x35c3, 0x5ae1, 0x2d70, 0x56b8, 0x6b5c, 0x75ae, 0x3ad7, 0x5d6b, + 0x6eb5, 0x375a, 0x1bad, 0x0dd6, 0x06eb, 0x4375, 0x21ba, 0x10dd, 0x086e, 0x0437, + 0x421b, 0x610d, 0x3086, 0x1843, 0x4c21, 0x2610, 0x5308, 0x6984, 0x74c2, 0x3a61, + 0x1d30, 0x4e98, 0x674c, 0x73a6, 0x39d3, 0x5ce9, 0x2e74, 0x573a, 0x2b9d, 0x15ce, + 0x0ae7, 0x4573, 0x62b9, 0x315c, 0x58ae, 0x2c57, 0x562b, 0x6b15, 0x358a, 0x1ac5, + 0x0d62, 0x06b1, 0x0358, 0x41ac, 0x60d6, 0x306b, 0x5835, 0x2c1a, 0x160d, 0x0b06, + 0x0583, 0x42c1, 0x2160, 0x50b0, 0x6858, 0x742c, 0x7a16, 0x3d0b, 0x5e85, 0x2f42, + 0x17a1, 0x0bd0, 0x45e8, 0x62f4, 0x717a, 0x38bd, 0x1c5e, 0x0e2f, 0x4717, 0x638b, + 0x71c5, 0x38e2, 0x1c71, 0x0e38, 0x471c, 0x638e, 0x31c7, 0x58e3, 0x6c71, 0x3638, + 0x5b1c, 0x6d8e, 0x36c7, 0x5b63, 0x6db1, 0x36d8, 0x5b6c, 0x6db6, 0x36db, 0x5b6d, + 0x2db6, 0x16db, 0x4b6d, 0x25b6, 0x12db, 0x496d, 0x24b6, 0x125b, 0x492d, 0x2496, + 0x124b, 0x4925, 0x2492, 0x1249, 0x0924, 0x4492, 0x2249, 0x1124, 0x4892, 0x2449, + 0x1224, 0x4912, 0x2489, 0x1244, 0x4922, 0x2491, 0x1248, 0x4924, 0x6492, 0x3249, + 0x1924, 0x4c92, 0x2649, 0x1324, 0x4992, 0x24c9, 0x1264, 0x4932, 0x2499, 0x124c, + 0x4926, 0x2493, 0x5249, 0x2924, 0x5492, 0x2a49, 0x1524, 0x4a92, 0x2549, 0x12a4, + 0x4952, 0x24a9, 0x1254, 0x492a, 0x2495, 0x124a, 0x0925, 0x0492, 0x0249, 0x0124, + 0x4092, 0x2049, 0x1024, 0x4812, 0x2409, 0x1204, 0x4902, 0x2481, 0x1240, 0x4920, + 0x6490, 0x7248, 0x7924, 0x7c92, 0x3e49, 0x1f24, 0x4f92, 0x27c9, 0x13e4, 0x49f2, + 0x24f9, 0x127c, 0x493e, 0x249f, 0x524f, 0x6927, 0x7493, 0x7a49, 0x3d24, 0x5e92, + 0x2f49, 0x17a4, 0x4bd2, 0x25e9, 0x12f4, 0x497a, 0x24bd, 0x125e, 0x092f, 0x4497, + 0x624b, 0x7125, 0x3892, 0x1c49, 0x0e24, 0x4712, 0x2389, 0x11c4, 0x48e2, 0x2471, + 0x1238, 0x491c, 0x648e, 0x3247, 0x5923, 0x6c91, 0x3648, 0x5b24, 0x6d92, 0x36c9, + 0x1b64, 0x4db2, 0x26d9, 0x136c, 0x49b6, 0x24db, 0x526d, 0x2936, 0x149b, 0x4a4d, + 0x2526, 0x1293, 0x4949, 0x24a4, 0x5252, 0x2929, 0x1494, 0x4a4a, 0x2525, 0x1292, + 0x0949, 0x04a4, 0x4252, 0x2129, 0x1094, 0x484a, 0x2425, 0x1212, 0x0909, 0x0484, + 0x4242, 0x2121, 0x1090, 0x4848, 0x6424, 0x7212, 0x3909, 0x1c84, 0x4e42, 0x2721, + 0x1390, 0x49c8, 0x64e4, 0x7272, 0x3939, 0x1c9c, 0x4e4e, 0x2727, 0x5393, 0x69c9, + 0x34e4, 0x5a72, 0x2d39, 0x169c, 0x4b4e, 0x25a7, 0x52d3, 0x6969, 0x34b4, 0x5a5a, + 0x2d2d, 0x1696, 0x0b4b, 0x45a5, 0x22d2, 0x1169, 0x08b4, 0x445a, 0x222d, 0x1116, + 0x088b, 0x4445, 0x2222, 0x1111, 0x0888, 0x4444, 0x6222, 0x3111, 0x1888, 0x4c44, + 0x6622, 0x3311, 0x1988, 0x4cc4, 0x6662, 0x3331, 0x1998, 0x4ccc, 0x6666, 0x3333, + 0x5999, 0x2ccc, 0x5666, 0x2b33, 0x5599, 0x2acc, 0x5566, 0x2ab3, 0x5559, 0x2aac, + 0x5556, 0x2aab, 0x5555, 0x2aaa, 0x1555, 0x0aaa, 0x0555, 0x02aa, 0x0155, 0x00aa, + 0x0055, 0x002a, 0x0015, 0x000a, 0x0005, 0x0002, 0x0001, +] diff --git a/feo3boy/src/apu/lfsr15_rev.txt b/feo3boy/src/apu/lfsr15_rev.txt new file mode 100644 index 0000000..8252115 --- /dev/null +++ b/feo3boy/src/apu/lfsr15_rev.txt @@ -0,0 +1,3279 @@ +[ + 0x0000, 0x7ffe, 0x7ffd, 0x00ee, 0x3bee, 0x7ffc, 0x00ed, 0x03fc, 0x01dc, 0x3bed, + 0x7ffb, 0x519e, 0x1e5b, 0x00ec, 0x03fb, 0x0efc, 0x6922, 0x01db, 0x3bec, 0x6ed7, + 0x04ea, 0x7ffa, 0x519d, 0x239b, 0x7371, 0x1e5a, 0x00eb, 0x4e41, 0x4688, 0x03fa, + 0x0efb, 0x3cdc, 0x07f8, 0x6921, 0x01da, 0x2b33, 0x3f11, 0x3beb, 0x6ed6, 0x1f49, + 0x77dc, 0x04e9, 0x7ff9, 0x79b2, 0x0fea, 0x519c, 0x239a, 0x27cc, 0x528c, 0x7370, + 0x1e59, 0x3fea, 0x13b6, 0x00ea, 0x4e40, 0x5447, 0x1d02, 0x4687, 0x03f9, 0x02ca, + 0x2544, 0x0efa, 0x3cdb, 0x28d1, 0x4f2f, 0x07f7, 0x6920, 0x5b85, 0x7748, 0x01d9, + 0x2b32, 0x0d8d, 0x12f8, 0x3f10, 0x3bea, 0x745f, 0x076a, 0x6ed5, 0x1f48, 0x4bff, + 0x1461, 0x77db, 0x04e8, 0x5fed, 0x406f, 0x7ff8, 0x79b1, 0x4776, 0x1a72, 0x0fe9, + 0x519b, 0x2257, 0x3dca, 0x2399, 0x27cb, 0x1ff8, 0x09de, 0x528b, 0x736f, 0x493c, + 0x2489, 0x1e58, 0x3fe9, 0x670b, 0x15df, 0x13b5, 0x00e9, 0x4aea, 0x367d, 0x4e3f, + 0x5446, 0x05d8, 0x5a49, 0x1d01, 0x4686, 0x6a10, 0x3095, 0x03f8, 0x02c9, 0x5dd3, + 0x6fc5, 0x2543, 0x0ef9, 0x56ac, 0x559a, 0x3cda, 0x28d0, 0x0078, 0x1df8, 0x4f2e, + 0x07f6, 0x5f89, 0x5535, 0x691f, 0x5b84, 0x6a9b, 0x3cb6, 0x7747, 0x01d8, 0x4a84, + 0x3707, 0x2b31, 0x0d8c, 0x14a4, 0x362c, 0x12f7, 0x3f0f, 0x537a, 0x0977, 0x3be9, + 0x745e, 0x6e34, 0x40d8, 0x0769, 0x6ed4, 0x49c5, 0x3954, 0x1f47, 0x4bfe, 0x7a32, + 0x03b8, 0x1460, 0x77da, 0x2d57, 0x2a69, 0x04e7, 0x5fec, 0x106c, 0x233d, 0x406e, + 0x7ff7, 0x1df0, 0x523d, 0x79b0, 0x4775, 0x2ac6, 0x7400, 0x1a71, 0x0fe8, 0x4f90, + 0x2511, 0x519a, 0x2256, 0x2632, 0x1a22, 0x3dc9, 0x2398, 0x3995, 0x29bf, 0x27ca, + 0x1ff7, 0x776d, 0x0277, 0x09dd, 0x528a, 0x44bd, 0x72d3, 0x736e, 0x493b, 0x10d8, + 0x6171, 0x2488, 0x1e57, 0x17f7, 0x28ba, 0x3fe8, 0x670a, 0x34f3, 0x7aa0, 0x15de, + 0x13b4, 0x0ba8, 0x348b, 0x00e8, 0x4ae9, 0x6d1e, 0x3912, 0x367c, 0x4e3e, 0x78ca, + 0x47d1, 0x5445, 0x05d7, 0x6ff9, 0x603a, 0x5a48, 0x1d00, 0x08e6, 0x609a, 0x4685, + 0x6a0f, 0x65eb, 0x2c21, 0x3094, 0x03f7, 0x6792, 0x2f60, 0x02c8, 0x5dd2, 0x6d9f, + 0x2797, 0x6fc4, 0x2542, 0x7ba3, 0x2037, 0x0ef8, 0x56ab, 0x0a30, 0x7490, 0x5599, + 0x3cd9, 0x4274, 0x5df6, 0x28cf, 0x0077, 0x3fff, 0x4076, 0x1df7, 0x4f2d, 0x41fd, + 0x11cd, 0x07f5, 0x5f88, 0x376b, 0x2ff6, 0x5534, 0x691e, 0x63ba, 0x06c6, 0x5b83, + 0x6a9a, 0x2dc2, 0x4bd8, 0x3cb5, 0x7746, 0x6d78, 0x4136, 0x01d7, 0x4a83, 0x2d9b, + 0x76bb, 0x3706, 0x2b30, 0x16cd, 0x1bb5, 0x0d8b, 0x14a3, 0x20fe, 0x1511, 0x362b, + 0x12f6, 0x0acc, 0x7f4d, 0x3f0e, 0x5379, 0x35a1, 0x4a2a, 0x0976, 0x3be8, 0x2940, + 0x6c50, 0x745d, 0x6e33, 0x6c9c, 0x5116, 0x40d7, 0x0768, 0x5584, 0x67f9, 0x6ed3, + 0x49c4, 0x724a, 0x2ccd, 0x3953, 0x1f46, 0x3ac1, 0x33cb, 0x4bfd, 0x7a31, 0x2577, + 0x5b40, 0x03b7, 0x145f, 0x17b2, 0x5ec1, 0x77d9, 0x2d56, 0x5af2, 0x7aff, 0x2a68, + 0x04e6, 0x69de, 0x7539, 0x5feb, 0x106b, 0x3183, 0x5843, 0x233c, 0x406d, 0x5b37, + 0x4f26, 0x7ff6, 0x1def, 0x026e, 0x6afe, 0x523c, 0x79af, 0x2c71, 0x666f, 0x4774, + 0x2ac5, 0x4aaa, 0x579a, 0x73ff, 0x1a70, 0x7d3a, 0x43e6, 0x0fe7, 0x4f8f, 0x632e, + 0x7d7a, 0x2510, 0x5199, 0x70b3, 0x0c4b, 0x2255, 0x2631, 0x52dd, 0x2df4, 0x1a21, + 0x3dc8, 0x644c, 0x5d3d, 0x2397, 0x3994, 0x5688, 0x64e3, 0x29be, 0x27c9, 0x5ccb, + 0x0166, 0x1ff6, 0x776c, 0x6721, 0x2345, 0x0276, 0x09dc, 0x12a3, 0x54e0, 0x5289, + 0x44bc, 0x1cab, 0x2774, 0x72d2, 0x736d, 0x1b60, 0x58f0, 0x493a, 0x10d7, 0x7874, + 0x175c, 0x6170, 0x2487, 0x430d, 0x560b, 0x1e56, 0x17f6, 0x3eb8, 0x1fe0, 0x28b9, + 0x3fe7, 0x7a1a, 0x20e6, 0x6709, 0x34f2, 0x781e, 0x6132, 0x7a9f, 0x15dd, 0x4510, + 0x4864, 0x13b3, 0x0ba7, 0x0bf4, 0x68c7, 0x348a, 0x00e7, 0x41f6, 0x2a1c, 0x4ae8, + 0x6d1d, 0x415d, 0x58ad, 0x3911, 0x367b, 0x154f, 0x2f2f, 0x4e3d, 0x78c9, 0x088e, + 0x60db, 0x47d0, 0x5444, 0x64bf, 0x7dd3, 0x05d6, 0x6ff8, 0x350a, 0x584c, 0x6039, + 0x5a47, 0x501d, 0x269e, 0x1cff, 0x08e5, 0x6829, 0x5c73, 0x6099, 0x4684, 0x7037, + 0x425f, 0x6a0e, 0x65ea, 0x7bd8, 0x5945, 0x2c20, 0x3093, 0x7067, 0x0e7b, 0x03f6, + 0x6791, 0x0d33, 0x3297, 0x2f5f, 0x02c7, 0x5f06, 0x7dae, 0x5dd1, 0x6d9e, 0x7836, + 0x4740, 0x2796, 0x6fc3, 0x1036, 0x5abc, 0x2541, 0x7ba2, 0x0858, 0x2bc8, 0x2036, + 0x0ef7, 0x3018, 0x4ced, 0x56aa, 0x0a2f, 0x1827, 0x754d, 0x748f, 0x5598, 0x0a52, + 0x1563, 0x3cd8, 0x4273, 0x0836, 0x4fa4, 0x5df5, 0x28ce, 0x13e6, 0x077e, 0x0076, + 0x3ffe, 0x01fe, 0x79b8, 0x4075, 0x1df6, 0x3491, 0x2946, 0x4f2c, 0x41fc, 0x0e81, + 0x0d11, 0x11cc, 0x07f4, 0x3a00, 0x1b16, 0x5f87, 0x376a, 0x206c, 0x61cf, 0x2ff5, + 0x5533, 0x3440, 0x21ac, 0x691d, 0x63b9, 0x48bf, 0x064b, 0x06c5, 0x5b82, 0x5be6, + 0x70e7, 0x6a99, 0x2dc1, 0x4325, 0x66e3, 0x4bd7, 0x3cb4, 0x541f, 0x6e0c, 0x7745, + 0x6d77, 0x10b0, 0x1c83, 0x4135, 0x01d6, 0x0d0b, 0x5660, 0x4a82, 0x2d9a, 0x3579, + 0x37cd, 0x76ba, 0x3705, 0x7b8e, 0x1273, 0x2b2f, 0x16cc, 0x5e45, 0x0c96, 0x1bb4, + 0x0d8a, 0x4897, 0x31df, 0x14a2, 0x20fd, 0x0db2, 0x2c7a, 0x1510, 0x362a, 0x0365, + 0x5d19, 0x12f5, 0x0acb, 0x0cbe, 0x45ab, 0x7f4c, 0x3f0d, 0x72a2, 0x5aa8, 0x5378, + 0x35a0, 0x4d80, 0x7cd1, 0x4a29, 0x0975, 0x0f9d, 0x11c6, 0x3be7, 0x293f, 0x2719, + 0x7c5d, 0x6c4f, 0x745c, 0x7687, 0x32ee, 0x6e32, 0x6c9b, 0x73c1, 0x3211, 0x5115, + 0x40d6, 0x31c8, 0x33b4, 0x0767, 0x5583, 0x29a8, 0x571d, 0x67f8, 0x6ed2, 0x5996, + 0x35e1, 0x49c3, 0x7249, 0x1bdc, 0x18e5, 0x2ccc, 0x3952, 0x41b3, 0x504f, 0x1f45, + 0x3ac0, 0x2bfe, 0x0474, 0x33ca, 0x4bfc, 0x625f, 0x6dc3, 0x7a30, 0x2576, 0x249f, + 0x5245, 0x5b3f, 0x03b6, 0x3ba5, 0x796a, 0x145e, 0x17b1, 0x304e, 0x5daf, 0x5ec0, + 0x77d8, 0x0590, 0x6e8d, 0x2d55, 0x5af1, 0x19db, 0x6880, 0x7afe, 0x2a67, 0x7326, + 0x65a3, 0x04e5, 0x69dd, 0x4ca6, 0x439f, 0x7538, 0x5fea, 0x2d0f, 0x4ee6, 0x106a, + 0x3182, 0x14ca, 0x4736, 0x5842, 0x233b, 0x6128, 0x1507, 0x406c, 0x5b36, 0x5790, + 0x09d4, 0x4f25, 0x7ff5, 0x07ee, 0x03ae, 0x1dee, 0x026d, 0x6030, 0x4358, 0x6afd, + 0x523b, 0x472c, 0x66d9, 0x79ae, 0x2c70, 0x3207, 0x117f, 0x666e, 0x4773, 0x3a79, + 0x36be, 0x2ac4, 0x4aa9, 0x6188, 0x6b07, 0x5799, 0x73fe, 0x209d, 0x0b1e, 0x1a6f, + 0x7d39, 0x75c6, 0x4b51, 0x43e5, 0x0fe6, 0x3e88, 0x6faf, 0x4f8e, 0x632d, 0x2125, + 0x6c1b, 0x7d79, 0x250f, 0x2885, 0x39fa, 0x5198, 0x70b2, 0x3d4a, 0x7c91, 0x0c4a, + 0x2254, 0x22b2, 0x324e, 0x2630, 0x52dc, 0x1774, 0x4362, 0x2df3, 0x1a20, 0x57fa, + 0x3b5d, 0x3dc7, 0x644b, 0x0dda, 0x3337, 0x5d3c, 0x2396, 0x757e, 0x3d7f, 0x3993, + 0x5687, 0x3a9c, 0x43b4, 0x64e2, 0x29bd, 0x497b, 0x4d38, 0x27c8, 0x5cca, 0x5ee4, + 0x6343, 0x0165, 0x1ff5, 0x39b8, 0x40ed, 0x776b, 0x6720, 0x0f2d, 0x477d, 0x2344, + 0x0275, 0x68ce, 0x2720, 0x09db, 0x12a2, 0x5e6d, 0x4ffb, 0x54df, 0x5288, 0x112b, + 0x3a2f, 0x44bb, 0x1caa, 0x25ff, 0x18c1, 0x2773, 0x72d1, 0x74ee, 0x1638, 0x736c, + 0x1b5f, 0x6554, 0x507e, 0x58ef, 0x4939, 0x0b66, 0x71dc, 0x10d6, 0x7873, 0x56e0, + 0x3a83, 0x175b, 0x616f, 0x19c2, 0x4d67, 0x2486, 0x430c, 0x3560, 0x53c5, 0x560a, + 0x1e55, 0x1b10, 0x25e6, 0x17f5, 0x3eb7, 0x16f4, 0x05bf, 0x1fdf, 0x28b8, 0x0ee3, + 0x34da, 0x3fe6, 0x7a19, 0x2aad, 0x6c83, 0x20e5, 0x6708, 0x316a, 0x785b, 0x34f1, + 0x781d, 0x180e, 0x6678, 0x6131, 0x7a9e, 0x4627, 0x57d7, 0x15dc, 0x450f, 0x532b, + 0x1957, 0x4863, 0x13b2, 0x4e0d, 0x2bb4, 0x0ba6, 0x0bf3, 0x6981, 0x1ede, 0x68c6, + 0x3489, 0x3b9d, 0x5f81, 0x00e6, 0x41f5, 0x129b, 0x7ee2, 0x2a1b, 0x4ae7, 0x242b, + 0x1348, 0x6d1c, 0x415c, 0x3ed0, 0x1189, 0x58ac, 0x3910, 0x04a6, 0x122f, 0x367a, + 0x154e, 0x4d23, 0x2e45, 0x2f2e, 0x4e3c, 0x62d0, 0x2fab, 0x78c8, 0x088d, 0x170d, + 0x7130, 0x60da, 0x47cf, 0x7b44, 0x115a, 0x5443, 0x64be, 0x1cdd, 0x213b, 0x7dd2, + 0x05d5, 0x3c66, 0x512c, 0x6ff7, 0x3509, 0x2b57, 0x2ace, 0x584b, 0x6038, 0x1ee6, + 0x63e9, 0x5a46, 0x501c, 0x2efc, 0x6077, 0x269d, 0x1cfe, 0x1928, 0x1978, 0x08e4, + 0x6828, 0x76e2, 0x5d6c, 0x5c72, 0x6098, 0x22fa, 0x6b89, 0x4683, 0x7036, 0x185d, + 0x5cb5, 0x425e, 0x6a0d, 0x6249, 0x0551, 0x65e9, 0x7bd7, 0x5623, 0x36c8, 0x5944, + 0x2c1f, 0x446b, 0x45cc, 0x3092, 0x7066, 0x37f5, 0x5e66, 0x0e7a, 0x03f5, 0x3764, + 0x1592, 0x6790, 0x0d32, 0x53de, 0x4b72, 0x3296, 0x2f5e, 0x4cd9, 0x7a6f, 0x02c6, + 0x5f05, 0x6b68, 0x1b8f, 0x7dad, 0x5dd0, 0x3da4, 0x3228, 0x6d9d, 0x7835, 0x052b, + 0x4ab3, 0x473f, 0x2795, 0x6ea0, 0x08af, 0x6fc2, 0x1035, 0x498e, 0x5c94, 0x5abb, + 0x2540, 0x41c6, 0x0a1b, 0x7ba1, 0x0857, 0x1b29, 0x726b, 0x2bc7, 0x2035, 0x7b57, + 0x2066, 0x0ef6, 0x3017, 0x3a42, 0x23f4, 0x4cec, 0x56a9, 0x198b, 0x7b20, 0x0a2e, + 0x1826, 0x372d, 0x6192, 0x754c, 0x748e, 0x645f, 0x6f22, 0x5597, 0x0a51, 0x06d9, + 0x30c4, 0x1562, 0x3cd7, 0x5903, 0x2653, 0x4272, 0x0835, 0x0a65, 0x3d19, 0x4fa3, + 0x5df4, 0x371a, 0x1319, 0x28cd, 0x13e5, 0x7bb6, 0x5468, 0x077d, 0x0075, 0x6a23, + 0x51cd, 0x3ffd, 0x01fd, 0x1e6e, 0x04ef, 0x79b7, 0x4074, 0x4aef, 0x097c, 0x1df5, + 0x3490, 0x6797, 0x413b, 0x2945, 0x4f2b, 0x70b8, 0x5610, 0x41fb, 0x0e80, 0x301d, + 0x21b1, 0x0d10, 0x11cb, 0x599b, 0x65a8, 0x07f3, 0x39ff, 0x7583, 0x163d, 0x1b15, + 0x5f86, 0x62d5, 0x6b8e, 0x3769, 0x206b, 0x5908, 0x65ad, 0x61ce, 0x2ff4, 0x62ac, + 0x1d9e, 0x5532, 0x343f, 0x61f7, 0x719b, 0x21ab, 0x691c, 0x61c9, 0x2839, 0x63b8, + 0x48be, 0x5097, 0x7612, 0x064a, 0x06c4, 0x65d6, 0x638a, 0x5b81, 0x5be5, 0x7ec1, + 0x385e, 0x70e6, 0x6a98, 0x335c, 0x35f8, 0x2dc0, 0x4324, 0x1eaa, 0x69e7, 0x66e2, + 0x4bd6, 0x222e, 0x5175, 0x3cb3, 0x541e, 0x2b0a, 0x5f60, 0x6e0b, 0x7744, 0x1dc7, + 0x424b, 0x6d76, 0x10af, 0x0b7f, 0x42e4, 0x1c82, 0x4134, 0x6496, 0x2fef, 0x01d5, + 0x0d0a, 0x4ff4, 0x7d11, 0x565f, 0x4a81, 0x69b5, 0x16a4, 0x2d99, 0x3578, 0x555b, + 0x4cb0, 0x37cc, 0x76b9, 0x6220, 0x419d, 0x3704, 0x7b8d, 0x6436, 0x45fe, 0x1272, + 0x2b2e, 0x62a7, 0x3141, 0x16cb, 0x5e44, 0x74c5, 0x0f74, 0x0c95, 0x1bb3, 0x6236, + 0x0ce2, 0x0d89, 0x4896, 0x3468, 0x60ff, 0x31de, 0x14a1, 0x0567, 0x2289, 0x20fc, + 0x0db1, 0x4952, 0x2a71, 0x2c79, 0x150f, 0x2433, 0x7fa7, 0x3629, 0x0364, 0x48e7, + 0x1391, 0x5d18, 0x12f4, 0x66b4, 0x22e7, 0x0aca, 0x0cbd, 0x71f5, 0x0e2c, 0x45aa, + 0x7f4b, 0x50c0, 0x1d99, 0x3f0c, 0x72a1, 0x3e33, 0x3b48, 0x5aa7, 0x5377, 0x3f91, + 0x0728, 0x359f, 0x4d7f, 0x55ce, 0x7330, 0x7cd0, 0x4a28, 0x6f59, 0x7f9f, 0x0974, + 0x0f9c, 0x59de, 0x54d8, 0x11c5, 0x3be6, 0x552d, 0x7962, 0x293e, 0x2718, 0x63e1, + 0x1c4e, 0x7c5c, 0x6c4e, 0x5c5f, 0x01a7, 0x745b, 0x7686, 0x2b93, 0x6ccf, 0x32ed, + 0x6e31, 0x2862, 0x5f2b, 0x6c9a, 0x73c0, 0x10ef, 0x7b08, 0x3210, 0x5114, 0x71c4, + 0x4ece, 0x40d5, 0x31c7, 0x32d6, 0x6657, 0x33b3, 0x0766, 0x20ce, 0x4670, 0x5582, + 0x29a7, 0x47b9, 0x3892, 0x571c, 0x67f7, 0x4c5a, 0x343a, 0x6ed1, 0x5995, 0x21d4, + 0x168c, 0x35e0, 0x49c2, 0x0710, 0x790c, 0x7248, 0x1bdb, 0x788c, 0x688a, 0x18e4, + 0x2ccb, 0x33fc, 0x3fa6, 0x3951, 0x41b2, 0x0a88, 0x6bcc, 0x504e, 0x1f44, 0x61f2, + 0x2e9e, 0x3abf, 0x2bfd, 0x56f9, 0x2d24, 0x0473, 0x33c9, 0x184a, 0x1244, 0x4bfb, + 0x625e, 0x44ee, 0x43fb, 0x6dc2, 0x7a2f, 0x1f88, 0x49da, 0x2575, 0x249e, 0x6945, + 0x5ff4, 0x5244, 0x5b3e, 0x2a23, 0x0fa4, 0x03b5, 0x3ba4, 0x1599, 0x649d, 0x7969, + 0x145d, 0x3385, 0x2ee9, 0x17b0, 0x304d, 0x7507, 0x7225, 0x5dae, 0x5ebf, 0x6b43, + 0x7196, 0x77d7, 0x058f, 0x7e9c, 0x4565, 0x6e8c, 0x2d54, 0x1ad0, 0x7924, 0x5af0, + 0x19da, 0x6ac1, 0x2d19, 0x687f, 0x7afd, 0x3887, 0x0e21, 0x2a66, 0x7325, 0x1c43, + 0x7607, 0x65a2, 0x04e4, 0x21a6, 0x42d9, 0x69dc, 0x4ca5, 0x0f69, 0x711a, 0x439e, + 0x7537, 0x5a33, 0x721a, 0x5fe9, 0x2d0e, 0x710f, 0x7155, 0x4ee5, 0x1069, 0x30fb, + 0x3f60, 0x3181, 0x14c9, 0x72ea, 0x7542, 0x4735, 0x5841, 0x593a, 0x1751, 0x233a, + 0x6127, 0x58a2, 0x510b, 0x1506, 0x406b, 0x4bcd, 0x5838, 0x5b35, 0x578f, 0x2de9, + 0x5a3e, 0x09d3, 0x4f24, 0x1456, 0x6917, 0x7ff4, 0x07ed, 0x5281, 0x73f5, 0x03ad, + 0x1ded, 0x3621, 0x7a95, 0x026c, 0x602f, 0x278c, 0x43a9, 0x4357, 0x6afc, 0x6c10, + 0x6875, 0x523a, 0x472b, 0x434d, 0x37c2, 0x66d8, 0x79ad, 0x61c4, 0x7cc6, 0x2c6f, + 0x3206, 0x18da, 0x7125, 0x117e, 0x666d, 0x1ed3, 0x18b6, 0x4772, 0x3a78, 0x05b4, + 0x4b67, 0x36bd, 0x2ac3, 0x5d61, 0x7260, 0x4aa8, 0x6187, 0x3d0e, 0x1074, 0x6b06, + 0x5798, 0x7eea, 0x59e6, 0x73fd, 0x209c, 0x4ddb, 0x38ef, 0x0b1d, 0x1a6e, 0x7c2e, + 0x1915, 0x7d38, 0x75c5, 0x42a9, 0x3106, 0x4b50, 0x43e4, 0x3847, 0x2834, 0x0fe5, + 0x3e87, 0x5c0e, 0x1a0b, 0x6fae, 0x4f8d, 0x2cb6, 0x0c7f, 0x632c, 0x2124, 0x1b78, + 0x4ef0, 0x6c1a, 0x7d78, 0x482e, 0x7712, 0x250e, 0x2884, 0x24db, 0x1124, 0x39f9, + 0x5197, 0x63b3, 0x337e, 0x70b1, 0x3d49, 0x656d, 0x7160, 0x7c90, 0x0c49, 0x268a, + 0x0946, 0x2253, 0x22b1, 0x120e, 0x311d, 0x324d, 0x262f, 0x0c27, 0x1bf3, 0x52db, + 0x1773, 0x5baa, 0x318c, 0x4361, 0x2df2, 0x4579, 0x0924, 0x1a1f, 0x57f9, 0x0673, + 0x2e24, 0x3b5c, 0x3dc6, 0x0a9c, 0x08d1, 0x644a, 0x0dd9, 0x1651, 0x3f6b, 0x3336, + 0x5d3b, 0x763b, 0x48b9, 0x2395, 0x757d, 0x7e42, 0x0b40, 0x3d7e, 0x3992, 0x7661, + 0x78a4, 0x5686, 0x3a9b, 0x79f4, 0x14d4, 0x43b3, 0x64e1, 0x0dee, 0x26c0, 0x29bc, + 0x497a, 0x065f, 0x3ae3, 0x4d37, 0x27c7, 0x5092, 0x1d4e, 0x5cc9, 0x5ee3, 0x06ed, + 0x72f5, 0x6342, 0x0164, 0x76cf, 0x5a79, 0x1ff4, 0x39b7, 0x086c, 0x46c6, 0x40ec, + 0x776a, 0x65ff, 0x02ec, 0x671f, 0x0f2c, 0x7385, 0x77e2, 0x477c, 0x2343, 0x6d24, + 0x4a30, 0x0274, 0x68cd, 0x0d39, 0x1c89, 0x271f, 0x09da, 0x3d50, 0x53cb, 0x12a1, + 0x5e6c, 0x3a48, 0x71a1, 0x4ffa, 0x54de, 0x21da, 0x760d, 0x5287, 0x112a, 0x7e48, + 0x7e2e, 0x3a2e, 0x44ba, 0x7e68, 0x4c72, 0x1ca9, 0x25fe, 0x023a, 0x059a, 0x18c0, + 0x2772, 0x5888, 0x1c29, 0x72d0, 0x74ed, 0x7e82, 0x7e28, 0x1637, 0x736b, 0x0645, + 0x4dc1, 0x1b5e, 0x6553, 0x11f4, 0x7ea7, 0x507d, 0x58ee, 0x677d, 0x641c, 0x4938, + 0x0b65, 0x4fda, 0x3e19, 0x71db, 0x10d5, 0x59c4, 0x32bc, 0x7872, 0x56df, 0x44d4, + 0x5eca, 0x3a82, 0x175a, 0x75ac, 0x5776, 0x616e, 0x19c1, 0x4c8c, 0x26ff, 0x4d66, + 0x2485, 0x298e, 0x0e67, 0x430b, 0x355f, 0x5e2b, 0x6b4e, 0x53c4, 0x5609, 0x2ee2, + 0x06bf, 0x1e54, 0x1b0f, 0x3a28, 0x653a, 0x25e5, 0x17f4, 0x3546, 0x5311, 0x3eb6, + 0x16f3, 0x1cc3, 0x5db9, 0x05be, 0x1fde, 0x0d73, 0x1f2f, 0x28b7, 0x0ee2, 0x2381, + 0x6d04, 0x34d9, 0x3fe5, 0x65d1, 0x6a81, 0x7a18, 0x2aac, 0x2618, 0x7230, 0x6c82, + 0x20e4, 0x3751, 0x6314, 0x6707, 0x3169, 0x0254, 0x3e9e, 0x785a, 0x34f0, 0x0bda, + 0x680f, 0x781c, 0x180d, 0x081c, 0x2d5f, 0x6677, 0x6130, 0x1350, 0x6f61, 0x7a9d, + 0x4626, 0x21f4, 0x4918, 0x57d6, 0x15db, 0x6bec, 0x4458, 0x450e, 0x532a, 0x3792, + 0x1adb, 0x1956, 0x4862, 0x3e53, 0x6385, 0x13b1, 0x4e0c, 0x675d, 0x3322, 0x2bb3, + 0x0ba5, 0x6bb7, 0x4b98, 0x0bf2, 0x6980, 0x54f8, 0x6e97, 0x1edd, 0x68c5, 0x461e, + 0x035c, 0x3488, 0x3b9c, 0x2094, 0x44b4, 0x5f80, 0x00e5, 0x5b7c, 0x17a9, 0x41f4, + 0x129a, 0x5014, 0x4570, 0x7ee1, 0x2a1a, 0x5931, 0x2225, 0x4ae6, 0x242a, 0x71bb, + 0x5c25, 0x1347, 0x6d1b, 0x75a3, 0x5734, 0x415b, 0x3ecf, 0x2904, 0x5afb, 0x1188, + 0x58ab, 0x5c2e, 0x38cd, 0x390f, 0x04a5, 0x141c, 0x18a0, 0x122e, 0x3679, 0x62fe, + 0x307f, 0x154d, 0x4d22, 0x1144, 0x792f, 0x2e44, 0x2f2d, 0x25b3, 0x5be0, 0x4e3b, + 0x62cf, 0x7e62, 0x216c, 0x2faa, 0x78c7, 0x3b03, 0x38aa, 0x088c, 0x170c, 0x5faf, + 0x19e5, 0x712f, 0x60d9, 0x1666, 0x6514, 0x47ce, 0x7b43, 0x7627, 0x0625, 0x1159, + 0x5442, 0x7ebc, 0x3271, 0x64bd, 0x1cdc, 0x40b2, 0x6acc, 0x213a, 0x7dd1, 0x37e2, + 0x0aef, 0x05d4, 0x3c65, 0x1b3e, 0x47f4, 0x512b, 0x6ff6, 0x7bed, 0x4e80, 0x3508, + 0x2b56, 0x52a1, 0x1468, 0x2acd, 0x584a, 0x4164, 0x7cd8, 0x6037, 0x1ee5, 0x53e5, + 0x42eb, 0x63e8, 0x5a45, 0x6574, 0x6b55, 0x501b, 0x2efb, 0x403b, 0x3390, 0x6076, + 0x269c, 0x2e65, 0x3859, 0x1cfd, 0x1927, 0x296e, 0x764d, 0x1977, 0x08e3, 0x549f, + 0x1d60, 0x6827, 0x76e1, 0x12bb, 0x7974, 0x5d6b, 0x5c71, 0x187d, 0x50d2, 0x6097, + 0x22f9, 0x07b4, 0x4c6c, 0x6b88, 0x4682, 0x70e1, 0x2eb0, 0x7035, 0x185c, 0x5e86, + 0x64a8, 0x5cb4, 0x425d, 0x02b3, 0x3153, 0x6a0c, 0x6248, 0x3a62, 0x284b, 0x0550, + 0x65e8, 0x30e4, 0x59ad, 0x7bd6, 0x5622, 0x4f54, 0x17bb, 0x36c7, 0x5943, 0x573d, + 0x3e65, 0x2c1e, 0x446a, 0x0ea9, 0x25c5, 0x45cb, 0x3091, 0x77a9, 0x3283, 0x7065, + 0x37f4, 0x2739, 0x2ef4, 0x5e65, 0x0e79, 0x3046, 0x6a93, 0x03f4, 0x3763, 0x1ca3, + 0x4dd3, 0x1591, 0x678f, 0x48df, 0x21ec, 0x0d31, 0x53dd, 0x00ac, 0x3058, 0x4b71, + 0x3295, 0x6a5a, 0x2e89, 0x2f5d, 0x4cd8, 0x3d6a, 0x2ec2, 0x7a6e, 0x02c5, 0x3357, + 0x2e77, 0x5f04, 0x6b67, 0x4224, 0x7512, 0x1b8e, 0x7dac, 0x4cc6, 0x5216, 0x5dcf, + 0x3da3, 0x1e34, 0x6e66, 0x3227, 0x6d9c, 0x5639, 0x2f84, 0x7834, 0x052a, 0x09f4, + 0x03c0, 0x4ab2, 0x473e, 0x3ed8, 0x7338, 0x2794, 0x6e9f, 0x00b4, 0x2897, 0x08ae, + 0x6fc1, 0x09a5, 0x3d91, 0x1034, 0x498d, 0x68e7, 0x0faf, 0x5c93, 0x5aba, 0x5204, + 0x35f3, 0x253f, 0x41c5, 0x0d53, 0x5672, 0x0a1a, 0x7ba0, 0x2be8, 0x3452, 0x0856, + 0x1b28, 0x1e1e, 0x3baf, 0x726a, 0x2bc6, 0x67c0, 0x2fbd, 0x2034, 0x7b56, 0x4a4a, + 0x25f8, 0x2065, 0x0ef5, 0x2dbb, 0x7500, 0x3016, 0x3a41, 0x4034, 0x15a4, 0x23f3, + 0x4ceb, 0x7d9a, 0x230c, 0x56a8, 0x198a, 0x34b9, 0x7b69, 0x7b1f, 0x0a2d, 0x4f6b, + 0x2665, 0x1825, 0x372c, 0x028e, 0x5b49, 0x6191, 0x754b, 0x290d, 0x0c5d, 0x748d, + 0x645e, 0x6d3e, 0x6c62, 0x6f21, 0x5596, 0x2c41, 0x6d8a, 0x0a50, 0x06d8, 0x409d, + 0x2a2e, 0x30c3, 0x1561, 0x6e54, 0x431f, 0x3cd6, 0x5902, 0x0234, 0x7079, 0x2652, + 0x4271, 0x4b18, 0x4cff, 0x0834, 0x0a64, 0x235d, 0x524f, 0x3d18, 0x4fa2, 0x15ff, + 0x538c, 0x5df3, 0x3719, 0x79e0, 0x0bba, 0x1318, 0x28cc, 0x1ea5, 0x2f72, 0x13e4, + 0x7bb5, 0x4796, 0x5fff, 0x5467, 0x077c, 0x0518, 0x368f, 0x0074, 0x6a22, 0x77fc, + 0x0ffc, 0x51cc, 0x3ffc, 0x23bb, 0x6ee9, 0x01fc, 0x1e6d, 0x003c, 0x51a2, 0x04ee, + 0x79b6, 0x13ba, 0x7463, 0x4073, 0x4aee, 0x3099, 0x4a88, 0x097b, 0x1df4, 0x2515, + 0x17fb, 0x348f, 0x6796, 0x203b, 0x63be, 0x413a, 0x2944, 0x67fd, 0x69e2, 0x4f2a, + 0x70b7, 0x5d41, 0x1b64, 0x560f, 0x41fa, 0x2f33, 0x703b, 0x0e7f, 0x301c, 0x1567, + 0x3a04, 0x21b0, 0x0d0f, 0x1277, 0x72a6, 0x11ca, 0x599a, 0x5053, 0x0594, 0x65a7, + 0x07f2, 0x66dd, 0x3e8c, 0x39fe, 0x7582, 0x4d3c, 0x112f, 0x163c, 0x1b14, 0x34de, + 0x4e11, 0x5f85, 0x62d4, 0x115e, 0x192c, 0x6b8d, 0x3768, 0x7a73, 0x41ca, 0x206a, + 0x5907, 0x131d, 0x70bc, 0x65ac, 0x61cd, 0x638e, 0x1dcb, 0x2ff3, 0x62ab, 0x0ce6, + 0x66b8, 0x1d9d, 0x5531, 0x01ab, 0x20d2, 0x343e, 0x61f6, 0x1248, 0x3389, 0x719a, + 0x21aa, 0x721e, 0x4bd1, 0x691b, 0x61c8, 0x18ba, 0x7c32, 0x2838, 0x63b7, 0x094a, + 0x0aa0, 0x48bd, 0x5096, 0x5a7d, 0x3d54, 0x7611, 0x0649, 0x6420, 0x2992, 0x06c3, + 0x65d5, 0x6318, 0x6bf0, 0x6389, 0x5b80, 0x2229, 0x6302, 0x5be4, 0x7ec0, 0x0af3, + 0x6578, 0x385d, 0x70e5, 0x3157, 0x77ad, 0x6a97, 0x335b, 0x521a, 0x09a9, 0x35f7, + 0x2dbf, 0x2310, 0x2c45, 0x4323, 0x1ea9, 0x3693, 0x2519, 0x69e6, 0x66e1, 0x4e15, + 0x01af, 0x4bd5, 0x222d, 0x77b1, 0x7fd4, 0x5174, 0x3cb2, 0x7fd0, 0x0ed0, 0x541d, + 0x2b09, 0x04bf, 0x771d, 0x5f5f, 0x6e0a, 0x1f1d, 0x5170, 0x7743, 0x1dc6, 0x1436, + 0x6f9a, 0x424a, 0x6d75, 0x465b, 0x3fbe, 0x10ae, 0x0b7e, 0x3652, 0x288f, 0x42e3, + 0x1c81, 0x4910, 0x1389, 0x4133, 0x6495, 0x38e7, 0x276c, 0x2fee, 0x01d4, 0x3cae, + 0x5da7, 0x0d09, 0x4ff3, 0x606f, 0x24e6, 0x7d10, 0x565e, 0x1fcc, 0x7fcc, 0x4a80, + 0x69b4, 0x038d, 0x3c8b, 0x16a3, 0x2d98, 0x07cb, 0x7433, 0x3577, 0x555a, 0x3929, + 0x7d83, 0x4caf, 0x37cb, 0x6766, 0x5a1c, 0x76b8, 0x621f, 0x5c48, 0x4715, 0x419c, + 0x3703, 0x0ecc, 0x3fd3, 0x7b8c, 0x6435, 0x1538, 0x4839, 0x45fd, 0x1271, 0x6cf2, + 0x5419, 0x2b2d, 0x62a6, 0x5882, 0x1fb5, 0x3140, 0x16ca, 0x245c, 0x4491, 0x5e43, + 0x74c4, 0x58c5, 0x6c25, 0x0f73, 0x0c94, 0x534e, 0x073d, 0x1bb2, 0x6235, 0x2ca2, + 0x4bad, 0x0ce1, 0x0d88, 0x2b05, 0x6a6f, 0x4895, 0x3467, 0x11a2, 0x4efb, 0x60fe, + 0x31dd, 0x2a9a, 0x04bb, 0x14a0, 0x0566, 0x5b15, 0x7d4f, 0x2288, 0x20fb, 0x1a45, + 0x3969, 0x0db0, 0x4951, 0x013b, 0x225e, 0x2a70, 0x2c78, 0x486b, 0x768e, 0x150e, + 0x2432, 0x45d3, 0x69bc, 0x7fa6, 0x3628, 0x7719, 0x354d, 0x0363, 0x48e6, 0x2fc4, + 0x0951, 0x1390, 0x5d17, 0x0e55, 0x5f5b, 0x12f3, 0x66b3, 0x2186, 0x3833, 0x22e6, + 0x0ac9, 0x0699, 0x3414, 0x0cbc, 0x71f4, 0x280e, 0x22bc, 0x0e2b, 0x45a9, 0x0339, + 0x7f20, 0x7f4a, 0x50bf, 0x3b1d, 0x1c23, 0x1d98, 0x3f0b, 0x6e06, 0x26ed, 0x72a0, + 0x3e32, 0x7f74, 0x1219, 0x3b47, 0x5aa6, 0x2473, 0x1f19, 0x5376, 0x3f90, 0x685f, + 0x662c, 0x0727, 0x359e, 0x50e9, 0x596a, 0x4d7e, 0x55cd, 0x78e1, 0x0c54, 0x732f, + 0x7ccf, 0x3e5c, 0x576d, 0x4a27, 0x6f58, 0x38c4, 0x4ec5, 0x7f9e, 0x0973, 0x516c, + 0x1748, 0x0f9b, 0x59dd, 0x091b, 0x2695, 0x54d7, 0x11c4, 0x5eb8, 0x773f, 0x3be5, + 0x552c, 0x72ca, 0x0b15, 0x7961, 0x293d, 0x5d10, 0x57ce, 0x2717, 0x63e0, 0x08a6, + 0x7c9b, 0x1c4d, 0x7c5b, 0x4700, 0x6642, 0x6c4d, 0x5c5e, 0x4b3c, 0x5a13, 0x01a6, + 0x745a, 0x1dc2, 0x5764, 0x7685, 0x2b92, 0x1726, 0x716b, 0x6cce, 0x32ec, 0x19af, + 0x1432, 0x6e30, 0x2861, 0x5fc9, 0x75dc, 0x5f2a, 0x6c99, 0x7ad2, 0x2ce3, 0x73bf, + 0x10ee, 0x3f35, 0x263a, 0x7b07, 0x320f, 0x195f, 0x2b9b, 0x5113, 0x71c3, 0x25cd, + 0x0395, 0x4ecd, 0x40d4, 0x6f96, 0x1afd, 0x31c6, 0x32d5, 0x35c8, 0x0c32, 0x6656, + 0x33b2, 0x06ad, 0x4246, 0x0765, 0x20cd, 0x2a03, 0x0fd1, 0x466f, 0x5581, 0x0751, + 0x36ee, 0x29a6, 0x47b8, 0x2f47, 0x3258, 0x3891, 0x571b, 0x443f, 0x0e4e, 0x67f6, + 0x4c59, 0x4da8, 0x74e7, 0x3439, 0x6ed0, 0x6d71, 0x6b3c, 0x5994, 0x21d3, 0x2e5e, + 0x3128, 0x168b, 0x35df, 0x55f7, 0x4657, 0x49c1, 0x070f, 0x7949, 0x42c0, 0x790b, + 0x7247, 0x581f, 0x18fc, 0x1bda, 0x788b, 0x1d35, 0x52e6, 0x6889, 0x18e3, 0x1ae4, + 0x172f, 0x2cca, 0x33fb, 0x26d4, 0x5d8e, 0x3fa5, 0x3950, 0x3fba, 0x17e2, 0x41b1, + 0x0a87, 0x62e9, 0x1bfe, 0x6bcb, 0x504d, 0x6528, 0x10aa, 0x1f43, 0x61f1, 0x7e7c, + 0x7e03, 0x2e9d, 0x3abe, 0x54b3, 0x68a2, 0x2bfc, 0x56f8, 0x53a0, 0x177e, 0x2d23, + 0x0472, 0x3b71, 0x29ef, 0x33c8, 0x1849, 0x5bfa, 0x1ab0, 0x1243, 0x4bfa, 0x0b7a, + 0x52ff, 0x625d, 0x44ed, 0x6f36, 0x5bb5, 0x43fa, 0x6dc1, 0x16e1, 0x364e, 0x7a2e, + 0x1f87, 0x5ad0, 0x05fa, 0x49d9, 0x2574, 0x60ae, 0x3c3a, 0x249d, 0x6944, 0x4e55, + 0x0ff0, 0x5ff3, 0x5243, 0x0bae, 0x6c56, 0x5b3d, 0x2a22, 0x706d, 0x5666, 0x0fa3, + 0x03b4, 0x288b, 0x25ec, 0x3ba3, 0x1598, 0x7b5d, 0x283f, 0x649c, 0x7968, 0x4c60, + 0x42df, 0x145c, 0x3384, 0x7641, 0x4dc7, 0x2ee8, 0x17af, 0x25b9, 0x2eb6, 0x304c, + 0x7506, 0x6e5a, 0x3e92, 0x7224, 0x5dad, 0x6cf8, 0x26f3, 0x5ebe, 0x6b42, 0x652e, + 0x7e22, 0x7195, 0x77d6, 0x1c7d, 0x7e1c, 0x058e, 0x7e9b, 0x3e0d, 0x5c19, 0x4564, + 0x6e8b, 0x44a8, 0x490c, 0x2d53, 0x1acf, 0x3316, 0x2160, 0x7923, 0x5aef, 0x1894, + 0x0619, 0x19d9, 0x6ac0, 0x47e8, 0x43ef, 0x2d18, 0x687e, 0x6bc0, 0x664b, 0x7afc, + 0x3886, 0x1680, 0x3b3c, 0x0e20, 0x2a65, 0x1385, 0x54cc, 0x7324, 0x1c42, 0x6cc3, + 0x3852, 0x7606, 0x65a1, 0x718f, 0x412f, 0x04e3, 0x21a5, 0x1631, 0x7d05, 0x42d8, + 0x69db, 0x5f54, 0x45f2, 0x4ca4, 0x0f68, 0x60f3, 0x4b5b, 0x7119, 0x439d, 0x37b6, + 0x50ff, 0x7536, 0x5a32, 0x73e9, 0x4559, 0x7219, 0x5fe8, 0x6491, 0x75fb, 0x2d0d, + 0x710e, 0x7149, 0x3111, 0x7154, 0x4ee4, 0x1118, 0x38e3, 0x1068, 0x30fa, 0x19ff, + 0x0b34, 0x3f5f, 0x3180, 0x2e18, 0x3ad7, 0x14c8, 0x72e9, 0x46ba, 0x4f98, 0x7541, + 0x4734, 0x2bbc, 0x5c67, 0x5840, 0x5939, 0x328b, 0x1fd4, 0x1750, 0x2339, 0x2768, + 0x68bb, 0x6126, 0x58a1, 0x60cf, 0x2cc1, 0x510a, 0x1505, 0x4a1e, 0x2fea, 0x406a, + 0x4bcc, 0x76af, 0x6af2, 0x5837, 0x5b34, 0x7af3, 0x7d6e, 0x578e, 0x2de8, 0x64d7, + 0x6fb9, 0x5a3d, 0x09d2, 0x15d3, 0x12ec, 0x4f23, 0x1455, 0x1a66, 0x7365, 0x6916, + 0x7ff3, 0x01d0, 0x77d0, 0x07ec, 0x5280, 0x1cf6, 0x1a16, 0x73f4, 0x03ac, 0x2331, + 0x3caa, 0x1dec, 0x3620, 0x40cc, 0x3906, 0x7a94, 0x026b, 0x6165, 0x2c15, 0x602e, + 0x278b, 0x7484, 0x6337, 0x43a8, 0x4356, 0x332b, 0x4b45, 0x6afb, 0x6c0f, 0x7c85, + 0x4393, 0x6874, 0x5239, 0x5da3, 0x09c8, 0x472a, 0x434c, 0x1173, 0x0c8a, 0x37c1, + 0x66d7, 0x1c77, 0x0d05, 0x79ac, 0x61c3, 0x063f, 0x7c51, 0x7cc5, 0x2c6e, 0x459f, + 0x5711, 0x3205, 0x18d9, 0x0468, 0x212f, 0x7124, 0x117d, 0x2e39, 0x194b, 0x666c, + 0x1ed2, 0x7ed6, 0x5072, 0x18b5, 0x4771, 0x4fef, 0x53b9, 0x3a77, 0x05b3, 0x6c77, + 0x1b83, 0x4b66, 0x36bc, 0x5e5a, 0x606b, 0x2ac2, 0x5d60, 0x5ca9, 0x23e8, 0x725f, + 0x4aa7, 0x5c88, 0x30b8, 0x6186, 0x3d0d, 0x545c, 0x1a79, 0x1073, 0x6b05, 0x0bfb, + 0x7c64, 0x5797, 0x7ee9, 0x37fc, 0x7d18, 0x59e5, 0x73fc, 0x24e2, 0x6541, 0x209b, + 0x4dda, 0x4a51, 0x7c39, 0x38ee, 0x0b1c, 0x4daf, 0x7d0c, 0x1a6d, 0x7c2d, 0x7c0e, + 0x7c1a, 0x1914, 0x7d37, 0x6f82, 0x381b, 0x75c4, 0x42a8, 0x7deb, 0x0b28, 0x3105, + 0x4b4f, 0x454d, 0x3b30, 0x43e3, 0x3846, 0x7cf9, 0x4dbb, 0x2833, 0x0fe4, 0x565a, + 0x7e16, 0x3e86, 0x5c0d, 0x2154, 0x38fa, 0x1a0a, 0x6fad, 0x7359, 0x1fc8, 0x4f8c, + 0x2cb5, 0x6ae6, 0x7c45, 0x0c7e, 0x632b, 0x4387, 0x5066, 0x2123, 0x1b77, 0x23dc, + 0x7d43, 0x4eef, 0x6c19, 0x4ba1, 0x4709, 0x7d77, 0x482d, 0x1fa9, 0x6f8e, 0x7711, + 0x250d, 0x7fc8, 0x2760, 0x2883, 0x24da, 0x3c7f, 0x1920, 0x1123, 0x39f8, 0x0588, + 0x4a7c, 0x5196, 0x63b2, 0x1b58, 0x7c26, 0x337d, 0x70b0, 0x66ac, 0x6be4, 0x3d48, + 0x656c, 0x099d, 0x75d0, 0x715f, 0x7c8f, 0x5a07, 0x4eb9, 0x0c48, 0x2689, 0x0b09, + 0x3827, 0x0945, 0x2252, 0x69b0, 0x1c17, 0x22b0, 0x120d, 0x6620, 0x42b4, 0x311c, + 0x324c, 0x74db, 0x0389, 0x262e, 0x0c26, 0x0fc5, 0x7df7, 0x1bf2, 0x52da, 0x5d82, + 0x1aa4, 0x1772, 0x5ba9, 0x05ee, 0x7408, 0x318b, 0x4360, 0x6989, 0x1c56, 0x2df1, + 0x4578, 0x2741, 0x24ee, 0x0923, 0x1a1e, 0x3c87, 0x0b53, 0x57f8, 0x0672, 0x5145, + 0x59f1, 0x2e23, 0x3b5b, 0x640a, 0x169f, 0x3dc5, 0x0a9b, 0x480e, 0x7d24, 0x08d0, + 0x6449, 0x31b2, 0x620a, 0x0dd8, 0x1650, 0x6a44, 0x20a7, 0x3f6a, 0x3335, 0x6280, + 0x217f, 0x5d3a, 0x763a, 0x7c07, 0x654d, 0x48b8, 0x2394, 0x2d94, 0x7e95, 0x757c, + 0x7e41, 0x2967, 0x4de6, 0x0b3f, 0x3d7d, 0x58dc, 0x07c7, 0x3991, 0x7660, 0x39d9, + 0x4a5d, 0x78a3, 0x5685, 0x798b, 0x708c, 0x3a9a, 0x79f3, 0x7010, 0x57a3, 0x14d3, + 0x43b2, 0x5501, 0x7ca4, 0x64e0, 0x0ded, 0x4e9a, 0x7c70, 0x26bf, 0x29bb, 0x742f, + 0x10c3, 0x4979, 0x065e, 0x1407, 0x7ef5, 0x3ae2, 0x4d36, 0x3e07, 0x3573, 0x27c6, + 0x5091, 0x11ee, 0x3808, 0x1d4d, 0x5cc8, 0x5cec, 0x2407, 0x5ee2, 0x06ec, 0x3522, + 0x6b11, 0x72f4, 0x6341, 0x4c2e, 0x35b4, 0x0163, 0x76ce, 0x4295, 0x0c07, 0x5a78, + 0x1ff3, 0x5556, 0x32aa, 0x39b6, 0x086b, 0x2b70, 0x107f, 0x46c5, 0x40eb, 0x56cd, + 0x3925, 0x7769, 0x65fe, 0x52bb, 0x1a85, 0x02eb, 0x671e, 0x0447, 0x1f5c, 0x0f2b, + 0x7384, 0x3c0f, 0x23a0, 0x77e1, 0x477b, 0x15e4, 0x6e39, 0x2342, 0x6d23, 0x2c26, + 0x2da0, 0x4a2f, 0x0273, 0x7d7f, 0x3ebd, 0x68cc, 0x0d38, 0x2bcd, 0x48c4, 0x1c88, + 0x271e, 0x5722, 0x4cab, 0x09d9, 0x3d4f, 0x333c, 0x6559, 0x53ca, 0x12a0, 0x2e4a, + 0x1862, 0x5e6b, 0x3a47, 0x30c9, 0x7588, 0x71a0, 0x4ff9, 0x4603, 0x3e38, 0x54dd, + 0x21d9, 0x6bd1, 0x7ea1, 0x760c, 0x5286, 0x37c7, 0x5c13, 0x1129, 0x7e47, 0x3ae8, + 0x7e4d, 0x7e2d, 0x3a2d, 0x6d09, 0x6762, 0x44b9, 0x7e67, 0x062a, 0x2973, 0x4c71, + 0x1ca8, 0x2ec7, 0x0d58, 0x25fd, 0x0239, 0x0bbf, 0x5d46, 0x0599, 0x18bf, 0x6bf5, + 0x143b, 0x2771, 0x5887, 0x4bb2, 0x218b, 0x1c28, 0x72cf, 0x5a18, 0x2a08, 0x74ec, + 0x7e81, 0x1ab5, 0x7646, 0x7e27, 0x1636, 0x455e, 0x76b4, 0x736a, 0x0644, 0x5077, + 0x7c13, 0x4dc0, 0x1b5d, 0x382c, 0x4813, 0x6552, 0x11f3, 0x0c0c, 0x3341, 0x7ea6, + 0x507c, 0x6291, 0x5980, 0x58ed, 0x677c, 0x709d, 0x628c, 0x641b, 0x4937, 0x621b, + 0x2213, 0x0b64, 0x4fd9, 0x699a, 0x3f76, 0x3e18, 0x71da, 0x2418, 0x5c44, 0x10d4, + 0x59c3, 0x5512, 0x20b3, 0x32bb, 0x7871, 0x4c3f, 0x33e1, 0x56de, 0x44d3, 0x1f6d, + 0x399d, 0x5ec9, 0x3a81, 0x57df, 0x286a, 0x1759, 0x75ab, 0x3e6d, 0x07d3, 0x5775, + 0x616d, 0x4711, 0x3b8a, 0x19c0, 0x4c8b, 0x2cf4, 0x766c, 0x26fe, 0x4d65, 0x034a, + 0x4198, 0x2484, 0x298d, 0x597b, 0x39e5, 0x0e66, 0x430a, 0x3425, 0x5404, 0x355e, + 0x5e2a, 0x487c, 0x3d89, 0x6b4d, 0x53c3, 0x4450, 0x22df, 0x5608, 0x2ee1, 0x190d, + 0x58e8, 0x06be, 0x1e53, 0x36ff, 0x6e85, 0x1b0e, 0x3a27, 0x1970, 0x0b4b, 0x6539, + 0x25e4, 0x68b3, 0x0ec8, 0x17f3, 0x3545, 0x1af5, 0x4df2, 0x5310, 0x3eb5, 0x3b82, + 0x048b, 0x16f2, 0x1cc2, 0x3c4b, 0x5691, 0x5db8, 0x05bd, 0x4921, 0x5fd2, 0x1fdd, + 0x0d72, 0x7444, 0x7997, 0x1f2e, 0x28b6, 0x3fcf, 0x00d3, 0x0ee1, 0x2380, 0x4e26, + 0x78af, 0x6d03, 0x34d8, 0x44a2, 0x7b88, 0x3fe4, 0x65d0, 0x6777, 0x4a69, 0x6a80, + 0x7a17, 0x535f, 0x2d3c, 0x2aab, 0x2617, 0x397a, 0x3aa6, 0x722f, 0x6c81, 0x0ab1, + 0x6d5d, 0x20e3, 0x3750, 0x639f, 0x7098, 0x6313, 0x6706, 0x6431, 0x1797, 0x3168, + 0x0253, 0x2c56, 0x79ff, 0x3e9d, 0x7859, 0x1288, 0x1534, 0x34ef, 0x0bd9, 0x41db, + 0x701c, 0x680e, 0x781b, 0x704c, 0x101b, 0x180c, 0x081b, 0x13cb, 0x3dd1, 0x2d5e, + 0x6676, 0x4517, 0x32f5, 0x612f, 0x134f, 0x4472, 0x16ab, 0x6f60, 0x7a9c, 0x4835, + 0x5318, 0x4625, 0x21f3, 0x67c7, 0x0aa7, 0x4917, 0x57d5, 0x4446, 0x45f9, 0x15da, + 0x6beb, 0x6287, 0x481a, 0x4457, 0x450d, 0x442b, 0x441a, 0x5329, 0x3791, 0x4528, + 0x3b67, 0x1ada, 0x1955, 0x4435, 0x032f, 0x4861, 0x3e52, 0x46f6, 0x6416, 0x6384, + 0x13b0, 0x126d, 0x4906, 0x4e0b, 0x675c, 0x5344, 0x2e2f, 0x3321, 0x2bb2, 0x15c9, + 0x6cee, 0x0ba4, 0x6bb6, 0x37ac, 0x59fd, 0x4b97, 0x0bf1, 0x4543, 0x6276, 0x697f, + 0x54f7, 0x4c24, 0x6455, 0x6e96, 0x1edc, 0x4461, 0x19b8, 0x68c4, 0x461d, 0x049c, + 0x31be, 0x035b, 0x3487, 0x5415, 0x611e, 0x3b9b, 0x2093, 0x57f0, 0x08dc, 0x44b3, + 0x5f7f, 0x2d4d, 0x2b29, 0x00e4, 0x5b7b, 0x4932, 0x7d30, 0x17a8, 0x41f3, 0x0ac2, + 0x4506, 0x1299, 0x5013, 0x102c, 0x0de4, 0x456f, 0x7ee0, 0x4824, 0x387d, 0x2a19, + 0x5930, 0x6c06, 0x6216, 0x2224, 0x4ae5, 0x62a2, 0x6f4f, 0x2429, 0x71ba, 0x33f2, + 0x165c, 0x5c24, 0x1346, 0x4614, 0x587e, 0x6d1a, 0x75a2, 0x0d69, 0x6a50, 0x5733, + 0x415a, 0x1873, 0x67b6, 0x3ece, 0x2903, 0x15f5, 0x1a2a, 0x5afa, 0x1187, 0x5333, + 0x6cd7, 0x58aa, 0x5c2d, 0x0eb1, 0x3c93, 0x38cc, 0x390e, 0x1fb1, 0x4dfa, 0x04a4, + 0x141b, 0x4640, 0x092f, 0x189f, 0x122d, 0x6373, 0x313c, 0x3678, 0x62fd, 0x220e, + 0x24fa, 0x307e, 0x154c, 0x67e2, 0x125c, 0x4d21, 0x1143, 0x7a58, 0x5804, 0x792e, + 0x2e43, 0x4424, 0x0692, 0x2f2c, 0x25b2, 0x6f7b, 0x0b5f, 0x5bdf, 0x4e3a, 0x16c6, + 0x1ac9, 0x62ce, 0x7e61, 0x5498, 0x067e, 0x216b, 0x2fa9, 0x4850, 0x2458, 0x78c6, + 0x3b02, 0x6deb, 0x5151, 0x38a9, 0x088b, 0x5e9d, 0x46e5, 0x170b, 0x5fae, 0x7ab7, + 0x2dfd, 0x19e4, 0x712e, 0x379b, 0x7174, 0x60d8, 0x1665, 0x136a, 0x1c62, 0x6513, + 0x47cd, 0x448d, 0x0b93, 0x7b42, 0x7626, 0x259e, 0x4584, 0x0624, 0x1158, 0x3310, + 0x5e3f, 0x5441, 0x7ebb, 0x4fd4, 0x274d, 0x3270, 0x64bc, 0x4a03, 0x15b8, 0x1cdb, + 0x40b1, 0x614a, 0x436c, 0x6acb, 0x2139, 0x4532, 0x4d94, 0x7dd0, 0x37e1, 0x24c7, + 0x6995, 0x0aee, 0x05d3, 0x74c0, 0x4b86, 0x3c64, 0x1b3d, 0x6691, 0x3197, 0x47f3, + 0x512a, 0x696e, 0x58c1, 0x6ff5, 0x7bec, 0x2d79, 0x7414, 0x4e7f, 0x3507, 0x3dec, + 0x4c13, 0x2b55, 0x52a0, 0x042c, 0x27d2, 0x1467, 0x2acc, 0x7aa6, 0x6ca2, 0x5849, + 0x4163, 0x594b, 0x357f, 0x7cd7, 0x6036, 0x6c21, 0x16fa, 0x1ee4, 0x53e4, 0x7271, + 0x509d, 0x42ea, 0x63e7, 0x3898, 0x0f6f, 0x5a44, 0x6573, 0x3f71, 0x11fa, 0x6b54, + 0x501a, 0x7935, 0x5e8c, 0x2efa, 0x403a, 0x2a34, 0x4d42, 0x338f, 0x6075, 0x483f, + 0x7f7a, 0x269b, 0x2e64, 0x1c04, 0x3e13, 0x3858, 0x1cfc, 0x0c90, 0x215a, 0x1926, + 0x296d, 0x7efb, 0x3aee, 0x764c, 0x1976, 0x78b5, 0x534a, 0x08e2, 0x549e, 0x458a, + 0x7f01, 0x1d5f, 0x6826, 0x0dff, 0x6dda, 0x76e0, 0x12ba, 0x02fd, 0x5cd4, 0x7973, + 0x5d6a, 0x6f6a, 0x7adb, 0x5c70, 0x187c, 0x5f3c, 0x5cf8, 0x50d1, 0x6096, 0x0739, + 0x2f1b, 0x22f8, 0x07b3, 0x2444, 0x1d59, 0x4c6b, 0x6b87, 0x791d, 0x1bae, 0x4681, + 0x70e0, 0x71d5, 0x3814, 0x2eaf, 0x7034, 0x340d, 0x4413, 0x185b, 0x5e85, 0x49eb, + 0x5eee, 0x64a7, 0x5cb3, 0x16b5, 0x49ad, 0x425c, 0x02b2, 0x223f, 0x2413, 0x3152, + 0x6a0b, 0x6231, 0x5bce, 0x6247, 0x3a61, 0x229a, 0x06f8, 0x284a, 0x054f, 0x62bd, + 0x2c9e, 0x65e7, 0x30e3, 0x3609, 0x352e, 0x59ac, 0x7bd5, 0x6b9f, 0x5487, 0x5621, + 0x4f53, 0x4b00, 0x29c7, 0x17ba, 0x36c6, 0x462f, 0x5f33, 0x5942, 0x573c, 0x5745, + 0x743b, 0x3e64, 0x2c1d, 0x4ba9, 0x0493, 0x4469, 0x0ea8, 0x1361, 0x26cb, 0x25c4, + 0x45ca, 0x38bb, 0x0cdd, 0x3090, 0x77a8, 0x5c3f, 0x7c7c, 0x3282, 0x7064, 0x1677, + 0x1fa0, 0x37f3, 0x2738, 0x4e91, 0x4985, 0x2ef3, 0x5e64, 0x5322, 0x0cb5, 0x0e78, + 0x3045, 0x75bd, 0x10cf, 0x6a92, 0x03f3, 0x0d84, 0x5ae9, 0x3762, 0x1ca2, 0x6820, + 0x066a, 0x4dd2, 0x1590, 0x5899, 0x2b01, 0x678e, 0x48de, 0x32cd, 0x1413, 0x21eb, + 0x0d30, 0x4c83, 0x0ea0, 0x53dc, 0x00ab, 0x6d35, 0x64ec, 0x3057, 0x4b70, 0x21fd, + 0x75e5, 0x3294, 0x6a59, 0x574e, 0x7cb0, 0x2e88, 0x2f5c, 0x6a6b, 0x3667, 0x4cd7, + 0x3d69, 0x2f95, 0x0df9, 0x2ec1, 0x7a6d, 0x188e, 0x4891, 0x02c4, 0x3356, 0x59be, + 0x4ea6, 0x2e76, 0x5f03, 0x1d71, 0x6362, 0x6b66, 0x4223, 0x4175, 0x43be, 0x7511, + 0x1b8d, 0x67d1, 0x55e3, 0x7dab, 0x4cc5, 0x2676, 0x550d, 0x5215, 0x5dce, 0x3463, + 0x306d, 0x3da2, 0x1e33, 0x3ee9, 0x14df, 0x6e65, 0x3226, 0x4d10, 0x119e, 0x6d9b, + 0x5638, 0x291e, 0x57af, 0x2f83, 0x7833, 0x1610, 0x7a47, 0x0529, 0x09f3, 0x6efa, + 0x1fff, 0x03bf, 0x4ab1, 0x6139, 0x73c8, 0x473d, 0x3ed7, 0x36cf, 0x5562, 0x7337, + 0x2793, 0x4ef7, 0x1cca, 0x6e9e, 0x00b3, 0x3bb6, 0x5a84, 0x2896, 0x08ad, 0x325f, + 0x60fa, 0x6fc0, 0x09a4, 0x20ae, 0x0c13, 0x3d90, 0x1033, 0x580b, 0x49f2, 0x498c, + 0x68e6, 0x40fe, 0x39c2, 0x0fae, 0x5c92, 0x32ff, 0x6848, 0x5ab9, 0x5203, 0x0376, + 0x32b6, 0x35f2, 0x253e, 0x31d9, 0x0613, 0x41c4, 0x0d52, 0x6dd4, 0x0877, 0x5671, + 0x0a19, 0x5430, 0x2a96, 0x7b9f, 0x2be7, 0x31f0, 0x2b7c, 0x3451, 0x0855, 0x70f8, + 0x4fc3, 0x1b27, 0x1e1d, 0x34a2, 0x016f, 0x3bae, 0x7269, 0x1359, 0x2cec, 0x2bc5, + 0x67bf, 0x4638, 0x35c0, 0x2fbc, 0x2033, 0x04b7, 0x60c7, 0x7b55, 0x4a49, 0x513d, + 0x76da, 0x25f7, 0x2064, 0x19d3, 0x149c, 0x0ef4, 0x2dba, 0x786c, 0x42a1, 0x74ff, + 0x3015, 0x71ed, 0x378a, 0x3a40, 0x4033, 0x68df, 0x634d, 0x15a3, 0x23f2, 0x447c, + 0x78f7, 0x4cea, 0x7d99, 0x3239, 0x4c3a, 0x230b, 0x56a7, 0x0562, 0x6502, 0x1989, + 0x34b8, 0x1ef7, 0x7300, 0x7b68, 0x7b1e, 0x7b31, 0x5b11, 0x0a2c, 0x4f6a, 0x6eb1, + 0x6b1d, 0x2664, 0x1824, 0x6470, 0x258d, 0x372b, 0x028d, 0x51de, 0x7775, 0x5b48, + 0x6190, 0x6680, 0x10f7, 0x754a, 0x290c, 0x17c3, 0x3931, 0x0c5c, 0x748c, 0x7d4b, + 0x3c53, 0x645d, 0x6d3d, 0x0177, 0x660a, 0x6c61, 0x6f20, 0x0add, 0x2284, 0x5595, + 0x2c40, 0x33dc, 0x52c7, 0x6d89, 0x0a4f, 0x1bc6, 0x74af, 0x06d7, 0x409c, 0x420e, + 0x40f7, 0x2a2d, 0x30c2, 0x4521, 0x2807, 0x1560, 0x6e53, 0x7de4, 0x56d9, 0x431e, + 0x3cd5, 0x20f7, 0x6aba, 0x5901, 0x0233, 0x12b4, 0x46d1, 0x7078, 0x2651, 0x7dbf, + 0x1a41, 0x4270, 0x4b17, 0x502e, 0x108b, 0x4cfe, 0x0833, 0x1047, 0x24b6, 0x0a63, + 0x235c, 0x078f, 0x672a, 0x524e, 0x3d17, 0x2d68, 0x3f3e, 0x4fa1, 0x15fe, 0x29d0, + 0x0453, 0x538b, 0x5df2, 0x3965, 0x6fe4, 0x3718, 0x79df, 0x5f9a, 0x02f7, 0x0bb9, + 0x1317, 0x47e2, 0x0dac, 0x28cb, 0x1ea4, 0x44ce, 0x1a91, 0x2f71, 0x13e3, 0x08f7, + 0x695d, 0x7bb4, 0x4795, 0x5e07, 0x0f37, 0x5ffe, 0x5466, 0x3ddb, 0x1d21, 0x077b, + 0x0517, 0x5b96, 0x1f68, 0x368e, 0x0073, 0x494d, 0x4e6e, 0x6a21, 0x77fb, 0x55ab, + 0x7390, 0x0ffb, 0x51cb, 0x2b44, 0x0137, 0x3ffb, 0x23ba, 0x2555, 0x3c1b, 0x6ee8, + 0x01fb, 0x4699, 0x041b, 0x1e6c, 0x003b, 0x00ff, 0x3bf1, 0x51a1, 0x04ed, 0x4e44, + 0x3f14, 0x79b5, 0x13b9, 0x02cd, 0x774b, 0x7462, 0x4072, 0x225a, 0x248c, 0x4aed, + 0x3098, 0x56af, 0x5538, 0x4a87, 0x097a, 0x49c8, 0x2a6c, 0x1df3, 0x2514, 0x3998, + 0x72d6, 0x17fa, 0x348e, 0x78cd, 0x609d, 0x6795, 0x203a, 0x4277, 0x11d0, 0x63bd, + 0x4139, 0x16d0, 0x7f50, 0x2943, 0x67fc, 0x3ac4, 0x5ec4, 0x69e1, 0x4f29, 0x2c74, + 0x43e9, 0x70b6, 0x5d40, 0x5cce, 0x54e3, 0x1b63, 0x560e, 0x7a1d, 0x4867, 0x41f9, + 0x2f32, 0x64c2, 0x26a1, 0x703a, 0x0e7e, 0x5f09, 0x5abf, 0x301b, 0x1566, 0x13e9, + 0x2949, 0x3a03, 0x21af, 0x5be9, 0x6e0f, 0x0d0e, 0x1276, 0x489a, 0x5d1c, 0x72a5, + 0x11c9, 0x768a, 0x33b7, 0x5999, 0x5052, 0x6262, 0x796d, 0x0593, 0x65a6, 0x2d12, + 0x150a, 0x07f1, 0x66dc, 0x3a7c, 0x0b21, 0x3e8b, 0x39fd, 0x22b5, 0x3b60, 0x7581, + 0x4d3b, 0x39bb, 0x2723, 0x112e, 0x163b, 0x0b69, 0x4d6a, 0x1b13, 0x34dd, 0x316d, + 0x57da, 0x4e10, 0x5f84, 0x242e, 0x1232, 0x62d3, 0x115d, 0x3c69, 0x63ec, 0x192b, + 0x6b8c, 0x624c, 0x45cf, 0x3767, 0x7a72, 0x3da7, 0x08b2, 0x41c9, 0x2069, 0x198e, + 0x6f25, 0x5906, 0x131c, 0x6a26, 0x097f, 0x70bb, 0x65ab, 0x62d8, 0x1da1, 0x61cc, + 0x638d, 0x335f, 0x5178, 0x1dca, 0x2ff2, 0x69b8, 0x41a0, 0x62aa, 0x0ce5, 0x056a, + 0x7faa, 0x66b7, 0x1d9c, 0x3f94, 0x7fa2, 0x5530, 0x01aa, 0x2865, 0x4ed1, 0x20d1, + 0x343d, 0x0713, 0x3fa9, 0x61f5, 0x1247, 0x1f8b, 0x0fa7, 0x3388, 0x7199, 0x1ad3, + 0x0e24, 0x21a9, 0x721d, 0x30fe, 0x1754, 0x4bd0, 0x691a, 0x3624, 0x6878, 0x61c7, + 0x18b9, 0x5d64, 0x59e9, 0x7c31, 0x2837, 0x2cb9, 0x7715, 0x63b6, 0x0949, 0x0c2a, + 0x0927, 0x0a9f, 0x48bc, 0x7664, 0x26c3, 0x5095, 0x5a7c, 0x6602, 0x4a33, 0x3d53, + 0x7610, 0x7e6b, 0x1c2c, 0x0648, 0x641f, 0x59c7, 0x5779, 0x2991, 0x06c2, 0x3549, + 0x1f32, 0x65d4, 0x6317, 0x0bdd, 0x6f64, 0x6bef, 0x6388, 0x6bba, 0x035f, 0x5b7f, + 0x2228, 0x75a6, 0x38d0, 0x6301, 0x5be3, 0x3b06, 0x6517, 0x7ebf, 0x0af2, 0x7bf0, + 0x7cdb, 0x6577, 0x385c, 0x54a2, 0x50d5, 0x70e4, 0x3156, 0x30e7, 0x3e68, 0x77ac, + 0x6a96, 0x48e2, 0x2e8c, 0x335a, 0x5219, 0x563c, 0x733b, 0x09a8, 0x35f6, 0x2beb, + 0x2fc0, 0x2dbe, 0x230f, 0x4f6e, 0x0c60, 0x2c44, 0x4322, 0x4b1b, 0x538f, 0x1ea8, + 0x3692, 0x23be, 0x7466, 0x2518, 0x69e5, 0x2f36, 0x72a9, 0x66e0, 0x4e14, 0x7a76, + 0x1dce, 0x01ae, 0x4bd4, 0x094d, 0x2995, 0x222c, 0x77b0, 0x2313, 0x01b2, 0x7fd3, + 0x5173, 0x465e, 0x138c, 0x3cb1, 0x7fcf, 0x07ce, 0x5a1f, 0x0ecf, 0x541c, 0x245f, + 0x0740, 0x2b08, 0x04be, 0x1a48, 0x7691, 0x771c, 0x5f5e, 0x069c, 0x7f23, 0x6e09, + 0x1f1c, 0x50ec, 0x5770, 0x516f, 0x7742, 0x5d13, 0x6645, 0x1dc5, 0x1435, 0x7ad5, + 0x2b9e, 0x6f99, 0x4249, 0x0754, 0x0e51, 0x6d74, 0x465a, 0x5822, 0x1732, 0x3fbd, + 0x10ad, 0x54b6, 0x29f2, 0x0b7d, 0x3651, 0x60b1, 0x6c59, 0x288e, 0x42e2, 0x25bc, + 0x26f6, 0x1c80, 0x490f, 0x1897, 0x664e, 0x1388, 0x4132, 0x5f57, 0x5102, 0x6494, + 0x38e6, 0x2e1b, 0x5c6a, 0x276b, 0x2fed, 0x7af6, 0x12ef, 0x01d3, 0x3cad, 0x6168, + 0x4b48, 0x5da6, 0x0d08, 0x45a2, 0x194e, 0x4ff2, 0x606e, 0x5c8b, 0x7c67, 0x24e5, + 0x7d0f, 0x6f85, 0x3b33, 0x565d, 0x1fcb, 0x438a, 0x470c, 0x7fcb, 0x4a7f, 0x66af, + 0x4ebc, 0x69b3, 0x038c, 0x5d85, 0x1c59, 0x3c8a, 0x16a2, 0x31b5, 0x2182, 0x2d97, + 0x07ca, 0x798e, 0x7ca7, 0x7432, 0x3576, 0x5cef, 0x35b7, 0x5559, 0x3928, 0x044a, + 0x6e3c, 0x7d82, 0x4cae, 0x2e4d, 0x3e3b, 0x37ca, 0x6765, 0x2eca, 0x143e, 0x5a1b, + 0x76b7, 0x382f, 0x5983, 0x621e, 0x5c47, 0x4c42, 0x286d, 0x4714, 0x419b, 0x3428, + 0x22e2, 0x3702, 0x0ecb, 0x3b85, 0x5fd5, 0x3fd2, 0x7b8b, 0x5362, 0x6d60, 0x6434, + 0x1537, 0x704f, 0x32f8, 0x4838, 0x45fc, 0x442e, 0x0332, 0x1270, 0x6cf1, 0x4546, + 0x19bb, 0x5418, 0x2b2c, 0x0ac5, 0x3880, 0x62a5, 0x5881, 0x1876, 0x6cda, 0x1fb4, + 0x313f, 0x67e5, 0x0695, 0x16c9, 0x245b, 0x5ea0, 0x7177, 0x4490, 0x5e42, 0x4a06, + 0x4d97, 0x74c3, 0x58c4, 0x3def, 0x6ca5, 0x6c24, 0x0f72, 0x7938, 0x7f7d, 0x0c93, + 0x534d, 0x0e02, 0x7ade, 0x073c, 0x1bb1, 0x3410, 0x49b0, 0x6234, 0x2ca1, 0x6ba2, + 0x5f36, 0x4bac, 0x0ce0, 0x167a, 0x0cb8, 0x0d87, 0x2b04, 0x4c86, 0x75e8, 0x6a6e, + 0x4894, 0x1d74, 0x55e6, 0x3466, 0x11a1, 0x1613, 0x73cb, 0x4efa, 0x60fd, 0x580e, + 0x684b, 0x31dc, 0x2a99, 0x70fb, 0x2cef, 0x04ba, 0x149f, 0x71f0, 0x78fa, 0x0565, + 0x5b14, 0x6473, 0x10fa, 0x7d4e, 0x2287, 0x1bc9, 0x280a, 0x20fa, 0x1a44, 0x104a, + 0x3f41, 0x3968, 0x0daf, 0x08fa, 0x1d24, 0x4950, 0x013a, 0x469c, 0x3f17, 0x225d, + 0x2a6f, 0x78d0, 0x7f53, 0x2c77, 0x486a, 0x5f0c, 0x6e12, 0x768d, 0x150d, 0x22b8, + 0x4d6d, 0x2431, 0x45d2, 0x1991, 0x1da4, 0x69bb, 0x7fa5, 0x0716, 0x0e27, 0x3627, + 0x7718, 0x7667, 0x1c2f, 0x354c, 0x0362, 0x3b09, 0x50d8, 0x48e5, 0x2fc3, 0x4b1e, + 0x72ac, 0x0950, 0x138f, 0x2462, 0x7f26, 0x5d16, 0x0e54, 0x54b9, 0x26f9, 0x5f5a, + 0x12f2, 0x45a5, 0x3b36, 0x66b2, 0x2185, 0x5cf2, 0x3e3e, 0x3832, 0x22e5, 0x5365, + 0x0335, 0x0ac8, 0x0698, 0x4a09, 0x7f80, 0x3413, 0x0cbb, 0x1d77, 0x684e, 0x71f3, + 0x280d, 0x08fd, 0x7f56, 0x22bb, 0x0e2a, 0x3b0c, 0x7f29, 0x45a8, 0x0338, 0x1d7a, + 0x7f2c, 0x7f1f, 0x7f49, 0x7f1c, 0x7f39, 0x50be, 0x3b1c, 0x031b, 0x50cb, 0x1c22, + 0x1d97, 0x0e1a, 0x7f46, 0x3f0a, 0x6e05, 0x4d60, 0x3b29, 0x26ec, 0x729f, 0x7f19, + 0x0328, 0x3e31, 0x7f73, 0x6841, 0x45b5, 0x1218, 0x3b46, 0x6df5, 0x7f36, 0x5aa5, + 0x2472, 0x2a52, 0x0345, 0x1f18, 0x5375, 0x50bb, 0x1d87, 0x3f8f, 0x685e, 0x76fb, + 0x0e37, 0x662b, 0x0726, 0x728f, 0x3b19, 0x359d, 0x50e8, 0x12d5, 0x22c8, 0x5969, + 0x4d7d, 0x0318, 0x7f63, 0x55cc, 0x78e0, 0x27f0, 0x5382, 0x0c53, 0x732e, 0x2fb3, + 0x50c8, 0x7cce, 0x3e5b, 0x2e7f, 0x1f25, 0x576c, 0x4a26, 0x1c1f, 0x0352, 0x6f57, + 0x38c3, 0x650a, 0x3f9c, 0x4ec4, 0x7f9d, 0x7f95, 0x1d94, 0x0972, 0x516b, 0x4193, + 0x686b, 0x1747, 0x0f9a, 0x0e17, 0x7708, 0x59dc, 0x091a, 0x26b6, 0x5ab2, 0x2694, + 0x54d6, 0x485a, 0x7f43, 0x11c3, 0x5eb7, 0x43dc, 0x247f, 0x773e, 0x3be4, 0x3f07, + 0x2a5f, 0x552b, 0x72c9, 0x6090, 0x3b53, 0x0b14, 0x7960, 0x14fd, 0x6e02, 0x293c, + 0x5d0f, 0x33aa, 0x1225, 0x57cd, 0x2716, 0x4d5d, 0x45c2, 0x63df, 0x08a5, 0x6f18, + 0x35aa, 0x7c9a, 0x1c4c, 0x2175, 0x3b26, 0x7c5a, 0x46ff, 0x4eaf, 0x50f5, 0x6641, + 0x6c4c, 0x26e9, 0x12e2, 0x5c5d, 0x4b3b, 0x1941, 0x0733, 0x5a12, 0x01a5, 0x137f, + 0x729c, 0x7459, 0x1dc1, 0x2988, 0x6638, 0x5763, 0x7684, 0x7f16, 0x0e44, 0x2b91, + 0x1725, 0x29e5, 0x4d8a, 0x716a, 0x6ccd, 0x0688, 0x0325, 0x32eb, 0x19ae, 0x3873, + 0x5976, 0x1431, 0x6e2f, 0x3e2e, 0x22d5, 0x2860, 0x5fc8, 0x6d53, 0x55d9, 0x75db, + 0x5f29, 0x0cab, 0x7f70, 0x6c98, 0x7ad1, 0x49a3, 0x78ed, 0x2ce2, 0x73be, 0x683e, + 0x27fd, 0x10ed, 0x3f34, 0x1d17, 0x12ff, 0x2639, 0x7b06, 0x0895, 0x45b2, 0x320e, + 0x195e, 0x6b6f, 0x5f67, 0x2b9a, 0x5112, 0x1215, 0x2706, 0x71c2, 0x25cc, 0x34c0, + 0x66bf, 0x0394, 0x4ecc, 0x7950, 0x3b43, 0x40d3, 0x6f95, 0x39e0, 0x2192, 0x1afc, + 0x31c5, 0x6df2, 0x5cff, 0x32d4, 0x35c7, 0x5035, 0x5d23, 0x0c31, 0x6655, 0x5ea7, + 0x7f33, 0x33b1, 0x06ac, 0x411c, 0x0e61, 0x4245, 0x0764, 0x5aa2, 0x54c6, 0x20cc, + 0x2a02, 0x2f15, 0x139c, 0x0fd0, 0x466e, 0x3bd4, 0x246f, 0x5580, 0x0750, 0x4055, + 0x095d, 0x36ed, 0x29a5, 0x2a4f, 0x72b9, 0x47b7, 0x2f46, 0x201d, 0x0ad5, 0x3257, + 0x3890, 0x38b3, 0x0342, 0x571a, 0x443e, 0x636b, 0x06a5, 0x0e4d, 0x67f5, 0x1f15, + 0x4a16, 0x4c58, 0x4da7, 0x6402, 0x22f2, 0x74e6, 0x3438, 0x731e, 0x5372, 0x6ecf, + 0x6d70, 0x4305, 0x383f, 0x6b3b, 0x5993, 0x50b8, 0x3e4b, 0x21d2, 0x2e5d, 0x51fc, + 0x0cc8, 0x3127, 0x168a, 0x515b, 0x1d84, 0x35de, 0x55f6, 0x658e, 0x3420, 0x4656, + 0x49c0, 0x3f8c, 0x7f8d, 0x070e, 0x7948, 0x018d, 0x7200, 0x42bf, 0x790a, 0x0f8a, + 0x685b, 0x7246, 0x581e, 0x68fd, 0x281a, 0x18fb, 0x1bd9, 0x76f8, 0x090a, 0x788a, + 0x1d34, 0x5a5f, 0x3634, 0x52e5, 0x6888, 0x1715, 0x0e34, 0x18e2, 0x1ae3, 0x422c, + 0x7725, 0x172e, 0x2cc9, 0x6628, 0x7674, 0x33fa, 0x26d3, 0x1eff, 0x7fb2, 0x5d8d, + 0x3fa4, 0x0195, 0x0723, 0x394f, 0x3fb9, 0x53ff, 0x69c8, 0x17e1, 0x41b0, 0x728c, + 0x1db1, 0x0a86, 0x62e8, 0x7793, 0x036f, 0x1bfd, 0x6bca, 0x46ef, 0x3b16, 0x504c, + 0x6527, 0x7cf2, 0x3559, 0x10a9, 0x1f42, 0x359a, 0x1c3c, 0x61f0, 0x7e7b, 0x07ad, + 0x48f2, 0x7e02, 0x2e9c, 0x6c3c, 0x50e5, 0x3abd, 0x54b2, 0x4115, 0x2fd0, 0x68a1, + 0x2bfb, 0x12d2, 0x4b2b, 0x56f7, 0x539f, 0x6051, 0x151a, 0x177d, 0x2d22, 0x5fb8, + 0x22c5, 0x0471, 0x3b70, 0x417e, 0x769a, 0x29ee, 0x33c7, 0x5966, 0x6e1f, 0x1848, + 0x5bf9, 0x6748, 0x243e, 0x1aaf, 0x1242, 0x6cbd, 0x4d7a, 0x4bf9, 0x0b79, 0x5e25, + 0x45df, 0x52fe, 0x625c, 0x0315, 0x199e, 0x44ec, 0x6f35, 0x5864, 0x2c84, 0x5bb4, + 0x43f9, 0x7ac1, 0x7f60, 0x6dc0, 0x16e0, 0x0f55, 0x4877, 0x364d, 0x7a2d, 0x55c9, + 0x5f19, 0x1f86, 0x5acf, 0x2ae7, 0x2a7c, 0x05f9, 0x49d8, 0x73ae, 0x78dd, 0x2573, + 0x60ad, 0x1482, 0x226a, 0x3c39, 0x249c, 0x27ed, 0x3f24, 0x6943, 0x4e54, 0x011d, + 0x6edc, 0x0fef, 0x5ff2, 0x3682, 0x537f, 0x5242, 0x0bad, 0x2f65, 0x6d7d, 0x6c55, + 0x5b3c, 0x0c50, 0x4312, 0x2a21, 0x706c, 0x4cf2, 0x3445, 0x5665, 0x0fa2, 0x35e6, + 0x732b, 0x03b3, 0x288a, 0x3d84, 0x74f3, 0x25eb, 0x3ba2, 0x2fb0, 0x22ff, 0x1597, + 0x7b5c, 0x2658, 0x59a0, 0x283e, 0x649b, 0x3146, 0x50c5, 0x7967, 0x4c5f, 0x2ea3, + 0x6b48, 0x42de, 0x145b, 0x7ccb, 0x384c, 0x3383, 0x7640, 0x1d53, 0x21df, 0x4dc6, + 0x2ee7, 0x6a86, 0x3e58, 0x17ae, 0x25b8, 0x3276, 0x2e6a, 0x2eb5, 0x304b, 0x2e7c, + 0x5209, 0x7505, 0x6e59, 0x2f77, 0x6802, 0x3e91, 0x7223, 0x6307, 0x1f22, 0x5dac, + 0x6cf7, 0x6a74, 0x0e5a, 0x26f2, 0x5ebd, 0x5769, 0x06b2, 0x6b41, 0x652d, 0x5304, + 0x4c65, 0x7e21, 0x7194, 0x7600, 0x4a23, 0x77d5, 0x1c7c, 0x53be, 0x4db4, 0x7e1b, + 0x058d, 0x1c1c, 0x640f, 0x7e9a, 0x3e0c, 0x32af, 0x5727, 0x5c18, 0x4563, 0x2218, + 0x034f, 0x6e8a, 0x44a7, 0x179c, 0x444b, 0x490b, 0x2d52, 0x6f54, 0x6378, 0x1ace, + 0x3315, 0x4b8b, 0x389d, 0x215f, 0x7922, 0x5bd3, 0x38c0, 0x5aee, 0x1893, 0x3072, + 0x3264, 0x0618, 0x19d8, 0x6507, 0x0ae2, 0x6abf, 0x47e7, 0x4e73, 0x49cd, 0x43ee, + 0x2d17, 0x1237, 0x3f99, 0x687d, 0x6bbf, 0x2e91, 0x4663, 0x664a, 0x7afb, 0x4ec1, + 0x342d, 0x3885, 0x167f, 0x78ff, 0x071b, 0x3b3b, 0x0e1f, 0x1d8c, 0x7f9a, 0x2a64, + 0x1384, 0x22da, 0x7955, 0x54cb, 0x7323, 0x7f92, 0x019a, 0x1c41, 0x6cc2, 0x5f1e, + 0x35eb, 0x3851, 0x7605, 0x637d, 0x1d91, 0x65a0, 0x718e, 0x282c, 0x5603, 0x412e, + 0x04e2, 0x096f, 0x659b, 0x21a4, 0x1630, 0x6b81, 0x1697, 0x7d04, 0x42d7, 0x2fe2, + 0x5168, 0x69da, 0x5f53, 0x423e, 0x3134, 0x45f1, 0x4ca3, 0x4190, 0x0cd5, 0x0f67, + 0x60f2, 0x227c, 0x7253, 0x4b5a, 0x7118, 0x18a9, 0x6868, 0x439c, 0x37b5, 0x7cb9, + 0x582b, 0x50fe, 0x7535, 0x1744, 0x690a, 0x5a31, 0x73e8, 0x7a88, 0x7917, 0x4558, + 0x7218, 0x7189, 0x0f97, 0x5fe7, 0x6490, 0x2edc, 0x42cc, 0x75fa, 0x2d0c, 0x0e14, + 0x720d, 0x710d, 0x7148, 0x3f53, 0x1be6, 0x3110, 0x7153, 0x0939, 0x7705, 0x4ee3, + 0x1117, 0x3371, 0x1908, 0x38e2, 0x1067, 0x59d9, 0x2827, 0x30f9, 0x19fe, 0x0c72, + 0x7897, 0x0b33, 0x3f5e, 0x48ac, 0x0917, 0x317f, 0x2e17, 0x08c4, 0x1d41, 0x3ad6, + 0x14c7, 0x26b3, 0x5a6c, 0x72e8, 0x46b9, 0x02df, 0x0771, 0x4f97, 0x7540, 0x1556, + 0x5aaf, 0x4733, 0x2bbb, 0x4ce0, 0x4252, 0x5c66, 0x583f, 0x2691, 0x0e6e, 0x5938, + 0x328a, 0x7da1, 0x20d9, 0x1fd3, 0x174f, 0x55fe, 0x54d3, 0x2338, 0x2767, 0x58e3, + 0x2a0f, 0x68ba, 0x6125, 0x4857, 0x2f22, 0x58a0, 0x60ce, 0x7dc6, 0x33be, 0x2cc0, + 0x5109, 0x67ec, 0x7f40, 0x1504, 0x4a1d, 0x6c43, 0x06b9, 0x2fe9, 0x4069, 0x11c0, + 0x4129, 0x4bcb, 0x76ae, 0x1ba8, 0x6662, 0x6af1, 0x5836, 0x4f19, 0x5eb4, 0x5b33, + 0x7af2, 0x752c, 0x0c3e, 0x7d6d, 0x578d, 0x43d9, 0x5d30, 0x2de7, 0x64d6, 0x0159, + 0x558d, 0x6fb8, 0x5a3c, 0x3088, 0x247c, 0x09d1, 0x15d2, 0x3670, 0x075d, 0x12eb, + 0x4f22, 0x773b, 0x4062, 0x1454, 0x1a65, 0x3dbd, 0x467b, 0x7364, 0x6915, 0x04dd, + 0x3be1, 0x7ff2, 0x01cf, 0x1e4e, 0x0fdd, 0x77cf, 0x07eb, 0x3f04, 0x13a9, 0x527f, + 0x1cf5, 0x2537, 0x29b2, 0x1a15, 0x73f3, 0x2504, 0x2a5c, 0x03ab, 0x2330, 0x5230, + 0x36fa, 0x3ca9, 0x1deb, 0x5528, 0x096a, 0x361f, 0x40cb, 0x3947, 0x47c4, 0x3905, + 0x7a93, 0x347e, 0x72c6, 0x026a, 0x6164, 0x28ad, 0x2f53, 0x2c14, 0x602d, 0x608d, + 0x202a, 0x278a, 0x7483, 0x5de9, 0x40e0, 0x6336, 0x43a7, 0x4d2b, 0x3b50, 0x4355, + 0x332a, 0x3d72, 0x6fa2, 0x4b44, 0x6afa, 0x0b11, 0x39ed, 0x6c0e, 0x7c84, 0x3241, + 0x4ed9, 0x4392, 0x6873, 0x6596, 0x795d, 0x5238, 0x5da2, 0x6e80, 0x03a1, 0x09c7, + 0x4729, 0x14fa, 0x66cc, 0x434b, 0x1172, 0x36b1, 0x31d2, 0x0c89, 0x37c0, 0x1266, + 0x6dff, 0x66d6, 0x1c76, 0x5653, 0x1b09, 0x0d04, 0x79ab, 0x2939, 0x219f, 0x61c2, + 0x063e, 0x70da, 0x32e1, 0x7c50, 0x7cc4, 0x11b9, 0x5d0c, 0x2c6d, 0x459e, 0x5a9b, + 0x35d4, 0x5710, 0x3204, 0x33a7, 0x5042, 0x18d8, 0x0467, 0x6db6, 0x511f, 0x212e, + 0x7123, 0x114d, 0x1222, 0x117c, 0x2e38, 0x2f9e, 0x2ba7, 0x194a, 0x666b, 0x57ca, + 0x5f74, 0x1ed1, 0x7ed5, 0x133b, 0x71cf, 0x5071, 0x18b4, 0x162b, 0x2713, 0x4770, + 0x4fee, 0x3a22, 0x25d9, 0x53b8, 0x3a76, 0x4d5a, 0x34cd, 0x05b2, 0x6c76, 0x784e, + 0x321b, 0x1b82, 0x4b65, 0x7a62, 0x45bf, 0x36bb, 0x5e59, 0x1585, 0x196b, 0x606a, + 0x2ac1, 0x63dc, 0x6b7c, 0x5d5f, 0x5ca8, 0x0544, 0x7b13, 0x23e7, 0x725e, 0x2059, + 0x08a2, 0x4aa6, 0x5c87, 0x0a0e, 0x2646, 0x30b7, 0x6185, 0x6f15, 0x130c, 0x3d0c, + 0x545b, 0x51c0, 0x1f4f, 0x1a78, 0x1072, 0x3918, 0x35a7, 0x6b04, 0x0bfa, 0x329d, + 0x10b6, 0x7c63, 0x5796, 0x7c97, 0x3566, 0x7ee8, 0x37fb, 0x23fa, 0x61fd, 0x7d17, + 0x59e4, 0x1692, 0x1c49, 0x73fb, 0x24e1, 0x0b46, 0x7e88, 0x6540, 0x209a, 0x2172, + 0x07ba, 0x4dd9, 0x4a50, 0x707f, 0x5059, 0x7c38, 0x38ed, 0x1fbb, 0x3b23, 0x0b1b, + 0x4dae, 0x7e09, 0x6534, 0x7d0b, 0x1a6c, 0x7c57, 0x7cff, 0x7c2c, 0x7c0d, 0x380e, + 0x6bd7, 0x7c19, 0x1913, 0x4a6f, 0x46fc, 0x7d36, 0x6f81, 0x2753, 0x1c0a, 0x381a, + 0x75c3, 0x4eac, 0x037c, 0x42a7, 0x7dea, 0x1a97, 0x3aca, 0x0b27, 0x3104, 0x38d6, + 0x50f2, 0x4b4e, 0x454c, 0x75ee, 0x54bf, 0x3b2f, 0x43e2, 0x663e, 0x4122, 0x3845, + 0x7cf8, 0x45e5, 0x2ea9, 0x4dba, 0x2832, 0x42d2, 0x6c49, 0x0fe3, 0x5659, 0x25df, + 0x7e0f, 0x7e15, 0x3e85, 0x26e6, 0x48ff, 0x5c0c, 0x2153, 0x060c, 0x2c08, 0x38f9, + 0x1a09, 0x3c9d, 0x12df, 0x6fac, 0x7358, 0x77c3, 0x68ae, 0x1fc7, 0x4f8b, 0x5c5a, + 0x2fdd, 0x2cb4, 0x6ae5, 0x7d61, 0x5704, 0x7c44, 0x0c7d, 0x0cf8, 0x4b38, 0x632a, + 0x4386, 0x09bb, 0x53ac, 0x5065, 0x2122, 0x193e, 0x605e, 0x1b76, 0x23db, 0x30ab, + 0x395c, 0x7d42, 0x4eee, 0x04ae, 0x0730, 0x6c18, 0x4ba0, 0x6a62, 0x3fc6, 0x4708, + 0x7d76, 0x5a0f, 0x540c, 0x482c, 0x1fa8, 0x4484, 0x3fb1, 0x6f8d, 0x7710, 0x5163, + 0x01a2, 0x250c, 0x7fc7, 0x0ec3, 0x5d9a, 0x275f, 0x2882, 0x137c, 0x7fbf, 0x24d9, + 0x3c7e, 0x7426, 0x41bd, 0x191f, 0x1122, 0x4e04, 0x7299, 0x39f7, 0x0587, 0x3e7f, + 0x17ee, 0x4a7b, 0x5195, 0x7456, 0x69d5, 0x63b1, 0x1b57, 0x702e, 0x0a93, 0x7c25, + 0x337c, 0x4bc4, 0x1dbe, 0x70af, 0x66ab, 0x20c5, 0x62f5, 0x6be3, 0x3d47, 0x2985, + 0x77a0, 0x656b, 0x099c, 0x2c38, 0x2cd6, 0x75cf, 0x715e, 0x1425, 0x6635, 0x7c8e, + 0x5a06, 0x5757, 0x173b, 0x4eb8, 0x0c47, 0x5760, 0x7732, 0x2688, 0x0b08, 0x57c1, + 0x3407, 0x3826, 0x0944, 0x5f4e, 0x7681, 0x2251, 0x69af, 0x3540, 0x26e0, 0x1c16, + 0x22af, 0x7f13, 0x1f0c, 0x120c, 0x661f, 0x595d, 0x18ef, 0x42b3, 0x311b, 0x464a, + 0x0e41, 0x324b, 0x74da, 0x6b2f, 0x1af0, 0x0388, 0x262d, 0x2b8e, 0x4239, 0x0c25, + 0x0fc4, 0x36e1, 0x6895, 0x7df6, 0x1bf1, 0x109d, 0x1722, 0x52d9, 0x5d81, 0x17d5, + 0x52f2, 0x1aa3, 0x1771, 0x29e2, 0x3641, 0x5ba8, 0x05ed, 0x3c2d, 0x4c06, 0x7407, + 0x318a, 0x58b4, 0x4d87, 0x435f, 0x6988, 0x4b79, 0x0b86, 0x1c55, 0x2df0, 0x7167, + 0x5e32, 0x4577, 0x2740, 0x15ab, 0x124f, 0x24ed, 0x0922, 0x312f, 0x6cca, 0x1a1d, + 0x3c86, 0x4ded, 0x1abc, 0x0b52, 0x57f7, 0x0685, 0x244b, 0x0671, 0x5144, 0x46d8, + 0x6269, 0x59f0, 0x2e22, 0x6ce1, 0x0322, 0x3b5a, 0x6409, 0x48f9, 0x530b, 0x169e, + 0x3dc4, 0x32e8, 0x45ec, 0x0a9a, 0x480d, 0x440d, 0x44f9, 0x7d23, 0x08cf, 0x2b1c, + 0x19ab, 0x6448, 0x31b1, 0x6111, 0x6f42, 0x6209, 0x0dd7, 0x3870, 0x5871, 0x164f, + 0x6a43, 0x67a9, 0x33d4, 0x20a6, 0x3f69, 0x5c37, 0x5973, 0x3334, 0x627f, 0x2206, + 0x29fb, 0x217e, 0x5d39, 0x142e, 0x76a7, 0x7639, 0x7c06, 0x4806, 0x1855, 0x654c, + 0x48b7, 0x4c9e, 0x6e2c, 0x2393, 0x2d93, 0x3eb0, 0x5c06, 0x7e94, 0x757b, 0x3e2b, + 0x6755, 0x7e40, 0x2966, 0x0d4b, 0x047e, 0x4de5, 0x0b3e, 0x0ebb, 0x22d2, 0x3d7c, + 0x58db, 0x6e78, 0x3b7d, 0x07c6, 0x3990, 0x285d, 0x418b, 0x765f, 0x39d8, 0x53f7, + 0x2d2f, 0x4a5c, 0x78a2, 0x7b7b, 0x5fc5, 0x5684, 0x798a, 0x00c6, 0x178a, 0x708b, + 0x3a99, 0x6d50, 0x1527, 0x79f2, 0x700f, 0x100e, 0x7a3a, 0x57a2, 0x14d2, 0x1191, + 0x55d6, 0x43b1, 0x5500, 0x3060, 0x365a, 0x7ca3, 0x64df, 0x75d8, 0x4884, 0x0dec, + 0x4e99, 0x6355, 0x1f93, 0x7c6f, 0x26be, 0x0cd0, 0x5f26, 0x29ba, 0x742e, 0x0486, + 0x5adc, 0x10c2, 0x4978, 0x0ca8, 0x2af4, 0x065d, 0x1406, 0x0e93, 0x6dcd, 0x7ef4, + 0x3ae1, 0x533d, 0x7f6d, 0x4d35, 0x3e06, 0x214d, 0x16ed, 0x3572, 0x27c5, 0x6c95, + 0x0f62, 0x5090, 0x11ed, 0x5e7f, 0x4406, 0x3807, 0x1d4c, 0x1ba1, 0x7ace, 0x5cc7, + 0x5ceb, 0x2f0e, 0x5bc1, 0x2406, 0x5ee1, 0x49a0, 0x2c91, 0x06eb, 0x3521, 0x547a, + 0x2580, 0x6b10, 0x72f3, 0x5b04, 0x78ea, 0x6340, 0x4c2d, 0x64f5, 0x60ba, 0x35b3, + 0x0162, 0x2cdf, 0x148f, 0x76cd, 0x4294, 0x377d, 0x49e5, 0x0c06, 0x5a77, 0x60ed, + 0x73bb, 0x1ff2, 0x5555, 0x1cbd, 0x0606, 0x32a9, 0x39b5, 0x683b, 0x2a89, 0x086a, + 0x2b6f, 0x4fb6, 0x24a9, 0x107e, 0x46c4, 0x1a34, 0x27fa, 0x40ea, 0x56cc, 0x6aad, + 0x3c46, 0x3924, 0x7768, 0x10ea, 0x2277, 0x65fd, 0x52ba, 0x74a2, 0x6950, 0x1a84, + 0x02ea, 0x0d9f, 0x3f31, 0x671d, 0x0446, 0x6fd7, 0x4e61, 0x1f5b, 0x0f2a, 0x1d14, + 0x012a, 0x7383, 0x3c0e, 0x040e, 0x01e0, 0x239f, 0x77e0, 0x544b, 0x12fc, 0x477a, + 0x15e3, 0x5dd7, 0x3cba, 0x6e38, 0x2341, 0x2636, 0x6175, 0x6d22, 0x2c25, 0x0a34, + 0x2ffa, 0x2d9f, 0x4a2e, 0x724e, 0x7b03, 0x0272, 0x7d7e, 0x568c, 0x2778, 0x3ebc, + 0x68cb, 0x0892, 0x5c77, 0x0d37, 0x2bcc, 0x083a, 0x0d15, 0x48c3, 0x1c87, 0x5e49, + 0x45af, 0x271d, 0x5721, 0x2c02, 0x5db3, 0x4caa, 0x09d8, 0x320b, 0x4b55, 0x3d4e, + 0x333b, 0x5ee8, 0x4fff, 0x6558, 0x53c9, 0x2ab1, 0x195b, 0x129f, 0x2e49, 0x1ce1, + 0x607b, 0x1861, 0x5e6a, 0x6b6c, 0x5c98, 0x3a46, 0x30c8, 0x7bba, 0x413f, 0x7587, + 0x719f, 0x7ec5, 0x5f64, 0x4ff8, 0x4602, 0x346c, 0x1395, 0x3e37, 0x54dc, 0x2b97, + 0x665b, 0x21d8, 0x6bd0, 0x44f2, 0x64a1, 0x7ea0, 0x760b, 0x7113, 0x510f, 0x5285, + 0x37c6, 0x05b8, 0x38f3, 0x5c12, 0x1128, 0x1212, 0x2e28, 0x7e46, 0x3ae7, 0x0870, + 0x1c8d, 0x7e4c, 0x7e2c, 0x4fde, 0x2703, 0x3a2c, 0x6d08, 0x0258, 0x491c, 0x6761, + 0x44b8, 0x71bf, 0x18a4, 0x7e66, 0x0629, 0x1b42, 0x42ef, 0x2972, 0x4c70, 0x3a66, + 0x25c9, 0x1ca7, 0x2ec6, 0x1e38, 0x289b, 0x0d57, 0x25fc, 0x34bd, 0x6c66, 0x0238, + 0x0bbe, 0x7800, 0x4a8c, 0x5d45, 0x0598, 0x1162, 0x66bc, 0x18be, 0x6bf4, 0x521e, + 0x7fd8, 0x143a, 0x2770, 0x0391, 0x4719, 0x5886, 0x4bb1, 0x5b19, 0x69c0, 0x218a, + 0x1c27, 0x6863, 0x4ec9, 0x72ce, 0x5a17, 0x5fcd, 0x0399, 0x2a07, 0x74eb, 0x794d, + 0x5d92, 0x7e80, 0x1ab4, 0x5ad4, 0x566a, 0x7645, 0x7e26, 0x331a, 0x3b40, 0x1635, + 0x455d, 0x1a03, 0x1fd8, 0x76b3, 0x7369, 0x40d0, 0x4397, 0x0643, 0x5076, 0x5cad, + 0x7d1c, 0x7c12, 0x4dbf, 0x6aea, 0x6f92, 0x1b5c, 0x382b, 0x0fc9, 0x24f2, 0x4812, + 0x6551, 0x39dd, 0x7c74, 0x11f2, 0x0c0b, 0x52bf, 0x2da4, 0x3340, 0x7ea5, 0x062e, + 0x218f, 0x507b, 0x6290, 0x5516, 0x07d7, 0x597f, 0x58ec, 0x1af9, 0x799b, 0x677b, + 0x709c, 0x41df, 0x16af, 0x628b, 0x641a, 0x37b0, 0x31c2, 0x4936, 0x621a, 0x0d6d, + 0x3c97, 0x2212, 0x0b63, 0x6def, 0x1c66, 0x4fd8, 0x6999, 0x2d7d, 0x3583, 0x3f75, + 0x3e17, 0x458e, 0x5cfc, 0x71d9, 0x2417, 0x360d, 0x743f, 0x5c43, 0x10d3, 0x32d1, + 0x7cb4, 0x59c2, 0x5511, 0x2922, 0x5566, 0x20b2, 0x32ba, 0x31f4, 0x35c4, 0x7870, + 0x4c3e, 0x6eb5, 0x3935, 0x33e0, 0x56dd, 0x5032, 0x0457, 0x44d2, 0x1f6c, 0x2559, + 0x774f, 0x399c, 0x5ec8, 0x64c6, 0x5d20, 0x3a80, 0x57de, 0x3dab, 0x517c, 0x2869, + 0x1758, 0x0c2e, 0x577d, 0x75aa, 0x3e6c, 0x4f72, 0x1dd2, 0x07d2, 0x5774, 0x5826, + 0x6652, 0x616c, 0x4710, 0x7992, 0x1442, 0x3b89, 0x19bf, 0x5ea4, 0x7ae2, 0x4c8a, + 0x2cf3, 0x104e, 0x6e16, 0x766b, 0x26fd, 0x4a0d, 0x7f30, 0x4d64, 0x0349, 0x12d9, + 0x1f29, 0x4197, 0x2483, 0x33ae, 0x50f9, 0x298c, 0x597a, 0x49a7, 0x5f6b, 0x39e4, + 0x0e65, 0x4059, 0x06a9, 0x4309, 0x3424, 0x6901, 0x7729, 0x5403, 0x355d, 0x4119, + 0x769e, 0x5e29, 0x487b, 0x1486, 0x6d81, 0x3d88, 0x6b4c, 0x327a, 0x0e5e, 0x53c2, + 0x444f, 0x3076, 0x4667, 0x22de, 0x5607, 0x4242, 0x582f, 0x2ee0, 0x190c, 0x08c8, + 0x4256, 0x58e7, 0x06bd, 0x7530, 0x0761, 0x1e52, 0x36fe, 0x28b1, 0x6fa6, 0x6e84, + 0x1b0d, 0x5a9f, 0x2bab, 0x3a26, 0x196f, 0x0a12, 0x10ba, 0x0b4a, 0x6538, 0x2757, + 0x54c3, 0x25e3, 0x68b2, 0x09bf, 0x3fca, 0x0ec7, 0x17f2, 0x20c9, 0x173f, 0x3544, + 0x1af4, 0x17d9, 0x0b8a, 0x4df1, 0x530f, 0x6115, 0x29ff, 0x3eb4, 0x3b81, 0x00ca, + 0x365e, 0x048a, 0x16f1, 0x2f12, 0x60be, 0x1cc1, 0x3c4a, 0x6fdb, 0x3cbe, 0x5690, + 0x5db7, 0x1ce5, 0x1399, 0x05bc, 0x4920, 0x1e3c, 0x7fdc, 0x5fd1, 0x1fdc, 0x0fcd, + 0x07db, 0x0d71, 0x7443, 0x6eb9, 0x5180, 0x7996, 0x1f2d, 0x6905, 0x466b, 0x28b5, + 0x3fce, 0x00ce, 0x7fe0, 0x00d2, 0x0ee0, 0x3bd1, 0x01bf, 0x237f, 0x4e25, 0x03de, + 0x5429, 0x78ae, 0x6d02, 0x15c2, 0x246c, 0x34d7, 0x44a1, 0x7352, 0x0edc, 0x7b87, + 0x3fe3, 0x557d, 0x5a2c, 0x65cf, 0x6776, 0x02ac, 0x2b15, 0x4a68, 0x6a7f, 0x4f12, + 0x074d, 0x7a16, 0x535e, 0x3bcd, 0x04cb, 0x2d3b, 0x2aaa, 0x4052, 0x1a55, 0x2616, + 0x3979, 0x27ae, 0x4be1, 0x3aa5, 0x722e, 0x40bb, 0x095a, 0x6c80, 0x0ab0, 0x3ef2, + 0x01bb, 0x6d5c, 0x20e2, 0x36ea, 0x1ddb, 0x374f, 0x639e, 0x5b67, 0x2239, 0x7097, + 0x6312, 0x73e3, 0x29a2, 0x6705, 0x6430, 0x237b, 0x77bd, 0x1796, 0x3167, 0x2a4c, + 0x2320, 0x0252, 0x2c55, 0x4758, 0x66ed, 0x79fe, 0x3e9c, 0x6154, 0x72b6, 0x7858, + 0x1287, 0x526d, 0x4e21, 0x1533, 0x34ee, 0x47b4, 0x7a83, 0x0bd8, 0x41da, 0x4acc, + 0x69f2, 0x701b, 0x680d, 0x601d, 0x2f43, 0x781a, 0x704b, 0x03da, 0x2525, 0x101a, + 0x180b, 0x201a, 0x7473, 0x081a, 0x13ca, 0x005a, 0x2b39, 0x3dd0, 0x2d5d, 0x47d7, + 0x0ad2, 0x6675, 0x4516, 0x7db4, 0x5425, 0x32f4, 0x612e, 0x3254, 0x19c8, 0x134e, + 0x4471, 0x7b26, 0x62b2, 0x16aa, 0x6f5f, 0x7912, 0x388d, 0x7a9b, 0x4834, 0x78aa, + 0x588e, 0x5317, 0x4624, 0x38b0, 0x1883, 0x21f2, 0x67c6, 0x4d05, 0x127d, 0x0aa6, + 0x4916, 0x4497, 0x033f, 0x57d4, 0x4445, 0x68a8, 0x6cfe, 0x45f8, 0x15d9, 0x5717, + 0x4553, 0x6bea, 0x6286, 0x240d, 0x4609, 0x4819, 0x4456, 0x2d42, 0x443b, 0x450c, + 0x442a, 0x15be, 0x4845, 0x4419, 0x5328, 0x6368, 0x3305, 0x3790, 0x4527, 0x6963, + 0x16d6, 0x3b66, 0x1ad9, 0x651d, 0x06a2, 0x1954, 0x4434, 0x55ec, 0x2468, 0x032e, + 0x4860, 0x0e4a, 0x5ead, 0x3e51, 0x46f5, 0x19a4, 0x314c, 0x6415, 0x6383, 0x7213, + 0x67f2, 0x13af, 0x126c, 0x34d3, 0x1fc1, 0x4905, 0x4e0a, 0x1f12, 0x6ce7, 0x675b, + 0x5343, 0x2a8f, 0x5e4f, 0x2e2e, 0x3320, 0x1c6c, 0x4a13, 0x2bb1, 0x15c8, 0x2326, + 0x449d, 0x6ced, 0x0ba3, 0x4c55, 0x7184, 0x6bb5, 0x37ab, 0x110d, 0x74d0, 0x59fc, + 0x4b96, 0x057d, 0x4da4, 0x0bf0, 0x4542, 0x734e, 0x58d1, 0x6275, 0x697e, 0x63ff, + 0x3dfc, 0x54f6, 0x4c23, 0x56c2, 0x370f, 0x6454, 0x6e95, 0x7b4c, 0x22ef, 0x1edb, + 0x4460, 0x4cce, 0x0ed8, 0x19b7, 0x68c3, 0x74e3, 0x3b92, 0x461c, 0x049b, 0x7b39, + 0x41a8, 0x31bd, 0x035a, 0x0f92, 0x3435, 0x3486, 0x5414, 0x7b83, 0x4721, 0x611d, + 0x3b9a, 0x731b, 0x287a, 0x2092, 0x57ef, 0x4970, 0x7b98, 0x08db, 0x44b2, 0x0b9d, + 0x536f, 0x5f7e, 0x2d4c, 0x4f85, 0x3fdf, 0x2b28, 0x00e3, 0x6ecc, 0x5fe2, 0x5b7a, + 0x4931, 0x6a05, 0x6441, 0x7d2f, 0x17a7, 0x5b2c, 0x6d6d, 0x41f2, 0x0ac1, 0x5579, + 0x1544, 0x4505, 0x1298, 0x4302, 0x705c, 0x5012, 0x102b, 0x0a47, 0x76c4, 0x0de3, + 0x456e, 0x7630, 0x383c, 0x7edf, 0x4823, 0x267f, 0x5a28, 0x387c, 0x2a18, 0x6b38, + 0x144b, 0x592f, 0x6c05, 0x1ec8, 0x622b, 0x6215, 0x2223, 0x648b, 0x5990, 0x4ae4, + 0x62a1, 0x65cb, 0x5c54, 0x6f4e, 0x2428, 0x50b5, 0x4c4f, 0x71b9, 0x33f1, 0x183f, + 0x37d7, 0x165b, 0x5c23, 0x25a8, 0x3e48, 0x1345, 0x4613, 0x5926, 0x6772, 0x587d, + 0x6d19, 0x21cf, 0x2ed7, 0x75a1, 0x0d68, 0x3746, 0x4cbb, 0x6a4f, 0x5732, 0x303b, + 0x2e5a, 0x4159, 0x1872, 0x02a8, 0x7d8f, 0x67b5, 0x3ecd, 0x51f9, 0x6e49, 0x2902, + 0x15f4, 0x050d, 0x0d94, 0x1a29, 0x5af9, 0x60e2, 0x0cc5, 0x1186, 0x5332, 0x1b96, + 0x2b11, 0x6cd6, 0x58a9, 0x3124, 0x4c93, 0x5c2c, 0x0eb0, 0x7b70, 0x0ced, 0x3c92, + 0x38cb, 0x42c7, 0x1687, 0x390d, 0x1fb0, 0x4a64, 0x4bb9, 0x4df9, 0x04a3, 0x5158, + 0x5f43, 0x141a, 0x463f, 0x1092, 0x48a1, 0x092e, 0x189e, 0x717e, 0x1d81, 0x122c, + 0x6372, 0x2fd7, 0x6a7b, 0x313b, 0x3677, 0x35db, 0x75f5, 0x62fc, 0x220d, 0x5bc8, + 0x3473, 0x24f9, 0x307d, 0x04d2, 0x55f3, 0x154b, 0x67e1, 0x4f0e, 0x11ae, 0x125b, + 0x4d20, 0x658b, 0x1620, 0x1142, 0x7a57, 0x204e, 0x1bbe, 0x5803, 0x792d, 0x166f, + 0x341d, 0x2e42, 0x4423, 0x67da, 0x0749, 0x0691, 0x2f2b, 0x4653, 0x7aeb, 0x25b1, + 0x6f7a, 0x31aa, 0x6241, 0x0b5e, 0x5bde, 0x2d07, 0x49bd, 0x4e39, 0x16c5, 0x7a12, + 0x2cae, 0x1ac8, 0x62cd, 0x3f89, 0x6baf, 0x7e60, 0x5497, 0x2be0, 0x0ca0, 0x067d, + 0x216a, 0x1374, 0x7f8a, 0x2fa8, 0x484f, 0x14f2, 0x535a, 0x2457, 0x78c5, 0x070b, + 0x0e0f, 0x3b01, 0x6dea, 0x7284, 0x0f7f, 0x5150, 0x38a8, 0x7313, 0x7945, 0x088a, + 0x5e9c, 0x3bc9, 0x6c31, 0x46e4, 0x170a, 0x018a, 0x6cb2, 0x5fad, 0x7ab6, 0x73a3, + 0x14ac, 0x2dfc, 0x19e3, 0x7138, 0x71fd, 0x712d, 0x379a, 0x751a, 0x04c7, 0x7173, + 0x60d7, 0x42bc, 0x2cfc, 0x1664, 0x1369, 0x7308, 0x0572, 0x1c61, 0x6512, 0x7208, + 0x7907, 0x47cc, 0x448c, 0x2d37, 0x5b21, 0x0b92, 0x7b41, 0x0f87, 0x6480, 0x7625, + 0x259d, 0x3030, 0x31e9, 0x4583, 0x0623, 0x37a5, 0x6858, 0x1157, 0x330f, 0x6adf, + 0x2aa6, 0x5e3e, 0x5440, 0x7243, 0x7108, 0x7eba, 0x4fd3, 0x3a5b, 0x610a, 0x274c, + 0x326f, 0x7525, 0x581b, 0x64bb, 0x4a02, 0x404e, 0x4f07, 0x15b7, 0x1cda, 0x68fa, + 0x73d8, 0x40b0, 0x6149, 0x6012, 0x2107, 0x436b, 0x6aca, 0x19ee, 0x2817, 0x2138, + 0x4531, 0x43c7, 0x1a51, 0x4d93, 0x7dcf, 0x18f8, 0x1057, 0x37e0, 0x24c6, 0x207f, + 0x2294, 0x6994, 0x0aed, 0x7143, 0x1bd6, 0x05d2, 0x74bf, 0x2612, 0x7d5b, 0x4b85, + 0x3c63, 0x76f5, 0x1107, 0x1b3c, 0x6690, 0x3d2c, 0x0dbc, 0x3196, 0x47f2, 0x2e07, + 0x0907, 0x5129, 0x696d, 0x2dd5, 0x3975, 0x58c0, 0x6ff4, 0x7887, 0x3f4e, 0x7beb, + 0x2d78, 0x7560, 0x495d, 0x7413, 0x4e7e, 0x14b7, 0x1d31, 0x3506, 0x3deb, 0x27aa, + 0x0147, 0x4c12, 0x2b54, 0x5a5c, 0x46a9, 0x529f, 0x042b, 0x0f0f, 0x6927, 0x27d1, + 0x1466, 0x05dd, 0x3631, 0x2acb, 0x7aa5, 0x6da4, 0x4bdd, 0x6ca1, 0x5848, 0x52e2, + 0x1761, 0x4162, 0x594a, 0x182c, 0x61d4, 0x357e, 0x7cd6, 0x1be1, 0x6885, 0x6035, + 0x6c20, 0x3aa1, 0x18c6, 0x16f9, 0x1ee3, 0x1712, 0x5d71, 0x53e3, 0x7270, 0x0a6a, + 0x21b6, 0x509c, 0x42e9, 0x74ca, 0x0e31, 0x63e6, 0x3897, 0x56fe, 0x722a, 0x0f6e, + 0x5a43, 0x18df, 0x310b, 0x6572, 0x3f70, 0x06f2, 0x71a6, 0x11f9, 0x6b53, 0x261d, + 0x1ae0, 0x5019, 0x7934, 0x40b7, 0x3395, 0x5e8b, 0x2ef9, 0x4229, 0x0fb4, 0x4039, + 0x2a33, 0x479b, 0x63c3, 0x4d41, 0x338e, 0x0af8, 0x7722, 0x6074, 0x483e, 0x11a7, + 0x0956, 0x7f79, 0x269a, 0x172b, 0x0c37, 0x2e63, 0x1c03, 0x6f3b, 0x2844, 0x3e12, + 0x3857, 0x714e, 0x2cc6, 0x1cfb, 0x0c8f, 0x6c7c, 0x7c3e, 0x2159, 0x1925, 0x6625, + 0x59f6, 0x296c, 0x7efa, 0x2b75, 0x48c9, 0x3aed, 0x764b, 0x699f, 0x7671, 0x1975, + 0x78b4, 0x2c5b, 0x0aac, 0x5349, 0x08e1, 0x33f7, 0x0934, 0x549d, 0x4589, 0x6696, + 0x50a2, 0x7f00, 0x1d5e, 0x229f, 0x26d0, 0x6825, 0x0dfe, 0x3eee, 0x5a89, 0x6dd9, + 0x76df, 0x1efc, 0x660f, 0x12b9, 0x02fc, 0x55b0, 0x553d, 0x5cd3, 0x7972, 0x3c6e, + 0x7faf, 0x5d69, 0x6f69, 0x5641, 0x01b7, 0x7ada, 0x5c6f, 0x5d8a, 0x2872, 0x187b, + 0x5f3b, 0x6478, 0x1da9, 0x5cf7, 0x50d0, 0x7700, 0x3fa1, 0x6095, 0x0738, 0x6d58, + 0x66c4, 0x2f1a, 0x22f7, 0x0192, 0x7fb7, 0x07b2, 0x2443, 0x2aec, 0x344a, 0x1d58, + 0x4c6a, 0x4b90, 0x0720, 0x6b86, 0x791c, 0x0c77, 0x20de, 0x1bad, 0x4680, 0x394c, + 0x4ede, 0x70df, 0x71d4, 0x0549, 0x6202, 0x3813, 0x2eae, 0x7d66, 0x3fb6, 0x7033, + 0x340c, 0x36e6, 0x1254, 0x4412, 0x185a, 0x53fc, 0x1f98, 0x5e84, 0x49ea, 0x74a7, + 0x2fff, 0x5eed, 0x64a6, 0x1b47, 0x69c5, 0x5cb2, 0x16b4, 0x2927, 0x1dd7, 0x49ac, + 0x425b, 0x17de, 0x5185, 0x02b1, 0x223e, 0x4ad1, 0x62b7, 0x2412, 0x3151, 0x1112, + 0x41ad, 0x6a0a, 0x6230, 0x374b, 0x0cf2, 0x5bcd, 0x6246, 0x7289, 0x0577, 0x3a60, + 0x2299, 0x7565, 0x61d9, 0x06f7, 0x2849, 0x669b, 0x1dae, 0x054e, 0x62bc, 0x61b0, + 0x639a, 0x2c9d, 0x65e6, 0x0a83, 0x336c, 0x30e2, 0x3608, 0x61ab, 0x65b8, 0x352d, + 0x59ab, 0x3d37, 0x62e5, 0x7bd4, 0x6b9e, 0x5b63, 0x70c8, 0x5486, 0x5620, 0x7790, + 0x098c, 0x4f52, 0x4aff, 0x1e8c, 0x5b8c, 0x29c6, 0x17b9, 0x7dda, 0x036c, 0x36c5, + 0x462e, 0x322f, 0x2235, 0x5f32, 0x5941, 0x1bfa, 0x75b3, 0x573b, 0x5744, 0x266c, + 0x6395, 0x743a, 0x3e63, 0x1903, 0x6bc7, 0x2c1c, 0x4ba8, 0x7093, 0x6bfc, 0x0492, + 0x4468, 0x46ec, 0x6f71, 0x0ea7, 0x1360, 0x24bd, 0x5bf0, 0x26ca, 0x25c3, 0x4d9e, + 0x3b13, 0x45c9, 0x38ba, 0x4b32, 0x630e, 0x0cdc, 0x308f, 0x5049, 0x38dd, 0x77a7, + 0x5c3e, 0x2c98, 0x7ecc, 0x7c7b, 0x3281, 0x1a5c, 0x6524, 0x7063, 0x1676, 0x73df, + 0x0aff, 0x1f9f, 0x37f2, 0x7cef, 0x7bfd, 0x2737, 0x4e90, 0x428b, 0x06cf, 0x4984, + 0x2ef2, 0x37eb, 0x3556, 0x5e63, 0x5321, 0x4d19, 0x299e, 0x0cb4, 0x0e77, 0x10a6, + 0x5786, 0x3044, 0x75bc, 0x0dd0, 0x65e1, 0x10ce, 0x6a91, 0x1062, 0x1f3f, 0x03f2, + 0x0d83, 0x6701, 0x6324, 0x5ae8, 0x3761, 0x3597, 0x0bea, 0x1ca1, 0x681f, 0x084e, + 0x0655, 0x0669, 0x4dd1, 0x24d1, 0x1c39, 0x158f, 0x5898, 0x4343, 0x642c, 0x2b00, + 0x678d, 0x61ed, 0x59d4, 0x48dd, 0x32cc, 0x0a7e, 0x761d, 0x1412, 0x21ea, 0x208a, + 0x7e78, 0x0d2f, 0x4c82, 0x2377, 0x3d60, 0x0e9f, 0x53db, 0x07aa, 0x4a40, 0x00aa, + 0x6d34, 0x79d6, 0x6aa3, 0x64eb, 0x3056, 0x2143, 0x48ef, 0x4b6f, 0x21fc, 0x6e6e, + 0x77b9, 0x75e4, 0x3293, 0x7dff, 0x3e75, 0x6a58, 0x574d, 0x6b25, 0x3367, 0x7caf, + 0x2e87, 0x2822, 0x2e99, 0x2f5b, 0x6a6a, 0x1792, 0x5226, 0x3666, 0x4cd6, 0x6c39, + 0x5649, 0x3d68, 0x2f94, 0x157b, 0x70f1, 0x0df8, 0x2ec0, 0x453c, 0x50e2, 0x7a6c, + 0x188d, 0x4380, 0x3163, 0x4890, 0x02c3, 0x3aba, 0x30f4, 0x3355, 0x59bd, 0x30dd, + 0x3869, 0x4ea5, 0x2e75, 0x43d2, 0x54af, 0x5f02, 0x1d70, 0x2a48, 0x6584, 0x6361, + 0x6b65, 0x4112, 0x7ce8, 0x4222, 0x4174, 0x0f4b, 0x2dcb, 0x43bd, 0x7510, 0x6ad5, + 0x2fcd, 0x1b8c, 0x67d0, 0x14e8, 0x231c, 0x55e2, 0x7daa, 0x689e, 0x4f7b, 0x4cc4, + 0x2675, 0x591c, 0x3603, 0x550c, 0x5214, 0x19f9, 0x2bf8, 0x5dcd, 0x3462, 0x024e, + 0x09b5, 0x306c, 0x3da1, 0x12cf, 0x7348, 0x1e32, 0x3ee8, 0x5263, 0x432f, 0x14de, + 0x6e64, 0x4376, 0x4b28, 0x3225, 0x4d0f, 0x4339, 0x2c51, 0x119d, 0x6d9a, 0x56f4, + 0x0c6d, 0x5637, 0x291d, 0x61a6, 0x1eb5, 0x57ae, 0x2f82, 0x2112, 0x539c, 0x7832, + 0x160f, 0x4754, 0x369f, 0x7a46, 0x0528, 0x604e, 0x23cb, 0x09f2, 0x6ef9, 0x3cf0, + 0x07fe, 0x1ffe, 0x03be, 0x6fff, 0x1517, 0x4ab0, 0x6138, 0x783c, 0x66e9, 0x73c7, + 0x473c, 0x177a, 0x3a89, 0x3ed6, 0x36ce, 0x3733, 0x65b3, 0x5561, 0x7336, 0x7892, + 0x2d1f, 0x2792, 0x4ef6, 0x79fa, 0x05a0, 0x1cc9, 0x6e9d, 0x5fb5, 0x797a, 0x00b2, + 0x3bb5, 0x2363, 0x3a0a, 0x5a83, 0x2895, 0x58cb, 0x22c2, 0x08ac, 0x325e, 0x53a6, + 0x3e98, 0x60f9, 0x6fbf, 0x046e, 0x0b2e, 0x09a3, 0x20ad, 0x3528, 0x758e, 0x0c12, + 0x3d8f, 0x3980, 0x3b6d, 0x1032, 0x580a, 0x6150, 0x4d48, 0x49f1, 0x498b, 0x417b, + 0x39c8, 0x68e5, 0x40fd, 0x5e0d, 0x11d6, 0x39c1, 0x0fad, 0x7bf6, 0x7697, 0x5c91, + 0x32fe, 0x1619, 0x72b2, 0x6847, 0x5ab8, 0x29eb, 0x5d29, 0x5202, 0x0375, 0x586a, + 0x59a6, 0x32b5, 0x35f1, 0x3f59, 0x33c4, 0x253d, 0x31d8, 0x7854, 0x505f, 0x0612, + 0x41c3, 0x5963, 0x626f, 0x0d51, 0x6dd3, 0x4fbc, 0x0d1b, 0x0876, 0x5670, 0x2d83, + 0x6e1c, 0x0a18, 0x542f, 0x475e, 0x1283, 0x2a95, 0x7b9e, 0x1845, 0x48a7, 0x2be6, + 0x31ef, 0x3d32, 0x21bc, 0x2b7b, 0x3450, 0x756b, 0x5bf6, 0x0854, 0x70f7, 0x5269, + 0x3a10, 0x4fc2, 0x1b26, 0x6745, 0x2956, 0x1e1c, 0x34a1, 0x021c, 0x5f91, 0x016e, + 0x3bad, 0x5134, 0x243b, 0x7268, 0x1358, 0x2f8c, 0x4e1d, 0x2ceb, 0x2bc4, 0x1aac, + 0x57e7, 0x67be, 0x4637, 0x2595, 0x62e0, 0x35bf, 0x2fbb, 0x0912, 0x123f, 0x2032, + 0x04b6, 0x152f, 0x116a, 0x60c6, 0x7b54, 0x6cba, 0x3c76, 0x4a48, 0x513c, 0x13fe, + 0x1b20, 0x76d9, 0x25f6, 0x6978, 0x4d77, 0x2063, 0x19d2, 0x211c, 0x34ea, 0x149b, + 0x0ef3, 0x4bf6, 0x317a, 0x2db9, 0x786b, 0x7bcf, 0x1648, 0x42a0, 0x74fe, 0x2de0, + 0x0b76, 0x3014, 0x71ec, 0x47b0, 0x113b, 0x3789, 0x3a3f, 0x5e22, 0x2730, 0x4032, + 0x68de, 0x4094, 0x3774, 0x634c, 0x15a2, 0x47fd, 0x45dc, 0x23f1, 0x447b, 0x57b8, + 0x7a7f, 0x78f6, 0x4ce9, 0x52fb, 0x3db4, 0x7d98, 0x3238, 0x1332, 0x6b99, 0x4c39, + 0x230a, 0x2e12, 0x6259, 0x56a6, 0x0561, 0x0bd4, 0x1938, 0x6501, 0x1988, 0x0312, + 0x63f9, 0x34b7, 0x1ef6, 0x673f, 0x2076, 0x72ff, 0x7b67, 0x31a1, 0x199b, 0x7b1d, + 0x7b30, 0x1ebf, 0x41d6, 0x5b10, 0x0a2b, 0x44e9, 0x08bf, 0x4f69, 0x6eb0, 0x5b5e, + 0x5913, 0x6b1c, 0x2663, 0x0dc7, 0x6f32, 0x1823, 0x646f, 0x4ac8, 0x1329, 0x258c, + 0x372a, 0x5861, 0x6a33, 0x028c, 0x51dd, 0x28e6, 0x4f36, 0x7774, 0x5b47, 0x3511, + 0x2c81, 0x618f, 0x667f, 0x0532, 0x69ee, 0x10f6, 0x7549, 0x5bb1, 0x5ed1, 0x290b, + 0x17c2, 0x0295, 0x70c3, 0x3930, 0x0c5b, 0x1d3c, 0x43f6, 0x748b, 0x7d4a, 0x7017, + 0x5d4d, 0x3c52, 0x645c, 0x7abe, 0x5cdb, 0x6d3c, 0x0176, 0x0796, 0x2950, 0x6609, + 0x6c60, 0x3df6, 0x7f5d, 0x6f1f, 0x0adc, 0x6058, 0x6809, 0x2283, 0x5594, 0x6dbd, + 0x3ad1, 0x2c3f, 0x33db, 0x5481, 0x4146, 0x52c6, 0x6d88, 0x27b5, 0x16dd, 0x0a4e, + 0x1bc5, 0x6019, 0x63ca, 0x74ae, 0x06d6, 0x0f52, 0x11dd, 0x409b, 0x420d, 0x401d, + 0x4206, 0x40f6, 0x2a2c, 0x4e89, 0x4874, 0x30c1, 0x4520, 0x7a50, 0x2f3f, 0x2806, + 0x155f, 0x364a, 0x64cf, 0x6e52, 0x7de3, 0x6a3c, 0x561b, 0x56d8, 0x431d, 0x14c2, + 0x7a2a, 0x3cd4, 0x20f6, 0x7816, 0x1b70, 0x6ab9, 0x5900, 0x55c6, 0x54f0, 0x0232, + 0x12b3, 0x1e16, 0x0e8b, 0x46d0, 0x7077, 0x741e, 0x5f16, 0x2650, 0x7dbe, 0x36a9, + 0x7047, 0x1a40, 0x426f, 0x1f83, 0x26ae, 0x4b16, 0x502d, 0x778b, 0x3028, 0x108a, + 0x4cfd, 0x4968, 0x5acc, 0x0832, 0x1046, 0x03d6, 0x1573, 0x24b5, 0x0a62, 0x2ae4, + 0x13f6, 0x235b, 0x078e, 0x008e, 0x1e00, 0x6729, 0x524d, 0x2b5f, 0x2a79, 0x3d16, + 0x2d67, 0x09fc, 0x2521, 0x3f3d, 0x4fa0, 0x05f6, 0x39a5, 0x15fd, 0x29cf, 0x51e6, + 0x0987, 0x0452, 0x538a, 0x5a67, 0x49d5, 0x5df1, 0x3964, 0x1016, 0x4a94, 0x6fe3, + 0x3717, 0x73ab, 0x5545, 0x79de, 0x5f99, 0x0096, 0x349b, 0x02f6, 0x0bb8, 0x4c1d, + 0x78da, 0x1316, 0x47e1, 0x23d5, 0x1807, 0x0dab, 0x28ca, 0x2570, 0x72e3, 0x1ea3, + 0x44cd, 0x4f4d, 0x67a2, 0x1a90, 0x2f70, 0x0152, 0x60aa, 0x13e2, 0x08f6, 0x2016, + 0x2047, 0x695c, 0x7bb3, 0x147f, 0x4284, 0x4794, 0x5e06, 0x4016, 0x407f, 0x0f36, + 0x5ffd, 0x52aa, 0x2267, 0x5465, 0x3dda, 0x6f03, 0x746f, 0x1d20, 0x077a, 0x3c36, + 0x7758, 0x0516, 0x5b95, 0x28ef, 0x4afa, 0x1f67, 0x368d, 0x46b4, 0x2499, 0x0072, + 0x494c, 0x0816, 0x30a5, 0x4e6d, 0x6a20, 0x27ea, 0x56bc, 0x77fa, 0x55aa, 0x0216, + 0x79c2, 0x738f, 0x0ffa, 0x0436, 0x3f21, 0x51ca, 0x2b43, 0x3cfa, 0x13c6, 0x0136, + 0x3ffa, 0x6940, 0x02da, 0x23b9, 0x2554, 0x1e87, 0x04fa, 0x3c1a, 0x6ee7, 0x0f1a, + 0x4e51, 0x01fa, 0x4698, 0x0056, 0x51ae, 0x041a, 0x1e6b, 0x011a, 0x3bfe, 0x003a, + 0x00fe, 0x001e, 0x00f0, 0x3bf0, 0x51a0, 0x1e5d, 0x6ed9, 0x04ec, 0x4e43, 0x468a, + 0x2b35, 0x3f13, 0x79b4, 0x0fec, 0x3fec, 0x13b8, 0x02cc, 0x2546, 0x5b87, 0x774a, + 0x7461, 0x076c, 0x5fef, 0x4071, 0x2259, 0x3dcc, 0x493e, 0x248b, 0x4aec, 0x367f, + 0x6a12, 0x3097, 0x56ae, 0x559c, 0x5f8b, 0x5537, 0x4a86, 0x3709, 0x537c, 0x0979, + 0x49c7, 0x3956, 0x2d59, 0x2a6b, 0x1df2, 0x523f, 0x4f92, 0x2513, 0x3997, 0x29c1, + 0x44bf, 0x72d5, 0x17f9, 0x28bc, 0x0baa, 0x348d, 0x78cc, 0x47d3, 0x08e8, 0x609c, + 0x6794, 0x2f62, 0x7ba5, 0x2039, 0x4276, 0x5df8, 0x41ff, 0x11cf, 0x63bc, 0x06c8, + 0x6d7a, 0x4138, 0x16cf, 0x1bb7, 0x0ace, 0x7f4f, 0x2942, 0x6c52, 0x5586, 0x67fb, + 0x3ac3, 0x33cd, 0x17b4, 0x5ec3, 0x69e0, 0x753b, 0x5b39, 0x4f28, 0x2c73, 0x6671, + 0x7d3c, 0x43e8, 0x70b5, 0x0c4d, 0x644e, 0x5d3f, 0x5ccd, 0x0168, 0x12a5, 0x54e2, + 0x1b62, 0x58f2, 0x430f, 0x560d, 0x7a1c, 0x20e8, 0x4512, 0x4866, 0x41f8, 0x2a1e, + 0x1551, 0x2f31, 0x64c1, 0x7dd5, 0x501f, 0x26a0, 0x7039, 0x4261, 0x7069, 0x0e7d, + 0x5f08, 0x7db0, 0x1038, 0x5abe, 0x301a, 0x4cef, 0x0a54, 0x1565, 0x13e8, 0x0780, + 0x3493, 0x2948, 0x3a02, 0x1b18, 0x3442, 0x21ae, 0x5be8, 0x70e9, 0x5421, 0x6e0e, + 0x0d0d, 0x5662, 0x7b90, 0x1275, 0x4899, 0x31e1, 0x0367, 0x5d1b, 0x72a4, 0x5aaa, + 0x0f9f, 0x11c8, 0x7689, 0x32f0, 0x31ca, 0x33b6, 0x5998, 0x35e3, 0x41b5, 0x5051, + 0x6261, 0x6dc5, 0x3ba7, 0x796c, 0x0592, 0x6e8f, 0x7328, 0x65a5, 0x2d11, 0x4ee8, + 0x612a, 0x1509, 0x07f0, 0x03b0, 0x472e, 0x66db, 0x3a7b, 0x36c0, 0x209f, 0x0b20, + 0x3e8a, 0x6fb1, 0x2887, 0x39fc, 0x22b4, 0x3250, 0x57fc, 0x3b5f, 0x7580, 0x3d81, + 0x497d, 0x4d3a, 0x39ba, 0x40ef, 0x68d0, 0x2722, 0x112d, 0x3a31, 0x74f0, 0x163a, + 0x0b68, 0x71de, 0x19c4, 0x4d69, 0x1b12, 0x25e8, 0x0ee5, 0x34dc, 0x316c, 0x785d, + 0x4629, 0x57d9, 0x4e0f, 0x2bb6, 0x3b9f, 0x5f83, 0x242d, 0x134a, 0x04a8, 0x1231, + 0x62d2, 0x2fad, 0x7b46, 0x115c, 0x3c68, 0x512e, 0x1ee8, 0x63eb, 0x192a, 0x197a, + 0x22fc, 0x6b8b, 0x624b, 0x0553, 0x446d, 0x45ce, 0x3766, 0x1594, 0x4cdb, 0x7a71, + 0x3da6, 0x322a, 0x6ea2, 0x08b1, 0x41c8, 0x0a1d, 0x7b59, 0x2068, 0x198d, 0x7b22, + 0x6461, 0x6f24, 0x5905, 0x2655, 0x371c, 0x131b, 0x6a25, 0x51cf, 0x4af1, 0x097e, + 0x70ba, 0x5612, 0x599d, 0x65aa, 0x62d7, 0x6b90, 0x62ae, 0x1da0, 0x61cb, 0x283b, + 0x65d8, 0x638c, 0x335e, 0x35fa, 0x2230, 0x5177, 0x1dc9, 0x424d, 0x6498, 0x2ff1, + 0x69b7, 0x16a6, 0x6222, 0x419f, 0x62a9, 0x3143, 0x6238, 0x0ce4, 0x0569, 0x228b, + 0x2435, 0x7fa9, 0x66b6, 0x22e9, 0x50c2, 0x1d9b, 0x3f93, 0x072a, 0x6f5b, 0x7fa1, + 0x552f, 0x7964, 0x5c61, 0x01a9, 0x2864, 0x5f2d, 0x71c6, 0x4ed0, 0x20d0, 0x4672, + 0x4c5c, 0x343c, 0x0712, 0x790e, 0x33fe, 0x3fa8, 0x61f4, 0x2ea0, 0x184c, 0x1246, + 0x1f8a, 0x49dc, 0x2a25, 0x0fa6, 0x3387, 0x2eeb, 0x6b45, 0x7198, 0x1ad2, 0x7926, + 0x3889, 0x0e23, 0x21a8, 0x42db, 0x5a35, 0x721c, 0x30fd, 0x3f62, 0x593c, 0x1753, + 0x4bcf, 0x583a, 0x1458, 0x6919, 0x3623, 0x7a97, 0x6c12, 0x6877, 0x61c6, 0x7cc8, + 0x1ed5, 0x18b8, 0x5d63, 0x7262, 0x7eec, 0x59e8, 0x7c30, 0x1917, 0x3849, 0x2836, + 0x2cb8, 0x0c81, 0x4830, 0x7714, 0x63b5, 0x3380, 0x268c, 0x0948, 0x0c29, 0x1bf5, + 0x457b, 0x0926, 0x0a9e, 0x08d3, 0x763d, 0x48bb, 0x7663, 0x78a6, 0x0df0, 0x26c2, + 0x5094, 0x1d50, 0x76d1, 0x5a7b, 0x6601, 0x02ee, 0x6d26, 0x4a32, 0x3d52, 0x53cd, + 0x21dc, 0x760f, 0x7e6a, 0x4c74, 0x588a, 0x1c2b, 0x0647, 0x4dc3, 0x677f, 0x641e, + 0x59c6, 0x32be, 0x75ae, 0x5778, 0x2990, 0x0e69, 0x2ee4, 0x06c1, 0x3548, 0x5313, + 0x0d75, 0x1f31, 0x65d3, 0x6a83, 0x3753, 0x6316, 0x0bdc, 0x6811, 0x1352, 0x6f63, + 0x6bee, 0x445a, 0x3e55, 0x6387, 0x6bb9, 0x4b9a, 0x4620, 0x035e, 0x5b7e, 0x17ab, + 0x5933, 0x2227, 0x75a5, 0x5736, 0x5c30, 0x38cf, 0x6300, 0x3081, 0x25b5, 0x5be2, + 0x3b05, 0x38ac, 0x1668, 0x6516, 0x7ebe, 0x3273, 0x37e4, 0x0af1, 0x7bef, 0x4e82, + 0x4166, 0x7cda, 0x6576, 0x6b57, 0x2e67, 0x385b, 0x54a1, 0x1d62, 0x187f, 0x50d4, + 0x70e3, 0x2eb2, 0x02b5, 0x3155, 0x30e6, 0x59af, 0x573f, 0x3e67, 0x77ab, 0x3285, + 0x3048, 0x6a95, 0x48e1, 0x21ee, 0x6a5c, 0x2e8b, 0x3359, 0x2e79, 0x4cc8, 0x5218, + 0x563b, 0x2f86, 0x3eda, 0x733a, 0x09a7, 0x3d93, 0x5206, 0x35f5, 0x2bea, 0x3454, + 0x67c2, 0x2fbf, 0x2dbd, 0x7502, 0x7d9c, 0x230e, 0x4f6d, 0x2667, 0x290f, 0x0c5f, + 0x2c43, 0x6d8c, 0x6e56, 0x4321, 0x4b1a, 0x4d01, 0x1601, 0x538e, 0x1ea7, 0x2f74, + 0x051a, 0x3691, 0x23bd, 0x6eeb, 0x13bc, 0x7465, 0x2517, 0x17fd, 0x67ff, 0x69e4, + 0x2f35, 0x703d, 0x1279, 0x72a8, 0x66df, 0x3e8e, 0x34e0, 0x4e13, 0x7a75, 0x41cc, + 0x6390, 0x1dcd, 0x01ad, 0x20d4, 0x7220, 0x4bd3, 0x094c, 0x0aa2, 0x6422, 0x2994, + 0x222b, 0x6304, 0x3159, 0x77af, 0x2312, 0x2c47, 0x4e17, 0x01b1, 0x7fd2, 0x0ed2, + 0x1f1f, 0x5172, 0x465d, 0x3fc0, 0x4912, 0x138b, 0x3cb0, 0x5da9, 0x1fce, 0x7fce, + 0x07cd, 0x7435, 0x6768, 0x5a1e, 0x0ece, 0x3fd5, 0x6cf4, 0x541b, 0x245e, 0x4493, + 0x5350, 0x073f, 0x2b07, 0x6a71, 0x2a9c, 0x04bd, 0x1a47, 0x396b, 0x486d, 0x7690, + 0x771b, 0x354f, 0x0e57, 0x5f5d, 0x069b, 0x3416, 0x033b, 0x7f22, 0x6e08, 0x26ef, + 0x2475, 0x1f1b, 0x50eb, 0x596c, 0x3e5e, 0x576f, 0x516e, 0x174a, 0x5eba, 0x7741, + 0x5d12, 0x57d0, 0x4702, 0x6644, 0x1dc4, 0x5766, 0x19b1, 0x1434, 0x7ad4, 0x2ce5, + 0x1961, 0x2b9d, 0x6f98, 0x1aff, 0x06af, 0x4248, 0x0753, 0x36f0, 0x4441, 0x0e50, + 0x6d73, 0x6b3e, 0x55f9, 0x4659, 0x5821, 0x18fe, 0x1ae6, 0x1731, 0x3fbc, 0x17e4, + 0x652a, 0x10ac, 0x54b5, 0x68a4, 0x3b73, 0x29f1, 0x0b7c, 0x5301, 0x16e3, 0x3650, + 0x60b0, 0x3c3c, 0x0bb0, 0x6c58, 0x288d, 0x25ee, 0x4c62, 0x42e1, 0x25bb, 0x2eb8, + 0x6cfa, 0x26f5, 0x1c7f, 0x7e1e, 0x44aa, 0x490e, 0x1896, 0x061b, 0x6bc2, 0x664d, + 0x1387, 0x54ce, 0x7191, 0x4131, 0x5f56, 0x45f4, 0x37b8, 0x5101, 0x6493, 0x75fd, + 0x111a, 0x38e5, 0x2e1a, 0x3ad9, 0x2bbe, 0x5c69, 0x276a, 0x68bd, 0x4a20, 0x2fec, + 0x7af5, 0x7d70, 0x15d5, 0x12ee, 0x01d2, 0x77d2, 0x2333, 0x3cac, 0x6167, 0x2c17, + 0x332d, 0x4b47, 0x5da5, 0x09ca, 0x1c79, 0x0d07, 0x45a1, 0x5713, 0x2e3b, 0x194d, + 0x4ff1, 0x53bb, 0x5e5c, 0x606d, 0x5c8a, 0x30ba, 0x0bfd, 0x7c66, 0x24e4, 0x6543, + 0x4db1, 0x7d0e, 0x6f84, 0x381d, 0x454f, 0x3b32, 0x565c, 0x7e18, 0x735b, 0x1fca, + 0x4389, 0x5068, 0x4ba3, 0x470b, 0x7fca, 0x2762, 0x058a, 0x4a7e, 0x66ae, 0x6be6, + 0x5a09, 0x4ebb, 0x69b2, 0x1c19, 0x74dd, 0x038b, 0x5d84, 0x1aa6, 0x698b, 0x1c58, + 0x3c89, 0x0b55, 0x640c, 0x16a1, 0x31b4, 0x620c, 0x6282, 0x2181, 0x2d96, 0x7e97, + 0x58de, 0x07c9, 0x798d, 0x708e, 0x5503, 0x7ca6, 0x7431, 0x10c5, 0x3e09, 0x3575, + 0x5cee, 0x2409, 0x4c30, 0x35b6, 0x5558, 0x32ac, 0x56cf, 0x3927, 0x0449, 0x1f5e, + 0x15e6, 0x6e3b, 0x7d81, 0x3ebf, 0x5724, 0x4cad, 0x2e4c, 0x1864, 0x4605, 0x3e3a, + 0x37c9, 0x5c15, 0x6d0b, 0x6764, 0x2ec9, 0x0d5a, 0x6bf7, 0x143d, 0x5a1a, 0x2a0a, + 0x4560, 0x76b6, 0x382e, 0x4815, 0x6293, 0x5982, 0x621d, 0x2215, 0x241a, 0x5c46, + 0x4c41, 0x33e3, 0x57e1, 0x286c, 0x4713, 0x3b8c, 0x034c, 0x419a, 0x3427, 0x5406, + 0x4452, 0x22e1, 0x3701, 0x6e87, 0x68b5, 0x0eca, 0x3b84, 0x048d, 0x4923, 0x5fd4, + 0x3fd1, 0x00d5, 0x44a4, 0x7b8a, 0x5361, 0x2d3e, 0x0ab3, 0x6d5f, 0x6433, 0x1799, + 0x128a, 0x1536, 0x704e, 0x101d, 0x4519, 0x32f7, 0x4837, 0x531a, 0x4448, 0x45fb, + 0x442d, 0x441c, 0x4437, 0x0331, 0x126f, 0x4908, 0x15cb, 0x6cf0, 0x4545, 0x6278, + 0x4463, 0x19ba, 0x5417, 0x6120, 0x2d4f, 0x2b2b, 0x0ac4, 0x4508, 0x4826, 0x387f, + 0x62a4, 0x6f51, 0x4616, 0x5880, 0x1875, 0x67b8, 0x5335, 0x6cd9, 0x1fb3, 0x4dfc, + 0x6375, 0x313e, 0x67e4, 0x125e, 0x4426, 0x0694, 0x16c8, 0x1acb, 0x4852, 0x245a, + 0x5e9f, 0x46e7, 0x379d, 0x7176, 0x448f, 0x0b95, 0x3312, 0x5e41, 0x4a05, 0x15ba, + 0x4534, 0x4d96, 0x74c2, 0x4b88, 0x6970, 0x58c3, 0x3dee, 0x4c15, 0x7aa8, 0x6ca4, + 0x6c23, 0x16fc, 0x389a, 0x0f71, 0x7937, 0x5e8e, 0x4841, 0x7f7c, 0x0c92, 0x215c, + 0x78b7, 0x534c, 0x0e01, 0x6ddc, 0x6f6c, 0x7add, 0x073b, 0x2f1d, 0x791f, 0x1bb0, + 0x340f, 0x4415, 0x16b7, 0x49af, 0x6233, 0x5bd0, 0x62bf, 0x2ca0, 0x6ba1, 0x5489, + 0x4631, 0x5f35, 0x4bab, 0x0495, 0x38bd, 0x0cdf, 0x1679, 0x1fa2, 0x5324, 0x0cb7, + 0x0d86, 0x5aeb, 0x589b, 0x2b03, 0x4c85, 0x0ea2, 0x21ff, 0x75e7, 0x6a6d, 0x3669, + 0x1890, 0x4893, 0x1d73, 0x6364, 0x67d3, 0x55e5, 0x3465, 0x306f, 0x4d12, 0x11a0, + 0x1612, 0x7a49, 0x613b, 0x73ca, 0x4ef9, 0x1ccc, 0x3261, 0x60fc, 0x580d, 0x49f4, + 0x3301, 0x684a, 0x31db, 0x0615, 0x5432, 0x2a98, 0x70fa, 0x4fc5, 0x135b, 0x2cee, + 0x04b9, 0x60c9, 0x19d5, 0x149e, 0x71ef, 0x378c, 0x447e, 0x78f9, 0x0564, 0x6504, + 0x7b33, 0x5b13, 0x6472, 0x258f, 0x6682, 0x10f9, 0x7d4d, 0x3c55, 0x0adf, 0x2286, + 0x1bc8, 0x74b1, 0x4523, 0x2809, 0x20f9, 0x6abc, 0x7dc1, 0x1a43, 0x1049, 0x24b8, + 0x2d6a, 0x3f40, 0x3967, 0x6fe6, 0x47e4, 0x0dae, 0x08f9, 0x695f, 0x3ddd, 0x1d23, + 0x494f, 0x4e70, 0x2b46, 0x0139, 0x469b, 0x041d, 0x4e46, 0x3f16, 0x225c, 0x248e, + 0x49ca, 0x2a6e, 0x78cf, 0x609f, 0x16d2, 0x7f52, 0x2c76, 0x43eb, 0x7a1f, 0x4869, + 0x5f0b, 0x5ac1, 0x5beb, 0x6e11, 0x768c, 0x33b9, 0x2d14, 0x150c, 0x22b7, 0x3b62, + 0x0b6b, 0x4d6c, 0x2430, 0x1234, 0x624e, 0x45d1, 0x1990, 0x6f27, 0x62da, 0x1da3, + 0x69ba, 0x41a2, 0x3f96, 0x7fa4, 0x0715, 0x3fab, 0x1ad5, 0x0e26, 0x3626, 0x687a, + 0x2cbb, 0x7717, 0x7666, 0x26c5, 0x7e6d, 0x1c2e, 0x354b, 0x1f34, 0x6bbc, 0x0361, + 0x3b08, 0x6519, 0x54a4, 0x50d7, 0x48e4, 0x2e8e, 0x2bed, 0x2fc2, 0x4b1d, 0x5391, + 0x2f38, 0x72ab, 0x094f, 0x2997, 0x4660, 0x138e, 0x2461, 0x0742, 0x069e, 0x7f25, + 0x5d15, 0x6647, 0x0756, 0x0e53, 0x54b8, 0x29f4, 0x25be, 0x26f8, 0x5f59, 0x5104, + 0x7af8, 0x12f1, 0x45a4, 0x1950, 0x6f87, 0x3b35, 0x66b1, 0x4ebe, 0x31b7, 0x2184, + 0x5cf1, 0x35b9, 0x2e4f, 0x3e3d, 0x3831, 0x5985, 0x342a, 0x22e4, 0x5364, 0x6d62, + 0x4430, 0x0334, 0x0ac7, 0x3882, 0x67e7, 0x0697, 0x4a08, 0x4d99, 0x793a, 0x7f7f, + 0x3412, 0x49b2, 0x167c, 0x0cba, 0x1d76, 0x55e8, 0x5810, 0x684d, 0x71f2, 0x78fc, + 0x1bcb, 0x280c, 0x08fc, 0x1d26, 0x78d2, 0x7f55, 0x22ba, 0x4d6f, 0x0718, 0x0e29, + 0x3b0b, 0x50da, 0x2464, 0x7f28, 0x45a7, 0x3b38, 0x5367, 0x0337, 0x1d79, 0x6850, + 0x3b0e, 0x7f2b, 0x7f1e, 0x7f3b, 0x0e1c, 0x7f48, 0x7f1b, 0x032a, 0x6df7, 0x7f38, + 0x50bd, 0x1d89, 0x7291, 0x3b1b, 0x031a, 0x7f65, 0x2fb5, 0x50ca, 0x1c21, 0x0354, + 0x7f97, 0x1d96, 0x0e19, 0x770a, 0x485c, 0x7f45, 0x3f09, 0x2a61, 0x14ff, 0x6e04, + 0x4d5f, 0x45c4, 0x2177, 0x3b28, 0x26eb, 0x12e4, 0x1381, 0x729e, 0x7f18, 0x0e46, + 0x068a, 0x0327, 0x3e30, 0x22d7, 0x0cad, 0x7f72, 0x6840, 0x27ff, 0x0897, 0x45b4, + 0x1217, 0x2708, 0x7952, 0x3b45, 0x6df4, 0x5d01, 0x5ea9, 0x7f35, 0x5aa4, 0x54c8, + 0x3bd6, 0x2471, 0x2a51, 0x72bb, 0x38b5, 0x0344, 0x1f17, 0x4a18, 0x7320, 0x5374, + 0x50ba, 0x3e4d, 0x515d, 0x1d86, 0x3f8e, 0x7f8f, 0x0f8c, 0x685d, 0x76fa, 0x090c, + 0x1717, 0x0e36, 0x662a, 0x7676, 0x0197, 0x0725, 0x728e, 0x1db3, 0x46f1, 0x3b18, + 0x359c, 0x1c3e, 0x6c3e, 0x50e7, 0x12d4, 0x4b2d, 0x5fba, 0x22c7, 0x5968, 0x6e21, + 0x6cbf, 0x4d7c, 0x0317, 0x19a0, 0x7ac3, 0x7f62, 0x55cb, 0x5f1b, 0x73b0, 0x78df, + 0x27ef, 0x3f26, 0x3684, 0x5381, 0x0c52, 0x4314, 0x35e8, 0x732d, 0x2fb2, 0x2301, + 0x3148, 0x50c7, 0x7ccd, 0x384e, 0x6a88, 0x3e5a, 0x2e7e, 0x520b, 0x6309, 0x1f24, + 0x576b, 0x06b4, 0x7602, 0x4a25, 0x1c1e, 0x6411, 0x221a, 0x0351, 0x6f56, 0x637a, + 0x5bd5, 0x38c2, 0x6509, 0x0ae4, 0x1239, 0x3f9b, 0x4ec3, 0x342f, 0x1d8e, 0x7f9c, + 0x7f94, 0x019c, 0x637f, 0x1d93, 0x0971, 0x659d, 0x2fe4, 0x516a, 0x4192, 0x0cd7, + 0x18ab, 0x686a, 0x1746, 0x690c, 0x718b, 0x0f99, 0x0e16, 0x720f, 0x093b, 0x7707, + 0x59db, 0x2829, 0x48ae, 0x0919, 0x26b5, 0x5a6e, 0x1558, 0x5ab1, 0x2693, 0x0e70, + 0x5600, 0x54d5, 0x4859, 0x2f24, 0x67ee, 0x7f42, 0x11c2, 0x412b, 0x4f1b, 0x5eb6, + 0x43db, 0x5d32, 0x308a, 0x247e, 0x773d, 0x4064, 0x04df, 0x3be3, 0x3f06, 0x13ab, + 0x2506, 0x2a5e, 0x552a, 0x096c, 0x3480, 0x72c8, 0x608f, 0x202c, 0x4d2d, 0x3b52, + 0x0b13, 0x39ef, 0x6598, 0x795f, 0x14fc, 0x66ce, 0x1268, 0x6e01, 0x293b, 0x21a1, + 0x11bb, 0x5d0e, 0x33a9, 0x5044, 0x114f, 0x1224, 0x57cc, 0x5f76, 0x162d, 0x2715, + 0x4d5c, 0x34cf, 0x7a64, 0x45c1, 0x63de, 0x6b7e, 0x205b, 0x08a4, 0x6f17, 0x130e, + 0x391a, 0x35a9, 0x7c99, 0x3568, 0x1694, 0x1c4b, 0x2174, 0x07bc, 0x1fbd, 0x3b25, + 0x7c59, 0x7d01, 0x4a71, 0x46fe, 0x4eae, 0x037e, 0x38d8, 0x50f4, 0x6640, 0x4124, + 0x42d4, 0x6c4b, 0x26e8, 0x4901, 0x3c9f, 0x12e1, 0x5c5c, 0x2fdf, 0x0cfa, 0x4b3a, + 0x1940, 0x6060, 0x04b0, 0x0732, 0x5a11, 0x540e, 0x5165, 0x01a4, 0x137e, 0x7fc1, + 0x4e06, 0x729b, 0x7458, 0x69d7, 0x4bc6, 0x1dc0, 0x2987, 0x77a2, 0x1427, 0x6637, + 0x5762, 0x7734, 0x5f50, 0x7683, 0x7f15, 0x1f0e, 0x464c, 0x0e43, 0x2b90, 0x423b, + 0x109f, 0x1724, 0x29e4, 0x3643, 0x58b6, 0x4d89, 0x7169, 0x5e34, 0x3131, 0x6ccc, + 0x0687, 0x244d, 0x6ce3, 0x0324, 0x32ea, 0x45ee, 0x2b1e, 0x19ad, 0x3872, 0x5873, + 0x5c39, 0x5975, 0x1430, 0x76a9, 0x4ca0, 0x6e2e, 0x3e2d, 0x6757, 0x0ebd, 0x22d4, + 0x285f, 0x418d, 0x7b7d, 0x5fc7, 0x6d52, 0x1529, 0x1193, 0x55d8, 0x75da, 0x4886, + 0x0cd2, 0x5f28, 0x0caa, 0x2af6, 0x533f, 0x7f6f, 0x6c97, 0x0f64, 0x1ba3, 0x7ad0, + 0x49a2, 0x2c93, 0x5b06, 0x78ec, 0x2ce1, 0x1491, 0x60ef, 0x73bd, 0x683d, 0x2a8b, + 0x1a36, 0x27fc, 0x10ec, 0x2279, 0x0da1, 0x3f33, 0x1d16, 0x012c, 0x544d, 0x12fe, + 0x2638, 0x6177, 0x7250, 0x7b05, 0x0894, 0x5c79, 0x5e4b, 0x45b1, 0x320d, 0x4b57, + 0x2ab3, 0x195d, 0x6b6e, 0x5c9a, 0x7ec7, 0x5f66, 0x2b99, 0x665d, 0x7115, 0x5111, + 0x1214, 0x2e2a, 0x4fe0, 0x2705, 0x71c1, 0x18a6, 0x3a68, 0x25cb, 0x34bf, 0x6c68, + 0x1164, 0x66be, 0x0393, 0x471b, 0x6865, 0x4ecb, 0x794f, 0x5d94, 0x331c, 0x3b42, + 0x40d2, 0x4399, 0x6aec, 0x6f94, 0x39df, 0x7c76, 0x0630, 0x2191, 0x1afb, 0x799d, + 0x37b2, 0x31c4, 0x6df1, 0x1c68, 0x4590, 0x5cfe, 0x32d3, 0x7cb6, 0x31f6, 0x35c6, + 0x5034, 0x0459, 0x64c8, 0x5d22, 0x0c30, 0x577f, 0x5828, 0x6654, 0x5ea6, 0x7ae4, + 0x4a0f, 0x7f32, 0x33b0, 0x50fb, 0x405b, 0x06ab, 0x411b, 0x76a0, 0x327c, 0x0e60, + 0x4244, 0x5831, 0x7532, 0x0763, 0x5aa1, 0x2bad, 0x2759, 0x54c5, 0x20cb, 0x1741, + 0x6117, 0x2a01, 0x2f14, 0x60c0, 0x1ce7, 0x139b, 0x0fcf, 0x07dd, 0x6907, 0x466d, + 0x3bd3, 0x01c1, 0x15c4, 0x246e, 0x557f, 0x5a2e, 0x4f14, 0x074f, 0x4054, 0x1a57, + 0x40bd, 0x095c, 0x36ec, 0x1ddd, 0x73e5, 0x29a4, 0x2a4e, 0x2322, 0x6156, 0x72b8, + 0x47b6, 0x7a85, 0x601f, 0x2f45, 0x201c, 0x7475, 0x47d9, 0x0ad4, 0x3256, 0x19ca, + 0x7914, 0x388f, 0x38b2, 0x1885, 0x4499, 0x0341, 0x5719, 0x4555, 0x2d44, 0x443d, + 0x636a, 0x3307, 0x651f, 0x06a4, 0x0e4c, 0x5eaf, 0x7215, 0x67f4, 0x1f14, 0x6ce9, + 0x1c6e, 0x4a15, 0x4c57, 0x7186, 0x057f, 0x4da6, 0x6401, 0x3dfe, 0x7b4e, 0x22f1, + 0x74e5, 0x3b94, 0x0f94, 0x3437, 0x731d, 0x287c, 0x0b9f, 0x5371, 0x6ece, 0x5fe4, + 0x5b2e, 0x6d6f, 0x4304, 0x705e, 0x7632, 0x383e, 0x6b3a, 0x144d, 0x648d, 0x5992, + 0x50b7, 0x4c51, 0x25aa, 0x3e4a, 0x21d1, 0x2ed9, 0x303d, 0x2e5c, 0x51fb, 0x6e4b, + 0x60e4, 0x0cc7, 0x3126, 0x4c95, 0x42c9, 0x1689, 0x515a, 0x5f45, 0x7180, 0x1d83, + 0x35dd, 0x75f7, 0x04d4, 0x55f5, 0x658d, 0x1622, 0x1671, 0x341f, 0x4655, 0x7aed, + 0x2d09, 0x49bf, 0x3f8b, 0x6bb1, 0x1376, 0x7f8c, 0x070d, 0x0e11, 0x7315, 0x7947, + 0x018c, 0x6cb4, 0x713a, 0x71ff, 0x42be, 0x2cfe, 0x720a, 0x7909, 0x0f89, 0x6482, + 0x37a7, 0x685a, 0x7245, 0x710a, 0x7527, 0x581d, 0x68fc, 0x73da, 0x19f0, 0x2819, + 0x18fa, 0x1059, 0x7145, 0x1bd8, 0x76f7, 0x1109, 0x2e09, 0x0909, 0x7889, 0x3f50, + 0x14b9, 0x1d33, 0x5a5e, 0x46ab, 0x05df, 0x3633, 0x52e4, 0x1763, 0x1be3, 0x6887, + 0x1714, 0x5d73, 0x74cc, 0x0e33, 0x18e1, 0x310d, 0x261f, 0x1ae2, 0x422b, 0x0fb6, + 0x0afa, 0x7724, 0x172d, 0x0c39, 0x7150, 0x2cc8, 0x6627, 0x59f8, 0x69a1, 0x7673, + 0x33f9, 0x0936, 0x22a1, 0x26d2, 0x1efe, 0x6611, 0x3c70, 0x7fb1, 0x5d8c, 0x2874, + 0x7702, 0x3fa3, 0x0194, 0x7fb9, 0x4b92, 0x0722, 0x394e, 0x4ee0, 0x7d68, 0x3fb8, + 0x53fe, 0x1f9a, 0x1b49, 0x69c7, 0x17e0, 0x5187, 0x1114, 0x41af, 0x728b, 0x0579, + 0x669d, 0x1db0, 0x0a85, 0x336e, 0x3d39, 0x62e7, 0x7792, 0x098e, 0x7ddc, 0x036e, + 0x1bfc, 0x75b5, 0x1905, 0x6bc9, 0x46ee, 0x6f73, 0x4da0, 0x3b15, 0x504b, 0x38df, + 0x1a5e, 0x6526, 0x7cf1, 0x7bff, 0x37ed, 0x3558, 0x10a8, 0x5788, 0x1064, 0x1f41, + 0x3599, 0x0bec, 0x24d3, 0x1c3b, 0x61ef, 0x59d6, 0x208c, 0x7e7a, 0x07ac, 0x4a42, + 0x2145, 0x48f1, 0x7e01, 0x3e77, 0x2824, 0x2e9b, 0x6c3b, 0x564b, 0x453e, 0x50e4, + 0x3abc, 0x30f6, 0x43d4, 0x54b1, 0x4114, 0x7cea, 0x6ad7, 0x2fcf, 0x68a0, 0x4f7d, + 0x19fb, 0x2bfa, 0x12d1, 0x734a, 0x4378, 0x4b2a, 0x56f6, 0x0c6f, 0x2114, 0x539e, + 0x6050, 0x23cd, 0x7001, 0x1519, 0x177c, 0x3a8b, 0x7894, 0x2d21, 0x5fb7, 0x797c, + 0x58cd, 0x22c4, 0x0470, 0x0b30, 0x3982, 0x3b6f, 0x417d, 0x39ca, 0x7bf8, 0x7699, + 0x29ed, 0x5d2b, 0x3f5b, 0x33c6, 0x5965, 0x6271, 0x2d85, 0x6e1e, 0x1847, 0x48a9, + 0x756d, 0x5bf8, 0x6747, 0x2958, 0x5136, 0x243d, 0x1aae, 0x57e9, 0x0914, 0x1241, + 0x6cbc, 0x3c78, 0x697a, 0x4d79, 0x4bf8, 0x317c, 0x2de2, 0x0b78, 0x5e24, 0x2732, + 0x47ff, 0x45de, 0x52fd, 0x3db6, 0x2e14, 0x625b, 0x0314, 0x63fb, 0x31a3, 0x199d, + 0x44eb, 0x08c1, 0x0dc9, 0x6f34, 0x5863, 0x6a35, 0x3513, 0x2c83, 0x5bb3, 0x5ed3, + 0x1d3e, 0x43f8, 0x7ac0, 0x5cdd, 0x3df8, 0x7f5f, 0x6dbf, 0x3ad3, 0x27b7, 0x16df, + 0x0f54, 0x11df, 0x4e8b, 0x4876, 0x364c, 0x64d1, 0x14c4, 0x7a2c, 0x55c8, 0x54f2, + 0x7420, 0x5f18, 0x1f85, 0x26b0, 0x496a, 0x5ace, 0x2ae6, 0x13f8, 0x2b61, 0x2a7b, + 0x05f8, 0x39a7, 0x5a69, 0x49d7, 0x73ad, 0x5547, 0x4c1f, 0x78dc, 0x2572, 0x72e5, + 0x0154, 0x60ac, 0x1481, 0x4286, 0x52ac, 0x2269, 0x3c38, 0x775a, 0x46b6, 0x249b, + 0x27ec, 0x56be, 0x0438, 0x3f23, 0x6942, 0x02dc, 0x0f1c, 0x4e53, 0x011c, 0x3c00, + 0x1e5f, 0x6edb, 0x0fee, 0x3fee, 0x076e, 0x5ff1, 0x3681, 0x6a14, 0x370b, 0x537e, + 0x5241, 0x4f94, 0x28be, 0x0bac, 0x2f64, 0x7ba7, 0x06ca, 0x6d7c, 0x6c54, 0x5588, + 0x753d, 0x5b3b, 0x0c4f, 0x6450, 0x58f4, 0x4311, 0x2a20, 0x1553, 0x4263, 0x706b, + 0x4cf1, 0x0a56, 0x1b1a, 0x3444, 0x5664, 0x7b92, 0x5aac, 0x0fa1, 0x35e5, 0x41b7, + 0x6e91, 0x732a, 0x03b2, 0x4730, 0x6fb3, 0x2889, 0x3d83, 0x497f, 0x3a33, 0x74f2, + 0x25ea, 0x0ee7, 0x2bb8, 0x3ba1, 0x2faf, 0x7b48, 0x197c, 0x22fe, 0x1596, 0x4cdd, + 0x0a1f, 0x7b5b, 0x2657, 0x371e, 0x5614, 0x599f, 0x283d, 0x65da, 0x424f, 0x649a, + 0x3145, 0x623a, 0x22eb, 0x50c4, 0x7966, 0x5c63, 0x4674, 0x4c5e, 0x2ea2, 0x184e, + 0x2eed, 0x6b47, 0x42dd, 0x5a37, 0x583c, 0x145a, 0x7cca, 0x1ed7, 0x1919, 0x384b, + 0x3382, 0x268e, 0x08d5, 0x763f, 0x1d52, 0x76d3, 0x53cf, 0x21de, 0x4dc5, 0x6781, + 0x0e6b, 0x2ee6, 0x6a85, 0x3755, 0x445c, 0x3e57, 0x17ad, 0x5935, 0x3083, 0x25b7, + 0x3275, 0x37e6, 0x6b59, 0x2e69, 0x2eb4, 0x02b7, 0x3287, 0x304a, 0x2e7b, 0x4cca, + 0x3d95, 0x5208, 0x7504, 0x7d9e, 0x6d8e, 0x6e58, 0x2f76, 0x051c, 0x17ff, 0x6801, + 0x3e90, 0x34e2, 0x20d6, 0x7222, 0x6306, 0x315b, 0x0ed4, 0x1f21, 0x5dab, 0x1fd0, + 0x3fd7, 0x6cf6, 0x6a73, 0x2a9e, 0x3551, 0x0e59, 0x26f1, 0x2477, 0x174c, 0x5ebc, + 0x5768, 0x19b3, 0x1b01, 0x06b1, 0x6b40, 0x55fb, 0x17e6, 0x652c, 0x5303, 0x16e5, + 0x25f0, 0x4c64, 0x7e20, 0x44ac, 0x54d0, 0x7193, 0x75ff, 0x111c, 0x68bf, 0x4a22, + 0x77d4, 0x2335, 0x09cc, 0x1c7b, 0x53bd, 0x5e5e, 0x6545, 0x4db3, 0x7e1a, 0x735d, + 0x2764, 0x058c, 0x1c1b, 0x74df, 0x0b57, 0x640e, 0x7e99, 0x58e0, 0x10c7, 0x3e0b, + 0x32ae, 0x56d1, 0x3ec1, 0x5726, 0x5c17, 0x6d0d, 0x2a0c, 0x4562, 0x2217, 0x241c, + 0x3b8e, 0x034e, 0x6e89, 0x68b7, 0x00d7, 0x44a6, 0x179b, 0x128c, 0x531c, 0x444a, + 0x490a, 0x15cd, 0x6122, 0x2d51, 0x6f53, 0x4618, 0x4dfe, 0x6377, 0x1acd, 0x4854, + 0x0b97, 0x3314, 0x4b8a, 0x6972, 0x16fe, 0x389c, 0x215e, 0x78b9, 0x2f1f, 0x7921, + 0x5bd2, 0x62c1, 0x0497, 0x38bf, 0x5aed, 0x589d, 0x366b, 0x1892, 0x3071, 0x4d14, + 0x1cce, 0x3263, 0x0617, 0x5434, 0x60cb, 0x19d7, 0x6506, 0x7b35, 0x3c57, 0x0ae1, + 0x6abe, 0x7dc3, 0x6fe8, 0x47e6, 0x4e72, 0x2b48, 0x2490, 0x49cc, 0x43ed, 0x7a21, + 0x33bb, 0x2d16, 0x1236, 0x6250, 0x41a4, 0x3f98, 0x687c, 0x2cbd, 0x1f36, 0x6bbe, + 0x2e90, 0x2bef, 0x2999, 0x4662, 0x6649, 0x0758, 0x5106, 0x7afa, 0x4ec0, 0x31b9, + 0x5987, 0x342c, 0x3884, 0x67e9, 0x49b4, 0x167e, 0x78fe, 0x1bcd, 0x4d71, 0x071a, + 0x3b3a, 0x5369, 0x7f3d, 0x0e1e, 0x1d8b, 0x7293, 0x0356, 0x7f99, 0x2a63, 0x1501, + 0x12e6, 0x1383, 0x22d9, 0x0caf, 0x270a, 0x7954, 0x54ca, 0x3bd8, 0x4a1a, 0x7322, + 0x7f91, 0x0f8e, 0x7678, 0x0199, 0x1c40, 0x6c40, 0x6e23, 0x6cc1, 0x5f1d, 0x73b2, + 0x4316, 0x35ea, 0x3850, 0x6a8a, 0x06b6, 0x7604, 0x637c, 0x5bd7, 0x3431, 0x1d90, + 0x659f, 0x2fe6, 0x690e, 0x718d, 0x282b, 0x48b0, 0x0e72, 0x5602, 0x412d, 0x4f1d, + 0x4066, 0x04e1, 0x096e, 0x3482, 0x39f1, 0x659a, 0x21a3, 0x11bd, 0x5f78, 0x162f, + 0x6b80, 0x205d, 0x356a, 0x1696, 0x7d03, 0x4a73, 0x4126, 0x42d6, 0x2fe1, 0x0cfc, + 0x5410, 0x5167, 0x69d9, 0x4bc8, 0x7736, 0x5f52, 0x423d, 0x10a1, 0x5e36, 0x3133, + 0x45f0, 0x2b20, 0x76ab, 0x4ca2, 0x418f, 0x7b7f, 0x4888, 0x0cd4, 0x0f66, 0x1ba5, + 0x1493, 0x60f1, 0x227b, 0x0da3, 0x6179, 0x7252, 0x4b59, 0x2ab5, 0x665f, 0x7117, + 0x18a8, 0x3a6a, 0x471d, 0x6867, 0x439b, 0x6aee, 0x799f, 0x37b4, 0x7cb8, 0x31f8, + 0x5781, 0x582a, 0x50fd, 0x405d, 0x5833, 0x7534, 0x1743, 0x6119, 0x07df, 0x6909, + 0x5a30, 0x4f16, 0x1ddf, 0x73e7, 0x7a87, 0x6021, 0x19cc, 0x7916, 0x4557, 0x2d46, + 0x5eb1, 0x7217, 0x7188, 0x0581, 0x3b96, 0x0f96, 0x5fe6, 0x5b30, 0x144f, 0x648f, + 0x2edb, 0x303f, 0x4c97, 0x42cb, 0x75f9, 0x04d6, 0x7aef, 0x2d0b, 0x0e13, 0x7317, + 0x2d00, 0x720c, 0x710c, 0x7529, 0x105b, 0x7147, 0x3f52, 0x14bb, 0x1765, 0x1be5, + 0x310f, 0x2621, 0x0c3b, 0x7152, 0x0938, 0x22a3, 0x2876, 0x7704, 0x4ee2, 0x7d6a, + 0x5189, 0x1116, 0x3370, 0x3d3b, 0x75b7, 0x1907, 0x38e1, 0x1a60, 0x578a, 0x1066, + 0x59d8, 0x208e, 0x3e79, 0x2826, 0x30f8, 0x43d6, 0x4f7f, 0x19fd, 0x0c71, 0x2116, + 0x3a8d, 0x7896, 0x0b32, 0x3984, 0x5d2d, 0x3f5d, 0x48ab, 0x756f, 0x57eb, 0x0916, + 0x317e, 0x2de4, 0x3db8, 0x2e16, 0x08c3, 0x0dcb, 0x5ed5, 0x1d40, 0x3ad5, 0x27b9, + 0x64d3, 0x14c6, 0x26b2, 0x496c, 0x39a9, 0x5a6b, 0x72e7, 0x0156, 0x775c, 0x46b8, + 0x02de, 0x0f1e, 0x3ff0, 0x0770, 0x4f96, 0x28c0, 0x558a, 0x753f, 0x1555, 0x4265, + 0x7b94, 0x5aae, 0x4732, 0x6fb5, 0x0ee9, 0x2bba, 0x4cdf, 0x0a21, 0x65dc, 0x4251, + 0x5c65, 0x4676, 0x5a39, 0x583e, 0x2690, 0x08d7, 0x6783, 0x0e6d, 0x5937, 0x3085, + 0x02b9, 0x3289, 0x7da0, 0x6d90, 0x34e4, 0x20d8, 0x1fd2, 0x3fd9, 0x2479, 0x174e, + 0x55fd, 0x17e8, 0x44ae, 0x54d2, 0x2337, 0x09ce, 0x735f, 0x2766, 0x58e2, 0x10c9, + 0x6d0f, 0x2a0e, 0x68b9, 0x00d9, 0x15cf, 0x6124, 0x4856, 0x0b99, 0x78bb, 0x2f21, + 0x589f, 0x366d, 0x5436, 0x60cd, 0x7dc5, 0x6fea, 0x7a23, 0x33bd, 0x2cbf, 0x1f38, + 0x075a, 0x5108, 0x67eb, 0x49b6, 0x536b, 0x7f3f, 0x1503, 0x12e8, 0x3bda, 0x4a1c, + 0x6c42, 0x6e25, 0x6a8c, 0x06b8, 0x2fe8, 0x6910, 0x4f1f, 0x4068, 0x11bf, 0x5f7a, + 0x4a75, 0x4128, 0x4bca, 0x7738, 0x2b22, 0x76ad, 0x1ba7, 0x1495, 0x2ab7, 0x6661, + 0x6af0, 0x79a1, 0x405f, 0x5835, 0x4f18, 0x1de1, 0x2d48, 0x5eb3, 0x5b32, 0x1451, + 0x04d8, 0x7af1, 0x752b, 0x105d, 0x2623, 0x0c3d, 0x7d6c, 0x518b, 0x1a62, 0x578c, + 0x43d8, 0x4f81, 0x3986, 0x5d2f, 0x2de6, 0x3dba, 0x27bb, 0x64d5, 0x0158, 0x775e, + 0x28c2, 0x558c, 0x6fb7, 0x0eeb, 0x4678, 0x5a3b, 0x3087, 0x02bb, 0x3fdb, 0x247b, + 0x09d0, 0x7361, 0x00db, 0x15d1, 0x366f, 0x5438, 0x1f3a, 0x075c, 0x12ea, 0x3bdc, + 0x6912, 0x4f21, 0x773a, 0x2b24, 0x79a3, 0x4061, 0x1453, 0x04da, 0x518d, 0x1a64, + 0x3dbc, 0x27bd, 0x0eed, 0x467a, 0x7363, 0x00dd, 0x3bde, 0x6914, 0x04dc, 0x518f, + 0x00df, 0x3be0, 0x7ff1, 0x7fef, 0x7fed, 0x01ce, 0x1e4d, 0x03ed, 0x238c, 0x0fdc, + 0x77ce, 0x7feb, 0x01cc, 0x07ea, 0x3f03, 0x6ec8, 0x4e32, 0x13a8, 0x527e, 0x1e4b, + 0x03eb, 0x1cf4, 0x2536, 0x3ccd, 0x1fe9, 0x29b1, 0x1a14, 0x238a, 0x0fda, 0x73f2, + 0x2503, 0x2248, 0x5fde, 0x2a5b, 0x03aa, 0x77cc, 0x7fe9, 0x232f, 0x522f, 0x4767, + 0x0d7e, 0x36f9, 0x3ca8, 0x01ca, 0x07e8, 0x1dea, 0x5527, 0x5b76, 0x7450, 0x0969, + 0x361e, 0x3f01, 0x6ec6, 0x40ca, 0x3946, 0x4bf0, 0x05c9, 0x47c3, 0x3904, 0x4e30, + 0x13a6, 0x7a92, 0x347d, 0x4adb, 0x492d, 0x72c5, 0x0269, 0x527c, 0x1e49, 0x6163, + 0x28ac, 0x66fc, 0x5dc4, 0x2f52, 0x2c13, 0x03e9, 0x1cf2, 0x602c, 0x608c, 0x6a01, + 0x569d, 0x2029, 0x2789, 0x2534, 0x3ccb, 0x7482, 0x5de8, 0x0069, 0x6712, 0x40df, + 0x6335, 0x1fe7, 0x29af, 0x43a6, 0x4d2a, 0x5cbc, 0x643d, 0x3b4f, 0x4354, 0x1a12, + 0x2388, 0x3329, 0x3d71, 0x5679, 0x631f, 0x6fa1, 0x4b43, 0x0fd8, 0x73f0, 0x6af9, + 0x0b10, 0x7d2b, 0x70a4, 0x39ec, 0x6c0d, 0x2501, 0x2246, 0x7c83, 0x3240, 0x52ce, + 0x3174, 0x4ed8, 0x4391, 0x5fdc, 0x2a59, 0x6872, 0x6595, 0x69cf, 0x17a3, 0x795c, + 0x5237, 0x03a8, 0x77ca, 0x5da1, 0x6e7f, 0x5ae3, 0x025f, 0x03a0, 0x09c6, 0x7fe7, + 0x232d, 0x4728, 0x14f9, 0x5b28, 0x2c62, 0x66cb, 0x434a, 0x522d, 0x4765, 0x1171, + 0x36b0, 0x4a9b, 0x20ef, 0x31d1, 0x0c88, 0x0d7c, 0x36f7, 0x37bf, 0x1265, 0x16be, + 0x6d69, 0x6dfe, 0x66d5, 0x3ca6, 0x01c8, 0x1c75, 0x5652, 0x2d8c, 0x375c, 0x1b08, + 0x0d03, 0x07e6, 0x1de8, 0x79aa, 0x2938, 0x41ee, 0x63ab, 0x219e, 0x61c1, 0x5525, + 0x5b74, 0x063d, 0x70d9, 0x2db3, 0x6c8d, 0x32e0, 0x7c4f, 0x744e, 0x0967, 0x7cc3, + 0x11b8, 0x2931, 0x0abd, 0x5d0b, 0x2c6c, 0x361c, 0x3eff, 0x459d, 0x5a9a, 0x3592, + 0x723b, 0x35d3, 0x570f, 0x6ec4, 0x40c8, 0x3203, 0x33a6, 0x5575, 0x3ab2, 0x5041, + 0x18d7, 0x3944, 0x4bee, 0x0466, 0x6db5, 0x2568, 0x34fb, 0x511e, 0x212d, 0x05c7, + 0x47c1, 0x7122, 0x114c, 0x64b0, 0x1540, 0x1221, 0x117b, 0x3902, 0x4e2e, 0x2e37, + 0x2f9d, 0x087f, 0x0be5, 0x2ba6, 0x1949, 0x13a4, 0x7a90, 0x666a, 0x57c9, 0x4501, + 0x41e7, 0x5f73, 0x1ed0, 0x347b, 0x4ad9, 0x7ed4, 0x133a, 0x414e, 0x7865, 0x71ce, + 0x5070, 0x492b, 0x72c3, 0x18b3, 0x162a, 0x1b51, 0x1294, 0x2712, 0x476f, 0x0267, + 0x527a, 0x4fed, 0x3a21, 0x1c9c, 0x3ea9, 0x25d8, 0x53b7, 0x1e47, 0x6161, 0x3a75, + 0x4d59, 0x42fe, 0x7a0b, 0x34cc, 0x05b1, 0x28aa, 0x66fa, 0x6c75, 0x784d, 0x780f, + 0x7827, 0x321a, 0x1b81, 0x5dc2, 0x2f50, 0x4b64, 0x7a61, 0x5ef7, 0x7058, 0x45be, + 0x36ba, 0x2c11, 0x03e7, 0x5e58, 0x1584, 0x0d24, 0x681a, 0x196a, 0x6069, 0x1cf0, + 0x602a, 0x2ac0, 0x63db, 0x500e, 0x7028, 0x6b7b, 0x5d5e, 0x608a, 0x69ff, 0x5ca7, + 0x0543, 0x7bc9, 0x1818, 0x7b12, 0x23e6, 0x569b, 0x2027, 0x725d, 0x2058, 0x3009, + 0x1027, 0x08a1, 0x4aa5, 0x2787, 0x2532, 0x5c86, 0x0a0d, 0x0849, 0x0827, 0x2645, + 0x30b6, 0x3cc9, 0x7480, 0x6184, 0x6f14, 0x0a43, 0x13d7, 0x130b, 0x3d0b, 0x5de6, + 0x0067, 0x545a, 0x51bf, 0x01ef, 0x7376, 0x1f4e, 0x1a77, 0x6710, 0x40dd, 0x1071, + 0x3917, 0x65f0, 0x76c0, 0x35a6, 0x6b03, 0x6333, 0x1fe5, 0x0bf9, 0x329c, 0x085d, + 0x0650, 0x10b5, 0x7c62, 0x29ad, 0x43a4, 0x5795, 0x7c96, 0x0ddf, 0x5083, 0x3565, + 0x7ee7, 0x4d28, 0x5cba, 0x37fa, 0x23f9, 0x06de, 0x1642, 0x61fc, 0x7d16, 0x643b, + 0x3b4d, 0x59e3, 0x1691, 0x0a8d, 0x456a, 0x1c48, 0x73fa, 0x4352, 0x1a10, 0x24e0, + 0x0b45, 0x0664, 0x7e33, 0x7e87, 0x653f, 0x2386, 0x3327, 0x2099, 0x2171, 0x762c, + 0x7652, 0x07b9, 0x4dd8, 0x3d6f, 0x5677, 0x4a4f, 0x707e, 0x79e5, 0x1b69, 0x5058, + 0x7c37, 0x631d, 0x6f9f, 0x38ec, 0x1fba, 0x2ca7, 0x3838, 0x3b22, 0x0b1a, 0x4b41, + 0x0fd6, 0x4dad, 0x7e08, 0x5bff, 0x4dcc, 0x6533, 0x7d0a, 0x73ee, 0x6af7, 0x1a6b, + 0x7c56, 0x7edb, 0x7c1f, 0x7cfe, 0x7c2b, 0x0b0e, 0x7d29, 0x7c0c, 0x380d, 0x429a, + 0x655e, 0x6bd6, 0x7c18, 0x70a2, 0x39ea, 0x1912, 0x4a6e, 0x63a4, 0x481f, 0x46fb, + 0x7d35, 0x6c0b, 0x24ff, 0x6f80, 0x2752, 0x24cc, 0x11ff, 0x1c09, 0x3819, 0x2244, + 0x7c81, 0x75c2, 0x4eab, 0x267b, 0x0c18, 0x037b, 0x42a6, 0x323e, 0x52cc, 0x7de9, + 0x1a96, 0x5b9b, 0x72db, 0x3ac9, 0x0b26, 0x3172, 0x4ed6, 0x3103, 0x38d5, 0x30ec, + 0x5a24, 0x50f1, 0x4b4d, 0x438f, 0x5fda, 0x454b, 0x75ed, 0x7100, 0x1c34, 0x54be, + 0x3b2e, 0x2a57, 0x6870, 0x43e1, 0x663d, 0x3878, 0x2197, 0x4121, 0x3844, 0x6593, + 0x69cd, 0x7cf7, 0x45e4, 0x0f5a, 0x74f8, 0x2ea8, 0x4db9, 0x17a1, 0x795a, 0x2831, + 0x42d1, 0x3376, 0x2a14, 0x6c48, 0x0fe2, 0x5235, 0x03a6, 0x5658, 0x25de, 0x158a, + 0x7e8d, 0x7e0e, 0x7e14, 0x77c8, 0x5d9f, 0x3e84, 0x26e5, 0x6b34, 0x1ac1, 0x48fe, + 0x5c0b, 0x6e7d, 0x5ae1, 0x2152, 0x060b, 0x6ab2, 0x277d, 0x2c07, 0x38f8, 0x025d, + 0x039e, 0x1a08, 0x3c9c, 0x3612, 0x1447, 0x12de, 0x6fab, 0x09c4, 0x7fe5, 0x7357, + 0x77c2, 0x5272, 0x5893, 0x68ad, 0x1fc6, 0x232b, 0x4726, 0x4f8a, 0x5c59, 0x592b, + 0x4bbe, 0x2fdc, 0x2cb3, 0x14f7, 0x5b26, 0x6ae4, 0x7d60, 0x2dda, 0x18cb, 0x5703, + 0x7c43, 0x2c60, 0x66c9, 0x0c7c, 0x0cf7, 0x61b5, 0x6c01, 0x4b37, 0x6329, 0x4348, + 0x522b, 0x4385, 0x09ba, 0x433e, 0x05a5, 0x53ab, 0x5064, 0x4763, 0x116f, 0x2121, + 0x193d, 0x1ec4, 0x5d52, 0x605d, 0x1b75, 0x36ae, 0x4a99, 0x23da, 0x30aa, 0x3cff, + 0x4943, 0x395b, 0x7d41, 0x20ed, 0x31cf, 0x4eed, 0x04ad, 0x0558, 0x6227, 0x072f, + 0x6c17, 0x0c86, 0x0d7a, 0x4b9f, 0x6a61, 0x3459, 0x6427, 0x3fc5, 0x4707, 0x36f5, + 0x37bd, 0x7d75, 0x5a0e, 0x6211, 0x6298, 0x540b, 0x482b, 0x1263, 0x16bc, 0x1fa7, + 0x4483, 0x74b6, 0x0b70, 0x3fb0, 0x6f8c, 0x6d67, 0x6dfc, 0x770f, 0x5162, 0x1db8, + 0x221f, 0x01a1, 0x250b, 0x66d3, 0x3ca4, 0x7fc6, 0x0ec2, 0x2afb, 0x4fe5, 0x5d99, + 0x275e, 0x01c6, 0x1c73, 0x2881, 0x137b, 0x6487, 0x69a6, 0x7fbe, 0x24d8, 0x5650, + 0x2d8a, 0x3c7d, 0x7425, 0x554c, 0x58f9, 0x41bc, 0x191e, 0x375a, 0x1b06, 0x1121, + 0x4e03, 0x62c6, 0x598c, 0x7298, 0x39f6, 0x0d01, 0x07e4, 0x0586, 0x3e7e, 0x7574, + 0x6788, 0x17ed, 0x4a7a, 0x1de6, 0x79a8, 0x5194, 0x7455, 0x4ae0, 0x70a9, 0x69d4, + 0x63b0, 0x2936, 0x41ec, 0x1b56, 0x702d, 0x300e, 0x5088, 0x0a92, 0x7c24, 0x63a9, + 0x219c, 0x337b, 0x4bc3, 0x61ba, 0x629d, 0x1dbd, 0x70ae, 0x61bf, 0x5523, 0x66aa, + 0x20c4, 0x61e8, 0x7eb2, 0x62f4, 0x6be2, 0x5b72, 0x063b, 0x3d46, 0x2984, 0x65c7, + 0x334d, 0x779f, 0x656a, 0x70d7, 0x2db1, 0x099b, 0x2c37, 0x1e9b, 0x10e0, 0x2cd5, + 0x75ce, 0x6c8b, 0x32de, 0x715d, 0x1424, 0x2853, 0x5c50, 0x6634, 0x7c8d, 0x7c4d, + 0x744c, 0x5a05, 0x5756, 0x2b84, 0x59cf, 0x173a, 0x4eb7, 0x0965, 0x7cc1, 0x0c46, + 0x575f, 0x6f4a, 0x551e, 0x7731, 0x2687, 0x11b6, 0x292f, 0x0b07, 0x57c0, 0x63d2, + 0x71e6, 0x3406, 0x3825, 0x0abb, 0x5d09, 0x0943, 0x5f4d, 0x66a5, 0x2424, 0x7680, + 0x2250, 0x2c6a, 0x361a, 0x69ae, 0x353f, 0x48d8, 0x3e24, 0x26df, 0x1c15, 0x3efd, + 0x459b, 0x22ae, 0x7f12, 0x50b1, 0x3f82, 0x1f0b, 0x120b, 0x5a98, 0x3590, 0x661e, + 0x595c, 0x55bf, 0x787d, 0x18ee, 0x42b2, 0x7239, 0x35d1, 0x311a, 0x4649, 0x0701, + 0x4c4b, 0x0e40, 0x324a, 0x570d, 0x6ec2, 0x74d9, 0x6b2e, 0x21c5, 0x32c7, 0x1aef, + 0x0387, 0x40c6, 0x3201, 0x262c, 0x2b8d, 0x71b5, 0x20bf, 0x4238, 0x0c24, 0x33a4, + 0x5573, 0x0fc3, 0x36e0, 0x47aa, 0x56ea, 0x6894, 0x7df5, 0x3ab0, 0x503f, 0x1bf0, + 0x109c, 0x61e3, 0x33ed, 0x1721, 0x52d8, 0x18d5, 0x3942, 0x5d80, 0x17d4, 0x0a79, + 0x44df, 0x52f1, 0x1aa2, 0x4bec, 0x0464, 0x1770, 0x29e1, 0x183b, 0x1f79, 0x3640, + 0x5ba7, 0x6db3, 0x2566, 0x05ec, 0x3c2c, 0x6936, 0x5292, 0x4c05, 0x7406, 0x34f9, + 0x511c, 0x3189, 0x58b3, 0x7bde, 0x37d3, 0x4d86, 0x435e, 0x212b, 0x05c5, 0x6987, + 0x4b78, 0x1b2f, 0x7618, 0x0b85, 0x1c54, 0x47bf, 0x7120, 0x2def, 0x7166, 0x1657, + 0x7ead, 0x5e31, 0x4576, 0x114a, 0x64ae, 0x273f, 0x15aa, 0x40a3, 0x1135, 0x124e, + 0x24ec, 0x153e, 0x121f, 0x0921, 0x312e, 0x62ef, 0x5c1f, 0x6cc9, 0x1a1c, 0x1179, + 0x3900, 0x3c85, 0x4dec, 0x140d, 0x7e53, 0x1abb, 0x0b51, 0x4e2c, 0x2e35, 0x57f6, + 0x0684, 0x25a4, 0x3af4, 0x244a, 0x0670, 0x2f9b, 0x087d, 0x5143, 0x46d7, 0x5fa0, + 0x54e9, 0x6268, 0x59ef, 0x0be3, 0x2ba4, 0x2e21, 0x6ce0, 0x6ba8, 0x3e44, 0x0321, + 0x3b59, 0x1947, 0x13a2, 0x6408, 0x48f8, 0x674e, 0x21e5, 0x530a, 0x169d, 0x7a8e, + 0x6668, 0x3dc3, 0x32e7, 0x1341, 0x6bdd, 0x45eb, 0x0a99, 0x57c7, 0x44ff, 0x480c, + 0x440c, 0x3783, 0x5005, 0x44f8, 0x7d22, 0x41e5, 0x5f71, 0x08ce, 0x2b1b, 0x5b6d, + 0x460f, 0x19aa, 0x6447, 0x1ece, 0x3479, 0x31b0, 0x6110, 0x2085, 0x71ac, 0x6f41, + 0x6208, 0x4ad7, 0x7ed2, 0x0dd6, 0x386f, 0x5922, 0x7594, 0x5870, 0x164e, 0x1338, + 0x414c, 0x6a42, 0x67a8, 0x28f5, 0x44c5, 0x33d3, 0x20a5, 0x7863, 0x71cc, 0x3f68, + 0x5c36, 0x59b5, 0x676e, 0x5972, 0x3333, 0x506e, 0x4929, 0x627e, 0x2205, 0x4fcb, + 0x7e73, 0x29fa, 0x217d, 0x72c1, 0x18b1, 0x5d38, 0x142d, 0x5879, 0x0636, 0x76a6, + 0x7638, 0x1628, 0x1b4f, 0x7c05, 0x4805, 0x11e5, 0x3a39, 0x1854, 0x654b, 0x1292, + 0x2710, 0x48b6, 0x4c9d, 0x3d41, 0x6d15, 0x6e2b, 0x2392, 0x476d, 0x0265, 0x2d92, + 0x3eaf, 0x0d2a, 0x7e39, 0x5c05, 0x7e93, 0x5278, 0x4feb, 0x757a, 0x3e2a, 0x21cb, + 0x7e59, 0x6754, 0x7e3f, 0x3a1f, 0x1c9a, 0x2965, 0x0d4a, 0x022b, 0x1cb4, 0x047d, + 0x4de4, 0x3ea7, 0x25d6, 0x0b3d, 0x0eba, 0x3537, 0x2ed3, 0x22d1, 0x3d7b, 0x53b5, + 0x1e45, 0x58da, 0x6e77, 0x3a19, 0x4c7d, 0x3b7c, 0x07c5, 0x615f, 0x3a73, 0x398f, + 0x285c, 0x759d, 0x297f, 0x418a, 0x765e, 0x4d57, 0x42fc, 0x39d7, 0x53f6, 0x5e1c, + 0x2609, 0x2d2e, 0x4a5b, 0x7a09, 0x34ca, 0x78a1, 0x7b7a, 0x65c2, 0x0d64, 0x5fc4, + 0x5683, 0x05af, 0x28a8, 0x7989, 0x00c5, 0x2372, 0x0245, 0x1789, 0x708a, 0x66f8, + 0x6c73, 0x3a98, 0x6d4f, 0x3742, 0x0bcb, 0x1526, 0x79f1, 0x784b, 0x780d, 0x700e, + 0x100d, 0x080d, 0x09e5, 0x7a39, 0x57a1, 0x7825, 0x3218, 0x14d1, 0x1190, 0x562a, + 0x4cb7, 0x55d5, 0x43b0, 0x1b7f, 0x5dc0, 0x54ff, 0x305f, 0x1e25, 0x3d5b, 0x3659, + 0x7ca2, 0x2f4e, 0x4b62, 0x64de, 0x75d7, 0x6a4b, 0x3348, 0x4883, 0x0deb, 0x7a5f, + 0x5ef5, 0x4e98, 0x6354, 0x4215, 0x272a, 0x1f92, 0x7c6e, 0x7056, 0x45bc, 0x26bd, + 0x0ccf, 0x779a, 0x572e, 0x5f25, 0x29b9, 0x36b8, 0x2c0f, 0x742d, 0x0485, 0x0e9a, + 0x1c94, 0x5adb, 0x10c1, 0x03e5, 0x5e56, 0x4977, 0x0ca7, 0x3037, 0x48d0, 0x2af3, + 0x065c, 0x1582, 0x0d22, 0x1405, 0x0e92, 0x009d, 0x12ac, 0x6dcc, 0x7ef3, 0x6818, + 0x1968, 0x3ae0, 0x533c, 0x5490, 0x2e56, 0x7f6c, 0x4d34, 0x6067, 0x1cee, 0x3e05, + 0x214c, 0x295f, 0x53d6, 0x16ec, 0x3571, 0x6028, 0x2abe, 0x27c4, 0x6c94, 0x4155, + 0x6565, 0x0f61, 0x508f, 0x63d9, 0x500c, 0x11ec, 0x5e7e, 0x402c, 0x5e77, 0x4405, + 0x3806, 0x7026, 0x6b79, 0x1d4b, 0x1ba0, 0x70d2, 0x186e, 0x7acd, 0x5cc6, 0x5d5c, + 0x6088, 0x5cea, 0x2f0d, 0x07a5, 0x3a53, 0x5bc0, 0x2405, 0x69fd, 0x5ca5, 0x5ee0, + 0x499f, 0x02a4, 0x30d5, 0x2c90, 0x06ea, 0x0541, 0x7bc7, 0x3520, 0x5479, 0x4f45, + 0x027f, 0x257f, 0x6b0f, 0x1816, 0x7b10, 0x72f2, 0x5b03, 0x4f5c, 0x7d8b, 0x78e9, + 0x633f, 0x23e4, 0x5699, 0x4c2c, 0x64f4, 0x34aa, 0x4a3b, 0x60b9, 0x35b2, 0x2025, + 0x725b, 0x0161, 0x2cde, 0x67b1, 0x2dac, 0x148e, 0x76cc, 0x2056, 0x3007, 0x4293, + 0x377c, 0x4025, 0x68d8, 0x49e4, 0x0c05, 0x1025, 0x089f, 0x5a76, 0x60ec, 0x0996, + 0x3ec9, 0x73ba, 0x1ff1, 0x4aa3, 0x2785, 0x5554, 0x1cbc, 0x00a5, 0x0d44, 0x0605, + 0x32a8, 0x2530, 0x5c84, 0x39b4, 0x683a, 0x51f5, 0x2bd9, 0x2a88, 0x0869, 0x0a0b, + 0x0847, 0x2b6e, 0x4fb5, 0x1e0f, 0x234e, 0x24a8, 0x107d, 0x0825, 0x2643, 0x46c3, + 0x1a33, 0x4b09, 0x6e45, 0x27f9, 0x40e9, 0x30b4, 0x3cc7, 0x56cb, 0x6aac, 0x0225, + 0x6d2f, 0x3c45, 0x3923, 0x747e, 0x6182, 0x7767, 0x10e9, 0x28fe, 0x2c32, 0x2276, + 0x65fc, 0x6f12, 0x0a41, 0x52b9, 0x74a1, 0x408e, 0x4787, 0x694f, 0x1a83, 0x13d5, + 0x1309, 0x02e9, 0x0d9e, 0x1e96, 0x15f0, 0x3f30, 0x671c, 0x3d09, 0x5de4, 0x0445, + 0x6fd6, 0x79d1, 0x77ed, 0x4e60, 0x1f5a, 0x0065, 0x5458, 0x0f29, 0x1d13, 0x0509, + 0x23ac, 0x0129, 0x7382, 0x51bd, 0x01ed, 0x3c0d, 0x040d, 0x002d, 0x03ff, 0x01df, + 0x239e, 0x7374, 0x1f4c, 0x77df, 0x544a, 0x1d05, 0x0d90, 0x12fb, 0x4779, 0x1a75, + 0x670e, 0x15e2, 0x5dd6, 0x6fc8, 0x6a9e, 0x3cb9, 0x6e37, 0x40db, 0x106f, 0x2340, + 0x2635, 0x1a25, 0x10db, 0x6174, 0x6d21, 0x3915, 0x65ee, 0x2c24, 0x0a33, 0x7493, + 0x376e, 0x2ff9, 0x2d9e, 0x76be, 0x35a4, 0x4a2d, 0x724d, 0x2cd0, 0x5af5, 0x7b02, + 0x0271, 0x6b01, 0x6331, 0x7d7d, 0x568b, 0x64e6, 0x1cae, 0x2777, 0x3ebb, 0x1fe3, + 0x0bf7, 0x68ca, 0x0891, 0x60de, 0x682c, 0x5c76, 0x0d36, 0x329a, 0x085b, 0x2bcb, + 0x0839, 0x4fa7, 0x0e84, 0x0d14, 0x48c2, 0x064e, 0x10b3, 0x1c86, 0x5e48, 0x0c99, + 0x0cc1, 0x45ae, 0x271c, 0x7c60, 0x29ab, 0x5720, 0x2c01, 0x0477, 0x3051, 0x5db2, + 0x4ca9, 0x43a2, 0x5793, 0x09d7, 0x320a, 0x1182, 0x75c9, 0x4b54, 0x3d4d, 0x7c94, + 0x0ddd, 0x333a, 0x5ee7, 0x6346, 0x5e70, 0x4ffe, 0x6557, 0x5081, 0x3563, 0x53c8, + 0x2ab0, 0x6c86, 0x532e, 0x195a, 0x129e, 0x7ee5, 0x4d26, 0x2e48, 0x1ce0, 0x213e, + 0x2eff, 0x607a, 0x1860, 0x5cb8, 0x37f8, 0x5e69, 0x6b6b, 0x1b92, 0x4991, 0x5c97, + 0x3a45, 0x23f7, 0x06dc, 0x30c7, 0x7bb9, 0x546b, 0x679a, 0x413e, 0x7586, 0x1640, + 0x61fa, 0x719e, 0x7ec4, 0x3861, 0x2b0d, 0x5f63, 0x4ff7, 0x7d14, 0x6439, 0x4601, + 0x346b, 0x6102, 0x48ea, 0x1394, 0x3e36, 0x3b4b, 0x59e1, 0x54db, 0x2b96, 0x6cd2, + 0x32d9, 0x665a, 0x21d7, 0x168f, 0x0a8b, 0x6bcf, 0x44f1, 0x43fe, 0x159c, 0x64a0, + 0x7e9f, 0x4568, 0x1c46, 0x760a, 0x7112, 0x7158, 0x58a5, 0x510e, 0x5284, 0x73f8, + 0x4350, 0x37c5, 0x05b7, 0x4b6a, 0x4dde, 0x38f2, 0x5c11, 0x1a0e, 0x24de, 0x1127, + 0x1211, 0x3120, 0x0676, 0x2e27, 0x7e45, 0x0b43, 0x0662, 0x3ae6, 0x086f, 0x46c9, + 0x0d3c, 0x1c8c, 0x7e4b, 0x7e31, 0x7e85, 0x7e2b, 0x4fdd, 0x3e1c, 0x4c8f, 0x2702, + 0x3a2b, 0x653d, 0x2384, 0x6d07, 0x0257, 0x3ea1, 0x21f7, 0x491b, 0x6760, 0x3325, + 0x2097, 0x44b7, 0x71be, 0x5c28, 0x141f, 0x18a3, 0x7e65, 0x216f, 0x762a, 0x0628, + 0x1b41, 0x47f7, 0x53e8, 0x42ee, 0x2971, 0x7650, 0x07b7, 0x4c6f, 0x3a65, 0x284e, + 0x0eac, 0x25c8, 0x1ca6, 0x4dd6, 0x3d6d, 0x2ec5, 0x1e37, 0x6e69, 0x00b7, 0x289a, + 0x0d56, 0x5675, 0x4a4d, 0x25fb, 0x34bc, 0x7b6c, 0x6d41, 0x6c65, 0x0237, 0x707c, + 0x79e3, 0x0bbd, 0x77ff, 0x0fff, 0x309c, 0x4a8b, 0x5d44, 0x1b67, 0x5056, 0x0597, + 0x1161, 0x192f, 0x0ce9, 0x66bb, 0x18bd, 0x7c35, 0x631b, 0x6bf3, 0x521d, 0x09ac, + 0x77b4, 0x7fd7, 0x1439, 0x6f9d, 0x38ea, 0x276f, 0x0390, 0x3c8e, 0x5c4b, 0x4718, + 0x5885, 0x1fb8, 0x2ca5, 0x4bb0, 0x5b18, 0x7d52, 0x45d6, 0x69bf, 0x2189, 0x3836, + 0x3b20, 0x1c26, 0x6862, 0x662f, 0x38c7, 0x4ec8, 0x72cd, 0x0b18, 0x4b3f, 0x5a16, + 0x5fcc, 0x75df, 0x25d0, 0x0398, 0x2a06, 0x0fd4, 0x4dab, 0x74ea, 0x794c, 0x42c3, + 0x26d7, 0x5d91, 0x7e7f, 0x7e06, 0x5bfd, 0x1ab3, 0x5ad3, 0x05fd, 0x7070, 0x5669, + 0x7644, 0x4dca, 0x6531, 0x7e25, 0x3319, 0x2163, 0x1683, 0x3b3f, 0x1634, 0x7d08, + 0x73ec, 0x455c, 0x1a02, 0x0b37, 0x328e, 0x1fd7, 0x76b2, 0x6af5, 0x1a69, 0x7368, + 0x40cf, 0x3909, 0x7c88, 0x4396, 0x0642, 0x7c54, 0x7ed9, 0x5075, 0x5cac, 0x23eb, + 0x37ff, 0x7d1b, 0x7c11, 0x7c1d, 0x7cfc, 0x4dbe, 0x6ae9, 0x7c48, 0x1fac, 0x6f91, + 0x1b5b, 0x7c29, 0x0b0c, 0x382a, 0x0fc8, 0x7dfa, 0x2744, 0x24f1, 0x4811, 0x7d27, + 0x7c0a, 0x6550, 0x39dc, 0x4a60, 0x4e9d, 0x7c73, 0x11f1, 0x380b, 0x4298, 0x0c0a, + 0x52be, 0x1a88, 0x2c29, 0x2da3, 0x333f, 0x655c, 0x6bd4, 0x7ea4, 0x062d, 0x2976, + 0x4bb5, 0x218e, 0x507a, 0x7c16, 0x70a0, 0x628f, 0x5515, 0x20b6, 0x3e70, 0x07d6, + 0x597e, 0x39e8, 0x1910, 0x58eb, 0x1af8, 0x4df5, 0x7447, 0x799a, 0x677a, 0x4a6c, + 0x63a2, 0x709b, 0x41de, 0x701f, 0x4475, 0x16ae, 0x628a, 0x481d, 0x46f9, 0x6419, + 0x37af, 0x5a00, 0x049f, 0x31c1, 0x4935, 0x7d33, 0x6c09, 0x6219, 0x0d6c, 0x6a53, + 0x0eb4, 0x3c96, 0x2211, 0x24fd, 0x6f7e, 0x0b62, 0x6dee, 0x5154, 0x136d, 0x1c65, + 0x4fd7, 0x2750, 0x24ca, 0x6998, 0x2d7c, 0x7417, 0x594e, 0x3582, 0x3f74, 0x11fd, + 0x1c07, 0x3e16, 0x458d, 0x7f04, 0x5f3f, 0x5cfb, 0x71d8, 0x3817, 0x2242, 0x2416, + 0x360c, 0x3531, 0x5748, 0x743e, 0x5c42, 0x7c7f, 0x75c0, 0x10d2, 0x32d0, 0x1416, + 0x5751, 0x7cb3, 0x59c1, 0x4ea9, 0x2679, 0x5510, 0x2921, 0x57b2, 0x36d2, 0x5565, + 0x20b1, 0x0c16, 0x0379, 0x32b9, 0x31f3, 0x2b7f, 0x463b, 0x35c3, 0x786f, 0x42a4, + 0x323c, 0x4c3d, 0x6eb4, 0x6b20, 0x17c6, 0x3934, 0x33df, 0x52ca, 0x7de7, 0x56dc, + 0x5031, 0x108e, 0x29d3, 0x0456, 0x44d1, 0x1a94, 0x5b99, 0x1f6b, 0x2558, 0x3c1e, + 0x02d0, 0x774e, 0x399b, 0x72d9, 0x3ac7, 0x5ec7, 0x64c5, 0x26a4, 0x489d, 0x5d1f, + 0x3a7f, 0x0b24, 0x3170, 0x57dd, 0x3daa, 0x08b5, 0x3362, 0x517b, 0x2868, 0x4ed4, + 0x3101, 0x1757, 0x0c2d, 0x092a, 0x59ca, 0x577c, 0x75a9, 0x38d3, 0x30ea, 0x3e6b, + 0x4f71, 0x0c63, 0x7a79, 0x1dd1, 0x07d1, 0x5a22, 0x50ef, 0x5773, 0x5825, 0x1735, + 0x189a, 0x6651, 0x616b, 0x4b4b, 0x438d, 0x470f, 0x7991, 0x7caa, 0x2ecd, 0x1441, + 0x3b88, 0x5fd8, 0x4549, 0x19be, 0x5ea3, 0x717a, 0x0e05, 0x7ae1, 0x4c89, 0x75eb, + 0x70fe, 0x2cf2, 0x104d, 0x3f44, 0x5f0f, 0x6e15, 0x766a, 0x1c32, 0x54bc, 0x26fc, + 0x4a0c, 0x7f83, 0x1d7d, 0x7f2f, 0x4d63, 0x3b2c, 0x2a55, 0x0348, 0x12d8, 0x22cb, + 0x2e82, 0x1f28, 0x4196, 0x686e, 0x43df, 0x2482, 0x33ad, 0x1228, 0x4eb2, 0x50f8, + 0x298b, 0x663b, 0x3876, 0x5979, 0x49a6, 0x78f0, 0x6b72, 0x5f6a, 0x39e3, 0x2195, + 0x411f, 0x0e64, 0x4058, 0x0960, 0x636e, 0x06a8, 0x4308, 0x3842, 0x6591, 0x3423, + 0x6900, 0x281d, 0x422f, 0x7728, 0x5402, 0x69cb, 0x7cf5, 0x355c, 0x4118, 0x2fd3, + 0x4181, 0x769d, 0x5e28, 0x45e2, 0x0f58, 0x487a, 0x1485, 0x226d, 0x2f68, 0x6d80, + 0x3d87, 0x74f6, 0x2ea6, 0x6b4b, 0x3279, 0x2e6d, 0x6a77, 0x0e5d, 0x53c1, 0x4db7, + 0x179f, 0x444e, 0x3075, 0x3267, 0x2e94, 0x4666, 0x22dd, 0x7958, 0x282f, 0x5606, + 0x4241, 0x3137, 0x7cbc, 0x582e, 0x2edf, 0x42cf, 0x3374, 0x190b, 0x08c7, 0x1d44, + 0x4ce3, 0x4255, 0x58e6, 0x2a12, 0x6c46, 0x06bc, 0x752f, 0x0c41, 0x3673, 0x0760, + 0x1e51, 0x0fe0, 0x5233, 0x36fd, 0x28b0, 0x2f56, 0x3d75, 0x6fa5, 0x6e83, 0x03a4, + 0x5656, 0x1b0c, 0x5a9e, 0x35d7, 0x2fa1, 0x2baa, 0x3a25, 0x25dc, 0x1588, 0x196e, + 0x0a11, 0x2649, 0x32a0, 0x10b9, 0x0b49, 0x7e8b, 0x7e0c, 0x6537, 0x2756, 0x1c0d, + 0x75f1, 0x54c2, 0x25e2, 0x7e12, 0x77c6, 0x68b1, 0x09be, 0x53af, 0x6a65, 0x3fc9, + 0x0ec6, 0x5d9d, 0x3e82, 0x17f1, 0x20c8, 0x62f8, 0x575a, 0x173e, 0x3543, 0x26e3, + 0x6b32, 0x1af3, 0x17d8, 0x52f5, 0x4b7c, 0x0b89, 0x4df0, 0x1abf, 0x48fc, 0x530e, + 0x6114, 0x6f45, 0x2209, 0x29fe, 0x3eb3, 0x5c09, 0x6e7b, 0x3b80, 0x00c9, 0x178d, + 0x3063, 0x365d, 0x0489, 0x5adf, 0x2150, 0x16f0, 0x2f11, 0x5bc4, 0x64f8, 0x60bd, + 0x1cc0, 0x0609, 0x6ab0, 0x3c49, 0x6fda, 0x4e64, 0x5dda, 0x3cbd, 0x568f, 0x277b, + 0x2c05, 0x5db6, 0x1ce4, 0x607e, 0x346f, 0x1398, 0x05bb, 0x38f6, 0x025b, 0x491f, + 0x1e3b, 0x289e, 0x5221, 0x7fdb, 0x5fd0, 0x039c, 0x1a06, 0x1fdb, 0x0fcc, 0x24f5, + 0x5519, 0x07da, 0x0d70, 0x3c9a, 0x3610, 0x7442, 0x6eb8, 0x3938, 0x3dae, 0x517f, + 0x7995, 0x1445, 0x12dc, 0x1f2c, 0x6904, 0x772c, 0x3079, 0x466a, 0x28b4, 0x6fa9, + 0x09c2, 0x3fcd, 0x00cd, 0x3661, 0x1e3f, 0x7fdf, 0x00d1, 0x7fe3, 0x7355, 0x0edf, + 0x3bd0, 0x04ce, 0x3ef5, 0x01be, 0x237e, 0x77c0, 0x5270, 0x4e24, 0x03dd, 0x2528, + 0x7db7, 0x5428, 0x78ad, 0x5891, 0x68ab, 0x6d01, 0x15c1, 0x4848, 0x55ef, 0x246b, + 0x34d6, 0x1fc4, 0x2329, 0x44a0, 0x7351, 0x58d4, 0x4cd1, 0x0edb, 0x7b86, 0x4724, + 0x4f88, 0x3fe2, 0x557c, 0x1547, 0x2682, 0x5a2b, 0x65ce, 0x5c57, 0x5929, 0x6775, + 0x02ab, 0x7d92, 0x1b99, 0x2b14, 0x4a67, 0x4bbc, 0x2fda, 0x6a7e, 0x4f11, 0x11b1, + 0x67dd, 0x074c, 0x7a15, 0x2cb1, 0x14f5, 0x535d, 0x3bcc, 0x6c34, 0x751d, 0x04ca, + 0x2d3a, 0x5b24, 0x6ae2, 0x2aa9, 0x4051, 0x4f0a, 0x43ca, 0x1a54, 0x2615, 0x7d5e, + 0x2dd8, 0x3978, 0x27ad, 0x014a, 0x6da7, 0x4be0, 0x3aa4, 0x18c9, 0x5701, 0x722d, + 0x40ba, 0x3398, 0x11aa, 0x0959, 0x6c7f, 0x7c41, 0x2c5e, 0x0aaf, 0x3ef1, 0x5a8c, + 0x5644, 0x01ba, 0x6d5b, 0x66c7, 0x0c7a, 0x20e1, 0x36e9, 0x1257, 0x292a, 0x1dda, + 0x374e, 0x0cf5, 0x61b3, 0x639d, 0x5b66, 0x70cb, 0x3232, 0x2238, 0x7096, 0x6bff, + 0x4b35, 0x6311, 0x73e2, 0x0b02, 0x4d1c, 0x29a1, 0x6704, 0x6327, 0x4346, 0x642f, + 0x237a, 0x3d63, 0x6e71, 0x77bc, 0x1795, 0x5229, 0x4383, 0x3166, 0x2a4b, 0x6587, + 0x14eb, 0x231f, 0x0251, 0x09b8, 0x433c, 0x2c54, 0x4757, 0x36a2, 0x783f, 0x66ec, + 0x79fd, 0x05a3, 0x53a9, 0x3e9b, 0x6153, 0x4d4b, 0x161c, 0x72b5, 0x7857, 0x5062, + 0x4761, 0x1286, 0x526c, 0x3a13, 0x2f8f, 0x4e20, 0x1532, 0x116d, 0x211f, 0x34ed, + 0x47b3, 0x113e, 0x57bb, 0x7a82, 0x0bd7, 0x193b, 0x1ec2, 0x41d9, 0x4acb, 0x132c, + 0x0535, 0x69f1, 0x701a, 0x5d50, 0x605b, 0x680c, 0x601c, 0x63cd, 0x7a53, 0x2f42, + 0x7819, 0x1b73, 0x36ac, 0x704a, 0x03d9, 0x1576, 0x09ff, 0x2524, 0x1019, 0x4a97, + 0x23d8, 0x180a, 0x2019, 0x204a, 0x6f06, 0x7472, 0x0819, 0x30a8, 0x3cfd, 0x13c9, + 0x0059, 0x51b1, 0x468d, 0x2b38, 0x3dcf, 0x4941, 0x3959, 0x2d5c, 0x47d6, 0x08eb, + 0x1bba, 0x0ad1, 0x6674, 0x7d3f, 0x20eb, 0x4515, 0x7db3, 0x103b, 0x70ec, 0x5424, + 0x32f3, 0x31cd, 0x4eeb, 0x612d, 0x3253, 0x57ff, 0x71e1, 0x19c7, 0x134d, 0x04ab, + 0x0556, 0x4470, 0x7b25, 0x6464, 0x6b93, 0x62b1, 0x16a9, 0x6225, 0x072d, 0x6f5e, + 0x7911, 0x3401, 0x7929, 0x388c, 0x7a9a, 0x6c15, 0x0c84, 0x4833, 0x78a9, 0x0df3, + 0x4c77, 0x588d, 0x5316, 0x0d78, 0x4b9d, 0x4623, 0x38af, 0x166b, 0x1d65, 0x1882, + 0x21f1, 0x6a5f, 0x3457, 0x67c5, 0x4d04, 0x1604, 0x7040, 0x127c, 0x0aa5, 0x6425, + 0x3fc3, 0x4915, 0x4496, 0x5353, 0x3419, 0x033e, 0x57d3, 0x4705, 0x36f3, 0x4444, + 0x68a7, 0x3b76, 0x2ebb, 0x6cfd, 0x45f7, 0x37bb, 0x7d73, 0x15d8, 0x5716, 0x2e3e, + 0x3820, 0x4552, 0x6be9, 0x5a0c, 0x620f, 0x6285, 0x240c, 0x4c33, 0x1867, 0x4608, + 0x4818, 0x6296, 0x5409, 0x4455, 0x2d41, 0x0ab6, 0x441f, 0x443a, 0x450b, 0x4829, + 0x1261, 0x4429, 0x15bd, 0x4537, 0x5e91, 0x4844, 0x4418, 0x16ba, 0x1fa5, 0x5327, + 0x6367, 0x67d6, 0x49f7, 0x3304, 0x378f, 0x4481, 0x74b4, 0x4526, 0x6962, 0x3de0, + 0x60a2, 0x16d5, 0x3b65, 0x0b6e, 0x3fae, 0x1ad8, 0x651c, 0x54a7, 0x0745, 0x06a1, + 0x1953, 0x6f8a, 0x6d65, 0x4433, 0x55eb, 0x5813, 0x50dd, 0x2467, 0x032d, 0x6dfa, + 0x770d, 0x485f, 0x0e49, 0x068d, 0x5d04, 0x5eac, 0x3e50, 0x5160, 0x1db6, 0x46f4, + 0x19a3, 0x7ac6, 0x2304, 0x314b, 0x6414, 0x221d, 0x019f, 0x6382, 0x7212, 0x093e, + 0x2f27, 0x67f1, 0x13ae, 0x2509, 0x66d1, 0x126b, 0x34d2, 0x7a67, 0x07bf, 0x1fc0, + 0x4904, 0x3ca2, 0x7fc4, 0x4e09, 0x1f11, 0x464f, 0x2450, 0x6ce6, 0x675a, 0x0ec0, + 0x2af9, 0x5342, 0x2a8e, 0x1a39, 0x5c7c, 0x5e4e, 0x2e2d, 0x4fe3, 0x5d97, 0x331f, + 0x1c6b, 0x4593, 0x7ae7, 0x4a12, 0x2bb0, 0x275c, 0x01c4, 0x15c7, 0x2325, 0x6159, + 0x1888, 0x449c, 0x6cec, 0x1c71, 0x287f, 0x0ba2, 0x4c54, 0x25ad, 0x5f48, 0x7183, + 0x6bb4, 0x1379, 0x6485, 0x37aa, 0x110c, 0x2e0c, 0x5d76, 0x74cf, 0x59fb, 0x69a4, + 0x7fbc, 0x4b95, 0x057c, 0x66a0, 0x6f76, 0x4da3, 0x0bef, 0x24d6, 0x564e, 0x4541, + 0x734d, 0x437b, 0x797f, 0x58d0, 0x6274, 0x2d88, 0x3c7b, 0x697d, 0x63fe, 0x31a6, + 0x5ce0, 0x3dfb, 0x54f5, 0x7423, 0x554a, 0x4c22, 0x56c1, 0x043b, 0x6a17, 0x370e, + 0x6453, 0x58f7, 0x41ba, 0x6e94, 0x7b4b, 0x197f, 0x623d, 0x22ee, 0x1eda, 0x191c, + 0x3758, 0x445f, 0x4ccd, 0x3d98, 0x315e, 0x0ed7, 0x19b6, 0x1b04, 0x111f, 0x68c2, + 0x74e2, 0x0b5a, 0x241f, 0x3b91, 0x461b, 0x4e01, 0x62c4, 0x049a, 0x7b38, 0x3c5a, + 0x6253, 0x41a7, 0x31bc, 0x598a, 0x7296, 0x0359, 0x0f91, 0x767b, 0x5bda, 0x3434, + 0x3485, 0x39f4, 0x0cff, 0x5413, 0x7b82, 0x488b, 0x3a6d, 0x4720, 0x611c, 0x07e2, + 0x0584, 0x3b99, 0x731a, 0x2d03, 0x22a6, 0x2879, 0x2091, 0x3e7c, 0x7572, 0x57ee, + 0x496f, 0x39ac, 0x4268, 0x7b97, 0x08da, 0x6786, 0x17eb, 0x44b1, 0x0b9c, 0x78be, + 0x49b9, 0x536e, 0x5f7d, 0x4a78, 0x1de4, 0x2d4b, 0x4f84, 0x3989, 0x02be, 0x3fde, + 0x2b27, 0x79a6, 0x5192, 0x00e2, 0x6ecb, 0x4e35, 0x224b, 0x5fe1, 0x5b79, 0x7453, + 0x4ade, 0x4930, 0x6a04, 0x56a0, 0x5cbf, 0x6440, 0x7d2e, 0x70a7, 0x69d2, 0x17a6, + 0x5b2b, 0x2c65, 0x16c1, 0x6d6c, 0x41f1, 0x63ae, 0x2934, 0x0ac0, 0x5578, 0x3ab5, + 0x64b3, 0x1543, 0x4504, 0x41ea, 0x1b54, 0x1297, 0x4301, 0x7a0e, 0x5efa, 0x705b, + 0x5011, 0x702b, 0x300c, 0x102a, 0x0a46, 0x13da, 0x65f3, 0x76c3, 0x0de2, 0x5086, + 0x0a90, 0x456d, 0x762f, 0x7655, 0x2caa, 0x383b, 0x7ede, 0x7c22, 0x63a7, 0x4822, + 0x267e, 0x0c1b, 0x30ef, 0x5a27, 0x387b, 0x219a, 0x3379, 0x2a17, 0x6b37, 0x1ac4, + 0x3615, 0x144a, 0x592e, 0x4bc1, 0x61b8, 0x6c04, 0x1ec7, 0x5d55, 0x055b, 0x622a, + 0x6214, 0x629b, 0x1dbb, 0x2222, 0x648a, 0x69a9, 0x62c9, 0x598f, 0x4ae3, 0x70ac, + 0x61bd, 0x62a0, 0x65ca, 0x3350, 0x2856, 0x5c53, 0x6f4d, 0x5521, 0x66a8, 0x2427, + 0x50b4, 0x3f85, 0x0704, 0x4c4e, 0x71b8, 0x20c2, 0x61e6, 0x33f0, 0x183e, 0x1f7c, + 0x7be1, 0x37d6, 0x165a, 0x7eb0, 0x62f2, 0x5c22, 0x25a7, 0x3af7, 0x6bab, 0x3e47, + 0x1344, 0x6be0, 0x5b70, 0x4612, 0x5925, 0x7597, 0x59b8, 0x6771, 0x587c, 0x0639, + 0x3d44, 0x6d18, 0x21ce, 0x7e5c, 0x353a, 0x2ed6, 0x75a0, 0x2982, 0x65c5, 0x0d67, + 0x3745, 0x0bce, 0x562d, 0x4cba, 0x6a4e, 0x334b, 0x779d, 0x5731, 0x303a, 0x48d3, + 0x5493, 0x2e59, 0x4158, 0x6568, 0x70d5, 0x1871, 0x02a7, 0x30d8, 0x4f5f, 0x7d8e, + 0x67b4, 0x2daf, 0x0999, 0x3ecc, 0x51f8, 0x2bdc, 0x4b0c, 0x6e48, 0x2901, 0x2c35, + 0x1e99, 0x15f3, 0x050c, 0x23af, 0x1d08, 0x0d93, 0x1a28, 0x10de, 0x2cd3, 0x5af8, + 0x60e1, 0x682f, 0x0c9c, 0x0cc4, 0x1185, 0x75cc, 0x6c89, 0x5331, 0x1b95, 0x4994, + 0x3864, 0x2b10, 0x6cd5, 0x32dc, 0x715b, 0x58a8, 0x3123, 0x0679, 0x3e1f, 0x4c92, + 0x5c2b, 0x1422, 0x2851, 0x0eaf, 0x7b6f, 0x6d44, 0x1932, 0x0cec, 0x3c91, 0x5c4e, + 0x6632, 0x38ca, 0x42c6, 0x26da, 0x2166, 0x1686, 0x390c, 0x7c8b, 0x7c4b, 0x1faf, + 0x4a63, 0x4ea0, 0x2979, 0x4bb8, 0x4df8, 0x744a, 0x5a03, 0x04a2, 0x5157, 0x1370, + 0x7f07, 0x5f42, 0x1419, 0x5754, 0x2b82, 0x463e, 0x1091, 0x29d6, 0x26a7, 0x48a0, + 0x092d, 0x59cd, 0x1738, 0x189d, 0x717d, 0x0e08, 0x7f86, 0x1d80, 0x122b, 0x4eb5, + 0x0963, 0x6371, 0x2fd6, 0x4184, 0x2e70, 0x6a7a, 0x313a, 0x7cbf, 0x0c44, 0x3676, + 0x35da, 0x2fa4, 0x1c10, 0x75f4, 0x62fb, 0x575d, 0x6f48, 0x220c, 0x5bc7, 0x64fb, + 0x6081, 0x3472, 0x24f8, 0x551c, 0x772f, 0x307c, 0x04d1, 0x3ef8, 0x484b, 0x55f2, + 0x154a, 0x2685, 0x11b4, 0x67e0, 0x4f0d, 0x43cd, 0x339b, 0x11ad, 0x125a, 0x292d, + 0x0b05, 0x4d1f, 0x658a, 0x14ee, 0x4d4e, 0x161f, 0x1141, 0x57be, 0x63d0, 0x7a56, + 0x204d, 0x6f09, 0x08ee, 0x1bbd, 0x5802, 0x71e4, 0x3404, 0x792c, 0x166e, 0x1d68, + 0x5356, 0x341c, 0x2e41, 0x3823, 0x0ab9, 0x4422, 0x67d9, 0x49fa, 0x54aa, 0x0748, + 0x0690, 0x5d07, 0x0941, 0x2f2a, 0x4652, 0x2453, 0x4596, 0x7aea, 0x25b0, 0x5f4b, + 0x66a3, 0x6f79, 0x31a9, 0x5ce3, 0x1982, 0x6240, 0x0b5d, 0x2422, 0x767e, 0x5bdd, + 0x2d06, 0x22a9, 0x78c1, 0x49bc, 0x4e38, 0x224e, 0x2c68, 0x16c4, 0x7a11, 0x5efd, + 0x7658, 0x2cad, 0x1ac7, 0x3618, 0x69ac, 0x62cc, 0x3f88, 0x0707, 0x3afa, 0x6bae, + 0x7e5f, 0x353d, 0x48d6, 0x5496, 0x2bdf, 0x4b0f, 0x6832, 0x0c9f, 0x067c, 0x3e22, + 0x26dd, 0x2169, 0x1373, 0x7f0a, 0x0e0b, 0x7f89, 0x2fa7, 0x1c13, 0x3efb, 0x484e, + 0x14f1, 0x4d51, 0x1d6b, 0x5359, 0x2456, 0x4599, 0x22ac, 0x78c4, 0x070a, 0x3afd, + 0x7f0d, 0x0e0e, 0x3b00, 0x7f10, 0x50af, 0x6de9, 0x7283, 0x030c, 0x76ec, 0x0f7e, + 0x514f, 0x3f80, 0x1f09, 0x38a7, 0x7312, 0x50ac, 0x6de6, 0x7944, 0x0889, 0x1209, + 0x5a96, 0x5e9b, 0x3bc8, 0x2a43, 0x12c6, 0x6c30, 0x46e3, 0x358e, 0x661c, 0x1709, + 0x0189, 0x7280, 0x0309, 0x6cb1, 0x5fac, 0x595a, 0x55bd, 0x7ab5, 0x73a2, 0x27e1, + 0x5a50, 0x14ab, 0x2dfb, 0x787b, 0x18ec, 0x19e2, 0x7137, 0x76e9, 0x0f7b, 0x71fc, + 0x712c, 0x42b0, 0x7237, 0x3799, 0x7519, 0x68ee, 0x657f, 0x04c6, 0x7172, 0x35cf, + 0x3118, 0x60d6, 0x42bb, 0x514c, 0x3f7d, 0x2cfb, 0x1663, 0x4647, 0x06ff, 0x1368, + 0x7307, 0x017e, 0x63f3, 0x0571, 0x1c60, 0x4c49, 0x0e3e, 0x6511, 0x7207, 0x1f06, + 0x38a4, 0x7906, 0x47cb, 0x3248, 0x570b, 0x448b, 0x2d36, 0x635c, 0x42f6, 0x5b20, + 0x0b91, 0x6ec0, 0x74d7, 0x7b40, 0x0f86, 0x730f, 0x50a9, 0x647f, 0x7624, 0x6b2c, + 0x21c3, 0x259c, 0x302f, 0x51ed, 0x5026, 0x31e8, 0x4582, 0x32c5, 0x1aed, 0x0622, + 0x37a4, 0x6de3, 0x7941, 0x6857, 0x1156, 0x0385, 0x40c4, 0x330e, 0x6ade, 0x39d1, + 0x6b60, 0x2aa5, 0x5e3d, 0x31ff, 0x262a, 0x543f, 0x7242, 0x0886, 0x1206, 0x7107, + 0x7eb9, 0x2b8b, 0x71b3, 0x4fd2, 0x3a5a, 0x34b1, 0x2f06, 0x6109, 0x274b, 0x20bd, + 0x4236, 0x326e, 0x7524, 0x5a93, 0x5e98, 0x581a, 0x64ba, 0x0c22, 0x33a2, 0x4a01, + 0x404d, 0x410d, 0x4046, 0x4f06, 0x15b6, 0x5571, 0x0fc1, 0x1cd9, 0x68f9, 0x3bc5, + 0x2a40, 0x73d7, 0x40af, 0x36de, 0x47a8, 0x6148, 0x6011, 0x200e, 0x6042, 0x2106, + 0x436a, 0x56e8, 0x6892, 0x6ac9, 0x19ed, 0x12c3, 0x6c2d, 0x2816, 0x2137, 0x7df3, + 0x3aae, 0x4530, 0x43c6, 0x4106, 0x7ce3, 0x1a50, 0x4d92, 0x503d, 0x1bee, 0x7dce, + 0x18f7, 0x46e0, 0x358b, 0x1056, 0x37df, 0x109a, 0x61e1, 0x24c5, 0x207e, 0x079e, + 0x1ef0, 0x2293, 0x6993, 0x33eb, 0x171f, 0x0aec, 0x7142, 0x6619, 0x1706, 0x1bd5, + 0x05d1, 0x52d6, 0x18d3, 0x74be, 0x2611, 0x421d, 0x53f0, 0x7d5a, 0x4b84, 0x3940, + 0x5d7e, 0x3c62, 0x76f4, 0x0186, 0x727d, 0x1106, 0x1b3b, 0x17d2, 0x0a77, 0x668f, + 0x3d2b, 0x7784, 0x5855, 0x0dbb, 0x3195, 0x44dd, 0x52ef, 0x47f1, 0x2e06, 0x0306, + 0x6cae, 0x0906, 0x5128, 0x1aa0, 0x4bea, 0x696c, 0x2dd4, 0x5e16, 0x416f, 0x3974, + 0x58bf, 0x0462, 0x176e, 0x6ff3, 0x7886, 0x5fa9, 0x5957, 0x3f4d, 0x7bea, 0x29df, + 0x1839, 0x2d77, 0x755f, 0x6739, 0x2ad8, 0x495c, 0x7412, 0x1f77, 0x363e, 0x4e7d, + 0x14b6, 0x55ba, 0x7ab2, 0x1d30, 0x3505, 0x5ba5, 0x6db1, 0x3dea, 0x27a9, 0x0f46, + 0x1473, 0x0146, 0x4c11, 0x2564, 0x05ea, 0x2b53, 0x5a5b, 0x739f, 0x27de, 0x46a8, + 0x529e, 0x3c2a, 0x6934, 0x042a, 0x0f0e, 0x010e, 0x0f00, 0x6926, 0x27d0, 0x5290, + 0x4c03, 0x1465, 0x05dc, 0x5a4d, 0x14a8, 0x3630, 0x2aca, 0x7404, 0x34f7, 0x7aa4, + 0x6da3, 0x279b, 0x2dc6, 0x4bdc, 0x6ca0, 0x511a, 0x3187, 0x5847, 0x52e1, 0x2df8, + 0x7878, 0x1760, 0x4161, 0x58b1, 0x7bdc, 0x5949, 0x182b, 0x7551, 0x2070, 0x61d3, + 0x357d, 0x37d1, 0x4d84, 0x7cd5, 0x1be0, 0x18e9, 0x19df, 0x6884, 0x6034, 0x435c, + 0x2129, 0x6c1f, 0x3aa0, 0x43b8, 0x2603, 0x18c5, 0x16f8, 0x05c3, 0x6985, 0x1ee2, + 0x1711, 0x7134, 0x76e6, 0x5d70, 0x53e2, 0x4b76, 0x1b2d, 0x726f, 0x0a69, 0x3d1d, + 0x3021, 0x21b5, 0x509b, 0x7616, 0x0b83, 0x42e8, 0x74c9, 0x0f78, 0x71f9, 0x0e30, + 0x63e5, 0x1c52, 0x47bd, 0x3896, 0x56fd, 0x2d28, 0x750b, 0x7229, 0x0f6d, 0x711e, + 0x2ded, 0x5a42, 0x18de, 0x7129, 0x42ad, 0x310a, 0x6571, 0x7164, 0x1655, 0x3f6f, + 0x06f1, 0x72f9, 0x3a4c, 0x71a5, 0x11f8, 0x7eab, 0x5e2f, 0x6b52, 0x261c, 0x7234, + 0x3796, 0x1adf, 0x5018, 0x4574, 0x1148, 0x7933, 0x40b6, 0x6ad0, 0x403f, 0x3394, + 0x5e8a, 0x64ac, 0x273d, 0x2ef8, 0x4228, 0x7516, 0x68eb, 0x0fb3, 0x4038, 0x15a8, + 0x40a1, 0x2a32, 0x479a, 0x6003, 0x203f, 0x63c2, 0x4d40, 0x1133, 0x124c, 0x338d, + 0x0af7, 0x657c, 0x04c3, 0x7721, 0x6073, 0x24ea, 0x153c, 0x483d, 0x11a6, 0x4eff, + 0x2fc8, 0x0955, 0x7f78, 0x121d, 0x091f, 0x2699, 0x172a, 0x716f, 0x35cc, 0x0c36, + 0x2e62, 0x312c, 0x62ed, 0x1c02, 0x6f3a, 0x5bb9, 0x7b61, 0x2843, 0x3e11, 0x5c1d, + 0x6cc7, 0x3856, 0x714d, 0x3115, 0x60d3, 0x2cc5, 0x1cfa, 0x1a1a, 0x1177, 0x0c8e, + 0x6c7b, 0x1b87, 0x4a55, 0x7c3d, 0x2158, 0x38fe, 0x3c83, 0x1924, 0x6624, 0x42b8, + 0x5149, 0x59f5, 0x296b, 0x4dea, 0x140b, 0x7ef9, 0x2b74, 0x1083, 0x2bd1, 0x48c8, + 0x3aec, 0x7e51, 0x1ab9, 0x764a, 0x699e, 0x3f7a, 0x2cf8, 0x7670, 0x1974, 0x0b4f, + 0x4e2a, 0x78b3, 0x2c5a, 0x7a03, 0x67cb, 0x0aab, 0x5348, 0x2e33, 0x57f4, 0x08e0, + 0x33f6, 0x1660, 0x4644, 0x0933, 0x549c, 0x0682, 0x25a2, 0x4588, 0x6695, 0x319b, + 0x7275, 0x50a1, 0x7eff, 0x3af2, 0x2448, 0x1d5d, 0x229e, 0x06fc, 0x1365, 0x26cf, + 0x6824, 0x066e, 0x2f99, 0x0dfd, 0x3eed, 0x14e3, 0x3bba, 0x5a88, 0x6dd8, 0x087b, + 0x5141, 0x76de, 0x1efb, 0x7304, 0x017b, 0x660e, 0x12b8, 0x46d5, 0x5f9e, 0x02fb, + 0x55af, 0x7394, 0x56b3, 0x553c, 0x5cd2, 0x54e7, 0x6266, 0x7971, 0x3c6d, 0x63f0, + 0x056e, 0x7fae, 0x5d68, 0x59ed, 0x0be1, 0x6f68, 0x5640, 0x733f, 0x2317, 0x01b6, + 0x7ad9, 0x2ba2, 0x2e1f, 0x5c6e, 0x5d89, 0x1c5d, 0x4c46, 0x2871, 0x187a, 0x6cde, + 0x6ba6, 0x5f3a, 0x6477, 0x10fe, 0x1995, 0x1da8, 0x5cf6, 0x3e42, 0x031f, 0x50cf, + 0x76ff, 0x0e3b, 0x650e, 0x3fa0, 0x6094, 0x3b57, 0x1945, 0x0737, 0x6d57, 0x55dd, + 0x34c4, 0x66c3, 0x2f19, 0x13a0, 0x6406, 0x22f6, 0x0191, 0x7204, 0x1f03, 0x7fb6, + 0x07b1, 0x48f6, 0x674c, 0x2442, 0x2aeb, 0x2a80, 0x4cf6, 0x3449, 0x1d57, 0x21e3, + 0x5308, 0x4c69, 0x4b8f, 0x38a1, 0x7903, 0x071f, 0x6b85, 0x169b, 0x7a8c, 0x791b, + 0x0c76, 0x789b, 0x7da5, 0x20dd, 0x1bac, 0x6666, 0x3dc1, 0x467f, 0x394b, 0x47c8, + 0x3245, 0x4edd, 0x70de, 0x32e5, 0x133f, 0x71d3, 0x0548, 0x7b17, 0x23fe, 0x6201, + 0x3812, 0x6bdb, 0x45e9, 0x2ead, 0x7d65, 0x5708, 0x4488, 0x3fb5, 0x7032, 0x0a97, + 0x57c5, 0x340b, 0x36e5, 0x6899, 0x15af, 0x1253, 0x4411, 0x44fd, 0x480a, 0x1859, + 0x53fb, 0x2d33, 0x6359, 0x1f97, 0x5e83, 0x440a, 0x3781, 0x49e9, 0x74a6, 0x6954, + 0x0a38, 0x2ffe, 0x5eec, 0x5003, 0x44f6, 0x64a5, 0x1b46, 0x42f3, 0x5b1d, 0x69c4, + 0x5cb1, 0x7d20, 0x41e3, 0x16b3, 0x2926, 0x556a, 0x4f76, 0x1dd6, 0x49ab, 0x5f6f, + 0x08cc, 0x425a, 0x17dd, 0x0b8e, 0x6ebd, 0x5184, 0x02b0, 0x2b19, 0x5b6b, 0x223d, + 0x4ad0, 0x69f6, 0x7b2a, 0x62b6, 0x2411, 0x460d, 0x19a8, 0x3150, 0x1111, 0x74d4, + 0x7b3d, 0x41ac, 0x6a09, 0x6445, 0x1ecc, 0x622f, 0x374a, 0x4cbf, 0x7b74, 0x0cf1, + 0x5bcc, 0x3477, 0x31ae, 0x6245, 0x7288, 0x0f83, 0x730c, 0x0576, 0x3a5f, 0x610e, + 0x2083, 0x2298, 0x7564, 0x4961, 0x1830, 0x61d8, 0x06f6, 0x71aa, 0x6f3f, 0x2848, + 0x669a, 0x50a6, 0x647c, 0x1dad, 0x054d, 0x6206, 0x4ad5, 0x62bb, 0x61af, 0x65bc, + 0x2670, 0x6399, 0x2c9c, 0x7ed0, 0x0dd4, 0x65e5, 0x0a82, 0x7621, 0x6b29, 0x336b, + 0x30e1, 0x386d, 0x5920, 0x3607, 0x61aa, 0x1eb9, 0x3737, 0x65b7, 0x352c, 0x7592, + 0x586e, 0x59aa, 0x3d36, 0x21c0, 0x2599, 0x62e4, 0x7bd3, 0x164c, 0x1336, 0x6b9d, + 0x5b62, 0x5917, 0x0299, 0x70c7, 0x5485, 0x414a, 0x6a40, 0x561f, 0x778f, 0x302c, + 0x51ea, 0x098b, 0x4f51, 0x67a6, 0x28f3, 0x4afe, 0x1e8b, 0x04fe, 0x254a, 0x5b8b, + 0x29c5, 0x44c3, 0x33d1, 0x17b8, 0x7dd9, 0x5023, 0x31e5, 0x036b, 0x36c4, 0x20a3, + 0x7861, 0x462d, 0x322e, 0x6ea6, 0x35fe, 0x2234, 0x5f31, 0x71ca, 0x3f66, 0x5940, + 0x1bf9, 0x457f, 0x32c2, 0x75b2, 0x573a, 0x5c34, 0x59b3, 0x5743, 0x266b, 0x2913, + 0x41d0, 0x6394, 0x7439, 0x676c, 0x5970, 0x3e62, 0x1902, 0x1aea, 0x061f, 0x6bc6, + 0x2c1b, 0x3331, 0x506c, 0x4ba7, 0x7092, 0x5507, 0x0d5e, 0x6bfb, 0x0491, 0x4927, + 0x627c, 0x4467, 0x46eb, 0x37a1, 0x6de0, 0x6f70, 0x0ea6, 0x2203, 0x4fc9, 0x135f, + 0x24bc, 0x2d6e, 0x5ac5, 0x5bef, 0x26c9, 0x7e71, 0x29f8, 0x25c2, 0x4d9d, 0x793e, + 0x6854, 0x3b12, 0x45c8, 0x217b, 0x72bf, 0x38b9, 0x4b31, 0x5fbe, 0x520f, 0x630d, + 0x0cdb, 0x18af, 0x5d36, 0x308e, 0x5048, 0x1153, 0x0382, 0x38dc, 0x77a6, 0x142b, + 0x5877, 0x5c3d, 0x2c97, 0x5b0a, 0x5c9e, 0x7ecb, 0x7c7a, 0x0634, 0x76a4, 0x3280, + 0x1a5b, 0x40c1, 0x330b, 0x6523, 0x7062, 0x7636, 0x1626, 0x1675, 0x73de, 0x19f4, + 0x0fba, 0x0afe, 0x1f9e, 0x1b4d, 0x7c03, 0x37f1, 0x7cee, 0x6adb, 0x39ce, 0x7bfc, + 0x2736, 0x4803, 0x11e3, 0x4e8f, 0x428a, 0x52b0, 0x7bab, 0x06ce, 0x4983, 0x3a37, + 0x1852, 0x2ef1, 0x37ea, 0x6b5d, 0x2aa2, 0x3555, 0x5e62, 0x6549, 0x1290, 0x5320, + 0x4d18, 0x1cd2, 0x2bf3, 0x299d, 0x0cb3, 0x270e, 0x48b4, 0x0e76, 0x10a5, 0x5e3a, + 0x31fc, 0x5785, 0x3043, 0x4c9b, 0x3d3f, 0x75bb, 0x0dcf, 0x5ed9, 0x0a25, 0x65e0, + 0x10cd, 0x6d13, 0x6e29, 0x6a90, 0x1061, 0x2627, 0x543c, 0x1f3e, 0x03f1, 0x2390, + 0x476b, 0x0d82, 0x6700, 0x5dc8, 0x567d, 0x6323, 0x5ae7, 0x0263, 0x2d90, 0x3760, + 0x3596, 0x723f, 0x0883, 0x0be9, 0x1ca0, 0x3ead, 0x0d28, 0x681e, 0x084d, 0x082b, + 0x0861, 0x0654, 0x0668, 0x7e37, 0x5c03, 0x4dd0, 0x24d0, 0x1203, 0x7104, 0x1c38, + 0x158e, 0x7e91, 0x5276, 0x5897, 0x4342, 0x05a9, 0x345d, 0x642b, 0x2aff, 0x4fe9, + 0x7578, 0x678c, 0x61ec, 0x7eb6, 0x2b88, 0x59d3, 0x48dc, 0x3e28, 0x21c9, 0x32cb, + 0x0a7d, 0x44e3, 0x1b33, 0x761c, 0x1411, 0x7e57, 0x6752, 0x21e9, 0x2089, 0x71b0, + 0x4fcf, 0x7e77, 0x0d2e, 0x7e3d, 0x3a1d, 0x4c81, 0x2376, 0x0249, 0x1e29, 0x3d5f, + 0x0e9e, 0x1c98, 0x2963, 0x53da, 0x07a9, 0x3a57, 0x34ae, 0x4a3f, 0x00a9, 0x0d48, + 0x0229, 0x6d33, 0x79d5, 0x77f1, 0x6fcc, 0x6aa2, 0x64ea, 0x1cb2, 0x047b, 0x3055, + 0x2142, 0x2f03, 0x6106, 0x48ee, 0x4b6e, 0x4de2, 0x3ea5, 0x21fb, 0x6e6d, 0x00bb, + 0x09b0, 0x77b8, 0x75e3, 0x25d4, 0x0b3b, 0x3292, 0x7dfe, 0x2748, 0x20ba, 0x3e74, + 0x6a57, 0x0eb8, 0x3535, 0x574c, 0x6b24, 0x17ca, 0x08b9, 0x3366, 0x7cae, 0x2ed1, + 0x22cf, 0x2e86, 0x2821, 0x4233, 0x326b, 0x2e98, 0x2f5a, 0x3d79, 0x53b3, 0x6a69, + 0x1791, 0x3067, 0x28a2, 0x5225, 0x3665, 0x1e43, 0x58d8, 0x4cd5, 0x6c38, 0x7521, + 0x5a90, 0x5648, 0x3d67, 0x6e75, 0x3a17, 0x2f93, 0x157a, 0x0a03, 0x103f, 0x70f0, + 0x0df7, 0x4c7b, 0x3b7a, 0x2ebf, 0x453b, 0x5e95, 0x5817, 0x50e1, 0x7a6b, 0x07c3, + 0x615d, 0x188c, 0x437f, 0x7983, 0x3d9c, 0x3162, 0x488f, 0x3a71, 0x398d, 0x02c2, + 0x3ab9, 0x64b7, 0x0c1f, 0x30f3, 0x3354, 0x285a, 0x759b, 0x59bc, 0x30dc, 0x4f63, + 0x4998, 0x3868, 0x4ea4, 0x297d, 0x4188, 0x2e74, 0x43d1, 0x339f, 0x49fe, 0x54ae, + 0x5f01, 0x765c, 0x4d55, 0x1d6f, 0x2a47, 0x12ca, 0x68f2, 0x6583, 0x6360, 0x42fa, + 0x39d5, 0x6b64, 0x4111, 0x404a, 0x410a, 0x7ce7, 0x4221, 0x53f4, 0x5e1a, 0x4173, + 0x0f4a, 0x1477, 0x279f, 0x2dca, 0x43bc, 0x2607, 0x2d2c, 0x750f, 0x6ad4, 0x4043, + 0x4f03, 0x2fcc, 0x1b8b, 0x4a59, 0x7a07, 0x67cf, 0x14e7, 0x3bbe, 0x7343, 0x231b, + 0x55e1, 0x34c8, 0x789f, 0x7da9, 0x689d, 0x15b3, 0x556e, 0x4f7a, 0x4cc3, 0x7b78, + 0x65c0, 0x2674, 0x591b, 0x029d, 0x6eaa, 0x3602, 0x550b, 0x0d62, 0x5fc2, 0x5213, + 0x19f8, 0x0fbe, 0x1cd6, 0x2bf7, 0x5dcc, 0x5681, 0x05ad, 0x3461, 0x024d, 0x1e2d, + 0x00bf, 0x09b4, 0x306b, 0x28a6, 0x7987, 0x3da0, 0x12ce, 0x68f6, 0x3bc2, 0x7347, + 0x1e31, 0x00c3, 0x2370, 0x3ee7, 0x5262, 0x03cf, 0x4749, 0x432e, 0x14dd, 0x0243, + 0x1787, 0x6e63, 0x4375, 0x2a3d, 0x73d4, 0x4b27, 0x3224, 0x7088, 0x66f6, 0x4d0e, + 0x4338, 0x236c, 0x3ee3, 0x2c50, 0x119c, 0x6c71, 0x3a96, 0x6d99, 0x56f3, 0x40ac, + 0x36db, 0x0c6c, 0x5636, 0x6d4d, 0x3740, 0x291c, 0x61a5, 0x5b58, 0x4abd, 0x1eb4, + 0x57ad, 0x0bc9, 0x1524, 0x2f81, 0x2111, 0x47a5, 0x6145, 0x539b, 0x7831, 0x79ef, + 0x7849, 0x160e, 0x4753, 0x525e, 0x03cb, 0x369e, 0x7a45, 0x780b, 0x700c, 0x0527, + 0x604d, 0x600e, 0x200b, 0x23ca, 0x09f1, 0x100b, 0x080b, 0x6ef8, 0x3cef, 0x004b, + 0x3ce1, 0x07fd, 0x1ffd, 0x09e3, 0x7a37, 0x03bd, 0x6ffe, 0x603f, 0x2103, 0x1516, + 0x4aaf, 0x579f, 0x7823, 0x6137, 0x783b, 0x4745, 0x432a, 0x66e8, 0x73c6, 0x3216, + 0x14cf, 0x473b, 0x1779, 0x4367, 0x56e5, 0x3a88, 0x3ed5, 0x118e, 0x5628, 0x36cd, + 0x3732, 0x6197, 0x590d, 0x65b2, 0x5560, 0x4cb5, 0x55d3, 0x7335, 0x7891, 0x688f, + 0x6ac6, 0x2d1e, 0x2791, 0x43ae, 0x1b7d, 0x4ef5, 0x79f9, 0x14d9, 0x023f, 0x059f, + 0x1cc8, 0x5dbe, 0x54fd, 0x6e9c, 0x5fb4, 0x19ea, 0x12c0, 0x7979, 0x00b1, 0x305d, + 0x1e23, 0x3bb4, 0x2362, 0x5254, 0x156c, 0x3a09, 0x5a82, 0x3d59, 0x3657, 0x2894, + 0x58ca, 0x6c2a, 0x2813, 0x22c1, 0x08ab, 0x7ca0, 0x2f4c, 0x325d, 0x53a5, 0x1783, + 0x6e5f, 0x3e97, 0x60f8, 0x4b60, 0x64dc, 0x6fbe, 0x046d, 0x2134, 0x7df0, 0x0b2d, + 0x09a2, 0x75d5, 0x6a49, 0x20ac, 0x3527, 0x6b16, 0x30ce, 0x758d, 0x0c11, 0x3346, + 0x4881, 0x3d8e, 0x397f, 0x3aab, 0x452d, 0x3b6c, 0x1031, 0x0de9, 0x7a5d, 0x5809, + 0x614f, 0x4371, 0x2a39, 0x4d47, 0x49f0, 0x5ef3, 0x4e96, 0x498a, 0x417a, 0x43c3, + 0x4103, 0x39c7, 0x68e4, 0x6352, 0x4213, 0x40fc, 0x5e0c, 0x0f3c, 0x427c, 0x11d5, + 0x39c0, 0x2728, 0x1f90, 0x0fac, 0x7bf5, 0x7ce0, 0x1a4d, 0x7696, 0x5c90, 0x7c6c, + 0x7054, 0x32fd, 0x1618, 0x73d0, 0x4b23, 0x72b1, 0x6846, 0x45ba, 0x26bb, 0x5ab7, + 0x29ea, 0x4d8f, 0x503a, 0x5d28, 0x5201, 0x0ccd, 0x7798, 0x0374, 0x5869, 0x2c89, + 0x265d, 0x59a5, 0x32b4, 0x572c, 0x5f23, 0x35f0, 0x3f58, 0x1beb, 0x7dcb, 0x33c3, + 0x253c, 0x29b7, 0x36b6, 0x31d7, 0x7853, 0x3220, 0x7084, 0x505e, 0x0611, 0x2c0d, + 0x742b, 0x41c2, 0x5962, 0x18f4, 0x46dd, 0x626e, 0x0d50, 0x0483, 0x0e98, 0x6dd2, + 0x4fbb, 0x24ae, 0x083f, 0x0d1a, 0x0875, 0x1c92, 0x5ad9, 0x566f, 0x2d82, 0x3588, + 0x1053, 0x6e1b, 0x0a17, 0x10bf, 0x03e3, 0x542e, 0x475d, 0x66f2, 0x4d0a, 0x1282, + 0x2a94, 0x5e54, 0x4975, 0x7b9d, 0x1844, 0x37dc, 0x1097, 0x48a6, 0x2be5, 0x0ca5, + 0x3035, 0x31ee, 0x3d31, 0x0dc1, 0x0a6f, 0x21bb, 0x2b7a, 0x48ce, 0x2af1, 0x344f, + 0x756a, 0x61de, 0x24c2, 0x5bf5, 0x0853, 0x065a, 0x1580, 0x70f6, 0x5268, 0x4334, + 0x2368, 0x3a0f, 0x4fc1, 0x0d20, 0x1403, 0x1b25, 0x6744, 0x207b, 0x079b, 0x2955, + 0x1e1b, 0x0e90, 0x009b, 0x34a0, 0x021b, 0x79c7, 0x55a1, 0x5f90, 0x016d, 0x12aa, + 0x6dca, 0x3bac, 0x5133, 0x1eed, 0x2290, 0x243a, 0x7267, 0x7ef1, 0x6816, 0x1357, + 0x2f8b, 0x3edf, 0x2c4c, 0x4e1c, 0x2cea, 0x1966, 0x3ade, 0x2bc3, 0x1aab, 0x6990, + 0x33e8, 0x57e6, 0x67bd, 0x533a, 0x548e, 0x4636, 0x2594, 0x6687, 0x6f2c, 0x62df, + 0x35be, 0x2e54, 0x7f6a, 0x2fba, 0x0911, 0x171c, 0x0ae9, 0x123e, 0x2031, 0x4d32, + 0x6065, 0x04b5, 0x152e, 0x1198, 0x6c6d, 0x1169, 0x60c5, 0x1cec, 0x3e03, 0x7b53, + 0x6cb9, 0x713f, 0x6616, 0x3c75, 0x4a47, 0x214a, 0x295d, 0x513b, 0x13fd, 0x2b66, + 0x0a5b, 0x1b1f, 0x76d8, 0x53d4, 0x16ea, 0x25f5, 0x6977, 0x1703, 0x1bd2, 0x4d76, + 0x2062, 0x356f, 0x6026, 0x19d1, 0x211b, 0x3a92, 0x6d95, 0x34e9, 0x149a, 0x2abc, + 0x27c2, 0x0ef2, 0x4bf5, 0x05ce, 0x52d3, 0x3179, 0x2db8, 0x6c92, 0x4153, 0x786a, + 0x7bce, 0x181d, 0x06e3, 0x1647, 0x429f, 0x6563, 0x0f5f, 0x74fd, 0x2ddf, 0x18d0, + 0x74bb, 0x0b75, 0x3013, 0x508d, 0x63d7, 0x71eb, 0x47af, 0x56ef, 0x40a8, 0x113a, + 0x3788, 0x500a, 0x11ea, 0x3a3e, 0x5e21, 0x260e, 0x421a, 0x272f, 0x4031, 0x5e7c, + 0x402a, 0x68dd, 0x4093, 0x478c, 0x7498, 0x3773, 0x634b, 0x5e75, 0x4403, 0x15a1, + 0x47fc, 0x53ed, 0x7d57, 0x45db, 0x23f0, 0x3804, 0x7024, 0x447a, 0x57b7, 0x36d7, + 0x0c68, 0x7a7e, 0x78f5, 0x6b77, 0x1d49, 0x4ce8, 0x52fa, 0x4b81, 0x393d, 0x3db3, + 0x7d97, 0x1b9e, 0x70d0, 0x3237, 0x1331, 0x053a, 0x6469, 0x6b98, 0x4c38, 0x186c, + 0x7acb, 0x2309, 0x2e11, 0x5d7b, 0x3c5f, 0x6258, 0x56a5, 0x5cc4, 0x5d5a, 0x0560, + 0x0bd3, 0x5632, 0x6d49, 0x1937, 0x6500, 0x6086, 0x5ce8, 0x1987, 0x0311, 0x76f1, + 0x0183, 0x63f8, 0x34b6, 0x2f0b, 0x07a3, 0x1ef5, 0x673e, 0x2add, 0x7556, 0x2075, + 0x72fe, 0x3a51, 0x5bbe, 0x7b66, 0x31a0, 0x727a, 0x1103, 0x199a, 0x7b1c, 0x2403, + 0x69fb, 0x7b2f, 0x1ebe, 0x373c, 0x2918, 0x41d5, 0x5b0f, 0x5ca3, 0x5ede, 0x0a2a, + 0x44e8, 0x1b38, 0x17cf, 0x08be, 0x4f68, 0x499d, 0x02a2, 0x6eaf, 0x5b5d, 0x4ac2, + 0x619c, 0x5912, 0x6b1b, 0x30d3, 0x2c8e, 0x2662, 0x0dc6, 0x0a74, 0x668c, 0x6f31, + 0x1822, 0x06e8, 0x053f, 0x646e, 0x4ac7, 0x61a1, 0x5b54, 0x1328, 0x258b, 0x7bc5, + 0x351e, 0x3729, 0x5860, 0x3d28, 0x7781, 0x6a32, 0x028b, 0x5477, 0x4f43, 0x51dc, + 0x28e5, 0x1e7d, 0x28d7, 0x4f35, 0x7773, 0x027d, 0x257d, 0x5b46, 0x3510, 0x5852, + 0x0db8, 0x2c80, 0x618e, 0x6b0d, 0x1814, 0x667e, 0x0531, 0x4ab9, 0x1eb0, 0x69ed, + 0x10f5, 0x7b0e, 0x72f0, 0x7548, 0x5bb0, 0x3192, 0x44da, 0x5ed0, 0x290a, 0x5b01, + 0x4f5a, 0x17c1, 0x0294, 0x5b4f, 0x1323, 0x70c2, 0x392f, 0x7d89, 0x78e7, 0x0c5a, + 0x1d3b, 0x52ec, 0x47ee, 0x43f5, 0x748a, 0x633d, 0x23e2, 0x7d49, 0x7016, 0x57a9, + 0x0bc5, 0x5d4c, 0x3c51, 0x5697, 0x4c2a, 0x645b, 0x7abd, 0x2e03, 0x0303, 0x5cda, + 0x6d3b, 0x64f2, 0x34a8, 0x0175, 0x0795, 0x6730, 0x13ef, 0x294f, 0x6608, 0x4a39, + 0x60b7, 0x6c5f, 0x3df5, 0x6cab, 0x0903, 0x7f5c, 0x6f1e, 0x35b0, 0x2023, 0x0adb, + 0x6057, 0x1520, 0x2f7d, 0x6808, 0x2282, 0x7259, 0x015f, 0x5593, 0x6dbc, 0x5125, + 0x1a9d, 0x3ad0, 0x2c3e, 0x2cdc, 0x67af, 0x33da, 0x5480, 0x2586, 0x7bc0, 0x4145, + 0x52c5, 0x2daa, 0x148c, 0x6d87, 0x27b4, 0x4be7, 0x6969, 0x16dc, 0x0a4d, 0x76ca, + 0x2054, 0x1bc4, 0x6018, 0x210d, 0x47a1, 0x63c9, 0x74ad, 0x3005, 0x4291, 0x06d5, + 0x0f51, 0x2dd1, 0x5e13, 0x11dc, 0x409a, 0x377a, 0x4023, 0x420c, 0x401c, 0x4085, + 0x5dfe, 0x4205, 0x40f5, 0x68d6, 0x49e2, 0x2a2b, 0x4e88, 0x416c, 0x3971, 0x4873, + 0x30c0, 0x0c03, 0x1023, 0x451f, 0x7a4f, 0x6141, 0x5397, 0x2f3e, 0x2805, 0x089d, + 0x5a74, 0x155e, 0x3649, 0x58bc, 0x045f, 0x64ce, 0x6e51, 0x60ea, 0x0994, 0x7de2, + 0x6a3b, 0x3519, 0x3724, 0x561a, 0x56d7, 0x3ec7, 0x73b8, 0x431c, 0x14c1, 0x176b, + 0x6ff0, 0x7a29, 0x3cd3, 0x1fef, 0x4aa1, 0x20f5, 0x7815, 0x782d, 0x79eb, 0x1b6f, + 0x6ab8, 0x2783, 0x5552, 0x58ff, 0x55c5, 0x7883, 0x5fa6, 0x54ef, 0x0231, 0x1cba, + 0x00a3, 0x12b2, 0x1e15, 0x2354, 0x4fad, 0x0e8a, 0x46cf, 0x0d42, 0x0603, 0x7076, + 0x741d, 0x5954, 0x3f4a, 0x5f15, 0x264f, 0x32a6, 0x252e, 0x7dbd, 0x36a8, 0x7845, + 0x160a, 0x7046, 0x1a3f, 0x5c82, 0x39b2, 0x426e, 0x1f82, 0x7be7, 0x29dc, 0x26ad, + 0x4b15, 0x6838, 0x51f3, 0x502c, 0x778a, 0x585b, 0x3d23, 0x3027, 0x1089, 0x2bd7, + 0x2a86, 0x4cfc, 0x4967, 0x1836, 0x2d74, 0x5acb, 0x0831, 0x0867, 0x0a09, 0x1045, + 0x03d5, 0x474f, 0x525a, 0x1572, 0x24b4, 0x0845, 0x2b6c, 0x0a61, 0x2ae3, 0x755c, + 0x6736, 0x13f5, 0x235a, 0x4fb3, 0x1e0d, 0x078d, 0x008d, 0x020d, 0x007f, 0x1dff, + 0x6728, 0x234c, 0x24a6, 0x524c, 0x2b5e, 0x2ad5, 0x4959, 0x2a78, 0x3d15, 0x107b, + 0x0823, 0x2d66, 0x09fb, 0x03c7, 0x369a, 0x2520, 0x3f3c, 0x2641, 0x46c1, 0x4f9f, + 0x05f5, 0x740f, 0x1f74, 0x39a4, 0x15fc, 0x1a31, 0x4b07, 0x29ce, 0x51e5, 0x777c, + 0x6a2d, 0x0986, 0x0451, 0x6e43, 0x27f7, 0x5389, 0x5a66, 0x363b, 0x4e7a, 0x49d4, + 0x5df0, 0x40e7, 0x30b2, 0x3963, 0x1015, 0x7a41, 0x7807, 0x4a93, 0x6fe2, 0x3cc5, + 0x56c9, 0x3716, 0x73aa, 0x14b3, 0x55b7, 0x5544, 0x79dd, 0x6aaa, 0x0223, 0x5f98, + 0x0095, 0x1e07, 0x0787, 0x349a, 0x02f5, 0x6d2d, 0x3c43, 0x0bb7, 0x4c1c, 0x7aaf, + 0x1d2d, 0x78d9, 0x1315, 0x3921, 0x747c, 0x47e0, 0x23d4, 0x7008, 0x0523, 0x1806, + 0x0daa, 0x6180, 0x7765, 0x28c9, 0x256f, 0x3502, 0x5ba2, 0x72e2, 0x1ea2, 0x10e7, + 0x28fc, 0x44cc, 0x4f4c, 0x0286, 0x5472, 0x67a1, 0x1a8f, 0x2c30, 0x2274, 0x2f6f, + 0x0151, 0x6dae, 0x3de7, 0x60a9, 0x13e1, 0x65fa, 0x6f10, 0x08f5, 0x2015, 0x6049, + 0x600a, 0x2046, 0x695b, 0x0a3f, 0x52b7, 0x7bb2, 0x147e, 0x27a6, 0x0f43, 0x4283, + 0x4793, 0x749f, 0x408c, 0x5e05, 0x4015, 0x400e, 0x4007, 0x407e, 0x0f35, 0x4785, + 0x694d, 0x5ffc, 0x52a9, 0x1470, 0x0143, 0x2266, 0x5464, 0x1a81, 0x13d3, 0x3dd9, + 0x6f02, 0x2007, 0x23c6, 0x746e, 0x1d1f, 0x1307, 0x02e7, 0x0779, 0x3c35, 0x4c0e, + 0x2561, 0x7757, 0x0515, 0x0d9c, 0x1e94, 0x5b94, 0x28ee, 0x4f3e, 0x51d7, 0x4af9, + 0x1f66, 0x15ee, 0x3f2e, 0x368c, 0x46b3, 0x05e7, 0x2b50, 0x2498, 0x0071, 0x671a, + 0x3d07, 0x494b, 0x0815, 0x09ed, 0x1007, 0x30a4, 0x4e6c, 0x5de2, 0x0443, 0x6a1f, + 0x27e9, 0x5a58, 0x739c, 0x56bb, 0x77f9, 0x6fd4, 0x79cf, 0x55a9, 0x0215, 0x0087, + 0x0207, 0x79c1, 0x738e, 0x77eb, 0x4e5e, 0x0ff9, 0x0435, 0x27db, 0x46a5, 0x3f20, + 0x51c9, 0x1f58, 0x0063, 0x2b42, 0x3cf9, 0x0807, 0x6ef4, 0x13c5, 0x0135, 0x5456, + 0x0f27, 0x3ff9, 0x693f, 0x529b, 0x3c27, 0x02d9, 0x23b8, 0x1d11, 0x0507, 0x2553, + 0x1e86, 0x28e0, 0x1e78, 0x04f9, 0x3c19, 0x23aa, 0x0127, 0x6ee6, 0x0f19, 0x6931, + 0x0427, 0x4e50, 0x01f9, 0x7380, 0x51bb, 0x4697, 0x0055, 0x3ceb, 0x0047, 0x51ad, + 0x0419, 0x01eb, 0x3c0b, 0x1e6a, 0x0119, 0x0f0b, 0x010b, 0x3bfd, 0x0039, 0x040b, + 0x002b, 0x00fd, 0x001d, 0x000f, 0x0001, 0x00ef, 0x3bef, 0x03fd, 0x01dd, 0x519f, + 0x1e5c, 0x0efd, 0x6923, 0x6ed8, 0x04eb, 0x239c, 0x7372, 0x4e42, 0x4689, 0x3cdd, + 0x07f9, 0x2b34, 0x3f12, 0x1f4a, 0x77dd, 0x79b3, 0x0feb, 0x27cd, 0x528d, 0x3feb, + 0x13b7, 0x5448, 0x1d03, 0x02cb, 0x2545, 0x28d2, 0x4f30, 0x5b86, 0x7749, 0x0d8e, + 0x12f9, 0x7460, 0x076b, 0x4c00, 0x1462, 0x5fee, 0x4070, 0x4777, 0x1a73, 0x2258, + 0x3dcb, 0x1ff9, 0x09df, 0x493d, 0x248a, 0x670c, 0x15e0, 0x4aeb, 0x367e, 0x05d9, + 0x5a4a, 0x6a11, 0x3096, 0x5dd4, 0x6fc6, 0x56ad, 0x559b, 0x0079, 0x1df9, 0x5f8a, + 0x5536, 0x6a9c, 0x3cb7, 0x4a85, 0x3708, 0x14a5, 0x362d, 0x537b, 0x0978, 0x6e35, + 0x40d9, 0x49c6, 0x3955, 0x7a33, 0x03b9, 0x2d58, 0x2a6a, 0x106d, 0x233e, 0x1df1, + 0x523e, 0x2ac7, 0x7401, 0x4f91, 0x2512, 0x2633, 0x1a23, 0x3996, 0x29c0, 0x776e, + 0x0278, 0x44be, 0x72d4, 0x10d9, 0x6172, 0x17f8, 0x28bb, 0x34f4, 0x7aa1, 0x0ba9, + 0x348c, 0x6d1f, 0x3913, 0x78cb, 0x47d2, 0x6ffa, 0x603b, 0x08e7, 0x609b, 0x65ec, + 0x2c22, 0x6793, 0x2f61, 0x6da0, 0x2798, 0x7ba4, 0x2038, 0x0a31, 0x7491, 0x4275, + 0x5df7, 0x4000, 0x4077, 0x41fe, 0x11ce, 0x376c, 0x2ff7, 0x63bb, 0x06c7, 0x2dc3, + 0x4bd9, 0x6d79, 0x4137, 0x2d9c, 0x76bc, 0x16ce, 0x1bb6, 0x20ff, 0x1512, 0x0acd, + 0x7f4e, 0x35a2, 0x4a2b, 0x2941, 0x6c51, 0x6c9d, 0x5117, 0x5585, 0x67fa, 0x724b, + 0x2cce, 0x3ac2, 0x33cc, 0x2578, 0x5b41, 0x17b3, 0x5ec2, 0x5af3, 0x7b00, 0x69df, + 0x753a, 0x3184, 0x5844, 0x5b38, 0x4f27, 0x026f, 0x6aff, 0x2c72, 0x6670, 0x4aab, + 0x579b, 0x7d3b, 0x43e7, 0x632f, 0x7d7b, 0x70b4, 0x0c4c, 0x52de, 0x2df5, 0x644d, + 0x5d3e, 0x5689, 0x64e4, 0x5ccc, 0x0167, 0x6722, 0x2346, 0x12a4, 0x54e1, 0x1cac, + 0x2775, 0x1b61, 0x58f1, 0x7875, 0x175d, 0x430e, 0x560c, 0x3eb9, 0x1fe1, 0x7a1b, + 0x20e7, 0x781f, 0x6133, 0x4511, 0x4865, 0x0bf5, 0x68c8, 0x41f7, 0x2a1d, 0x415e, + 0x58ae, 0x1550, 0x2f30, 0x088f, 0x60dc, 0x64c0, 0x7dd4, 0x350b, 0x584d, 0x501e, + 0x269f, 0x682a, 0x5c74, 0x7038, 0x4260, 0x7bd9, 0x5946, 0x7068, 0x0e7c, 0x0d34, + 0x3298, 0x5f07, 0x7daf, 0x7837, 0x4741, 0x1037, 0x5abd, 0x0859, 0x2bc9, 0x3019, + 0x4cee, 0x1828, 0x754e, 0x0a53, 0x1564, 0x0837, 0x4fa5, 0x13e7, 0x077f, 0x01ff, + 0x79b9, 0x3492, 0x2947, 0x0e82, 0x0d12, 0x3a01, 0x1b17, 0x206d, 0x61d0, 0x3441, + 0x21ad, 0x48c0, 0x064c, 0x5be7, 0x70e8, 0x4326, 0x66e4, 0x5420, 0x6e0d, 0x10b1, + 0x1c84, 0x0d0c, 0x5661, 0x357a, 0x37ce, 0x7b8f, 0x1274, 0x5e46, 0x0c97, 0x4898, + 0x31e0, 0x0db3, 0x2c7b, 0x0366, 0x5d1a, 0x0cbf, 0x45ac, 0x72a3, 0x5aa9, 0x4d81, + 0x7cd2, 0x0f9e, 0x11c7, 0x271a, 0x7c5e, 0x7688, 0x32ef, 0x73c2, 0x3212, 0x31c9, + 0x33b5, 0x29a9, 0x571e, 0x5997, 0x35e2, 0x1bdd, 0x18e6, 0x41b4, 0x5050, 0x2bff, + 0x0475, 0x6260, 0x6dc4, 0x24a0, 0x5246, 0x3ba6, 0x796b, 0x304f, 0x5db0, 0x0591, + 0x6e8e, 0x19dc, 0x6881, 0x7327, 0x65a4, 0x4ca7, 0x43a0, 0x2d10, 0x4ee7, 0x14cb, + 0x4737, 0x6129, 0x1508, 0x5791, 0x09d5, 0x07ef, 0x03af, 0x6031, 0x4359, 0x472d, + 0x66da, 0x3208, 0x1180, 0x3a7a, 0x36bf, 0x6189, 0x6b08, 0x209e, 0x0b1f, 0x75c7, + 0x4b52, 0x3e89, 0x6fb0, 0x2126, 0x6c1c, 0x2886, 0x39fb, 0x3d4b, 0x7c92, 0x22b3, + 0x324f, 0x1775, 0x4363, 0x57fb, 0x3b5e, 0x0ddb, 0x3338, 0x757f, 0x3d80, 0x3a9d, + 0x43b5, 0x497c, 0x4d39, 0x5ee5, 0x6344, 0x39b9, 0x40ee, 0x0f2e, 0x477e, 0x68cf, + 0x2721, 0x5e6e, 0x4ffc, 0x112c, 0x3a30, 0x2600, 0x18c2, 0x74ef, 0x1639, 0x6555, + 0x507f, 0x0b67, 0x71dd, 0x56e1, 0x3a84, 0x19c3, 0x4d68, 0x3561, 0x53c6, 0x1b11, + 0x25e7, 0x16f5, 0x05c0, 0x0ee4, 0x34db, 0x2aae, 0x6c84, 0x316b, 0x785c, 0x180f, + 0x6679, 0x4628, 0x57d8, 0x532c, 0x1958, 0x4e0e, 0x2bb5, 0x6982, 0x1edf, 0x3b9e, + 0x5f82, 0x129c, 0x7ee3, 0x242c, 0x1349, 0x3ed1, 0x118a, 0x04a7, 0x1230, 0x4d24, + 0x2e46, 0x62d1, 0x2fac, 0x170e, 0x7131, 0x7b45, 0x115b, 0x1cde, 0x213c, 0x3c67, + 0x512d, 0x2b58, 0x2acf, 0x1ee7, 0x63ea, 0x2efd, 0x6078, 0x1929, 0x1979, 0x76e3, + 0x5d6d, 0x22fb, 0x6b8a, 0x185e, 0x5cb6, 0x624a, 0x0552, 0x5624, 0x36c9, 0x446c, + 0x45cd, 0x37f6, 0x5e67, 0x3765, 0x1593, 0x53df, 0x4b73, 0x4cda, 0x7a70, 0x6b69, + 0x1b90, 0x3da5, 0x3229, 0x052c, 0x4ab4, 0x6ea1, 0x08b0, 0x498f, 0x5c95, 0x41c7, + 0x0a1c, 0x1b2a, 0x726c, 0x7b58, 0x2067, 0x3a43, 0x23f5, 0x198c, 0x7b21, 0x372e, + 0x6193, 0x6460, 0x6f23, 0x06da, 0x30c5, 0x5904, 0x2654, 0x0a66, 0x3d1a, 0x371b, + 0x131a, 0x7bb7, 0x5469, 0x6a24, 0x51ce, 0x1e6f, 0x04f0, 0x4af0, 0x097d, 0x6798, + 0x413c, 0x70b9, 0x5611, 0x301e, 0x21b2, 0x599c, 0x65a9, 0x7584, 0x163e, 0x62d6, + 0x6b8f, 0x5909, 0x65ae, 0x62ad, 0x1d9f, 0x61f8, 0x719c, 0x61ca, 0x283a, 0x5098, + 0x7613, 0x65d7, 0x638b, 0x7ec2, 0x385f, 0x335d, 0x35f9, 0x1eab, 0x69e8, 0x222f, + 0x5176, 0x2b0b, 0x5f61, 0x1dc8, 0x424c, 0x0b80, 0x42e5, 0x6497, 0x2ff0, 0x4ff5, + 0x7d12, 0x69b6, 0x16a5, 0x555c, 0x4cb1, 0x6221, 0x419e, 0x6437, 0x45ff, 0x62a8, + 0x3142, 0x74c6, 0x0f75, 0x6237, 0x0ce3, 0x3469, 0x6100, 0x0568, 0x228a, 0x4953, + 0x2a72, 0x2434, 0x7fa8, 0x48e8, 0x1392, 0x66b5, 0x22e8, 0x71f6, 0x0e2d, 0x50c1, + 0x1d9a, 0x3e34, 0x3b49, 0x3f92, 0x0729, 0x55cf, 0x7331, 0x6f5a, 0x7fa0, 0x59df, + 0x54d9, 0x552e, 0x7963, 0x63e2, 0x1c4f, 0x5c60, 0x01a8, 0x2b94, 0x6cd0, 0x2863, + 0x5f2c, 0x10f0, 0x7b09, 0x71c5, 0x4ecf, 0x32d7, 0x6658, 0x20cf, 0x4671, 0x47ba, + 0x3893, 0x4c5b, 0x343b, 0x21d5, 0x168d, 0x0711, 0x790d, 0x788d, 0x688b, 0x33fd, + 0x3fa7, 0x0a89, 0x6bcd, 0x61f3, 0x2e9f, 0x56fa, 0x2d25, 0x184b, 0x1245, 0x44ef, + 0x43fc, 0x1f89, 0x49db, 0x6946, 0x5ff5, 0x2a24, 0x0fa5, 0x159a, 0x649e, 0x3386, + 0x2eea, 0x7508, 0x7226, 0x6b44, 0x7197, 0x7e9d, 0x4566, 0x1ad1, 0x7925, 0x6ac2, + 0x2d1a, 0x3888, 0x0e22, 0x1c44, 0x7608, 0x21a7, 0x42da, 0x0f6a, 0x711b, 0x5a34, + 0x721b, 0x7110, 0x7156, 0x30fc, 0x3f61, 0x72eb, 0x7543, 0x593b, 0x1752, 0x58a3, + 0x510c, 0x4bce, 0x5839, 0x2dea, 0x5a3f, 0x1457, 0x6918, 0x5282, 0x73f6, 0x3622, + 0x7a96, 0x278d, 0x43aa, 0x6c11, 0x6876, 0x434e, 0x37c3, 0x61c5, 0x7cc7, 0x18db, + 0x7126, 0x1ed4, 0x18b7, 0x05b5, 0x4b68, 0x5d62, 0x7261, 0x3d0f, 0x1075, 0x7eeb, + 0x59e7, 0x4ddc, 0x38f0, 0x7c2f, 0x1916, 0x42aa, 0x3107, 0x3848, 0x2835, 0x5c0f, + 0x1a0c, 0x2cb7, 0x0c80, 0x1b79, 0x4ef1, 0x482f, 0x7713, 0x24dc, 0x1125, 0x63b4, + 0x337f, 0x656e, 0x7161, 0x268b, 0x0947, 0x120f, 0x311e, 0x0c28, 0x1bf4, 0x5bab, + 0x318d, 0x457a, 0x0925, 0x0674, 0x2e25, 0x0a9d, 0x08d2, 0x1652, 0x3f6c, 0x763c, + 0x48ba, 0x7e43, 0x0b41, 0x7662, 0x78a5, 0x79f5, 0x14d5, 0x0def, 0x26c1, 0x0660, + 0x3ae4, 0x5093, 0x1d4f, 0x06ee, 0x72f6, 0x76d0, 0x5a7a, 0x086d, 0x46c7, 0x6600, + 0x02ed, 0x7386, 0x77e3, 0x6d25, 0x4a31, 0x0d3a, 0x1c8a, 0x3d51, 0x53cc, 0x3a49, + 0x71a2, 0x21db, 0x760e, 0x7e49, 0x7e2f, 0x7e69, 0x4c73, 0x023b, 0x059b, 0x5889, + 0x1c2a, 0x7e83, 0x7e29, 0x0646, 0x4dc2, 0x11f5, 0x7ea8, 0x677e, 0x641d, 0x4fdb, + 0x3e1a, 0x59c5, 0x32bd, 0x44d5, 0x5ecb, 0x75ad, 0x5777, 0x4c8d, 0x2700, 0x298f, + 0x0e68, 0x5e2c, 0x6b4f, 0x2ee3, 0x06c0, 0x3a29, 0x653b, 0x3547, 0x5312, 0x1cc4, + 0x5dba, 0x0d74, 0x1f30, 0x2382, 0x6d05, 0x65d2, 0x6a82, 0x2619, 0x7231, 0x3752, + 0x6315, 0x0255, 0x3e9f, 0x0bdb, 0x6810, 0x081d, 0x2d60, 0x1351, 0x6f62, 0x21f5, + 0x4919, 0x6bed, 0x4459, 0x3793, 0x1adc, 0x3e54, 0x6386, 0x675e, 0x3323, 0x6bb8, + 0x4b99, 0x54f9, 0x6e98, 0x461f, 0x035d, 0x2095, 0x44b5, 0x5b7d, 0x17aa, 0x5015, + 0x4571, 0x5932, 0x2226, 0x71bc, 0x5c26, 0x75a4, 0x5735, 0x2905, 0x5afc, 0x5c2f, + 0x38ce, 0x141d, 0x18a1, 0x62ff, 0x3080, 0x1145, 0x7930, 0x25b4, 0x5be1, 0x7e63, + 0x216d, 0x3b04, 0x38ab, 0x5fb0, 0x19e6, 0x1667, 0x6515, 0x7628, 0x0626, 0x7ebd, + 0x3272, 0x40b3, 0x6acd, 0x37e3, 0x0af0, 0x1b3f, 0x47f5, 0x7bee, 0x4e81, 0x52a2, + 0x1469, 0x4165, 0x7cd9, 0x53e6, 0x42ec, 0x6575, 0x6b56, 0x403c, 0x3391, 0x2e66, + 0x385a, 0x296f, 0x764e, 0x54a0, 0x1d61, 0x12bc, 0x7975, 0x187e, 0x50d3, 0x07b5, + 0x4c6d, 0x70e2, 0x2eb1, 0x5e87, 0x64a9, 0x02b4, 0x3154, 0x3a63, 0x284c, 0x30e5, + 0x59ae, 0x4f55, 0x17bc, 0x573e, 0x3e66, 0x0eaa, 0x25c6, 0x77aa, 0x3284, 0x273a, + 0x2ef5, 0x3047, 0x6a94, 0x1ca4, 0x4dd4, 0x48e0, 0x21ed, 0x00ad, 0x3059, 0x6a5b, + 0x2e8a, 0x3d6b, 0x2ec3, 0x3358, 0x2e78, 0x4225, 0x7513, 0x4cc7, 0x5217, 0x1e35, + 0x6e67, 0x563a, 0x2f85, 0x09f5, 0x03c1, 0x3ed9, 0x7339, 0x00b5, 0x2898, 0x09a6, + 0x3d92, 0x68e8, 0x0fb0, 0x5205, 0x35f4, 0x0d54, 0x5673, 0x2be9, 0x3453, 0x1e1f, + 0x3bb0, 0x67c1, 0x2fbe, 0x4a4b, 0x25f9, 0x2dbc, 0x7501, 0x4035, 0x15a5, 0x7d9b, + 0x230d, 0x34ba, 0x7b6a, 0x4f6c, 0x2666, 0x028f, 0x5b4a, 0x290e, 0x0c5e, 0x6d3f, + 0x6c63, 0x2c42, 0x6d8b, 0x409e, 0x2a2f, 0x6e55, 0x4320, 0x0235, 0x707a, 0x4b19, + 0x4d00, 0x235e, 0x5250, 0x1600, 0x538d, 0x79e1, 0x0bbb, 0x1ea6, 0x2f73, 0x4797, + 0x6000, 0x0519, 0x3690, 0x77fd, 0x0ffd, 0x23bc, 0x6eea, 0x003d, 0x51a3, 0x13bb, + 0x7464, 0x309a, 0x4a89, 0x2516, 0x17fc, 0x203c, 0x63bf, 0x67fe, 0x69e3, 0x5d42, + 0x1b65, 0x2f34, 0x703c, 0x1568, 0x3a05, 0x1278, 0x72a7, 0x5054, 0x0595, 0x66de, + 0x3e8d, 0x4d3d, 0x1130, 0x34df, 0x4e12, 0x115f, 0x192d, 0x7a74, 0x41cb, 0x131e, + 0x70bd, 0x638f, 0x1dcc, 0x0ce7, 0x66b9, 0x01ac, 0x20d3, 0x1249, 0x338a, 0x721f, + 0x4bd2, 0x18bb, 0x7c33, 0x094b, 0x0aa1, 0x5a7e, 0x3d55, 0x6421, 0x2993, 0x6319, + 0x6bf1, 0x222a, 0x6303, 0x0af4, 0x6579, 0x3158, 0x77ae, 0x521b, 0x09aa, 0x2311, + 0x2c46, 0x3694, 0x251a, 0x4e16, 0x01b0, 0x77b2, 0x7fd5, 0x7fd1, 0x0ed1, 0x04c0, + 0x771e, 0x1f1e, 0x5171, 0x1437, 0x6f9b, 0x465c, 0x3fbf, 0x3653, 0x2890, 0x4911, + 0x138a, 0x38e8, 0x276d, 0x3caf, 0x5da8, 0x6070, 0x24e7, 0x1fcd, 0x7fcd, 0x038e, + 0x3c8c, 0x07cc, 0x7434, 0x392a, 0x7d84, 0x6767, 0x5a1d, 0x5c49, 0x4716, 0x0ecd, + 0x3fd4, 0x1539, 0x483a, 0x6cf3, 0x541a, 0x5883, 0x1fb6, 0x245d, 0x4492, 0x58c6, + 0x6c26, 0x534f, 0x073e, 0x2ca3, 0x4bae, 0x2b06, 0x6a70, 0x11a3, 0x4efc, 0x2a9b, + 0x04bc, 0x5b16, 0x7d50, 0x1a46, 0x396a, 0x013c, 0x225f, 0x486c, 0x768f, 0x45d4, + 0x69bd, 0x771a, 0x354e, 0x2fc5, 0x0952, 0x0e56, 0x5f5c, 0x2187, 0x3834, 0x069a, + 0x3415, 0x280f, 0x22bd, 0x033a, 0x7f21, 0x3b1e, 0x1c24, 0x6e07, 0x26ee, 0x7f75, + 0x121a, 0x2474, 0x1f1a, 0x6860, 0x662d, 0x50ea, 0x596b, 0x78e2, 0x0c55, 0x3e5d, + 0x576e, 0x38c5, 0x4ec6, 0x516d, 0x1749, 0x091c, 0x2696, 0x5eb9, 0x7740, 0x72cb, + 0x0b16, 0x5d11, 0x57cf, 0x08a7, 0x7c9c, 0x4701, 0x6643, 0x4b3d, 0x5a14, 0x1dc3, + 0x5765, 0x1727, 0x716c, 0x19b0, 0x1433, 0x5fca, 0x75dd, 0x7ad3, 0x2ce4, 0x3f36, + 0x263b, 0x1960, 0x2b9c, 0x25ce, 0x0396, 0x6f97, 0x1afe, 0x35c9, 0x0c33, 0x06ae, + 0x4247, 0x2a04, 0x0fd2, 0x0752, 0x36ef, 0x2f48, 0x3259, 0x4440, 0x0e4f, 0x4da9, + 0x74e8, 0x6d72, 0x6b3d, 0x2e5f, 0x3129, 0x55f8, 0x4658, 0x794a, 0x42c1, 0x5820, + 0x18fd, 0x1d36, 0x52e7, 0x1ae5, 0x1730, 0x26d5, 0x5d8f, 0x3fbb, 0x17e3, 0x62ea, + 0x1bff, 0x6529, 0x10ab, 0x7e7d, 0x7e04, 0x54b4, 0x68a3, 0x53a1, 0x177f, 0x3b72, + 0x29f0, 0x5bfb, 0x1ab1, 0x0b7b, 0x5300, 0x6f37, 0x5bb6, 0x16e2, 0x364f, 0x5ad1, + 0x05fb, 0x60af, 0x3c3b, 0x4e56, 0x0ff1, 0x0baf, 0x6c57, 0x706e, 0x5667, 0x288c, + 0x25ed, 0x7b5e, 0x2840, 0x4c61, 0x42e0, 0x7642, 0x4dc8, 0x25ba, 0x2eb7, 0x6e5b, + 0x3e93, 0x6cf9, 0x26f4, 0x652f, 0x7e23, 0x1c7e, 0x7e1d, 0x3e0e, 0x5c1a, 0x44a9, + 0x490d, 0x3317, 0x2161, 0x1895, 0x061a, 0x47e9, 0x43f0, 0x6bc1, 0x664c, 0x1681, + 0x3b3d, 0x1386, 0x54cd, 0x6cc4, 0x3853, 0x7190, 0x4130, 0x1632, 0x7d06, 0x5f55, + 0x45f3, 0x60f4, 0x4b5c, 0x37b7, 0x5100, 0x73ea, 0x455a, 0x6492, 0x75fc, 0x714a, + 0x3112, 0x1119, 0x38e4, 0x1a00, 0x0b35, 0x2e19, 0x3ad8, 0x46bb, 0x4f99, 0x2bbd, + 0x5c68, 0x328c, 0x1fd5, 0x2769, 0x68bc, 0x60d0, 0x2cc2, 0x4a1f, 0x2feb, 0x76b0, + 0x6af3, 0x7af4, 0x7d6f, 0x64d8, 0x6fba, 0x15d4, 0x12ed, 0x1a67, 0x7366, 0x01d1, + 0x77d1, 0x1cf7, 0x1a17, 0x2332, 0x3cab, 0x40cd, 0x3907, 0x6166, 0x2c16, 0x7485, + 0x6338, 0x332c, 0x4b46, 0x7c86, 0x4394, 0x5da4, 0x09c9, 0x1174, 0x0c8b, 0x1c78, + 0x0d06, 0x0640, 0x7c52, 0x45a0, 0x5712, 0x0469, 0x2130, 0x2e3a, 0x194c, 0x7ed7, + 0x5073, 0x4ff0, 0x53ba, 0x6c78, 0x1b84, 0x5e5b, 0x606c, 0x5caa, 0x23e9, 0x5c89, + 0x30b9, 0x545d, 0x1a7a, 0x0bfc, 0x7c65, 0x37fd, 0x7d19, 0x24e3, 0x6542, 0x4a52, + 0x7c3a, 0x4db0, 0x7d0d, 0x7c0f, 0x7c1b, 0x6f83, 0x381c, 0x7dec, 0x0b29, 0x454e, + 0x3b31, 0x7cfa, 0x4dbc, 0x565b, 0x7e17, 0x2155, 0x38fb, 0x735a, 0x1fc9, 0x6ae7, + 0x7c46, 0x4388, 0x5067, 0x23dd, 0x7d44, 0x4ba2, 0x470a, 0x1faa, 0x6f8f, 0x7fc9, + 0x2761, 0x3c80, 0x1921, 0x0589, 0x4a7d, 0x1b59, 0x7c27, 0x66ad, 0x6be5, 0x099e, + 0x75d1, 0x5a08, 0x4eba, 0x0b0a, 0x3828, 0x69b1, 0x1c18, 0x6621, 0x42b5, 0x74dc, + 0x038a, 0x0fc6, 0x7df8, 0x5d83, 0x1aa5, 0x05ef, 0x7409, 0x698a, 0x1c57, 0x2742, + 0x24ef, 0x3c88, 0x0b54, 0x5146, 0x59f2, 0x640b, 0x16a0, 0x480f, 0x7d25, 0x31b3, + 0x620b, 0x6a45, 0x20a8, 0x6281, 0x2180, 0x7c08, 0x654e, 0x2d95, 0x7e96, 0x2968, + 0x4de7, 0x58dd, 0x07c8, 0x39da, 0x4a5e, 0x798c, 0x708d, 0x7011, 0x57a4, 0x5502, + 0x7ca5, 0x4e9b, 0x7c71, 0x7430, 0x10c4, 0x1408, 0x7ef6, 0x3e08, 0x3574, 0x11ef, + 0x3809, 0x5ced, 0x2408, 0x3523, 0x6b12, 0x4c2f, 0x35b5, 0x4296, 0x0c08, 0x5557, + 0x32ab, 0x2b71, 0x1080, 0x56ce, 0x3926, 0x52bc, 0x1a86, 0x0448, 0x1f5d, 0x3c10, + 0x23a1, 0x15e5, 0x6e3a, 0x2c27, 0x2da1, 0x7d80, 0x3ebe, 0x2bce, 0x48c5, 0x5723, + 0x4cac, 0x333d, 0x655a, 0x2e4b, 0x1863, 0x30ca, 0x7589, 0x4604, 0x3e39, 0x6bd2, + 0x7ea2, 0x37c8, 0x5c14, 0x3ae9, 0x7e4e, 0x6d0a, 0x6763, 0x062b, 0x2974, 0x2ec8, + 0x0d59, 0x0bc0, 0x5d47, 0x6bf6, 0x143c, 0x4bb3, 0x218c, 0x5a19, 0x2a09, 0x1ab6, + 0x7647, 0x455f, 0x76b5, 0x5078, 0x7c14, 0x382d, 0x4814, 0x0c0d, 0x3342, 0x6292, + 0x5981, 0x709e, 0x628d, 0x621c, 0x2214, 0x699b, 0x3f77, 0x2419, 0x5c45, 0x5513, + 0x20b4, 0x4c40, 0x33e2, 0x1f6e, 0x399e, 0x57e0, 0x286b, 0x3e6e, 0x07d4, 0x4712, + 0x3b8b, 0x2cf5, 0x766d, 0x034b, 0x4199, 0x597c, 0x39e6, 0x3426, 0x5405, 0x487d, + 0x3d8a, 0x4451, 0x22e0, 0x190e, 0x58e9, 0x3700, 0x6e86, 0x1971, 0x0b4c, 0x68b4, + 0x0ec9, 0x1af6, 0x4df3, 0x3b83, 0x048c, 0x3c4c, 0x5692, 0x4922, 0x5fd3, 0x7445, + 0x7998, 0x3fd0, 0x00d4, 0x4e27, 0x78b0, 0x44a3, 0x7b89, 0x6778, 0x4a6a, 0x5360, + 0x2d3d, 0x397b, 0x3aa7, 0x0ab2, 0x6d5e, 0x63a0, 0x7099, 0x6432, 0x1798, 0x2c57, + 0x7a00, 0x1289, 0x1535, 0x41dc, 0x701d, 0x704d, 0x101c, 0x13cc, 0x3dd2, 0x4518, + 0x32f6, 0x4473, 0x16ac, 0x4836, 0x5319, 0x67c8, 0x0aa8, 0x4447, 0x45fa, 0x6288, + 0x481b, 0x442c, 0x441b, 0x4529, 0x3b68, 0x4436, 0x0330, 0x46f7, 0x6417, 0x126e, + 0x4907, 0x5345, 0x2e30, 0x15ca, 0x6cef, 0x37ad, 0x59fe, 0x4544, 0x6277, 0x4c25, + 0x6456, 0x4462, 0x19b9, 0x049d, 0x31bf, 0x5416, 0x611f, 0x57f1, 0x08dd, 0x2d4e, + 0x2b2a, 0x4933, 0x7d31, 0x0ac3, 0x4507, 0x102d, 0x0de5, 0x4825, 0x387e, 0x6c07, + 0x6217, 0x62a3, 0x6f50, 0x33f3, 0x165d, 0x4615, 0x587f, 0x0d6a, 0x6a51, 0x1874, + 0x67b7, 0x15f6, 0x1a2b, 0x5334, 0x6cd8, 0x0eb2, 0x3c94, 0x1fb2, 0x4dfb, 0x4641, + 0x0930, 0x6374, 0x313d, 0x220f, 0x24fb, 0x67e3, 0x125d, 0x7a59, 0x5805, 0x4425, + 0x0693, 0x6f7c, 0x0b60, 0x16c7, 0x1aca, 0x5499, 0x067f, 0x4851, 0x2459, 0x6dec, + 0x5152, 0x5e9e, 0x46e6, 0x7ab8, 0x2dfe, 0x379c, 0x7175, 0x136b, 0x1c63, 0x448e, + 0x0b94, 0x259f, 0x4585, 0x3311, 0x5e40, 0x4fd5, 0x274e, 0x4a04, 0x15b9, 0x614b, + 0x436d, 0x4533, 0x4d95, 0x24c8, 0x6996, 0x74c1, 0x4b87, 0x6692, 0x3198, 0x696f, + 0x58c2, 0x2d7a, 0x7415, 0x3ded, 0x4c14, 0x042d, 0x27d3, 0x7aa7, 0x6ca3, 0x594c, + 0x3580, 0x6c22, 0x16fb, 0x7272, 0x509e, 0x3899, 0x0f70, 0x3f72, 0x11fb, 0x7936, + 0x5e8d, 0x2a35, 0x4d43, 0x4840, 0x7f7b, 0x1c05, 0x3e14, 0x0c91, 0x215b, 0x7efc, + 0x3aef, 0x78b6, 0x534b, 0x458b, 0x7f02, 0x0e00, 0x6ddb, 0x02fe, 0x5cd5, 0x6f6b, + 0x7adc, 0x5f3d, 0x5cf9, 0x073a, 0x2f1c, 0x2445, 0x1d5a, 0x791e, 0x1baf, 0x71d6, + 0x3815, 0x340e, 0x4414, 0x49ec, 0x5eef, 0x16b6, 0x49ae, 0x2240, 0x2414, 0x6232, + 0x5bcf, 0x229b, 0x06f9, 0x62be, 0x2c9f, 0x360a, 0x352f, 0x6ba0, 0x5488, 0x4b01, + 0x29c8, 0x4630, 0x5f34, 0x5746, 0x743c, 0x4baa, 0x0494, 0x1362, 0x26cc, 0x38bc, + 0x0cde, 0x5c40, 0x7c7d, 0x1678, 0x1fa1, 0x4e92, 0x4986, 0x5323, 0x0cb6, 0x75be, + 0x10d0, 0x0d85, 0x5aea, 0x6821, 0x066b, 0x589a, 0x2b02, 0x32ce, 0x1414, 0x4c84, + 0x0ea1, 0x6d36, 0x64ed, 0x21fe, 0x75e6, 0x574f, 0x7cb1, 0x6a6c, 0x3668, 0x2f96, + 0x0dfa, 0x188f, 0x4892, 0x59bf, 0x4ea7, 0x1d72, 0x6363, 0x4176, 0x43bf, 0x67d2, + 0x55e4, 0x2677, 0x550e, 0x3464, 0x306e, 0x3eea, 0x14e0, 0x4d11, 0x119f, 0x291f, + 0x57b0, 0x1611, 0x7a48, 0x6efb, 0x2000, 0x613a, 0x73c9, 0x36d0, 0x5563, 0x4ef8, + 0x1ccb, 0x3bb7, 0x5a85, 0x3260, 0x60fb, 0x20af, 0x0c14, 0x580c, 0x49f3, 0x40ff, + 0x39c3, 0x3300, 0x6849, 0x0377, 0x32b7, 0x31da, 0x0614, 0x6dd5, 0x0878, 0x5431, + 0x2a97, 0x31f1, 0x2b7d, 0x70f9, 0x4fc4, 0x34a3, 0x0170, 0x135a, 0x2ced, 0x4639, + 0x35c1, 0x04b8, 0x60c8, 0x513e, 0x76db, 0x19d4, 0x149d, 0x786d, 0x42a2, 0x71ee, + 0x378b, 0x68e0, 0x634e, 0x447d, 0x78f8, 0x323a, 0x4c3b, 0x0563, 0x6503, 0x1ef8, + 0x7301, 0x7b32, 0x5b12, 0x6eb2, 0x6b1e, 0x6471, 0x258e, 0x51df, 0x7776, 0x6681, + 0x10f8, 0x17c4, 0x3932, 0x7d4c, 0x3c54, 0x0178, 0x660b, 0x0ade, 0x2285, 0x33dd, + 0x52c8, 0x1bc7, 0x74b0, 0x420f, 0x40f8, 0x4522, 0x2808, 0x7de5, 0x56da, 0x20f8, + 0x6abb, 0x12b5, 0x46d2, 0x7dc0, 0x1a42, 0x502f, 0x108c, 0x1048, 0x24b7, 0x0790, + 0x672b, 0x2d69, 0x3f3f, 0x29d1, 0x0454, 0x3966, 0x6fe5, 0x5f9b, 0x02f8, 0x47e3, + 0x0dad, 0x44cf, 0x1a92, 0x08f8, 0x695e, 0x5e08, 0x0f38, 0x3ddc, 0x1d22, 0x5b97, + 0x1f69, 0x494e, 0x4e6f, 0x55ac, 0x7391, 0x2b45, 0x0138, 0x2556, 0x3c1c, 0x469a, + 0x041c, 0x0100, 0x3bf2, 0x4e45, 0x3f15, 0x02ce, 0x774c, 0x225b, 0x248d, 0x56b0, + 0x5539, 0x49c9, 0x2a6d, 0x3999, 0x72d7, 0x78ce, 0x609e, 0x4278, 0x11d1, 0x16d1, + 0x7f51, 0x3ac5, 0x5ec5, 0x2c75, 0x43ea, 0x5ccf, 0x54e4, 0x7a1e, 0x4868, 0x64c3, + 0x26a2, 0x5f0a, 0x5ac0, 0x13ea, 0x294a, 0x5bea, 0x6e10, 0x489b, 0x5d1d, 0x768b, + 0x33b8, 0x6263, 0x796e, 0x2d13, 0x150b, 0x3a7d, 0x0b22, 0x22b6, 0x3b61, 0x39bc, + 0x2724, 0x0b6a, 0x4d6b, 0x316e, 0x57db, 0x242f, 0x1233, 0x3c6a, 0x63ed, 0x624d, + 0x45d0, 0x3da8, 0x08b3, 0x198f, 0x6f26, 0x6a27, 0x0980, 0x62d9, 0x1da2, 0x3360, + 0x5179, 0x69b9, 0x41a1, 0x056b, 0x7fab, 0x3f95, 0x7fa3, 0x2866, 0x4ed2, 0x0714, + 0x3faa, 0x1f8c, 0x0fa8, 0x1ad4, 0x0e25, 0x30ff, 0x1755, 0x3625, 0x6879, 0x5d65, + 0x59ea, 0x2cba, 0x7716, 0x0c2b, 0x0928, 0x7665, 0x26c4, 0x6603, 0x4a34, 0x7e6c, + 0x1c2d, 0x59c8, 0x577a, 0x354a, 0x1f33, 0x0bde, 0x6f65, 0x6bbb, 0x0360, 0x75a7, + 0x38d1, 0x3b07, 0x6518, 0x7bf1, 0x7cdc, 0x54a3, 0x50d6, 0x30e8, 0x3e69, 0x48e3, + 0x2e8d, 0x563d, 0x733c, 0x2bec, 0x2fc1, 0x4f6f, 0x0c61, 0x4b1c, 0x5390, 0x23bf, + 0x7467, 0x2f37, 0x72aa, 0x7a77, 0x1dcf, 0x094e, 0x2996, 0x2314, 0x01b3, 0x465f, + 0x138d, 0x07cf, 0x5a20, 0x2460, 0x0741, 0x1a49, 0x7692, 0x069d, 0x7f24, 0x50ed, + 0x5771, 0x5d14, 0x6646, 0x7ad6, 0x2b9f, 0x0755, 0x0e52, 0x5823, 0x1733, 0x54b7, + 0x29f3, 0x60b2, 0x6c5a, 0x25bd, 0x26f7, 0x1898, 0x664f, 0x5f58, 0x5103, 0x2e1c, + 0x5c6b, 0x7af7, 0x12f0, 0x6169, 0x4b49, 0x45a3, 0x194f, 0x5c8c, 0x7c68, 0x6f86, + 0x3b34, 0x438b, 0x470d, 0x66b0, 0x4ebd, 0x5d86, 0x1c5a, 0x31b6, 0x2183, 0x798f, + 0x7ca8, 0x5cf0, 0x35b8, 0x044b, 0x6e3d, 0x2e4e, 0x3e3c, 0x2ecb, 0x143f, 0x3830, + 0x5984, 0x4c43, 0x286e, 0x3429, 0x22e3, 0x3b86, 0x5fd6, 0x5363, 0x6d61, 0x7050, + 0x32f9, 0x442f, 0x0333, 0x4547, 0x19bc, 0x0ac6, 0x3881, 0x1877, 0x6cdb, 0x67e6, + 0x0696, 0x5ea1, 0x7178, 0x4a07, 0x4d98, 0x3df0, 0x6ca6, 0x7939, 0x7f7e, 0x0e03, + 0x7adf, 0x3411, 0x49b1, 0x6ba3, 0x5f37, 0x167b, 0x0cb9, 0x4c87, 0x75e9, 0x1d75, + 0x55e7, 0x1614, 0x73cc, 0x580f, 0x684c, 0x70fc, 0x2cf0, 0x71f1, 0x78fb, 0x6474, + 0x10fb, 0x1bca, 0x280b, 0x104b, 0x3f42, 0x08fb, 0x1d25, 0x469d, 0x3f18, 0x78d1, + 0x7f54, 0x5f0d, 0x6e13, 0x22b9, 0x4d6e, 0x1992, 0x1da5, 0x0717, 0x0e28, 0x7668, + 0x1c30, 0x3b0a, 0x50d9, 0x4b1f, 0x72ad, 0x2463, 0x7f27, 0x54ba, 0x26fa, 0x45a6, + 0x3b37, 0x5cf3, 0x3e3f, 0x5366, 0x0336, 0x4a0a, 0x7f81, 0x1d78, 0x684f, 0x08fe, + 0x7f57, 0x3b0d, 0x7f2a, 0x1d7b, 0x7f2d, 0x7f1d, 0x7f3a, 0x031c, 0x50cc, 0x0e1b, + 0x7f47, 0x4d61, 0x3b2a, 0x7f1a, 0x0329, 0x6842, 0x45b6, 0x6df6, 0x7f37, 0x2a53, + 0x0346, 0x50bc, 0x1d88, 0x76fc, 0x0e38, 0x7290, 0x3b1a, 0x12d6, 0x22c9, 0x0319, + 0x7f64, 0x27f1, 0x5383, 0x2fb4, 0x50c9, 0x2e80, 0x1f26, 0x1c20, 0x0353, 0x650b, + 0x3f9d, 0x7f96, 0x1d95, 0x4194, 0x686c, 0x0e18, 0x7709, 0x26b7, 0x5ab3, 0x485b, + 0x7f44, 0x43dd, 0x2480, 0x3f08, 0x2a60, 0x6091, 0x3b54, 0x14fe, 0x6e03, 0x33ab, + 0x1226, 0x4d5e, 0x45c3, 0x6f19, 0x35ab, 0x2176, 0x3b27, 0x4eb0, 0x50f6, 0x26ea, + 0x12e3, 0x1942, 0x0734, 0x1380, 0x729d, 0x2989, 0x6639, 0x7f17, 0x0e45, 0x29e6, + 0x4d8b, 0x0689, 0x0326, 0x3874, 0x5977, 0x3e2f, 0x22d6, 0x6d54, 0x55da, 0x0cac, + 0x7f71, 0x49a4, 0x78ee, 0x683f, 0x27fe, 0x1d18, 0x1300, 0x0896, 0x45b3, 0x6b70, + 0x5f68, 0x1216, 0x2707, 0x34c1, 0x66c0, 0x7951, 0x3b44, 0x39e1, 0x2193, 0x6df3, + 0x5d00, 0x5036, 0x5d24, 0x5ea8, 0x7f34, 0x411d, 0x0e62, 0x5aa3, 0x54c7, 0x2f16, + 0x139d, 0x3bd5, 0x2470, 0x4056, 0x095e, 0x2a50, 0x72ba, 0x201e, 0x0ad6, 0x38b4, + 0x0343, 0x636c, 0x06a6, 0x1f16, 0x4a17, 0x6403, 0x22f3, 0x731f, 0x5373, 0x4306, + 0x3840, 0x50b9, 0x3e4c, 0x51fd, 0x0cc9, 0x515c, 0x1d85, 0x658f, 0x3421, 0x3f8d, + 0x7f8e, 0x018e, 0x7201, 0x0f8b, 0x685c, 0x68fe, 0x281b, 0x76f9, 0x090b, 0x5a60, + 0x3635, 0x1716, 0x0e35, 0x422d, 0x7726, 0x6629, 0x7675, 0x1f00, 0x7fb3, 0x0196, + 0x0724, 0x5400, 0x69c9, 0x728d, 0x1db2, 0x7794, 0x0370, 0x46f0, 0x3b17, 0x7cf3, + 0x355a, 0x359b, 0x1c3d, 0x07ae, 0x48f3, 0x6c3d, 0x50e6, 0x4116, 0x2fd1, 0x12d3, + 0x4b2c, 0x6052, 0x151b, 0x5fb9, 0x22c6, 0x417f, 0x769b, 0x5967, 0x6e20, 0x6749, + 0x243f, 0x6cbe, 0x4d7b, 0x5e26, 0x45e0, 0x0316, 0x199f, 0x5865, 0x2c85, 0x7ac2, + 0x7f61, 0x0f56, 0x4878, 0x55ca, 0x5f1a, 0x2ae8, 0x2a7d, 0x73af, 0x78de, 0x1483, + 0x226b, 0x27ee, 0x3f25, 0x011e, 0x6edd, 0x3683, 0x5380, 0x2f66, 0x6d7e, 0x0c51, + 0x4313, 0x4cf3, 0x3446, 0x35e7, 0x732c, 0x3d85, 0x74f4, 0x2fb1, 0x2300, 0x2659, + 0x59a1, 0x3147, 0x50c6, 0x2ea4, 0x6b49, 0x7ccc, 0x384d, 0x1d54, 0x21e0, 0x6a87, + 0x3e59, 0x3277, 0x2e6b, 0x2e7d, 0x520a, 0x2f78, 0x6803, 0x6308, 0x1f23, 0x6a75, + 0x0e5b, 0x576a, 0x06b3, 0x5305, 0x4c66, 0x7601, 0x4a24, 0x53bf, 0x4db5, 0x1c1d, + 0x6410, 0x32b0, 0x5728, 0x2219, 0x0350, 0x179d, 0x444c, 0x6f55, 0x6379, 0x4b8c, + 0x389e, 0x5bd4, 0x38c1, 0x3073, 0x3265, 0x6508, 0x0ae3, 0x4e74, 0x49ce, 0x1238, + 0x3f9a, 0x2e92, 0x4664, 0x4ec2, 0x342e, 0x7900, 0x071c, 0x1d8d, 0x7f9b, 0x22db, + 0x7956, 0x7f93, 0x019b, 0x5f1f, 0x35ec, 0x637e, 0x1d92, 0x282d, 0x5604, 0x0970, + 0x659c, 0x6b82, 0x1698, 0x2fe3, 0x5169, 0x423f, 0x3135, 0x4191, 0x0cd6, 0x227d, + 0x7254, 0x18aa, 0x6869, 0x7cba, 0x582c, 0x1745, 0x690b, 0x7a89, 0x7918, 0x718a, + 0x0f98, 0x2edd, 0x42cd, 0x0e15, 0x720e, 0x3f54, 0x1be7, 0x093a, 0x7706, 0x3372, + 0x1909, 0x59da, 0x2828, 0x0c73, 0x7898, 0x48ad, 0x0918, 0x08c5, 0x1d42, 0x26b4, + 0x5a6d, 0x02e0, 0x0772, 0x1557, 0x5ab0, 0x4ce1, 0x4253, 0x2692, 0x0e6f, 0x7da2, + 0x20da, 0x55ff, 0x54d4, 0x58e4, 0x2a10, 0x4858, 0x2f23, 0x7dc7, 0x33bf, 0x67ed, + 0x7f41, 0x6c44, 0x06ba, 0x11c1, 0x412a, 0x1ba9, 0x6663, 0x4f1a, 0x5eb5, 0x752d, + 0x0c3f, 0x43da, 0x5d31, 0x015a, 0x558e, 0x3089, 0x247d, 0x3671, 0x075e, 0x773c, + 0x4063, 0x3dbe, 0x467c, 0x04de, 0x3be2, 0x1e4f, 0x0fde, 0x3f05, 0x13aa, 0x2538, + 0x29b3, 0x2505, 0x2a5d, 0x5231, 0x36fb, 0x5529, 0x096b, 0x3948, 0x47c5, 0x347f, + 0x72c7, 0x28ae, 0x2f54, 0x608e, 0x202b, 0x5dea, 0x40e1, 0x4d2c, 0x3b51, 0x3d73, + 0x6fa3, 0x0b12, 0x39ee, 0x3242, 0x4eda, 0x6597, 0x795e, 0x6e81, 0x03a2, 0x14fb, + 0x66cd, 0x36b2, 0x31d3, 0x1267, 0x6e00, 0x5654, 0x1b0a, 0x293a, 0x21a0, 0x70db, + 0x32e2, 0x11ba, 0x5d0d, 0x5a9c, 0x35d5, 0x33a8, 0x5043, 0x6db7, 0x5120, 0x114e, + 0x1223, 0x2f9f, 0x2ba8, 0x57cb, 0x5f75, 0x133c, 0x71d0, 0x162c, 0x2714, 0x3a23, + 0x25da, 0x4d5b, 0x34ce, 0x784f, 0x321c, 0x7a63, 0x45c0, 0x1586, 0x196c, 0x63dd, + 0x6b7d, 0x0545, 0x7b14, 0x205a, 0x08a3, 0x0a0f, 0x2647, 0x6f16, 0x130d, 0x51c1, + 0x1f50, 0x3919, 0x35a8, 0x329e, 0x10b7, 0x7c98, 0x3567, 0x23fb, 0x61fe, 0x1693, + 0x1c4a, 0x0b47, 0x7e89, 0x2173, 0x07bb, 0x7080, 0x505a, 0x1fbc, 0x3b24, 0x7e0a, + 0x6535, 0x7c58, 0x7d00, 0x380f, 0x6bd8, 0x4a70, 0x46fd, 0x2754, 0x1c0b, 0x4ead, + 0x037d, 0x1a98, 0x3acb, 0x38d7, 0x50f3, 0x75ef, 0x54c0, 0x663f, 0x4123, 0x45e6, + 0x2eaa, 0x42d3, 0x6c4a, 0x25e0, 0x7e10, 0x26e7, 0x4900, 0x060d, 0x2c09, 0x3c9e, + 0x12e0, 0x77c4, 0x68af, 0x5c5b, 0x2fde, 0x7d62, 0x5705, 0x0cf9, 0x4b39, 0x09bc, + 0x53ad, 0x193f, 0x605f, 0x30ac, 0x395d, 0x04af, 0x0731, 0x6a63, 0x3fc7, 0x5a10, + 0x540d, 0x4485, 0x3fb2, 0x5164, 0x01a3, 0x0ec4, 0x5d9b, 0x137d, 0x7fc0, 0x7427, + 0x41be, 0x4e05, 0x729a, 0x3e80, 0x17ef, 0x7457, 0x69d6, 0x702f, 0x0a94, 0x4bc5, + 0x1dbf, 0x20c6, 0x62f6, 0x2986, 0x77a1, 0x2c39, 0x2cd7, 0x1426, 0x6636, 0x5758, + 0x173c, 0x5761, 0x7733, 0x57c2, 0x3408, 0x5f4f, 0x7682, 0x3541, 0x26e1, 0x7f14, + 0x1f0d, 0x595e, 0x18f0, 0x464b, 0x0e42, 0x6b30, 0x1af1, 0x2b8f, 0x423a, 0x36e2, + 0x6896, 0x109e, 0x1723, 0x17d6, 0x52f3, 0x29e3, 0x3642, 0x3c2e, 0x4c07, 0x58b5, + 0x4d88, 0x4b7a, 0x0b87, 0x7168, 0x5e33, 0x15ac, 0x1250, 0x3130, 0x6ccb, 0x4dee, + 0x1abd, 0x0686, 0x244c, 0x46d9, 0x626a, 0x6ce2, 0x0323, 0x48fa, 0x530c, 0x32e9, + 0x45ed, 0x440e, 0x44fa, 0x2b1d, 0x19ac, 0x6112, 0x6f43, 0x3871, 0x5872, 0x67aa, + 0x33d5, 0x5c38, 0x5974, 0x2207, 0x29fc, 0x142f, 0x76a8, 0x4807, 0x1856, 0x4c9f, + 0x6e2d, 0x3eb1, 0x5c07, 0x3e2c, 0x6756, 0x0d4c, 0x047f, 0x0ebc, 0x22d3, 0x6e79, + 0x3b7e, 0x285e, 0x418c, 0x53f8, 0x2d30, 0x7b7c, 0x5fc6, 0x00c7, 0x178b, 0x6d51, + 0x1528, 0x100f, 0x7a3b, 0x1192, 0x55d7, 0x3061, 0x365b, 0x75d9, 0x4885, 0x6356, + 0x1f94, 0x0cd1, 0x5f27, 0x0487, 0x5add, 0x0ca9, 0x2af5, 0x0e94, 0x6dce, 0x533e, + 0x7f6e, 0x214e, 0x16ee, 0x6c96, 0x0f63, 0x5e80, 0x4407, 0x1ba2, 0x7acf, 0x2f0f, + 0x5bc2, 0x49a1, 0x2c92, 0x547b, 0x2581, 0x5b05, 0x78eb, 0x64f6, 0x60bb, 0x2ce0, + 0x1490, 0x377e, 0x49e6, 0x60ee, 0x73bc, 0x1cbe, 0x0607, 0x683c, 0x2a8a, 0x4fb7, + 0x24aa, 0x1a35, 0x27fb, 0x6aae, 0x3c47, 0x10eb, 0x2278, 0x74a3, 0x6951, 0x0da0, + 0x3f32, 0x6fd8, 0x4e62, 0x1d15, 0x012b, 0x040f, 0x01e1, 0x544c, 0x12fd, 0x5dd8, + 0x3cbb, 0x2637, 0x6176, 0x0a35, 0x2ffb, 0x724f, 0x7b04, 0x568d, 0x2779, 0x0893, + 0x5c78, 0x083b, 0x0d16, 0x5e4a, 0x45b0, 0x2c03, 0x5db4, 0x320c, 0x4b56, 0x5ee9, + 0x5000, 0x2ab2, 0x195c, 0x1ce2, 0x607c, 0x6b6d, 0x5c99, 0x7bbb, 0x4140, 0x7ec6, + 0x5f65, 0x346d, 0x1396, 0x2b98, 0x665c, 0x44f3, 0x64a2, 0x7114, 0x5110, 0x05b9, + 0x38f4, 0x1213, 0x2e29, 0x0871, 0x1c8e, 0x4fdf, 0x2704, 0x0259, 0x491d, 0x71c0, + 0x18a5, 0x1b43, 0x42f0, 0x3a67, 0x25ca, 0x1e39, 0x289c, 0x34be, 0x6c67, 0x7801, + 0x4a8d, 0x1163, 0x66bd, 0x521f, 0x7fd9, 0x0392, 0x471a, 0x5b1a, 0x69c1, 0x6864, + 0x4eca, 0x5fce, 0x039a, 0x794e, 0x5d93, 0x5ad5, 0x566b, 0x331b, 0x3b41, 0x1a04, + 0x1fd9, 0x40d1, 0x4398, 0x5cae, 0x7d1d, 0x6aeb, 0x6f93, 0x0fca, 0x24f3, 0x39de, + 0x7c75, 0x52c0, 0x2da5, 0x062f, 0x2190, 0x5517, 0x07d8, 0x1afa, 0x799c, 0x41e0, + 0x16b0, 0x37b1, 0x31c3, 0x0d6e, 0x3c98, 0x6df0, 0x1c67, 0x2d7e, 0x3584, 0x458f, + 0x5cfd, 0x360e, 0x7440, 0x32d2, 0x7cb5, 0x2923, 0x5567, 0x31f5, 0x35c5, 0x6eb6, + 0x3936, 0x5033, 0x0458, 0x255a, 0x7750, 0x64c7, 0x5d21, 0x3dac, 0x517d, 0x0c2f, + 0x577e, 0x4f73, 0x1dd3, 0x5827, 0x6653, 0x7993, 0x1443, 0x5ea5, 0x7ae3, 0x104f, + 0x6e17, 0x4a0e, 0x7f31, 0x12da, 0x1f2a, 0x33af, 0x50fa, 0x49a8, 0x5f6c, 0x405a, + 0x06aa, 0x6902, 0x772a, 0x411a, 0x769f, 0x1487, 0x6d82, 0x327b, 0x0e5f, 0x3077, + 0x4668, 0x4243, 0x5830, 0x08c9, 0x4257, 0x7531, 0x0762, 0x28b2, 0x6fa7, 0x5aa0, + 0x2bac, 0x0a13, 0x10bb, 0x2758, 0x54c4, 0x09c0, 0x3fcb, 0x20ca, 0x1740, 0x17da, + 0x0b8b, 0x6116, 0x2a00, 0x00cb, 0x365f, 0x2f13, 0x60bf, 0x6fdc, 0x3cbf, 0x1ce6, + 0x139a, 0x1e3d, 0x7fdd, 0x0fce, 0x07dc, 0x6eba, 0x5181, 0x6906, 0x466c, 0x00cf, + 0x7fe1, 0x3bd2, 0x01c0, 0x03df, 0x542a, 0x15c3, 0x246d, 0x7353, 0x0edd, 0x557e, + 0x5a2d, 0x02ad, 0x2b16, 0x4f13, 0x074e, 0x3bce, 0x04cc, 0x4053, 0x1a56, 0x27af, + 0x4be2, 0x40bc, 0x095b, 0x3ef3, 0x01bc, 0x36eb, 0x1ddc, 0x5b68, 0x223a, 0x73e4, + 0x29a3, 0x237c, 0x77be, 0x2a4d, 0x2321, 0x4759, 0x66ee, 0x6155, 0x72b7, 0x526e, + 0x4e22, 0x47b5, 0x7a84, 0x4acd, 0x69f3, 0x601e, 0x2f44, 0x03db, 0x2526, 0x201b, + 0x7474, 0x005b, 0x2b3a, 0x47d8, 0x0ad3, 0x7db5, 0x5426, 0x3255, 0x19c9, 0x7b27, + 0x62b3, 0x7913, 0x388e, 0x78ab, 0x588f, 0x38b1, 0x1884, 0x4d06, 0x127e, 0x4498, + 0x0340, 0x68a9, 0x6cff, 0x5718, 0x4554, 0x240e, 0x460a, 0x2d43, 0x443c, 0x15bf, + 0x4846, 0x6369, 0x3306, 0x6964, 0x16d7, 0x651e, 0x06a3, 0x55ed, 0x2469, 0x0e4b, + 0x5eae, 0x19a5, 0x314d, 0x7214, 0x67f3, 0x34d4, 0x1fc2, 0x1f13, 0x6ce8, 0x2a90, + 0x5e50, 0x1c6d, 0x4a14, 0x2327, 0x449e, 0x4c56, 0x7185, 0x110e, 0x74d1, 0x057e, + 0x4da5, 0x734f, 0x58d2, 0x6400, 0x3dfd, 0x56c3, 0x3710, 0x7b4d, 0x22f0, 0x4ccf, + 0x0ed9, 0x74e4, 0x3b93, 0x7b3a, 0x41a9, 0x0f93, 0x3436, 0x7b84, 0x4722, 0x731c, + 0x287b, 0x4971, 0x7b99, 0x0b9e, 0x5370, 0x4f86, 0x3fe0, 0x6ecd, 0x5fe3, 0x6a06, + 0x6442, 0x5b2d, 0x6d6e, 0x557a, 0x1545, 0x4303, 0x705d, 0x0a48, 0x76c5, 0x7631, + 0x383d, 0x2680, 0x5a29, 0x6b39, 0x144c, 0x1ec9, 0x622c, 0x648c, 0x5991, 0x65cc, + 0x5c55, 0x50b6, 0x4c50, 0x1840, 0x37d8, 0x25a9, 0x3e49, 0x5927, 0x6773, 0x21d0, + 0x2ed8, 0x3747, 0x4cbc, 0x303c, 0x2e5b, 0x02a9, 0x7d90, 0x51fa, 0x6e4a, 0x050e, + 0x0d95, 0x60e3, 0x0cc6, 0x1b97, 0x2b12, 0x3125, 0x4c94, 0x7b71, 0x0cee, 0x42c8, + 0x1688, 0x4a65, 0x4bba, 0x5159, 0x5f44, 0x1093, 0x48a2, 0x717f, 0x1d82, 0x2fd8, + 0x6a7c, 0x35dc, 0x75f6, 0x5bc9, 0x3474, 0x04d3, 0x55f4, 0x4f0f, 0x11af, 0x658c, + 0x1621, 0x204f, 0x1bbf, 0x1670, 0x341e, 0x67db, 0x074a, 0x4654, 0x7aec, 0x31ab, + 0x6242, 0x2d08, 0x49be, 0x7a13, 0x2caf, 0x3f8a, 0x6bb0, 0x2be1, 0x0ca1, 0x1375, + 0x7f8b, 0x14f3, 0x535b, 0x070c, 0x0e10, 0x7285, 0x0f80, 0x7314, 0x7946, 0x3bca, + 0x6c32, 0x018b, 0x6cb3, 0x73a4, 0x14ad, 0x7139, 0x71fe, 0x751b, 0x04c8, 0x42bd, + 0x2cfd, 0x7309, 0x0573, 0x7209, 0x7908, 0x2d38, 0x5b22, 0x0f88, 0x6481, 0x3031, + 0x31ea, 0x37a6, 0x6859, 0x6ae0, 0x2aa7, 0x7244, 0x7109, 0x3a5c, 0x610b, 0x7526, + 0x581c, 0x404f, 0x4f08, 0x68fb, 0x73d9, 0x6013, 0x2108, 0x19ef, 0x2818, 0x43c8, + 0x1a52, 0x18f9, 0x1058, 0x2080, 0x2295, 0x7144, 0x1bd7, 0x2613, 0x7d5c, 0x76f6, + 0x1108, 0x3d2d, 0x0dbd, 0x2e08, 0x0908, 0x2dd6, 0x3976, 0x7888, 0x3f4f, 0x7561, + 0x495e, 0x14b8, 0x1d32, 0x27ab, 0x0148, 0x5a5d, 0x46aa, 0x0f10, 0x6928, 0x05de, + 0x3632, 0x6da5, 0x4bde, 0x52e3, 0x1762, 0x182d, 0x61d5, 0x1be2, 0x6886, 0x3aa2, + 0x18c7, 0x1713, 0x5d72, 0x0a6b, 0x21b7, 0x74cb, 0x0e32, 0x56ff, 0x722b, 0x18e0, + 0x310c, 0x06f3, 0x71a7, 0x261e, 0x1ae1, 0x40b8, 0x3396, 0x422a, 0x0fb5, 0x479c, + 0x63c4, 0x0af9, 0x7723, 0x11a8, 0x0957, 0x172c, 0x0c38, 0x6f3c, 0x2845, 0x714f, + 0x2cc7, 0x6c7d, 0x7c3f, 0x6626, 0x59f7, 0x2b76, 0x48ca, 0x69a0, 0x7672, 0x2c5c, + 0x0aad, 0x33f8, 0x0935, 0x6697, 0x50a3, 0x22a0, 0x26d1, 0x3eef, 0x5a8a, 0x1efd, + 0x6610, 0x55b1, 0x553e, 0x3c6f, 0x7fb0, 0x5642, 0x01b8, 0x5d8b, 0x2873, 0x6479, + 0x1daa, 0x7701, 0x3fa2, 0x6d59, 0x66c5, 0x0193, 0x7fb8, 0x2aed, 0x344b, 0x4b91, + 0x0721, 0x0c78, 0x20df, 0x394d, 0x4edf, 0x054a, 0x6203, 0x7d67, 0x3fb7, 0x36e7, + 0x1255, 0x53fd, 0x1f99, 0x74a8, 0x3000, 0x1b48, 0x69c6, 0x2928, 0x1dd8, 0x17df, + 0x5186, 0x4ad2, 0x62b8, 0x1113, 0x41ae, 0x374c, 0x0cf3, 0x728a, 0x0578, 0x7566, + 0x61da, 0x669c, 0x1daf, 0x61b1, 0x639b, 0x0a84, 0x336d, 0x61ac, 0x65b9, 0x3d38, + 0x62e6, 0x5b64, 0x70c9, 0x7791, 0x098d, 0x1e8d, 0x5b8d, 0x7ddb, 0x036d, 0x3230, + 0x2236, 0x1bfb, 0x75b4, 0x266d, 0x6396, 0x1904, 0x6bc8, 0x7094, 0x6bfd, 0x46ed, + 0x6f72, 0x24be, 0x5bf1, 0x4d9f, 0x3b14, 0x4b33, 0x630f, 0x504a, 0x38de, 0x2c99, + 0x7ecd, 0x1a5d, 0x6525, 0x73e0, 0x0b00, 0x7cf0, 0x7bfe, 0x428c, 0x06d0, 0x37ec, + 0x3557, 0x4d1a, 0x299f, 0x10a7, 0x5787, 0x0dd1, 0x65e2, 0x1063, 0x1f40, 0x6702, + 0x6325, 0x3598, 0x0beb, 0x084f, 0x0656, 0x24d2, 0x1c3a, 0x4344, 0x642d, 0x61ee, + 0x59d5, 0x0a7f, 0x761e, 0x208b, 0x7e79, 0x2378, 0x3d61, 0x07ab, 0x4a41, 0x79d7, + 0x6aa4, 0x2144, 0x48f0, 0x6e6f, 0x77ba, 0x7e00, 0x3e76, 0x6b26, 0x3368, 0x2823, + 0x2e9a, 0x1793, 0x5227, 0x6c3a, 0x564a, 0x157c, 0x70f2, 0x453d, 0x50e3, 0x4381, + 0x3164, 0x3abb, 0x30f5, 0x30de, 0x386a, 0x43d3, 0x54b0, 0x2a49, 0x6585, 0x4113, + 0x7ce9, 0x0f4c, 0x2dcc, 0x6ad6, 0x2fce, 0x14e9, 0x231d, 0x689f, 0x4f7c, 0x591d, + 0x3604, 0x19fa, 0x2bf9, 0x024f, 0x09b6, 0x12d0, 0x7349, 0x5264, 0x4330, 0x4377, + 0x4b29, 0x433a, 0x2c52, 0x56f5, 0x0c6e, 0x61a7, 0x1eb6, 0x2113, 0x539d, 0x4755, + 0x36a0, 0x604f, 0x23cc, 0x3cf1, 0x07ff, 0x7000, 0x1518, 0x783d, 0x66ea, 0x177b, + 0x3a8a, 0x3734, 0x65b4, 0x7893, 0x2d20, 0x79fb, 0x05a1, 0x5fb6, 0x797b, 0x2364, + 0x3a0b, 0x58cc, 0x22c3, 0x53a7, 0x3e99, 0x046f, 0x0b2f, 0x3529, 0x758f, 0x3981, + 0x3b6e, 0x6151, 0x4d49, 0x417c, 0x39c9, 0x5e0e, 0x11d7, 0x7bf7, 0x7698, 0x161a, + 0x72b3, 0x29ec, 0x5d2a, 0x586b, 0x59a7, 0x3f5a, 0x33c5, 0x7855, 0x5060, 0x5964, + 0x6270, 0x4fbd, 0x0d1c, 0x2d84, 0x6e1d, 0x475f, 0x1284, 0x1846, 0x48a8, 0x3d33, + 0x21bd, 0x756c, 0x5bf7, 0x526a, 0x3a11, 0x6746, 0x2957, 0x021d, 0x5f92, 0x5135, + 0x243c, 0x2f8d, 0x4e1e, 0x1aad, 0x57e8, 0x2596, 0x62e1, 0x0913, 0x1240, 0x1530, + 0x116b, 0x6cbb, 0x3c77, 0x13ff, 0x1b21, 0x6979, 0x4d78, 0x211d, 0x34eb, 0x4bf7, + 0x317b, 0x7bd0, 0x1649, 0x2de1, 0x0b77, 0x47b1, 0x113c, 0x5e23, 0x2731, 0x4095, + 0x3775, 0x47fe, 0x45dd, 0x57b9, 0x7a80, 0x52fc, 0x3db5, 0x1333, 0x6b9a, 0x2e13, + 0x625a, 0x0bd5, 0x1939, 0x0313, 0x63fa, 0x6740, 0x2077, 0x31a2, 0x199c, 0x1ec0, + 0x41d7, 0x44ea, 0x08c0, 0x5b5f, 0x5914, 0x0dc8, 0x6f33, 0x4ac9, 0x132a, 0x5862, + 0x6a34, 0x28e7, 0x4f37, 0x3512, 0x2c82, 0x0533, 0x69ef, 0x5bb2, 0x5ed2, 0x0296, + 0x70c4, 0x1d3d, 0x43f7, 0x7018, 0x5d4e, 0x7abf, 0x5cdc, 0x0797, 0x2951, 0x3df7, + 0x7f5e, 0x6059, 0x680a, 0x6dbe, 0x3ad2, 0x5482, 0x4147, 0x27b6, 0x16de, 0x601a, + 0x63cb, 0x0f53, 0x11de, 0x401e, 0x4207, 0x4e8a, 0x4875, 0x7a51, 0x2f40, 0x364b, + 0x64d0, 0x6a3d, 0x561c, 0x14c3, 0x7a2b, 0x7817, 0x1b71, 0x55c7, 0x54f1, 0x1e17, + 0x0e8c, 0x741f, 0x5f17, 0x36aa, 0x7048, 0x1f84, 0x26af, 0x778c, 0x3029, 0x4969, + 0x5acd, 0x03d7, 0x1574, 0x2ae5, 0x13f7, 0x008f, 0x1e01, 0x2b60, 0x2a7a, 0x09fd, + 0x2522, 0x05f7, 0x39a6, 0x51e7, 0x0988, 0x5a68, 0x49d6, 0x1017, 0x4a95, 0x73ac, + 0x5546, 0x0097, 0x349c, 0x4c1e, 0x78db, 0x23d6, 0x1808, 0x2571, 0x72e4, 0x4f4e, + 0x67a3, 0x0153, 0x60ab, 0x2017, 0x2048, 0x1480, 0x4285, 0x4017, 0x4080, 0x52ab, + 0x2268, 0x6f04, 0x7470, 0x3c37, 0x7759, 0x28f0, 0x4afb, 0x46b5, 0x249a, 0x0817, + 0x30a6, 0x27eb, 0x56bd, 0x0217, 0x79c3, 0x0437, 0x3f22, 0x3cfb, 0x13c7, 0x6941, + 0x02db, 0x1e88, 0x04fb, 0x0f1b, 0x4e52, 0x0057, 0x51af, 0x011b, 0x3bff, 0x001f, + 0x00f1, 0x1e5e, 0x6eda, 0x468b, 0x2b36, 0x0fed, 0x3fed, 0x2547, 0x5b88, 0x076d, + 0x5ff0, 0x3dcd, 0x493f, 0x3680, 0x6a13, 0x559d, 0x5f8c, 0x370a, 0x537d, 0x3957, + 0x2d5a, 0x5240, 0x4f93, 0x29c2, 0x44c0, 0x28bd, 0x0bab, 0x47d4, 0x08e9, 0x2f63, + 0x7ba6, 0x5df9, 0x4200, 0x06c9, 0x6d7b, 0x1bb8, 0x0acf, 0x6c53, 0x5587, 0x33ce, + 0x17b5, 0x753c, 0x5b3a, 0x6672, 0x7d3d, 0x0c4e, 0x644f, 0x0169, 0x12a6, 0x58f3, + 0x4310, 0x20e9, 0x4513, 0x2a1f, 0x1552, 0x7dd6, 0x5020, 0x4262, 0x706a, 0x7db1, + 0x1039, 0x4cf0, 0x0a55, 0x0781, 0x3494, 0x1b19, 0x3443, 0x70ea, 0x5422, 0x5663, + 0x7b91, 0x31e2, 0x0368, 0x5aab, 0x0fa0, 0x32f1, 0x31cb, 0x35e4, 0x41b6, 0x6dc6, + 0x3ba8, 0x6e90, 0x7329, 0x4ee9, 0x612b, 0x03b1, 0x472f, 0x36c1, 0x20a0, 0x6fb2, + 0x2888, 0x3251, 0x57fd, 0x3d82, 0x497e, 0x40f0, 0x68d1, 0x3a32, 0x74f1, 0x71df, + 0x19c5, 0x25e9, 0x0ee6, 0x785e, 0x462a, 0x2bb7, 0x3ba0, 0x134b, 0x04a9, 0x2fae, + 0x7b47, 0x512f, 0x1ee9, 0x197b, 0x22fd, 0x0554, 0x446e, 0x1595, 0x4cdc, 0x322b, + 0x6ea3, 0x0a1e, 0x7b5a, 0x7b23, 0x6462, 0x2656, 0x371d, 0x51d0, 0x4af2, 0x5613, + 0x599e, 0x6b91, 0x62af, 0x283c, 0x65d9, 0x35fb, 0x2231, 0x424e, 0x6499, 0x16a7, + 0x6223, 0x3144, 0x6239, 0x228c, 0x2436, 0x22ea, 0x50c3, 0x072b, 0x6f5c, 0x7965, + 0x5c62, 0x5f2e, 0x71c7, 0x4673, 0x4c5d, 0x790f, 0x33ff, 0x2ea1, 0x184d, 0x49dd, + 0x2a26, 0x2eec, 0x6b46, 0x7927, 0x388a, 0x42dc, 0x5a36, 0x3f63, 0x593d, 0x583b, + 0x1459, 0x7a98, 0x6c13, 0x7cc9, 0x1ed6, 0x7263, 0x7eed, 0x1918, 0x384a, 0x0c82, + 0x4831, 0x3381, 0x268d, 0x1bf6, 0x457c, 0x08d4, 0x763e, 0x78a7, 0x0df1, 0x1d51, + 0x76d2, 0x02ef, 0x6d27, 0x53ce, 0x21dd, 0x4c75, 0x588b, 0x4dc4, 0x6780, 0x32bf, + 0x75af, 0x0e6a, 0x2ee5, 0x5314, 0x0d76, 0x6a84, 0x3754, 0x6812, 0x1353, 0x445b, + 0x3e56, 0x4b9b, 0x4621, 0x17ac, 0x5934, 0x5737, 0x5c31, 0x3082, 0x25b6, 0x38ad, + 0x1669, 0x3274, 0x37e5, 0x4e83, 0x4167, 0x6b58, 0x2e68, 0x1d63, 0x1880, 0x2eb3, + 0x02b6, 0x59b0, 0x5740, 0x3286, 0x3049, 0x21ef, 0x6a5d, 0x2e7a, 0x4cc9, 0x2f87, + 0x3edb, 0x3d94, 0x5207, 0x3455, 0x67c3, 0x7503, 0x7d9d, 0x2668, 0x2910, 0x6d8d, + 0x6e57, 0x4d02, 0x1602, 0x2f75, 0x051b, 0x6eec, 0x13bd, 0x17fe, 0x6800, 0x703e, + 0x127a, 0x3e8f, 0x34e1, 0x41cd, 0x6391, 0x20d5, 0x7221, 0x0aa3, 0x6423, 0x6305, + 0x315a, 0x2c48, 0x4e18, 0x0ed3, 0x1f20, 0x3fc1, 0x4913, 0x5daa, 0x1fcf, 0x7436, + 0x6769, 0x3fd6, 0x6cf5, 0x4494, 0x5351, 0x6a72, 0x2a9d, 0x396c, 0x486e, 0x3550, + 0x0e58, 0x3417, 0x033c, 0x26f0, 0x2476, 0x596d, 0x3e5f, 0x174b, 0x5ebb, 0x57d1, + 0x4703, 0x5767, 0x19b2, 0x2ce6, 0x1962, 0x1b00, 0x06b0, 0x36f1, 0x4442, 0x6b3f, + 0x55fa, 0x18ff, 0x1ae7, 0x17e5, 0x652b, 0x68a5, 0x3b74, 0x5302, 0x16e4, 0x3c3d, + 0x0bb1, 0x25ef, 0x4c63, 0x2eb9, 0x6cfb, 0x7e1f, 0x44ab, 0x061c, 0x6bc3, 0x54cf, + 0x7192, 0x45f5, 0x37b9, 0x75fe, 0x111b, 0x3ada, 0x2bbf, 0x68be, 0x4a21, 0x7d71, + 0x15d6, 0x77d3, 0x2334, 0x2c18, 0x332e, 0x09cb, 0x1c7a, 0x5714, 0x2e3c, 0x53bc, + 0x5e5d, 0x30bb, 0x0bfe, 0x6544, 0x4db2, 0x381e, 0x4550, 0x7e19, 0x735c, 0x5069, + 0x4ba4, 0x2763, 0x058b, 0x6be7, 0x5a0a, 0x1c1a, 0x74de, 0x1aa7, 0x698c, 0x0b56, + 0x640d, 0x620d, 0x6283, 0x7e98, 0x58df, 0x708f, 0x5504, 0x10c6, 0x3e0a, 0x240a, + 0x4c31, 0x32ad, 0x56d0, 0x1f5f, 0x15e7, 0x3ec0, 0x5725, 0x1865, 0x4606, 0x5c16, + 0x6d0c, 0x0d5b, 0x6bf8, 0x2a0b, 0x4561, 0x4816, 0x6294, 0x2216, 0x241b, 0x33e4, + 0x57e2, 0x3b8d, 0x034d, 0x5407, 0x4453, 0x6e88, 0x68b6, 0x048e, 0x4924, 0x00d6, + 0x44a5, 0x2d3f, 0x0ab4, 0x179a, 0x128b, 0x101e, 0x451a, 0x531b, 0x4449, 0x441d, + 0x4438, 0x4909, 0x15cc, 0x6279, 0x4464, 0x6121, 0x2d50, 0x4509, 0x4827, 0x6f52, + 0x4617, 0x67b9, 0x5336, 0x4dfd, 0x6376, 0x125f, 0x4427, 0x1acc, 0x4853, 0x46e8, + 0x379e, 0x0b96, 0x3313, 0x15bb, 0x4535, 0x4b89, 0x6971, 0x4c16, 0x7aa9, 0x16fd, + 0x389b, 0x5e8f, 0x4842, 0x215d, 0x78b8, 0x6ddd, 0x6f6d, 0x2f1e, 0x7920, 0x4416, + 0x16b8, 0x5bd1, 0x62c0, 0x548a, 0x4632, 0x0496, 0x38be, 0x1fa3, 0x5325, 0x5aec, + 0x589c, 0x0ea3, 0x2200, 0x366a, 0x1891, 0x6365, 0x67d4, 0x3070, 0x4d13, 0x7a4a, + 0x613c, 0x1ccd, 0x3262, 0x49f5, 0x3302, 0x0616, 0x5433, 0x4fc6, 0x135c, 0x60ca, + 0x19d6, 0x378d, 0x447f, 0x6505, 0x7b34, 0x2590, 0x6683, 0x3c56, 0x0ae0, 0x74b2, + 0x4524, 0x6abd, 0x7dc2, 0x24b9, 0x2d6b, 0x6fe7, 0x47e5, 0x6960, 0x3dde, 0x4e71, + 0x2b47, 0x041e, 0x4e47, 0x248f, 0x49cb, 0x60a0, 0x16d3, 0x43ec, 0x7a20, 0x5ac2, + 0x5bec, 0x33ba, 0x2d15, 0x3b63, 0x0b6c, 0x1235, 0x624f, 0x6f28, 0x62db, 0x41a3, + 0x3f97, 0x3fac, 0x1ad6, 0x687b, 0x2cbc, 0x26c6, 0x7e6e, 0x1f35, 0x6bbd, 0x651a, + 0x54a5, 0x2e8f, 0x2bee, 0x5392, 0x2f39, 0x2998, 0x4661, 0x0743, 0x069f, 0x6648, + 0x0757, 0x29f5, 0x25bf, 0x5105, 0x7af9, 0x1951, 0x6f88, 0x4ebf, 0x31b8, 0x35ba, + 0x2e50, 0x5986, 0x342b, 0x6d63, 0x4431, 0x3883, 0x67e8, 0x4d9a, 0x793b, 0x49b3, + 0x167d, 0x55e9, 0x5811, 0x78fd, 0x1bcc, 0x1d27, 0x78d3, 0x4d70, 0x0719, 0x50db, + 0x2465, 0x3b39, 0x5368, 0x6851, 0x3b0f, 0x7f3c, 0x0e1d, 0x032b, 0x6df8, 0x1d8a, + 0x7292, 0x7f66, 0x2fb6, 0x0355, 0x7f98, 0x770b, 0x485d, 0x2a62, 0x1500, 0x45c5, + 0x2178, 0x12e5, 0x1382, 0x0e47, 0x068b, 0x22d8, 0x0cae, 0x2800, 0x0898, 0x2709, + 0x7953, 0x5d02, 0x5eaa, 0x54c9, 0x3bd7, 0x72bc, 0x38b6, 0x4a19, 0x7321, 0x3e4e, + 0x515e, 0x7f90, 0x0f8d, 0x090d, 0x1718, 0x7677, 0x0198, 0x1db4, 0x46f2, 0x1c3f, + 0x6c3f, 0x4b2e, 0x5fbb, 0x6e22, 0x6cc0, 0x19a1, 0x7ac4, 0x5f1c, 0x73b1, 0x3f27, + 0x3685, 0x4315, 0x35e9, 0x2302, 0x3149, 0x384f, 0x6a89, 0x520c, 0x630a, 0x06b5, + 0x7603, 0x6412, 0x221b, 0x637b, 0x5bd6, 0x0ae5, 0x123a, 0x3430, 0x1d8f, 0x019d, + 0x6380, 0x659e, 0x2fe5, 0x0cd8, 0x18ac, 0x690d, 0x718c, 0x7210, 0x093c, 0x282a, + 0x48af, 0x5a6f, 0x1559, 0x0e71, 0x5601, 0x2f25, 0x67ef, 0x412c, 0x4f1c, 0x5d33, + 0x308b, 0x4065, 0x04e0, 0x13ac, 0x2507, 0x096d, 0x3481, 0x202d, 0x4d2e, 0x39f0, + 0x6599, 0x66cf, 0x1269, 0x21a2, 0x11bc, 0x5045, 0x1150, 0x5f77, 0x162e, 0x34d0, + 0x7a65, 0x6b7f, 0x205c, 0x130f, 0x391b, 0x3569, 0x1695, 0x07bd, 0x1fbe, 0x7d02, + 0x4a72, 0x037f, 0x38d9, 0x4125, 0x42d5, 0x4902, 0x3ca0, 0x2fe0, 0x0cfb, 0x6061, + 0x04b1, 0x540f, 0x5166, 0x7fc2, 0x4e07, 0x69d8, 0x4bc7, 0x77a3, 0x1428, 0x7735, + 0x5f51, 0x1f0f, 0x464d, 0x423c, 0x10a0, 0x3644, 0x58b7, 0x5e35, 0x3132, 0x244e, + 0x6ce4, 0x45ef, 0x2b1f, 0x5874, 0x5c3a, 0x76aa, 0x4ca1, 0x6758, 0x0ebe, 0x418e, + 0x7b7e, 0x152a, 0x1194, 0x4887, 0x0cd3, 0x2af7, 0x5340, 0x0f65, 0x1ba4, 0x2c94, + 0x5b07, 0x1492, 0x60f0, 0x2a8c, 0x1a37, 0x227a, 0x0da2, 0x012d, 0x544e, 0x6178, + 0x7251, 0x5c7a, 0x5e4c, 0x4b58, 0x2ab4, 0x5c9b, 0x7ec8, 0x665e, 0x7116, 0x2e2b, + 0x4fe1, 0x18a7, 0x3a69, 0x6c69, 0x1165, 0x471c, 0x6866, 0x5d95, 0x331d, 0x439a, + 0x6aed, 0x7c77, 0x0631, 0x799e, 0x37b3, 0x1c69, 0x4591, 0x7cb7, 0x31f7, 0x045a, + 0x64c9, 0x5780, 0x5829, 0x7ae5, 0x4a10, 0x50fc, 0x405c, 0x76a1, 0x327d, 0x5832, + 0x7533, 0x2bae, 0x275a, 0x1742, 0x6118, 0x60c1, 0x1ce8, 0x07de, 0x6908, 0x01c2, + 0x15c5, 0x5a2f, 0x4f15, 0x1a58, 0x40be, 0x1dde, 0x73e6, 0x2323, 0x6157, 0x7a86, + 0x6020, 0x7476, 0x47da, 0x19cb, 0x7915, 0x1886, 0x449a, 0x4556, 0x2d45, 0x3308, + 0x6520, 0x5eb0, 0x7216, 0x6cea, 0x1c6f, 0x7187, 0x0580, 0x3dff, 0x7b4f, 0x3b95, + 0x0f95, 0x287d, 0x0ba0, 0x5fe5, 0x5b2f, 0x705f, 0x7633, 0x144e, 0x648e, 0x4c52, + 0x25ab, 0x2eda, 0x303e, 0x6e4c, 0x60e5, 0x4c96, 0x42ca, 0x5f46, 0x7181, 0x75f8, + 0x04d5, 0x1623, 0x1672, 0x7aee, 0x2d0a, 0x6bb2, 0x1377, 0x0e12, 0x7316, 0x6cb5, + 0x713b, 0x2cff, 0x720b, 0x6483, 0x37a8, 0x710b, 0x7528, 0x73db, 0x19f1, 0x105a, + 0x7146, 0x110a, 0x2e0a, 0x3f51, 0x14ba, 0x46ac, 0x05e0, 0x1764, 0x1be4, 0x5d74, + 0x74cd, 0x310e, 0x2620, 0x0fb7, 0x0afb, 0x0c3a, 0x7151, 0x59f9, 0x69a2, 0x0937, + 0x22a2, 0x6612, 0x3c71, 0x2875, 0x7703, 0x7fba, 0x4b93, 0x4ee1, 0x7d69, 0x1f9b, + 0x1b4a, 0x5188, 0x1115, 0x057a, 0x669e, 0x336f, 0x3d3a, 0x098f, 0x7ddd, 0x75b6, + 0x1906, 0x6f74, 0x4da1, 0x38e0, 0x1a5f, 0x7c00, 0x37ee, 0x5789, 0x1065, 0x0bed, + 0x24d4, 0x59d7, 0x208d, 0x4a43, 0x2146, 0x3e78, 0x2825, 0x564c, 0x453f, 0x30f7, + 0x43d5, 0x7ceb, 0x6ad8, 0x4f7e, 0x19fc, 0x734b, 0x4379, 0x0c70, 0x2115, 0x23ce, + 0x7002, 0x3a8c, 0x7895, 0x797d, 0x58ce, 0x0b31, 0x3983, 0x39cb, 0x7bf9, 0x5d2c, + 0x3f5c, 0x6272, 0x2d86, 0x48aa, 0x756e, 0x2959, 0x5137, 0x57ea, 0x0915, 0x3c79, + 0x697b, 0x317d, 0x2de3, 0x2733, 0x4800, 0x3db7, 0x2e15, 0x63fc, 0x31a4, 0x08c2, + 0x0dca, 0x6a36, 0x3514, 0x5ed4, 0x1d3f, 0x5cde, 0x3df9, 0x3ad4, 0x27b8, 0x11e0, + 0x4e8c, 0x64d2, 0x14c5, 0x54f3, 0x7421, 0x26b1, 0x496b, 0x13f9, 0x2b62, 0x39a8, + 0x5a6a, 0x5548, 0x4c20, 0x72e6, 0x0155, 0x4287, 0x52ad, 0x775b, 0x46b7, 0x56bf, + 0x0439, 0x02dd, 0x0f1d, 0x3c01, 0x1e60, 0x3fef, 0x076f, 0x6a15, 0x370c, 0x4f95, + 0x28bf, 0x7ba8, 0x06cb, 0x5589, 0x753e, 0x6451, 0x58f5, 0x1554, 0x4264, 0x0a57, + 0x1b1b, 0x7b93, 0x5aad, 0x41b8, 0x6e92, 0x4731, 0x6fb4, 0x4980, 0x3a34, 0x0ee8, + 0x2bb9, 0x7b49, 0x197d, 0x4cde, 0x0a20, 0x371f, 0x5615, 0x65db, 0x4250, 0x623b, + 0x22ec, 0x5c64, 0x4675, 0x184f, 0x2eee, 0x5a38, 0x583d, 0x1ed8, 0x191a, 0x268f, + 0x08d6, 0x76d4, 0x53d0, 0x6782, 0x0e6c, 0x3756, 0x445d, 0x5936, 0x3084, 0x37e7, + 0x6b5a, 0x02b8, 0x3288, 0x4ccb, 0x3d96, 0x7d9f, 0x6d8f, 0x051d, 0x1800, 0x34e3, + 0x20d7, 0x315c, 0x0ed5, 0x1fd1, 0x3fd8, 0x2a9f, 0x3552, 0x2478, 0x174d, 0x19b4, + 0x1b02, 0x55fc, 0x17e7, 0x16e6, 0x25f1, 0x44ad, 0x54d1, 0x111d, 0x68c0, 0x2336, + 0x09cd, 0x5e5f, 0x6546, 0x735e, 0x2765, 0x74e0, 0x0b58, 0x58e1, 0x10c8, 0x56d2, + 0x3ec2, 0x6d0e, 0x2a0d, 0x241d, 0x3b8f, 0x68b8, 0x00d8, 0x128d, 0x531d, 0x15ce, + 0x6123, 0x4619, 0x4dff, 0x4855, 0x0b98, 0x6973, 0x16ff, 0x78ba, 0x2f20, 0x62c2, + 0x0498, 0x589e, 0x366c, 0x4d15, 0x1ccf, 0x5435, 0x60cc, 0x7b36, 0x3c58, 0x7dc4, + 0x6fe9, 0x2b49, 0x2491, 0x7a22, 0x33bc, 0x6251, 0x41a5, 0x2cbe, 0x1f37, 0x2bf0, + 0x299a, 0x0759, 0x5107, 0x31ba, 0x5988, 0x67ea, 0x49b5, 0x1bce, 0x4d72, 0x536a, + 0x7f3e, 0x7294, 0x0357, 0x1502, 0x12e7, 0x0cb0, 0x270b, 0x3bd9, 0x4a1b, 0x0f8f, + 0x7679, 0x6c41, 0x6e24, 0x73b3, 0x4317, 0x6a8b, 0x06b7, 0x5bd8, 0x3432, 0x2fe7, + 0x690f, 0x48b1, 0x0e73, 0x4f1e, 0x4067, 0x3483, 0x39f2, 0x11be, 0x5f79, 0x205e, + 0x356b, 0x4a74, 0x4127, 0x0cfd, 0x5411, 0x4bc9, 0x7737, 0x10a2, 0x5e37, 0x2b21, + 0x76ac, 0x7b80, 0x4889, 0x1ba6, 0x1494, 0x0da4, 0x617a, 0x2ab6, 0x6660, 0x3a6b, + 0x471e, 0x6aef, 0x79a0, 0x31f9, 0x5782, 0x405e, 0x5834, 0x611a, 0x07e0, 0x4f17, + 0x1de0, 0x6022, 0x19cd, 0x2d47, 0x5eb2, 0x0582, 0x3b97, 0x5b31, 0x1450, 0x3040, + 0x4c98, 0x04d7, 0x7af0, 0x7318, 0x2d01, 0x752a, 0x105c, 0x14bc, 0x1766, 0x2622, + 0x0c3c, 0x22a4, 0x2877, 0x7d6b, 0x518a, 0x3d3c, 0x75b8, 0x1a61, 0x578b, 0x208f, + 0x3e7a, 0x43d7, 0x4f80, 0x2117, 0x3a8e, 0x3985, 0x5d2e, 0x7570, 0x57ec, 0x2de5, + 0x3db9, 0x0dcc, 0x5ed6, 0x27ba, 0x64d4, 0x496d, 0x39aa, 0x0157, 0x775d, 0x0f1f, + 0x3ff1, 0x28c1, 0x558b, 0x4266, 0x7b95, 0x6fb6, 0x0eea, 0x0a22, 0x65dd, 0x4677, + 0x5a3a, 0x08d8, 0x6784, 0x3086, 0x02ba, 0x6d91, 0x34e5, 0x3fda, 0x247a, 0x17e9, + 0x44af, 0x09cf, 0x7360, 0x10ca, 0x6d10, 0x00da, 0x15d0, 0x0b9a, 0x78bc, 0x366e, + 0x5437, 0x6feb, 0x7a24, 0x1f39, 0x075b, 0x49b7, 0x536c, 0x12e9, 0x3bdb, 0x6e26, + 0x6a8d, 0x6911, 0x4f20, 0x5f7b, 0x4a76, 0x7739, 0x2b23, 0x1496, 0x2ab8, 0x79a2, + 0x4060, 0x1de2, 0x2d49, 0x1452, 0x04d9, 0x105e, 0x2624, 0x518c, 0x1a63, 0x4f82, + 0x3987, 0x3dbb, 0x27bc, 0x775f, 0x28c3, 0x0eec, 0x4679, 0x02bc, 0x3fdc, 0x7362, + 0x00dc, 0x5439, 0x1f3b, 0x3bdd, 0x6913, 0x2b25, 0x79a4, 0x04db, 0x518e, 0x27be, + 0x0eee, 0x00de, 0x3bdf, 0x5190, 0x00e0, 0x7ff0, 0x7fee, 0x03ee, 0x238d, 0x7fec, + 0x01cd, 0x6ec9, 0x4e33, 0x1e4c, 0x03ec, 0x3cce, 0x1fea, 0x238b, 0x0fdb, 0x2249, + 0x5fdf, 0x77cd, 0x7fea, 0x4768, 0x0d7f, 0x01cb, 0x07e9, 0x5b77, 0x7451, 0x3f02, + 0x6ec7, 0x4bf1, 0x05ca, 0x4e31, 0x13a7, 0x4adc, 0x492e, 0x527d, 0x1e4a, 0x66fd, + 0x5dc5, 0x03ea, 0x1cf3, 0x6a02, 0x569e, 0x2535, 0x3ccc, 0x006a, 0x6713, 0x1fe8, + 0x29b0, 0x5cbd, 0x643e, 0x1a13, 0x2389, 0x567a, 0x6320, 0x0fd9, 0x73f1, 0x7d2c, + 0x70a5, 0x2502, 0x2247, 0x52cf, 0x3175, 0x5fdd, 0x2a5a, 0x69d0, 0x17a4, 0x03a9, + 0x77cb, 0x5ae4, 0x0260, 0x7fe8, 0x232e, 0x5b29, 0x2c63, 0x522e, 0x4766, 0x4a9c, + 0x20f0, 0x0d7d, 0x36f8, 0x16bf, 0x6d6a, 0x3ca7, 0x01c9, 0x2d8d, 0x375d, 0x07e7, + 0x1de9, 0x41ef, 0x63ac, 0x5526, 0x5b75, 0x2db4, 0x6c8e, 0x744f, 0x0968, 0x2932, + 0x0abe, 0x361d, 0x3f00, 0x3593, 0x723c, 0x6ec5, 0x40c9, 0x5576, 0x3ab3, 0x3945, + 0x4bef, 0x2569, 0x34fc, 0x05c8, 0x47c2, 0x64b1, 0x1541, 0x3903, 0x4e2f, 0x0880, + 0x0be6, 0x13a5, 0x7a91, 0x4502, 0x41e8, 0x347c, 0x4ada, 0x414f, 0x7866, 0x492c, + 0x72c4, 0x1b52, 0x1295, 0x0268, 0x527b, 0x1c9d, 0x3eaa, 0x1e48, 0x6162, 0x42ff, + 0x7a0c, 0x28ab, 0x66fb, 0x7810, 0x7828, 0x5dc3, 0x2f51, 0x5ef8, 0x7059, 0x2c12, + 0x03e8, 0x0d25, 0x681b, 0x1cf1, 0x602b, 0x500f, 0x7029, 0x608b, 0x6a00, 0x7bca, + 0x1819, 0x569c, 0x2028, 0x300a, 0x1028, 0x2788, 0x2533, 0x084a, 0x0828, 0x3cca, + 0x7481, 0x0a44, 0x13d8, 0x5de7, 0x0068, 0x01f0, 0x7377, 0x6711, 0x40de, 0x65f1, + 0x76c1, 0x6334, 0x1fe6, 0x085e, 0x0651, 0x29ae, 0x43a5, 0x0de0, 0x5084, 0x4d29, + 0x5cbb, 0x06df, 0x1643, 0x643c, 0x3b4e, 0x0a8e, 0x456b, 0x4353, 0x1a11, 0x0665, + 0x7e34, 0x2387, 0x3328, 0x762d, 0x7653, 0x3d70, 0x5678, 0x79e6, 0x1b6a, 0x631e, + 0x6fa0, 0x2ca8, 0x3839, 0x4b42, 0x0fd7, 0x5c00, 0x4dcd, 0x73ef, 0x6af8, 0x7edc, + 0x7c20, 0x0b0f, 0x7d2a, 0x429b, 0x655f, 0x70a3, 0x39eb, 0x63a5, 0x4820, 0x6c0c, + 0x2500, 0x24cd, 0x1200, 0x2245, 0x7c82, 0x267c, 0x0c19, 0x323f, 0x52cd, 0x5b9c, + 0x72dc, 0x3173, 0x4ed7, 0x30ed, 0x5a25, 0x4390, 0x5fdb, 0x7101, 0x1c35, 0x2a58, + 0x6871, 0x3879, 0x2198, 0x6594, 0x69ce, 0x0f5b, 0x74f9, 0x17a2, 0x795b, 0x3377, + 0x2a15, 0x5236, 0x03a7, 0x158b, 0x7e8e, 0x77c9, 0x5da0, 0x6b35, 0x1ac2, 0x6e7e, + 0x5ae2, 0x6ab3, 0x277e, 0x025e, 0x039f, 0x3613, 0x1448, 0x09c5, 0x7fe6, 0x5273, + 0x5894, 0x232c, 0x4727, 0x592c, 0x4bbf, 0x14f8, 0x5b27, 0x2ddb, 0x18cc, 0x2c61, + 0x66ca, 0x61b6, 0x6c02, 0x4349, 0x522c, 0x433f, 0x05a6, 0x4764, 0x1170, 0x1ec5, + 0x5d53, 0x36af, 0x4a9a, 0x3d00, 0x4944, 0x20ee, 0x31d0, 0x0559, 0x6228, 0x0c87, + 0x0d7b, 0x345a, 0x6428, 0x36f6, 0x37be, 0x6212, 0x6299, 0x1264, 0x16bd, 0x74b7, + 0x0b71, 0x6d68, 0x6dfd, 0x1db9, 0x2220, 0x66d4, 0x3ca5, 0x2afc, 0x4fe6, 0x01c7, + 0x1c74, 0x6488, 0x69a7, 0x5651, 0x2d8b, 0x554d, 0x58fa, 0x375b, 0x1b07, 0x62c7, + 0x598d, 0x0d02, 0x07e5, 0x7575, 0x6789, 0x1de7, 0x79a9, 0x4ae1, 0x70aa, 0x2937, + 0x41ed, 0x300f, 0x5089, 0x63aa, 0x219d, 0x61bb, 0x629e, 0x61c0, 0x5524, 0x61e9, + 0x7eb3, 0x5b73, 0x063c, 0x65c8, 0x334e, 0x70d8, 0x2db2, 0x1e9c, 0x10e1, 0x6c8c, + 0x32df, 0x2854, 0x5c51, 0x7c4e, 0x744d, 0x2b85, 0x59d0, 0x0966, 0x7cc2, 0x6f4b, + 0x551f, 0x11b7, 0x2930, 0x63d3, 0x71e7, 0x0abc, 0x5d0a, 0x66a6, 0x2425, 0x2c6b, + 0x361b, 0x48d9, 0x3e25, 0x3efe, 0x459c, 0x50b2, 0x3f83, 0x5a99, 0x3591, 0x55c0, + 0x787e, 0x723a, 0x35d2, 0x0702, 0x4c4c, 0x570e, 0x6ec3, 0x21c6, 0x32c8, 0x40c7, + 0x3202, 0x71b6, 0x20c0, 0x33a5, 0x5574, 0x47ab, 0x56eb, 0x3ab1, 0x5040, 0x61e4, + 0x33ee, 0x18d6, 0x3943, 0x0a7a, 0x44e0, 0x4bed, 0x0465, 0x183c, 0x1f7a, 0x6db4, + 0x2567, 0x6937, 0x5293, 0x34fa, 0x511d, 0x7bdf, 0x37d4, 0x212c, 0x05c6, 0x1b30, + 0x7619, 0x47c0, 0x7121, 0x1658, 0x7eae, 0x114b, 0x64af, 0x40a4, 0x1136, 0x153f, + 0x1220, 0x62f0, 0x5c20, 0x117a, 0x3901, 0x140e, 0x7e54, 0x4e2d, 0x2e36, 0x25a5, + 0x3af5, 0x2f9c, 0x087e, 0x5fa1, 0x54ea, 0x0be4, 0x2ba5, 0x6ba9, 0x3e45, 0x1948, + 0x13a3, 0x674f, 0x21e6, 0x7a8f, 0x6669, 0x1342, 0x6bde, 0x57c8, 0x4500, 0x3784, + 0x5006, 0x41e6, 0x5f72, 0x5b6e, 0x4610, 0x1ecf, 0x347a, 0x2086, 0x71ad, 0x4ad8, + 0x7ed3, 0x5923, 0x7595, 0x1339, 0x414d, 0x28f6, 0x44c6, 0x7864, 0x71cd, 0x59b6, + 0x676f, 0x506f, 0x492a, 0x4fcc, 0x7e74, 0x72c2, 0x18b2, 0x587a, 0x0637, 0x1629, + 0x1b50, 0x11e6, 0x3a3a, 0x1293, 0x2711, 0x3d42, 0x6d16, 0x476e, 0x0266, 0x0d2b, + 0x7e3a, 0x5279, 0x4fec, 0x21cc, 0x7e5a, 0x3a20, 0x1c9b, 0x022c, 0x1cb5, 0x3ea8, + 0x25d7, 0x3538, 0x2ed4, 0x53b6, 0x1e46, 0x3a1a, 0x4c7e, 0x6160, 0x3a74, 0x759e, + 0x2980, 0x4d58, 0x42fd, 0x5e1d, 0x260a, 0x7a0a, 0x34cb, 0x65c3, 0x0d65, 0x05b0, + 0x28a9, 0x2373, 0x0246, 0x66f9, 0x6c74, 0x3743, 0x0bcc, 0x784c, 0x780e, 0x080e, + 0x09e6, 0x7826, 0x3219, 0x562b, 0x4cb8, 0x1b80, 0x5dc1, 0x1e26, 0x3d5c, 0x2f4f, + 0x4b63, 0x6a4c, 0x3349, 0x7a60, 0x5ef6, 0x4216, 0x272b, 0x7057, 0x45bd, 0x779b, + 0x572f, 0x36b9, 0x2c10, 0x0e9b, 0x1c95, 0x03e6, 0x5e57, 0x3038, 0x48d1, 0x1583, + 0x0d23, 0x009e, 0x12ad, 0x6819, 0x1969, 0x5491, 0x2e57, 0x6068, 0x1cef, 0x2960, + 0x53d7, 0x6029, 0x2abf, 0x4156, 0x6566, 0x63da, 0x500d, 0x402d, 0x5e78, 0x7027, + 0x6b7a, 0x70d3, 0x186f, 0x5d5d, 0x6089, 0x07a6, 0x3a54, 0x69fe, 0x5ca6, 0x02a5, + 0x30d6, 0x0542, 0x7bc8, 0x4f46, 0x0280, 0x1817, 0x7b11, 0x4f5d, 0x7d8c, 0x23e5, + 0x569a, 0x34ab, 0x4a3c, 0x2026, 0x725c, 0x67b2, 0x2dad, 0x2057, 0x3008, 0x4026, + 0x68d9, 0x1026, 0x08a0, 0x0997, 0x3eca, 0x4aa4, 0x2786, 0x00a6, 0x0d45, 0x2531, + 0x5c85, 0x51f6, 0x2bda, 0x0a0c, 0x0848, 0x1e10, 0x234f, 0x0826, 0x2644, 0x4b0a, + 0x6e46, 0x30b5, 0x3cc8, 0x0226, 0x6d30, 0x747f, 0x6183, 0x28ff, 0x2c33, 0x6f13, + 0x0a42, 0x408f, 0x4788, 0x13d6, 0x130a, 0x1e97, 0x15f1, 0x3d0a, 0x5de5, 0x79d2, + 0x77ee, 0x0066, 0x5459, 0x050a, 0x23ad, 0x51be, 0x01ee, 0x002e, 0x0400, 0x7375, + 0x1f4d, 0x1d06, 0x0d91, 0x1a76, 0x670f, 0x6fc9, 0x6a9f, 0x40dc, 0x1070, 0x1a26, + 0x10dc, 0x3916, 0x65ef, 0x7494, 0x376f, 0x76bf, 0x35a5, 0x2cd1, 0x5af6, 0x6b02, + 0x6332, 0x64e7, 0x1caf, 0x1fe4, 0x0bf8, 0x60df, 0x682d, 0x329b, 0x085c, 0x4fa8, + 0x0e85, 0x064f, 0x10b4, 0x0c9a, 0x0cc2, 0x7c61, 0x29ac, 0x0478, 0x3052, 0x43a3, + 0x5794, 0x1183, 0x75ca, 0x7c95, 0x0dde, 0x6347, 0x5e71, 0x5082, 0x3564, 0x6c87, + 0x532f, 0x7ee6, 0x4d27, 0x213f, 0x2f00, 0x5cb9, 0x37f9, 0x1b93, 0x4992, 0x23f8, + 0x06dd, 0x546c, 0x679b, 0x1641, 0x61fb, 0x3862, 0x2b0e, 0x7d15, 0x643a, 0x6103, + 0x48eb, 0x3b4c, 0x59e2, 0x6cd3, 0x32da, 0x1690, 0x0a8c, 0x43ff, 0x159d, 0x4569, + 0x1c47, 0x7159, 0x58a6, 0x73f9, 0x4351, 0x4b6b, 0x4ddf, 0x1a0f, 0x24df, 0x3121, + 0x0677, 0x0b44, 0x0663, 0x46ca, 0x0d3d, 0x7e32, 0x7e86, 0x3e1d, 0x4c90, 0x653e, + 0x2385, 0x3ea2, 0x21f8, 0x3326, 0x2098, 0x5c29, 0x1420, 0x2170, 0x762b, 0x47f8, + 0x53e9, 0x7651, 0x07b8, 0x284f, 0x0ead, 0x4dd7, 0x3d6e, 0x6e6a, 0x00b8, 0x5676, + 0x4a4e, 0x7b6d, 0x6d42, 0x707d, 0x79e4, 0x1000, 0x309d, 0x1b68, 0x5057, 0x1930, + 0x0cea, 0x7c36, 0x631c, 0x09ad, 0x77b5, 0x6f9e, 0x38eb, 0x3c8f, 0x5c4c, 0x1fb9, + 0x2ca6, 0x7d53, 0x45d7, 0x3837, 0x3b21, 0x6630, 0x38c8, 0x0b19, 0x4b40, 0x75e0, + 0x25d1, 0x0fd5, 0x4dac, 0x42c4, 0x26d8, 0x7e07, 0x5bfe, 0x05fe, 0x7071, 0x4dcb, + 0x6532, 0x2164, 0x1684, 0x7d09, 0x73ed, 0x0b38, 0x328f, 0x6af6, 0x1a6a, 0x390a, + 0x7c89, 0x7c55, 0x7eda, 0x23ec, 0x3800, 0x7c1e, 0x7cfd, 0x7c49, 0x1fad, 0x7c2a, + 0x0b0d, 0x7dfb, 0x2745, 0x7d28, 0x7c0b, 0x4a61, 0x4e9e, 0x380c, 0x4299, 0x1a89, + 0x2c2a, 0x655d, 0x6bd5, 0x2977, 0x4bb6, 0x7c17, 0x70a1, 0x20b7, 0x3e71, 0x39e9, + 0x1911, 0x4df6, 0x7448, 0x4a6d, 0x63a3, 0x7020, 0x4476, 0x481e, 0x46fa, 0x5a01, + 0x04a0, 0x7d34, 0x6c0a, 0x6a54, 0x0eb5, 0x24fe, 0x6f7f, 0x5155, 0x136e, 0x2751, + 0x24cb, 0x7418, 0x594f, 0x11fe, 0x1c08, 0x7f05, 0x5f40, 0x3818, 0x2243, 0x3532, + 0x5749, 0x7c80, 0x75c1, 0x1417, 0x5752, 0x4eaa, 0x267a, 0x57b3, 0x36d3, 0x0c17, + 0x037a, 0x2b80, 0x463c, 0x42a5, 0x323d, 0x6b21, 0x17c7, 0x52cb, 0x7de8, 0x108f, + 0x29d4, 0x1a95, 0x5b9a, 0x3c1f, 0x02d1, 0x72da, 0x3ac8, 0x26a5, 0x489e, 0x0b25, + 0x3171, 0x08b6, 0x3363, 0x4ed5, 0x3102, 0x092b, 0x59cb, 0x38d4, 0x30eb, 0x0c64, + 0x7a7a, 0x5a23, 0x50f0, 0x1736, 0x189b, 0x4b4c, 0x438e, 0x7cab, 0x2ece, 0x5fd9, + 0x454a, 0x717b, 0x0e06, 0x75ec, 0x70ff, 0x3f45, 0x5f10, 0x1c33, 0x54bd, 0x7f84, + 0x1d7e, 0x3b2d, 0x2a56, 0x22cc, 0x2e83, 0x686f, 0x43e0, 0x1229, 0x4eb3, 0x663c, + 0x3877, 0x78f1, 0x6b73, 0x2196, 0x4120, 0x0961, 0x636f, 0x3843, 0x6592, 0x281e, + 0x4230, 0x69cc, 0x7cf6, 0x2fd4, 0x4182, 0x45e3, 0x0f59, 0x226e, 0x2f69, 0x74f7, + 0x2ea7, 0x2e6e, 0x6a78, 0x4db8, 0x17a0, 0x3268, 0x2e95, 0x7959, 0x2830, 0x3138, + 0x7cbd, 0x42d0, 0x3375, 0x1d45, 0x4ce4, 0x2a13, 0x6c47, 0x0c42, 0x3674, 0x0fe1, + 0x5234, 0x2f57, 0x3d76, 0x03a5, 0x5657, 0x35d8, 0x2fa2, 0x25dd, 0x1589, 0x264a, + 0x32a1, 0x7e8c, 0x7e0d, 0x1c0e, 0x75f2, 0x7e13, 0x77c7, 0x53b0, 0x6a66, 0x5d9e, + 0x3e83, 0x62f9, 0x575b, 0x26e4, 0x6b33, 0x52f6, 0x4b7d, 0x1ac0, 0x48fd, 0x6f46, + 0x220a, 0x5c0a, 0x6e7c, 0x178e, 0x3064, 0x5ae0, 0x2151, 0x5bc5, 0x64f9, 0x060a, + 0x6ab1, 0x4e65, 0x5ddb, 0x277c, 0x2c06, 0x607f, 0x3470, 0x38f7, 0x025c, 0x289f, + 0x5222, 0x039d, 0x1a07, 0x24f6, 0x551a, 0x3c9b, 0x3611, 0x3939, 0x3daf, 0x1446, + 0x12dd, 0x772d, 0x307a, 0x6faa, 0x09c3, 0x3662, 0x1e40, 0x7fe4, 0x7356, 0x04cf, + 0x3ef6, 0x77c1, 0x5271, 0x2529, 0x7db8, 0x5892, 0x68ac, 0x4849, 0x55f0, 0x1fc5, + 0x232a, 0x58d5, 0x4cd2, 0x4725, 0x4f89, 0x1548, 0x2683, 0x5c58, 0x592a, 0x7d93, + 0x1b9a, 0x4bbd, 0x2fdb, 0x11b2, 0x67de, 0x2cb2, 0x14f6, 0x6c35, 0x751e, 0x5b25, + 0x6ae3, 0x4f0b, 0x43cb, 0x7d5f, 0x2dd9, 0x014b, 0x6da8, 0x18ca, 0x5702, 0x3399, + 0x11ab, 0x7c42, 0x2c5f, 0x5a8d, 0x5645, 0x66c8, 0x0c7b, 0x1258, 0x292b, 0x0cf6, + 0x61b4, 0x70cc, 0x3233, 0x6c00, 0x4b36, 0x0b03, 0x4d1d, 0x6328, 0x4347, 0x3d64, + 0x6e72, 0x522a, 0x4384, 0x6588, 0x14ec, 0x09b9, 0x433d, 0x36a3, 0x7840, 0x05a4, + 0x53aa, 0x4d4c, 0x161d, 0x5063, 0x4762, 0x3a14, 0x2f90, 0x116e, 0x2120, 0x113f, + 0x57bc, 0x193c, 0x1ec3, 0x132d, 0x0536, 0x5d51, 0x605c, 0x63ce, 0x7a54, 0x1b74, + 0x36ad, 0x1577, 0x0a00, 0x4a98, 0x23d9, 0x204b, 0x6f07, 0x30a9, 0x3cfe, 0x51b2, + 0x468e, 0x4942, 0x395a, 0x08ec, 0x1bbb, 0x7d40, 0x20ec, 0x103c, 0x70ed, 0x31ce, + 0x4eec, 0x5800, 0x71e2, 0x04ac, 0x0557, 0x6465, 0x6b94, 0x6226, 0x072e, 0x3402, + 0x792a, 0x6c16, 0x0c85, 0x0df4, 0x4c78, 0x0d79, 0x4b9e, 0x166c, 0x1d66, 0x6a60, + 0x3458, 0x1605, 0x7041, 0x6426, 0x3fc4, 0x5354, 0x341a, 0x4706, 0x36f4, 0x3b77, + 0x2ebc, 0x37bc, 0x7d74, 0x2e3f, 0x3821, 0x5a0d, 0x6210, 0x4c34, 0x1868, 0x6297, + 0x540a, 0x0ab7, 0x4420, 0x482a, 0x1262, 0x4538, 0x5e92, 0x16bb, 0x1fa6, 0x67d7, + 0x49f8, 0x4482, 0x74b5, 0x3de1, 0x60a3, 0x0b6f, 0x3faf, 0x54a8, 0x0746, 0x6f8b, + 0x6d66, 0x5814, 0x50de, 0x6dfb, 0x770e, 0x068e, 0x5d05, 0x5161, 0x1db7, 0x7ac7, + 0x2305, 0x221e, 0x01a0, 0x093f, 0x2f28, 0x250a, 0x66d2, 0x7a68, 0x07c0, 0x3ca3, + 0x7fc5, 0x4650, 0x2451, 0x0ec1, 0x2afa, 0x1a3a, 0x5c7d, 0x4fe4, 0x5d98, 0x4594, + 0x7ae8, 0x275d, 0x01c5, 0x615a, 0x1889, 0x1c72, 0x2880, 0x25ae, 0x5f49, 0x137a, + 0x6486, 0x2e0d, 0x5d77, 0x69a5, 0x7fbd, 0x66a1, 0x6f77, 0x24d7, 0x564f, 0x437c, + 0x7980, 0x2d89, 0x3c7c, 0x31a7, 0x5ce1, 0x7424, 0x554b, 0x043c, 0x6a18, 0x58f8, + 0x41bb, 0x1980, 0x623e, 0x191d, 0x3759, 0x3d99, 0x315f, 0x1b05, 0x1120, 0x0b5b, + 0x2420, 0x4e02, 0x62c5, 0x3c5b, 0x6254, 0x598b, 0x7297, 0x767c, 0x5bdb, 0x39f5, + 0x0d00, 0x488c, 0x3a6e, 0x07e3, 0x0585, 0x2d04, 0x22a7, 0x3e7d, 0x7573, 0x39ad, + 0x4269, 0x6787, 0x17ec, 0x78bf, 0x49ba, 0x4a79, 0x1de5, 0x398a, 0x02bf, 0x79a7, + 0x5193, 0x4e36, 0x224c, 0x7454, 0x4adf, 0x56a1, 0x5cc0, 0x70a8, 0x69d3, 0x2c66, + 0x16c2, 0x63af, 0x2935, 0x3ab6, 0x64b4, 0x41eb, 0x1b55, 0x7a0f, 0x5efb, 0x702c, + 0x300d, 0x13db, 0x65f4, 0x5087, 0x0a91, 0x7656, 0x2cab, 0x7c23, 0x63a8, 0x0c1c, + 0x30f0, 0x219b, 0x337a, 0x1ac5, 0x3616, 0x4bc2, 0x61b9, 0x5d56, 0x055c, 0x629c, + 0x1dbc, 0x69aa, 0x62ca, 0x70ad, 0x61be, 0x3351, 0x2857, 0x5522, 0x66a9, 0x3f86, + 0x0705, 0x20c3, 0x61e7, 0x1f7d, 0x7be2, 0x7eb1, 0x62f3, 0x3af8, 0x6bac, 0x6be1, + 0x5b71, 0x7598, 0x59b9, 0x063a, 0x3d45, 0x7e5d, 0x353b, 0x2983, 0x65c6, 0x0bcf, + 0x562e, 0x334c, 0x779e, 0x48d4, 0x5494, 0x6569, 0x70d6, 0x30d9, 0x4f60, 0x2db0, + 0x099a, 0x2bdd, 0x4b0d, 0x2c36, 0x1e9a, 0x23b0, 0x1d09, 0x10df, 0x2cd4, 0x6830, + 0x0c9d, 0x75cd, 0x6c8a, 0x4995, 0x3865, 0x32dd, 0x715c, 0x067a, 0x3e20, 0x1423, + 0x2852, 0x6d45, 0x1933, 0x5c4f, 0x6633, 0x26db, 0x2167, 0x7c8c, 0x7c4c, 0x4ea1, + 0x297a, 0x744b, 0x5a04, 0x1371, 0x7f08, 0x5755, 0x2b83, 0x29d7, 0x26a8, 0x59ce, + 0x1739, 0x0e09, 0x7f87, 0x4eb6, 0x0964, 0x4185, 0x2e71, 0x7cc0, 0x0c45, 0x2fa5, + 0x1c11, 0x575e, 0x6f49, 0x64fc, 0x6082, 0x551d, 0x7730, 0x3ef9, 0x484c, 0x2686, + 0x11b5, 0x43ce, 0x339c, 0x292e, 0x0b06, 0x14ef, 0x4d4f, 0x57bf, 0x63d1, 0x6f0a, + 0x08ef, 0x71e5, 0x3405, 0x1d69, 0x5357, 0x3824, 0x0aba, 0x49fb, 0x54ab, 0x5d08, + 0x0942, 0x2454, 0x4597, 0x5f4c, 0x66a4, 0x5ce4, 0x1983, 0x2423, 0x767f, 0x22aa, + 0x78c2, 0x224f, 0x2c69, 0x5efe, 0x7659, 0x3619, 0x69ad, 0x0708, 0x3afb, 0x353e, + 0x48d7, 0x4b10, 0x6833, 0x3e23, 0x26de, 0x7f0b, 0x0e0c, 0x1c14, 0x3efc, 0x4d52, + 0x1d6c, 0x459a, 0x22ad, 0x3afe, 0x7f0e, 0x7f11, 0x50b0, 0x030d, 0x76ed, 0x3f81, + 0x1f0a, 0x50ad, 0x6de7, 0x120a, 0x5a97, 0x2a44, 0x12c7, 0x358f, 0x661d, 0x7281, + 0x030a, 0x595b, 0x55be, 0x27e2, 0x5a51, 0x787c, 0x18ed, 0x76ea, 0x0f7c, 0x42b1, + 0x7238, 0x68ef, 0x6580, 0x35d0, 0x3119, 0x514d, 0x3f7e, 0x4648, 0x0700, 0x017f, + 0x63f4, 0x4c4a, 0x0e3f, 0x1f07, 0x38a5, 0x3249, 0x570c, 0x635d, 0x42f7, 0x6ec1, + 0x74d8, 0x7310, 0x50aa, 0x6b2d, 0x21c4, 0x51ee, 0x5027, 0x32c6, 0x1aee, 0x6de4, + 0x7942, 0x0386, 0x40c5, 0x39d2, 0x6b61, 0x3200, 0x262b, 0x0887, 0x1207, 0x2b8c, + 0x71b4, 0x34b2, 0x2f07, 0x20be, 0x4237, 0x5a94, 0x5e99, 0x0c23, 0x33a3, 0x410e, + 0x4047, 0x5572, 0x0fc2, 0x3bc6, 0x2a41, 0x36df, 0x47a9, 0x200f, 0x6043, 0x56e9, + 0x6893, 0x12c4, 0x6c2e, 0x7df4, 0x3aaf, 0x4107, 0x7ce4, 0x503e, 0x1bef, 0x46e1, + 0x358c, 0x109b, 0x61e2, 0x079f, 0x1ef1, 0x33ec, 0x1720, 0x661a, 0x1707, 0x52d7, + 0x18d4, 0x421e, 0x53f1, 0x3941, 0x5d7f, 0x0187, 0x727e, 0x17d3, 0x0a78, 0x7785, + 0x5856, 0x44de, 0x52f0, 0x0307, 0x6caf, 0x1aa1, 0x4beb, 0x5e17, 0x4170, 0x0463, + 0x176f, 0x5faa, 0x5958, 0x29e0, 0x183a, 0x673a, 0x2ad9, 0x1f78, 0x363f, 0x55bb, + 0x7ab3, 0x5ba6, 0x6db2, 0x0f47, 0x1474, 0x2565, 0x05eb, 0x73a0, 0x27df, 0x3c2b, + 0x6935, 0x010f, 0x0f01, 0x5291, 0x4c04, 0x5a4e, 0x14a9, 0x7405, 0x34f8, 0x279c, + 0x2dc7, 0x511b, 0x3188, 0x2df9, 0x7879, 0x58b2, 0x7bdd, 0x7552, 0x2071, 0x37d2, + 0x4d85, 0x18ea, 0x19e0, 0x435d, 0x212a, 0x43b9, 0x2604, 0x05c4, 0x6986, 0x7135, + 0x76e7, 0x4b77, 0x1b2e, 0x3d1e, 0x3022, 0x7617, 0x0b84, 0x0f79, 0x71fa, 0x1c53, + 0x47be, 0x2d29, 0x750c, 0x711f, 0x2dee, 0x712a, 0x42ae, 0x7165, 0x1656, 0x72fa, + 0x3a4d, 0x7eac, 0x5e30, 0x7235, 0x3797, 0x4575, 0x1149, 0x6ad1, 0x4040, 0x64ad, + 0x273e, 0x7517, 0x68ec, 0x15a9, 0x40a2, 0x6004, 0x2040, 0x1134, 0x124d, 0x657d, + 0x04c4, 0x24eb, 0x153d, 0x4f00, 0x2fc9, 0x121e, 0x0920, 0x7170, 0x35cd, 0x312d, + 0x62ee, 0x5bba, 0x7b62, 0x5c1e, 0x6cc8, 0x3116, 0x60d4, 0x1a1b, 0x1178, 0x1b88, + 0x4a56, 0x38ff, 0x3c84, 0x42b9, 0x514a, 0x4deb, 0x140c, 0x1084, 0x2bd2, 0x7e52, + 0x1aba, 0x3f7b, 0x2cf9, 0x0b50, 0x4e2b, 0x7a04, 0x67cc, 0x2e34, 0x57f5, 0x1661, + 0x4645, 0x0683, 0x25a3, 0x319c, 0x7276, 0x3af3, 0x2449, 0x06fd, 0x1366, 0x066f, + 0x2f9a, 0x14e4, 0x3bbb, 0x087c, 0x5142, 0x7305, 0x017c, 0x46d6, 0x5f9f, 0x7395, + 0x56b4, 0x54e8, 0x6267, 0x63f1, 0x056f, 0x59ee, 0x0be2, 0x7340, 0x2318, 0x2ba3, + 0x2e20, 0x1c5e, 0x4c47, 0x6cdf, 0x6ba7, 0x10ff, 0x1996, 0x3e43, 0x0320, 0x0e3c, + 0x650f, 0x3b58, 0x1946, 0x55de, 0x34c5, 0x13a1, 0x6407, 0x7205, 0x1f04, 0x48f7, + 0x674d, 0x2a81, 0x4cf7, 0x21e4, 0x5309, 0x38a2, 0x7904, 0x169c, 0x7a8d, 0x789c, + 0x7da6, 0x6667, 0x3dc2, 0x47c9, 0x3246, 0x32e6, 0x1340, 0x7b18, 0x23ff, 0x6bdc, + 0x45ea, 0x5709, 0x4489, 0x0a98, 0x57c6, 0x689a, 0x15b0, 0x44fe, 0x480b, 0x2d34, + 0x635a, 0x440b, 0x3782, 0x6955, 0x0a39, 0x5004, 0x44f7, 0x42f4, 0x5b1e, 0x7d21, + 0x41e4, 0x556b, 0x4f77, 0x5f70, 0x08cd, 0x0b8f, 0x6ebe, 0x2b1a, 0x5b6c, 0x69f7, + 0x7b2b, 0x460e, 0x19a9, 0x74d5, 0x7b3e, 0x6446, 0x1ecd, 0x4cc0, 0x7b75, 0x3478, + 0x31af, 0x0f84, 0x730d, 0x610f, 0x2084, 0x4962, 0x1831, 0x71ab, 0x6f40, 0x50a7, + 0x647d, 0x6207, 0x4ad6, 0x65bd, 0x2671, 0x7ed1, 0x0dd5, 0x7622, 0x6b2a, 0x386e, + 0x5921, 0x1eba, 0x3738, 0x7593, 0x586f, 0x21c1, 0x259a, 0x164d, 0x1337, 0x5918, + 0x029a, 0x414b, 0x6a41, 0x302d, 0x51eb, 0x67a7, 0x28f4, 0x04ff, 0x254b, 0x44c4, + 0x33d2, 0x5024, 0x31e6, 0x20a4, 0x7862, 0x6ea7, 0x35ff, 0x71cb, 0x3f67, 0x4580, + 0x32c3, 0x5c35, 0x59b4, 0x2914, 0x41d1, 0x676d, 0x5971, 0x1aeb, 0x0620, 0x3332, + 0x506d, 0x5508, 0x0d5f, 0x4928, 0x627d, 0x37a2, 0x6de1, 0x2204, 0x4fca, 0x2d6f, + 0x5ac6, 0x7e72, 0x29f9, 0x793f, 0x6855, 0x217c, 0x72c0, 0x5fbf, 0x5210, 0x18b0, + 0x5d37, 0x1154, 0x0383, 0x142c, 0x5878, 0x5b0b, 0x5c9f, 0x0635, 0x76a5, 0x40c2, + 0x330c, 0x7637, 0x1627, 0x19f5, 0x0fbb, 0x1b4e, 0x7c04, 0x6adc, 0x39cf, 0x4804, + 0x11e4, 0x52b1, 0x7bac, 0x3a38, 0x1853, 0x6b5e, 0x2aa3, 0x654a, 0x1291, 0x1cd3, + 0x2bf4, 0x270f, 0x48b5, 0x5e3b, 0x31fd, 0x4c9c, 0x3d40, 0x5eda, 0x0a26, 0x6d14, + 0x6e2a, 0x2628, 0x543d, 0x2391, 0x476c, 0x5dc9, 0x567e, 0x0264, 0x2d91, 0x7240, + 0x0884, 0x3eae, 0x0d29, 0x082c, 0x0862, 0x7e38, 0x5c04, 0x1204, 0x7105, 0x7e92, + 0x5277, 0x05aa, 0x345e, 0x4fea, 0x7579, 0x7eb7, 0x2b89, 0x3e29, 0x21ca, 0x44e4, + 0x1b34, 0x7e58, 0x6753, 0x71b1, 0x4fd0, 0x7e3e, 0x3a1e, 0x024a, 0x1e2a, 0x1c99, + 0x2964, 0x3a58, 0x34af, 0x0d49, 0x022a, 0x77f2, 0x6fcd, 0x1cb3, 0x047c, 0x2f04, + 0x6107, 0x4de3, 0x3ea6, 0x00bc, 0x09b1, 0x25d5, 0x0b3c, 0x2749, 0x20bb, 0x0eb9, + 0x3536, 0x17cb, 0x08ba, 0x2ed2, 0x22d0, 0x4234, 0x326c, 0x3d7a, 0x53b4, 0x3068, + 0x28a3, 0x1e44, 0x58d9, 0x7522, 0x5a91, 0x6e76, 0x3a18, 0x0a04, 0x1040, 0x4c7c, + 0x3b7b, 0x5e96, 0x5818, 0x07c4, 0x615e, 0x7984, 0x3d9d, 0x3a72, 0x398e, 0x64b8, + 0x0c20, 0x285b, 0x759c, 0x4f64, 0x4999, 0x297e, 0x4189, 0x33a0, 0x49ff, 0x765d, + 0x4d56, 0x12cb, 0x68f3, 0x42fb, 0x39d6, 0x404b, 0x410b, 0x53f5, 0x5e1b, 0x1478, + 0x27a0, 0x2608, 0x2d2d, 0x4044, 0x4f04, 0x4a5a, 0x7a08, 0x3bbf, 0x7344, 0x34c9, + 0x78a0, 0x15b4, 0x556f, 0x7b79, 0x65c1, 0x029e, 0x6eab, 0x0d63, 0x5fc3, 0x0fbf, + 0x1cd7, 0x5682, 0x05ae, 0x1e2e, 0x00c0, 0x28a7, 0x7988, 0x68f7, 0x3bc3, 0x00c4, + 0x2371, 0x03d0, 0x474a, 0x0244, 0x1788, 0x2a3e, 0x73d5, 0x7089, 0x66f7, 0x236d, + 0x3ee4, 0x6c72, 0x3a97, 0x40ad, 0x36dc, 0x6d4e, 0x3741, 0x5b59, 0x4abe, 0x0bca, + 0x1525, 0x47a6, 0x6146, 0x79f0, 0x784a, 0x525f, 0x03cc, 0x780c, 0x700d, 0x600f, + 0x200c, 0x100c, 0x080c, 0x004c, 0x3ce2, 0x09e4, 0x7a38, 0x6040, 0x2104, 0x57a0, + 0x7824, 0x4746, 0x432b, 0x3217, 0x14d0, 0x4368, 0x56e6, 0x118f, 0x5629, 0x6198, + 0x590e, 0x4cb6, 0x55d4, 0x6890, 0x6ac7, 0x43af, 0x1b7e, 0x14da, 0x0240, 0x5dbf, + 0x54fe, 0x19eb, 0x12c1, 0x305e, 0x1e24, 0x5255, 0x156d, 0x3d5a, 0x3658, 0x6c2b, + 0x2814, 0x7ca1, 0x2f4d, 0x1784, 0x6e60, 0x4b61, 0x64dd, 0x2135, 0x7df1, 0x75d6, + 0x6a4a, 0x6b17, 0x30cf, 0x3347, 0x4882, 0x3aac, 0x452e, 0x0dea, 0x7a5e, 0x4372, + 0x2a3a, 0x5ef4, 0x4e97, 0x43c4, 0x4104, 0x6353, 0x4214, 0x0f3d, 0x427d, 0x2729, + 0x1f91, 0x7ce1, 0x1a4e, 0x7c6d, 0x7055, 0x73d1, 0x4b24, 0x45bb, 0x26bc, 0x4d90, + 0x503b, 0x0cce, 0x7799, 0x2c8a, 0x265e, 0x572d, 0x5f24, 0x1bec, 0x7dcc, 0x29b8, + 0x36b7, 0x3221, 0x7085, 0x2c0e, 0x742c, 0x18f5, 0x46de, 0x0484, 0x0e99, 0x24af, + 0x0840, 0x1c93, 0x5ada, 0x3589, 0x1054, 0x10c0, 0x03e4, 0x66f3, 0x4d0b, 0x5e55, + 0x4976, 0x37dd, 0x1098, 0x0ca6, 0x3036, 0x0dc2, 0x0a70, 0x48cf, 0x2af2, 0x61df, + 0x24c3, 0x065b, 0x1581, 0x4335, 0x2369, 0x0d21, 0x1404, 0x207c, 0x079c, 0x0e91, + 0x009c, 0x79c8, 0x55a2, 0x12ab, 0x6dcb, 0x1eee, 0x2291, 0x7ef2, 0x6817, 0x3ee0, + 0x2c4d, 0x1967, 0x3adf, 0x6991, 0x33e9, 0x533b, 0x548f, 0x6688, 0x6f2d, 0x2e55, + 0x7f6b, 0x171d, 0x0aea, 0x4d33, 0x6066, 0x1199, 0x6c6e, 0x1ced, 0x3e04, 0x7140, + 0x6617, 0x214b, 0x295e, 0x2b67, 0x0a5c, 0x53d5, 0x16eb, 0x1704, 0x1bd3, 0x3570, + 0x6027, 0x3a93, 0x6d96, 0x2abd, 0x27c3, 0x05cf, 0x52d4, 0x6c93, 0x4154, 0x181e, + 0x06e4, 0x6564, 0x0f60, 0x18d1, 0x74bc, 0x508e, 0x63d8, 0x56f0, 0x40a9, 0x500b, + 0x11eb, 0x260f, 0x421b, 0x5e7d, 0x402b, 0x478d, 0x7499, 0x5e76, 0x4404, 0x53ee, + 0x7d58, 0x3805, 0x7025, 0x36d8, 0x0c69, 0x6b78, 0x1d4a, 0x4b82, 0x393e, 0x1b9f, + 0x70d1, 0x053b, 0x646a, 0x186d, 0x7acc, 0x5d7c, 0x3c60, 0x5cc5, 0x5d5b, 0x5633, + 0x6d4a, 0x6087, 0x5ce9, 0x76f2, 0x0184, 0x2f0c, 0x07a4, 0x2ade, 0x7557, 0x3a52, + 0x5bbf, 0x727b, 0x1104, 0x2404, 0x69fc, 0x373d, 0x2919, 0x5ca4, 0x5edf, 0x1b39, + 0x17d0, 0x499e, 0x02a3, 0x4ac3, 0x619d, 0x30d4, 0x2c8f, 0x0a75, 0x668d, 0x06e9, + 0x0540, 0x61a2, 0x5b55, 0x7bc6, 0x351f, 0x3d29, 0x7782, 0x5478, 0x4f44, 0x1e7e, + 0x28d8, 0x027e, 0x257e, 0x5853, 0x0db9, 0x6b0e, 0x1815, 0x4aba, 0x1eb1, 0x7b0f, + 0x72f1, 0x3193, 0x44db, 0x5b02, 0x4f5b, 0x5b50, 0x1324, 0x7d8a, 0x78e8, 0x52ed, + 0x47ef, 0x633e, 0x23e3, 0x57aa, 0x0bc6, 0x5698, 0x4c2b, 0x2e04, 0x0304, 0x64f3, + 0x34a9, 0x6731, 0x13f0, 0x4a3a, 0x60b8, 0x6cac, 0x0904, 0x35b1, 0x2024, 0x1521, + 0x2f7e, 0x725a, 0x0160, 0x5126, 0x1a9e, 0x2cdd, 0x67b0, 0x2587, 0x7bc1, 0x2dab, + 0x148d, 0x4be8, 0x696a, 0x76cb, 0x2055, 0x210e, 0x47a2, 0x3006, 0x4292, 0x2dd2, + 0x5e14, 0x377b, 0x4024, 0x4086, 0x5dff, 0x68d7, 0x49e3, 0x416d, 0x3972, 0x0c04, + 0x1024, 0x6142, 0x5398, 0x089e, 0x5a75, 0x58bd, 0x0460, 0x60eb, 0x0995, 0x351a, + 0x3725, 0x3ec8, 0x73b9, 0x176c, 0x6ff1, 0x1ff0, 0x4aa2, 0x782e, 0x79ec, 0x2784, + 0x5553, 0x7884, 0x5fa7, 0x1cbb, 0x00a4, 0x2355, 0x4fae, 0x0d43, 0x0604, 0x5955, + 0x3f4b, 0x32a7, 0x252f, 0x7846, 0x160b, 0x5c83, 0x39b3, 0x7be8, 0x29dd, 0x6839, + 0x51f4, 0x585c, 0x3d24, 0x2bd8, 0x2a87, 0x1837, 0x2d75, 0x0868, 0x0a0a, 0x4750, + 0x525b, 0x0846, 0x2b6d, 0x755d, 0x6737, 0x4fb4, 0x1e0e, 0x020e, 0x0080, 0x234d, + 0x24a7, 0x2ad6, 0x495a, 0x107c, 0x0824, 0x03c8, 0x369b, 0x2642, 0x46c2, 0x7410, + 0x1f75, 0x1a32, 0x4b08, 0x777d, 0x6a2e, 0x6e44, 0x27f8, 0x363c, 0x4e7b, 0x40e8, + 0x30b3, 0x7a42, 0x7808, 0x3cc6, 0x56ca, 0x14b4, 0x55b8, 0x6aab, 0x0224, 0x1e08, + 0x0788, 0x6d2e, 0x3c44, 0x7ab0, 0x1d2e, 0x3922, 0x747d, 0x7009, 0x0524, 0x6181, + 0x7766, 0x3503, 0x5ba3, 0x10e8, 0x28fd, 0x0287, 0x5473, 0x2c31, 0x2275, 0x6daf, + 0x3de8, 0x65fb, 0x6f11, 0x604a, 0x600b, 0x0a40, 0x52b8, 0x27a7, 0x0f44, 0x74a0, + 0x408d, 0x400f, 0x4008, 0x4786, 0x694e, 0x1471, 0x0144, 0x1a82, 0x13d4, 0x2008, + 0x23c7, 0x1308, 0x02e8, 0x4c0f, 0x2562, 0x0d9d, 0x1e95, 0x4f3f, 0x51d8, 0x15ef, + 0x3f2f, 0x05e8, 0x2b51, 0x671b, 0x3d08, 0x09ee, 0x1008, 0x5de3, 0x0444, 0x5a59, + 0x739d, 0x6fd5, 0x79d0, 0x0088, 0x0208, 0x77ec, 0x4e5f, 0x27dc, 0x46a6, 0x1f59, + 0x0064, 0x0808, 0x6ef5, 0x5457, 0x0f28, 0x529c, 0x3c28, 0x1d12, 0x0508, 0x28e1, + 0x1e79, 0x23ab, 0x0128, 0x6932, 0x0428, 0x7381, 0x51bc, 0x3cec, 0x0048, 0x01ec, + 0x3c0c, 0x0f0c, 0x010c, 0x040c, 0x002c, 0x0010, 0x0002, 0x03fe, 0x01de, 0x0efe, + 0x6924, 0x239d, 0x7373, 0x3cde, 0x07fa, 0x1f4b, 0x77de, 0x27ce, 0x528e, 0x5449, + 0x1d04, 0x28d3, 0x4f31, 0x0d8f, 0x12fa, 0x4c01, 0x1463, 0x4778, 0x1a74, 0x1ffa, + 0x09e0, 0x670d, 0x15e1, 0x05da, 0x5a4b, 0x5dd5, 0x6fc7, 0x007a, 0x1dfa, 0x6a9d, + 0x3cb8, 0x14a6, 0x362e, 0x6e36, 0x40da, 0x7a34, 0x03ba, 0x106e, 0x233f, 0x2ac8, + 0x7402, 0x2634, 0x1a24, 0x776f, 0x0279, 0x10da, 0x6173, 0x34f5, 0x7aa2, 0x6d20, + 0x3914, 0x6ffb, 0x603c, 0x65ed, 0x2c23, 0x6da1, 0x2799, 0x0a32, 0x7492, 0x4001, + 0x4078, 0x376d, 0x2ff8, 0x2dc4, 0x4bda, 0x2d9d, 0x76bd, 0x2100, 0x1513, 0x35a3, + 0x4a2c, 0x6c9e, 0x5118, 0x724c, 0x2ccf, 0x2579, 0x5b42, 0x5af4, 0x7b01, 0x3185, + 0x5845, 0x0270, 0x6b00, 0x4aac, 0x579c, 0x6330, 0x7d7c, 0x52df, 0x2df6, 0x568a, + 0x64e5, 0x6723, 0x2347, 0x1cad, 0x2776, 0x7876, 0x175e, 0x3eba, 0x1fe2, 0x7820, + 0x6134, 0x0bf6, 0x68c9, 0x415f, 0x58af, 0x0890, 0x60dd, 0x350c, 0x584e, 0x682b, + 0x5c75, 0x7bda, 0x5947, 0x0d35, 0x3299, 0x7838, 0x4742, 0x085a, 0x2bca, 0x1829, + 0x754f, 0x0838, 0x4fa6, 0x0200, 0x79ba, 0x0e83, 0x0d13, 0x206e, 0x61d1, 0x48c1, + 0x064d, 0x4327, 0x66e5, 0x10b2, 0x1c85, 0x357b, 0x37cf, 0x5e47, 0x0c98, 0x0db4, + 0x2c7c, 0x0cc0, 0x45ad, 0x4d82, 0x7cd3, 0x271b, 0x7c5f, 0x73c3, 0x3213, 0x29aa, + 0x571f, 0x1bde, 0x18e7, 0x2c00, 0x0476, 0x24a1, 0x5247, 0x3050, 0x5db1, 0x19dd, + 0x6882, 0x4ca8, 0x43a1, 0x14cc, 0x4738, 0x5792, 0x09d6, 0x6032, 0x435a, 0x3209, + 0x1181, 0x618a, 0x6b09, 0x75c8, 0x4b53, 0x2127, 0x6c1d, 0x3d4c, 0x7c93, 0x1776, + 0x4364, 0x0ddc, 0x3339, 0x3a9e, 0x43b6, 0x5ee6, 0x6345, 0x0f2f, 0x477f, 0x5e6f, + 0x4ffd, 0x2601, 0x18c3, 0x6556, 0x5080, 0x56e2, 0x3a85, 0x3562, 0x53c7, 0x16f6, + 0x05c1, 0x2aaf, 0x6c85, 0x1810, 0x667a, 0x532d, 0x1959, 0x6983, 0x1ee0, 0x129d, + 0x7ee4, 0x3ed2, 0x118b, 0x4d25, 0x2e47, 0x170f, 0x7132, 0x1cdf, 0x213d, 0x2b59, + 0x2ad0, 0x2efe, 0x6079, 0x76e4, 0x5d6e, 0x185f, 0x5cb7, 0x5625, 0x36ca, 0x37f7, + 0x5e68, 0x53e0, 0x4b74, 0x6b6a, 0x1b91, 0x052d, 0x4ab5, 0x4990, 0x5c96, 0x1b2b, + 0x726d, 0x3a44, 0x23f6, 0x372f, 0x6194, 0x06db, 0x30c6, 0x0a67, 0x3d1b, 0x7bb8, + 0x546a, 0x1e70, 0x04f1, 0x6799, 0x413d, 0x301f, 0x21b3, 0x7585, 0x163f, 0x590a, + 0x65af, 0x61f9, 0x719d, 0x5099, 0x7614, 0x7ec3, 0x3860, 0x1eac, 0x69e9, 0x2b0c, + 0x5f62, 0x0b81, 0x42e6, 0x4ff6, 0x7d13, 0x555d, 0x4cb2, 0x6438, 0x4600, 0x74c7, + 0x0f76, 0x346a, 0x6101, 0x4954, 0x2a73, 0x48e9, 0x1393, 0x71f7, 0x0e2e, 0x3e35, + 0x3b4a, 0x55d0, 0x7332, 0x59e0, 0x54da, 0x63e3, 0x1c50, 0x2b95, 0x6cd1, 0x10f1, + 0x7b0a, 0x32d8, 0x6659, 0x47bb, 0x3894, 0x21d6, 0x168e, 0x788e, 0x688c, 0x0a8a, + 0x6bce, 0x56fb, 0x2d26, 0x44f0, 0x43fd, 0x6947, 0x5ff6, 0x159b, 0x649f, 0x7509, + 0x7227, 0x7e9e, 0x4567, 0x6ac3, 0x2d1b, 0x1c45, 0x7609, 0x0f6b, 0x711c, 0x7111, + 0x7157, 0x72ec, 0x7544, 0x58a4, 0x510d, 0x2deb, 0x5a40, 0x5283, 0x73f7, 0x278e, + 0x43ab, 0x434f, 0x37c4, 0x18dc, 0x7127, 0x05b6, 0x4b69, 0x3d10, 0x1076, 0x4ddd, + 0x38f1, 0x42ab, 0x3108, 0x5c10, 0x1a0d, 0x1b7a, 0x4ef2, 0x24dd, 0x1126, 0x656f, + 0x7162, 0x1210, 0x311f, 0x5bac, 0x318e, 0x0675, 0x2e26, 0x1653, 0x3f6d, 0x7e44, + 0x0b42, 0x79f6, 0x14d6, 0x0661, 0x3ae5, 0x06ef, 0x72f7, 0x086e, 0x46c8, 0x7387, + 0x77e4, 0x0d3b, 0x1c8b, 0x3a4a, 0x71a3, 0x7e4a, 0x7e30, 0x023c, 0x059c, 0x7e84, + 0x7e2a, 0x11f6, 0x7ea9, 0x4fdc, 0x3e1b, 0x44d6, 0x5ecc, 0x4c8e, 0x2701, 0x5e2d, + 0x6b50, 0x3a2a, 0x653c, 0x1cc5, 0x5dbb, 0x2383, 0x6d06, 0x261a, 0x7232, 0x0256, + 0x3ea0, 0x081e, 0x2d61, 0x21f6, 0x491a, 0x3794, 0x1add, 0x675f, 0x3324, 0x54fa, + 0x6e99, 0x2096, 0x44b6, 0x5016, 0x4572, 0x71bd, 0x5c27, 0x2906, 0x5afd, 0x141e, + 0x18a2, 0x1146, 0x7931, 0x7e64, 0x216e, 0x5fb1, 0x19e7, 0x7629, 0x0627, 0x40b4, + 0x6ace, 0x1b40, 0x47f6, 0x52a3, 0x146a, 0x53e7, 0x42ed, 0x403d, 0x3392, 0x2970, + 0x764f, 0x12bd, 0x7976, 0x07b6, 0x4c6e, 0x5e88, 0x64aa, 0x3a64, 0x284d, 0x4f56, + 0x17bd, 0x0eab, 0x25c7, 0x273b, 0x2ef6, 0x1ca5, 0x4dd5, 0x00ae, 0x305a, 0x3d6c, + 0x2ec4, 0x4226, 0x7514, 0x1e36, 0x6e68, 0x09f6, 0x03c2, 0x00b6, 0x2899, 0x68e9, + 0x0fb1, 0x0d55, 0x5674, 0x1e20, 0x3bb1, 0x4a4c, 0x25fa, 0x4036, 0x15a6, 0x34bb, + 0x7b6b, 0x0290, 0x5b4b, 0x6d40, 0x6c64, 0x409f, 0x2a30, 0x0236, 0x707b, 0x235f, + 0x5251, 0x79e2, 0x0bbc, 0x4798, 0x6001, 0x77fe, 0x0ffe, 0x003e, 0x51a4, 0x309b, + 0x4a8a, 0x203d, 0x63c0, 0x5d43, 0x1b66, 0x1569, 0x3a06, 0x5055, 0x0596, 0x4d3e, + 0x1131, 0x1160, 0x192e, 0x131f, 0x70be, 0x0ce8, 0x66ba, 0x124a, 0x338b, 0x18bc, + 0x7c34, 0x5a7f, 0x3d56, 0x631a, 0x6bf2, 0x0af5, 0x657a, 0x521c, 0x09ab, 0x3695, + 0x251b, 0x77b3, 0x7fd6, 0x04c1, 0x771f, 0x1438, 0x6f9c, 0x3654, 0x2891, 0x38e9, + 0x276e, 0x6071, 0x24e8, 0x038f, 0x3c8d, 0x392b, 0x7d85, 0x5c4a, 0x4717, 0x153a, + 0x483b, 0x5884, 0x1fb7, 0x58c7, 0x6c27, 0x2ca4, 0x4baf, 0x11a4, 0x4efd, 0x5b17, + 0x7d51, 0x013d, 0x2260, 0x45d5, 0x69be, 0x2fc6, 0x0953, 0x2188, 0x3835, 0x2810, + 0x22be, 0x3b1f, 0x1c25, 0x7f76, 0x121b, 0x6861, 0x662e, 0x78e3, 0x0c56, 0x38c6, + 0x4ec7, 0x091d, 0x2697, 0x72cc, 0x0b17, 0x08a8, 0x7c9d, 0x4b3e, 0x5a15, 0x1728, + 0x716d, 0x5fcb, 0x75de, 0x3f37, 0x263c, 0x25cf, 0x0397, 0x35ca, 0x0c34, 0x2a05, + 0x0fd3, 0x2f49, 0x325a, 0x4daa, 0x74e9, 0x2e60, 0x312a, 0x794b, 0x42c2, 0x1d37, + 0x52e8, 0x26d6, 0x5d90, 0x62eb, 0x1c00, 0x7e7e, 0x7e05, 0x53a2, 0x1780, 0x5bfc, + 0x1ab2, 0x6f38, 0x5bb7, 0x5ad2, 0x05fc, 0x4e57, 0x0ff2, 0x706f, 0x5668, 0x7b5f, + 0x2841, 0x7643, 0x4dc9, 0x6e5c, 0x3e94, 0x6530, 0x7e24, 0x3e0f, 0x5c1b, 0x3318, + 0x2162, 0x47ea, 0x43f1, 0x1682, 0x3b3e, 0x6cc5, 0x3854, 0x1633, 0x7d07, 0x60f5, + 0x4b5d, 0x73eb, 0x455b, 0x714b, 0x3113, 0x1a01, 0x0b36, 0x46bc, 0x4f9a, 0x328d, + 0x1fd6, 0x60d1, 0x2cc3, 0x76b1, 0x6af4, 0x64d9, 0x6fbb, 0x1a68, 0x7367, 0x1cf8, + 0x1a18, 0x40ce, 0x3908, 0x7486, 0x6339, 0x7c87, 0x4395, 0x1175, 0x0c8c, 0x0641, + 0x7c53, 0x046a, 0x2131, 0x7ed8, 0x5074, 0x6c79, 0x1b85, 0x5cab, 0x23ea, 0x545e, + 0x1a7b, 0x37fe, 0x7d1a, 0x4a53, 0x7c3b, 0x7c10, 0x7c1c, 0x7ded, 0x0b2a, 0x7cfb, + 0x4dbd, 0x2156, 0x38fc, 0x6ae8, 0x7c47, 0x23de, 0x7d45, 0x1fab, 0x6f90, 0x3c81, + 0x1922, 0x1b5a, 0x7c28, 0x099f, 0x75d2, 0x0b0b, 0x3829, 0x6622, 0x42b6, 0x0fc7, + 0x7df9, 0x05f0, 0x740a, 0x2743, 0x24f0, 0x5147, 0x59f3, 0x4810, 0x7d26, 0x6a46, + 0x20a9, 0x7c09, 0x654f, 0x2969, 0x4de8, 0x39db, 0x4a5f, 0x7012, 0x57a5, 0x4e9c, + 0x7c72, 0x1409, 0x7ef7, 0x11f0, 0x380a, 0x3524, 0x6b13, 0x4297, 0x0c09, 0x2b72, + 0x1081, 0x52bd, 0x1a87, 0x3c11, 0x23a2, 0x2c28, 0x2da2, 0x2bcf, 0x48c6, 0x333e, + 0x655b, 0x30cb, 0x758a, 0x6bd3, 0x7ea3, 0x3aea, 0x7e4f, 0x062c, 0x2975, 0x0bc1, + 0x5d48, 0x4bb4, 0x218d, 0x1ab7, 0x7648, 0x5079, 0x7c15, 0x0c0e, 0x3343, 0x709f, + 0x628e, 0x699c, 0x3f78, 0x5514, 0x20b5, 0x1f6f, 0x399f, 0x3e6f, 0x07d5, 0x2cf6, + 0x766e, 0x597d, 0x39e7, 0x487e, 0x3d8b, 0x190f, 0x58ea, 0x1972, 0x0b4d, 0x1af7, + 0x4df4, 0x3c4d, 0x5693, 0x7446, 0x7999, 0x4e28, 0x78b1, 0x6779, 0x4a6b, 0x397c, + 0x3aa8, 0x63a1, 0x709a, 0x2c58, 0x7a01, 0x41dd, 0x701e, 0x13cd, 0x3dd3, 0x4474, + 0x16ad, 0x67c9, 0x0aa9, 0x6289, 0x481c, 0x452a, 0x3b69, 0x46f8, 0x6418, 0x5346, + 0x2e31, 0x37ae, 0x59ff, 0x4c26, 0x6457, 0x049e, 0x31c0, 0x57f2, 0x08de, 0x4934, + 0x7d32, 0x102e, 0x0de6, 0x6c08, 0x6218, 0x33f4, 0x165e, 0x0d6b, 0x6a52, 0x15f7, + 0x1a2c, 0x0eb3, 0x3c95, 0x4642, 0x0931, 0x2210, 0x24fc, 0x7a5a, 0x5806, 0x6f7d, + 0x0b61, 0x549a, 0x0680, 0x6ded, 0x5153, 0x7ab9, 0x2dff, 0x136c, 0x1c64, 0x25a0, + 0x4586, 0x4fd6, 0x274f, 0x614c, 0x436e, 0x24c9, 0x6997, 0x6693, 0x3199, 0x2d7b, + 0x7416, 0x042e, 0x27d4, 0x594d, 0x3581, 0x7273, 0x509f, 0x3f73, 0x11fc, 0x2a36, + 0x4d44, 0x1c06, 0x3e15, 0x7efd, 0x3af0, 0x458c, 0x7f03, 0x02ff, 0x5cd6, 0x5f3e, + 0x5cfa, 0x2446, 0x1d5b, 0x71d7, 0x3816, 0x49ed, 0x5ef0, 0x2241, 0x2415, 0x229c, + 0x06fa, 0x360b, 0x3530, 0x4b02, 0x29c9, 0x5747, 0x743d, 0x1363, 0x26cd, 0x5c41, + 0x7c7e, 0x4e93, 0x4987, 0x75bf, 0x10d1, 0x6822, 0x066c, 0x32cf, 0x1415, 0x6d37, + 0x64ee, 0x5750, 0x7cb2, 0x2f97, 0x0dfb, 0x59c0, 0x4ea8, 0x4177, 0x43c0, 0x2678, + 0x550f, 0x3eeb, 0x14e1, 0x2920, 0x57b1, 0x6efc, 0x2001, 0x36d1, 0x5564, 0x3bb8, + 0x5a86, 0x20b0, 0x0c15, 0x4100, 0x39c4, 0x0378, 0x32b8, 0x6dd6, 0x0879, 0x31f2, + 0x2b7e, 0x34a4, 0x0171, 0x463a, 0x35c2, 0x513f, 0x76dc, 0x786e, 0x42a3, 0x68e1, + 0x634f, 0x323b, 0x4c3c, 0x1ef9, 0x7302, 0x6eb3, 0x6b1f, 0x51e0, 0x7777, 0x17c5, + 0x3933, 0x0179, 0x660c, 0x33de, 0x52c9, 0x4210, 0x40f9, 0x7de6, 0x56db, 0x12b6, + 0x46d3, 0x5030, 0x108d, 0x0791, 0x672c, 0x29d2, 0x0455, 0x5f9c, 0x02f9, 0x44d0, + 0x1a93, 0x5e09, 0x0f39, 0x5b98, 0x1f6a, 0x55ad, 0x7392, 0x2557, 0x3c1d, 0x0101, + 0x3bf3, 0x02cf, 0x774d, 0x56b1, 0x553a, 0x399a, 0x72d8, 0x4279, 0x11d2, 0x3ac6, + 0x5ec6, 0x5cd0, 0x54e5, 0x64c4, 0x26a3, 0x13eb, 0x294b, 0x489c, 0x5d1e, 0x6264, + 0x796f, 0x3a7e, 0x0b23, 0x39bd, 0x2725, 0x316f, 0x57dc, 0x3c6b, 0x63ee, 0x3da9, + 0x08b4, 0x6a28, 0x0981, 0x3361, 0x517a, 0x056c, 0x7fac, 0x2867, 0x4ed3, 0x1f8d, + 0x0fa9, 0x3100, 0x1756, 0x5d66, 0x59eb, 0x0c2c, 0x0929, 0x6604, 0x4a35, 0x59c9, + 0x577b, 0x0bdf, 0x6f66, 0x75a8, 0x38d2, 0x7bf2, 0x7cdd, 0x30e9, 0x3e6a, 0x563e, + 0x733d, 0x4f70, 0x0c62, 0x23c0, 0x7468, 0x7a78, 0x1dd0, 0x2315, 0x01b4, 0x07d0, + 0x5a21, 0x1a4a, 0x7693, 0x50ee, 0x5772, 0x7ad7, 0x2ba0, 0x5824, 0x1734, 0x60b3, + 0x6c5b, 0x1899, 0x6650, 0x2e1d, 0x5c6c, 0x616a, 0x4b4a, 0x5c8d, 0x7c69, 0x438c, + 0x470e, 0x5d87, 0x1c5b, 0x7990, 0x7ca9, 0x044c, 0x6e3e, 0x2ecc, 0x1440, 0x4c44, + 0x286f, 0x3b87, 0x5fd7, 0x7051, 0x32fa, 0x4548, 0x19bd, 0x1878, 0x6cdc, 0x5ea2, + 0x7179, 0x3df1, 0x6ca7, 0x0e04, 0x7ae0, 0x6ba4, 0x5f38, 0x4c88, 0x75ea, 0x1615, + 0x73cd, 0x70fd, 0x2cf1, 0x6475, 0x10fc, 0x104c, 0x3f43, 0x469e, 0x3f19, 0x5f0e, + 0x6e14, 0x1993, 0x1da6, 0x7669, 0x1c31, 0x4b20, 0x72ae, 0x54bb, 0x26fb, 0x5cf4, + 0x3e40, 0x4a0b, 0x7f82, 0x08ff, 0x7f58, 0x1d7c, 0x7f2e, 0x031d, 0x50cd, 0x4d62, + 0x3b2b, 0x6843, 0x45b7, 0x2a54, 0x0347, 0x76fd, 0x0e39, 0x12d7, 0x22ca, 0x27f2, + 0x5384, 0x2e81, 0x1f27, 0x650c, 0x3f9e, 0x4195, 0x686d, 0x26b8, 0x5ab4, 0x43de, + 0x2481, 0x6092, 0x3b55, 0x33ac, 0x1227, 0x6f1a, 0x35ac, 0x4eb1, 0x50f7, 0x1943, + 0x0735, 0x298a, 0x663a, 0x29e7, 0x4d8c, 0x3875, 0x5978, 0x6d55, 0x55db, 0x49a5, + 0x78ef, 0x1d19, 0x1301, 0x6b71, 0x5f69, 0x34c2, 0x66c1, 0x39e2, 0x2194, 0x5037, + 0x5d25, 0x411e, 0x0e63, 0x2f17, 0x139e, 0x4057, 0x095f, 0x201f, 0x0ad7, 0x636d, + 0x06a7, 0x6404, 0x22f4, 0x4307, 0x3841, 0x51fe, 0x0cca, 0x6590, 0x3422, 0x018f, + 0x7202, 0x68ff, 0x281c, 0x5a61, 0x3636, 0x422e, 0x7727, 0x1f01, 0x7fb4, 0x5401, + 0x69ca, 0x7795, 0x0371, 0x7cf4, 0x355b, 0x07af, 0x48f4, 0x4117, 0x2fd2, 0x6053, + 0x151c, 0x4180, 0x769c, 0x674a, 0x2440, 0x5e27, 0x45e1, 0x5866, 0x2c86, 0x0f57, + 0x4879, 0x2ae9, 0x2a7e, 0x1484, 0x226c, 0x011f, 0x6ede, 0x2f67, 0x6d7f, 0x4cf4, + 0x3447, 0x3d86, 0x74f5, 0x265a, 0x59a2, 0x2ea5, 0x6b4a, 0x1d55, 0x21e1, 0x3278, + 0x2e6c, 0x2f79, 0x6804, 0x6a76, 0x0e5c, 0x5306, 0x4c67, 0x53c0, 0x4db6, 0x32b1, + 0x5729, 0x179e, 0x444d, 0x4b8d, 0x389f, 0x3074, 0x3266, 0x4e75, 0x49cf, 0x2e93, + 0x4665, 0x7901, 0x071d, 0x22dc, 0x7957, 0x5f20, 0x35ed, 0x282e, 0x5605, 0x6b83, + 0x1699, 0x4240, 0x3136, 0x227e, 0x7255, 0x7cbb, 0x582d, 0x7a8a, 0x7919, 0x2ede, + 0x42ce, 0x3f55, 0x1be8, 0x3373, 0x190a, 0x0c74, 0x7899, 0x08c6, 0x1d43, 0x02e1, + 0x0773, 0x4ce2, 0x4254, 0x7da3, 0x20db, 0x58e5, 0x2a11, 0x7dc8, 0x33c0, 0x6c45, + 0x06bb, 0x1baa, 0x6664, 0x752e, 0x0c40, 0x015b, 0x558f, 0x3672, 0x075f, 0x3dbf, + 0x467d, 0x1e50, 0x0fdf, 0x2539, 0x29b4, 0x5232, 0x36fc, 0x3949, 0x47c6, 0x28af, + 0x2f55, 0x5deb, 0x40e2, 0x3d74, 0x6fa4, 0x3243, 0x4edb, 0x6e82, 0x03a3, 0x36b3, + 0x31d4, 0x5655, 0x1b0b, 0x70dc, 0x32e3, 0x5a9d, 0x35d6, 0x6db8, 0x5121, 0x2fa0, + 0x2ba9, 0x133d, 0x71d1, 0x3a24, 0x25db, 0x7850, 0x321d, 0x1587, 0x196d, 0x0546, + 0x7b15, 0x0a10, 0x2648, 0x51c2, 0x1f51, 0x329f, 0x10b8, 0x23fc, 0x61ff, 0x0b48, + 0x7e8a, 0x7081, 0x505b, 0x7e0b, 0x6536, 0x3810, 0x6bd9, 0x2755, 0x1c0c, 0x1a99, + 0x3acc, 0x75f0, 0x54c1, 0x45e7, 0x2eab, 0x25e1, 0x7e11, 0x060e, 0x2c0a, 0x77c5, + 0x68b0, 0x7d63, 0x5706, 0x09bd, 0x53ae, 0x30ad, 0x395e, 0x6a64, 0x3fc8, 0x4486, + 0x3fb3, 0x0ec5, 0x5d9c, 0x7428, 0x41bf, 0x3e81, 0x17f0, 0x7030, 0x0a95, 0x20c7, + 0x62f7, 0x2c3a, 0x2cd8, 0x5759, 0x173d, 0x57c3, 0x3409, 0x3542, 0x26e2, 0x595f, + 0x18f1, 0x6b31, 0x1af2, 0x36e3, 0x6897, 0x17d7, 0x52f4, 0x3c2f, 0x4c08, 0x4b7b, + 0x0b88, 0x15ad, 0x1251, 0x4def, 0x1abe, 0x46da, 0x626b, 0x48fb, 0x530d, 0x440f, + 0x44fb, 0x6113, 0x6f44, 0x67ab, 0x33d6, 0x2208, 0x29fd, 0x4808, 0x1857, 0x3eb2, + 0x5c08, 0x0d4d, 0x0480, 0x6e7a, 0x3b7f, 0x53f9, 0x2d31, 0x00c8, 0x178c, 0x1010, + 0x7a3c, 0x3062, 0x365c, 0x6357, 0x1f95, 0x0488, 0x5ade, 0x0e95, 0x6dcf, 0x214f, + 0x16ef, 0x5e81, 0x4408, 0x2f10, 0x5bc3, 0x547c, 0x2582, 0x64f7, 0x60bc, 0x377f, + 0x49e7, 0x1cbf, 0x0608, 0x4fb8, 0x24ab, 0x6aaf, 0x3c48, 0x74a4, 0x6952, 0x6fd9, + 0x4e63, 0x0410, 0x01e2, 0x5dd9, 0x3cbc, 0x0a36, 0x2ffc, 0x568e, 0x277a, 0x083c, + 0x0d17, 0x2c04, 0x5db5, 0x5eea, 0x5001, 0x1ce3, 0x607d, 0x7bbc, 0x4141, 0x346e, + 0x1397, 0x44f4, 0x64a3, 0x05ba, 0x38f5, 0x0872, 0x1c8f, 0x025a, 0x491e, 0x1b44, + 0x42f1, 0x1e3a, 0x289d, 0x7802, 0x4a8e, 0x5220, 0x7fda, 0x5b1b, 0x69c2, 0x5fcf, + 0x039b, 0x5ad6, 0x566c, 0x1a05, 0x1fda, 0x5caf, 0x7d1e, 0x0fcb, 0x24f4, 0x52c1, + 0x2da6, 0x5518, 0x07d9, 0x41e1, 0x16b1, 0x0d6f, 0x3c99, 0x2d7f, 0x3585, 0x360f, + 0x7441, 0x2924, 0x5568, 0x6eb7, 0x3937, 0x255b, 0x7751, 0x3dad, 0x517e, 0x4f74, + 0x1dd4, 0x7994, 0x1444, 0x1050, 0x6e18, 0x12db, 0x1f2b, 0x49a9, 0x5f6d, 0x6903, + 0x772b, 0x1488, 0x6d83, 0x3078, 0x4669, 0x08ca, 0x4258, 0x28b3, 0x6fa8, 0x0a14, + 0x10bc, 0x09c1, 0x3fcc, 0x17db, 0x0b8c, 0x00cc, 0x3660, 0x6fdd, 0x3cc0, 0x1e3e, + 0x7fde, 0x6ebb, 0x5182, 0x00d0, 0x7fe2, 0x03e0, 0x542b, 0x7354, 0x0ede, 0x02ae, + 0x2b17, 0x3bcf, 0x04cd, 0x27b0, 0x4be3, 0x3ef4, 0x01bd, 0x5b69, 0x223b, 0x237d, + 0x77bf, 0x475a, 0x66ef, 0x526f, 0x4e23, 0x4ace, 0x69f4, 0x03dc, 0x2527, 0x005c, + 0x2b3b, 0x7db6, 0x5427, 0x7b28, 0x62b4, 0x78ac, 0x5890, 0x4d07, 0x127f, 0x68aa, + 0x6d00, 0x240f, 0x460b, 0x15c0, 0x4847, 0x6965, 0x16d8, 0x55ee, 0x246a, 0x19a6, + 0x314e, 0x34d5, 0x1fc3, 0x2a91, 0x5e51, 0x2328, 0x449f, 0x110f, 0x74d2, 0x7350, + 0x58d3, 0x56c4, 0x3711, 0x4cd0, 0x0eda, 0x7b3b, 0x41aa, 0x7b85, 0x4723, 0x4972, + 0x7b9a, 0x4f87, 0x3fe1, 0x6a07, 0x6443, 0x557b, 0x1546, 0x0a49, 0x76c6, 0x2681, + 0x5a2a, 0x1eca, 0x622d, 0x65cd, 0x5c56, 0x1841, 0x37d9, 0x5928, 0x6774, 0x3748, + 0x4cbd, 0x02aa, 0x7d91, 0x050f, 0x0d96, 0x1b98, 0x2b13, 0x7b72, 0x0cef, 0x4a66, + 0x4bbb, 0x1094, 0x48a3, 0x2fd9, 0x6a7d, 0x5bca, 0x3475, 0x4f10, 0x11b0, 0x2050, + 0x1bc0, 0x67dc, 0x074b, 0x31ac, 0x6243, 0x7a14, 0x2cb0, 0x2be2, 0x0ca2, 0x14f4, + 0x535c, 0x7286, 0x0f81, 0x3bcb, 0x6c33, 0x73a5, 0x14ae, 0x751c, 0x04c9, 0x730a, + 0x0574, 0x2d39, 0x5b23, 0x3032, 0x31eb, 0x6ae1, 0x2aa8, 0x3a5d, 0x610c, 0x4050, + 0x4f09, 0x6014, 0x2109, 0x43c9, 0x1a53, 0x2081, 0x2296, 0x2614, 0x7d5d, 0x3d2e, + 0x0dbe, 0x2dd7, 0x3977, 0x7562, 0x495f, 0x27ac, 0x0149, 0x0f11, 0x6929, 0x6da6, + 0x4bdf, 0x182e, 0x61d6, 0x3aa3, 0x18c8, 0x0a6c, 0x21b8, 0x5700, 0x722c, 0x06f4, + 0x71a8, 0x40b9, 0x3397, 0x479d, 0x63c5, 0x11a9, 0x0958, 0x6f3d, 0x2846, 0x6c7e, + 0x7c40, 0x2b77, 0x48cb, 0x2c5d, 0x0aae, 0x6698, 0x50a4, 0x3ef0, 0x5a8b, 0x55b2, + 0x553f, 0x5643, 0x01b9, 0x647a, 0x1dab, 0x6d5a, 0x66c6, 0x2aee, 0x344c, 0x0c79, + 0x20e0, 0x054b, 0x6204, 0x36e8, 0x1256, 0x74a9, 0x3001, 0x2929, 0x1dd9, 0x4ad3, + 0x62b9, 0x374d, 0x0cf4, 0x7567, 0x61db, 0x61b2, 0x639c, 0x61ad, 0x65ba, 0x5b65, + 0x70ca, 0x1e8e, 0x5b8e, 0x3231, 0x2237, 0x266e, 0x6397, 0x7095, 0x6bfe, 0x24bf, + 0x5bf2, 0x4b34, 0x6310, 0x2c9a, 0x7ece, 0x73e1, 0x0b01, 0x428d, 0x06d1, 0x4d1b, + 0x29a0, 0x0dd2, 0x65e3, 0x6703, 0x6326, 0x0850, 0x0657, 0x4345, 0x642e, 0x0a80, + 0x761f, 0x2379, 0x3d62, 0x79d8, 0x6aa5, 0x6e70, 0x77bb, 0x6b27, 0x3369, 0x1794, + 0x5228, 0x157d, 0x70f3, 0x4382, 0x3165, 0x30df, 0x386b, 0x2a4a, 0x6586, 0x0f4d, + 0x2dcd, 0x14ea, 0x231e, 0x591e, 0x3605, 0x0250, 0x09b7, 0x5265, 0x4331, 0x433b, + 0x2c53, 0x61a8, 0x1eb7, 0x4756, 0x36a1, 0x3cf2, 0x0800, 0x783e, 0x66eb, 0x3735, + 0x65b5, 0x79fc, 0x05a2, 0x2365, 0x3a0c, 0x53a8, 0x3e9a, 0x352a, 0x7590, 0x6152, + 0x4d4a, 0x5e0f, 0x11d8, 0x161b, 0x72b4, 0x586c, 0x59a8, 0x7856, 0x5061, 0x4fbe, + 0x0d1d, 0x4760, 0x1285, 0x3d34, 0x21be, 0x526b, 0x3a12, 0x021e, 0x5f93, 0x2f8e, + 0x4e1f, 0x2597, 0x62e2, 0x1531, 0x116c, 0x1400, 0x1b22, 0x211e, 0x34ec, 0x7bd1, + 0x164a, 0x47b2, 0x113d, 0x4096, 0x3776, 0x57ba, 0x7a81, 0x1334, 0x6b9b, 0x0bd6, + 0x193a, 0x6741, 0x2078, 0x1ec1, 0x41d8, 0x5b60, 0x5915, 0x4aca, 0x132b, 0x28e8, + 0x4f38, 0x0534, 0x69f0, 0x0297, 0x70c5, 0x7019, 0x5d4f, 0x0798, 0x2952, 0x605a, + 0x680b, 0x5483, 0x4148, 0x601b, 0x63cc, 0x401f, 0x4208, 0x7a52, 0x2f41, 0x6a3e, + 0x561d, 0x7818, 0x1b72, 0x1e18, 0x0e8d, 0x36ab, 0x7049, 0x778d, 0x302a, 0x03d8, + 0x1575, 0x0090, 0x1e02, 0x09fe, 0x2523, 0x51e8, 0x0989, 0x1018, 0x4a96, 0x0098, + 0x349d, 0x23d7, 0x1809, 0x4f4f, 0x67a4, 0x2018, 0x2049, 0x4018, 0x4081, 0x6f05, + 0x7471, 0x28f1, 0x4afc, 0x0818, 0x30a7, 0x0218, 0x79c4, 0x3cfc, 0x13c8, 0x1e89, + 0x04fc, 0x0058, 0x51b0, 0x0020, 0x00f2, 0x468c, 0x2b37, 0x2548, 0x5b89, 0x3dce, + 0x4940, 0x559e, 0x5f8d, 0x3958, 0x2d5b, 0x29c3, 0x44c1, 0x47d5, 0x08ea, 0x5dfa, + 0x4201, 0x1bb9, 0x0ad0, 0x33cf, 0x17b6, 0x6673, 0x7d3e, 0x016a, 0x12a7, 0x20ea, + 0x4514, 0x7dd7, 0x5021, 0x7db2, 0x103a, 0x0782, 0x3495, 0x70eb, 0x5423, 0x31e3, + 0x0369, 0x32f2, 0x31cc, 0x6dc7, 0x3ba9, 0x4eea, 0x612c, 0x36c2, 0x20a1, 0x3252, + 0x57fe, 0x40f1, 0x68d2, 0x71e0, 0x19c6, 0x785f, 0x462b, 0x134c, 0x04aa, 0x5130, + 0x1eea, 0x0555, 0x446f, 0x322c, 0x6ea4, 0x7b24, 0x6463, 0x51d1, 0x4af3, 0x6b92, + 0x62b0, 0x35fc, 0x2232, 0x16a8, 0x6224, 0x228d, 0x2437, 0x072c, 0x6f5d, 0x5f2f, + 0x71c8, 0x7910, 0x3400, 0x49de, 0x2a27, 0x7928, 0x388b, 0x3f64, 0x593e, 0x7a99, + 0x6c14, 0x7264, 0x7eee, 0x0c83, 0x4832, 0x1bf7, 0x457d, 0x78a8, 0x0df2, 0x02f0, + 0x6d28, 0x4c76, 0x588c, 0x32c0, 0x75b0, 0x5315, 0x0d77, 0x6813, 0x1354, 0x4b9c, + 0x4622, 0x5738, 0x5c32, 0x38ae, 0x166a, 0x4e84, 0x4168, 0x1d64, 0x1881, 0x59b1, + 0x5741, 0x21f0, 0x6a5e, 0x2f88, 0x3edc, 0x3456, 0x67c4, 0x2669, 0x2911, 0x4d03, + 0x1603, 0x6eed, 0x13be, 0x703f, 0x127b, 0x41ce, 0x6392, 0x0aa4, 0x6424, 0x2c49, + 0x4e19, 0x3fc2, 0x4914, 0x7437, 0x676a, 0x4495, 0x5352, 0x396d, 0x486f, 0x3418, + 0x033d, 0x596e, 0x3e60, 0x57d2, 0x4704, 0x2ce7, 0x1963, 0x36f2, 0x4443, 0x1900, + 0x1ae8, 0x68a6, 0x3b75, 0x3c3e, 0x0bb2, 0x2eba, 0x6cfc, 0x061d, 0x6bc4, 0x45f6, + 0x37ba, 0x3adb, 0x2bc0, 0x7d72, 0x15d7, 0x2c19, 0x332f, 0x5715, 0x2e3d, 0x30bc, + 0x0bff, 0x381f, 0x4551, 0x506a, 0x4ba5, 0x6be8, 0x5a0b, 0x1aa8, 0x698d, 0x620e, + 0x6284, 0x7090, 0x5505, 0x240b, 0x4c32, 0x1f60, 0x15e8, 0x1866, 0x4607, 0x0d5c, + 0x6bf9, 0x4817, 0x6295, 0x33e5, 0x57e3, 0x5408, 0x4454, 0x048f, 0x4925, 0x2d40, + 0x0ab5, 0x101f, 0x451b, 0x441e, 0x4439, 0x627a, 0x4465, 0x450a, 0x4828, 0x67ba, + 0x5337, 0x1260, 0x4428, 0x46e9, 0x379f, 0x15bc, 0x4536, 0x4c17, 0x7aaa, 0x5e90, + 0x4843, 0x6dde, 0x6f6e, 0x4417, 0x16b9, 0x548b, 0x4633, 0x1fa4, 0x5326, 0x0ea4, + 0x2201, 0x6366, 0x67d5, 0x7a4b, 0x613d, 0x49f6, 0x3303, 0x4fc7, 0x135d, 0x378e, + 0x4480, 0x2591, 0x6684, 0x74b3, 0x4525, 0x24ba, 0x2d6c, 0x6961, 0x3ddf, 0x041f, + 0x4e48, 0x60a1, 0x16d4, 0x5ac3, 0x5bed, 0x3b64, 0x0b6d, 0x6f29, 0x62dc, 0x3fad, + 0x1ad7, 0x26c7, 0x7e6f, 0x651b, 0x54a6, 0x5393, 0x2f3a, 0x0744, 0x06a0, 0x29f6, + 0x25c0, 0x1952, 0x6f89, 0x35bb, 0x2e51, 0x6d64, 0x4432, 0x4d9b, 0x793c, 0x55ea, + 0x5812, 0x1d28, 0x78d4, 0x50dc, 0x2466, 0x6852, 0x3b10, 0x032c, 0x6df9, 0x7f67, + 0x2fb7, 0x770c, 0x485e, 0x45c6, 0x2179, 0x0e48, 0x068c, 0x2801, 0x0899, 0x5d03, + 0x5eab, 0x72bd, 0x38b7, 0x3e4f, 0x515f, 0x090e, 0x1719, 0x1db5, 0x46f3, 0x4b2f, + 0x5fbc, 0x19a2, 0x7ac5, 0x3f28, 0x3686, 0x2303, 0x314a, 0x520d, 0x630b, 0x6413, + 0x221c, 0x0ae6, 0x123b, 0x019e, 0x6381, 0x0cd9, 0x18ad, 0x7211, 0x093d, 0x5a70, + 0x155a, 0x2f26, 0x67f0, 0x5d34, 0x308c, 0x13ad, 0x2508, 0x202e, 0x4d2f, 0x66d0, + 0x126a, 0x5046, 0x1151, 0x34d1, 0x7a66, 0x1310, 0x391c, 0x07be, 0x1fbf, 0x0380, + 0x38da, 0x4903, 0x3ca1, 0x6062, 0x04b2, 0x7fc3, 0x4e08, 0x77a4, 0x1429, 0x1f10, + 0x464e, 0x3645, 0x58b8, 0x244f, 0x6ce5, 0x5875, 0x5c3b, 0x6759, 0x0ebf, 0x152b, + 0x1195, 0x2af8, 0x5341, 0x2c95, 0x5b08, 0x2a8d, 0x1a38, 0x012e, 0x544f, 0x5c7b, + 0x5e4d, 0x5c9c, 0x7ec9, 0x2e2c, 0x4fe2, 0x6c6a, 0x1166, 0x5d96, 0x331e, 0x7c78, + 0x0632, 0x1c6a, 0x4592, 0x045b, 0x64ca, 0x7ae6, 0x4a11, 0x76a2, 0x327e, 0x2baf, + 0x275b, 0x60c2, 0x1ce9, 0x01c3, 0x15c6, 0x1a59, 0x40bf, 0x2324, 0x6158, 0x7477, + 0x47db, 0x1887, 0x449b, 0x3309, 0x6521, 0x6ceb, 0x1c70, 0x3e00, 0x7b50, 0x287e, + 0x0ba1, 0x7060, 0x7634, 0x4c53, 0x25ac, 0x6e4d, 0x60e6, 0x5f47, 0x7182, 0x1624, + 0x1673, 0x6bb3, 0x1378, 0x6cb6, 0x713c, 0x6484, 0x37a9, 0x73dc, 0x19f2, 0x110b, + 0x2e0b, 0x46ad, 0x05e1, 0x5d75, 0x74ce, 0x0fb8, 0x0afc, 0x59fa, 0x69a3, 0x6613, + 0x3c72, 0x7fbb, 0x4b94, 0x1f9c, 0x1b4b, 0x057b, 0x669f, 0x0990, 0x7dde, 0x6f75, + 0x4da2, 0x7c01, 0x37ef, 0x0bee, 0x24d5, 0x4a44, 0x2147, 0x564d, 0x4540, 0x7cec, + 0x6ad9, 0x734c, 0x437a, 0x23cf, 0x7003, 0x797e, 0x58cf, 0x39cc, 0x7bfa, 0x6273, + 0x2d87, 0x295a, 0x5138, 0x3c7a, 0x697c, 0x2734, 0x4801, 0x63fd, 0x31a5, 0x6a37, + 0x3515, 0x5cdf, 0x3dfa, 0x11e1, 0x4e8d, 0x54f4, 0x7422, 0x13fa, 0x2b63, 0x5549, + 0x4c21, 0x4288, 0x52ae, 0x56c0, 0x043a, 0x3c02, 0x1e61, 0x6a16, 0x370d, 0x7ba9, + 0x06cc, 0x6452, 0x58f6, 0x0a58, 0x1b1c, 0x41b9, 0x6e93, 0x4981, 0x3a35, 0x7b4a, + 0x197e, 0x3720, 0x5616, 0x623c, 0x22ed, 0x1850, 0x2eef, 0x1ed9, 0x191b, 0x76d5, + 0x53d1, 0x3757, 0x445e, 0x37e8, 0x6b5b, 0x4ccc, 0x3d97, 0x051e, 0x1801, 0x315d, + 0x0ed6, 0x2aa0, 0x3553, 0x19b5, 0x1b03, 0x16e7, 0x25f2, 0x111e, 0x68c1, 0x5e60, + 0x6547, 0x74e1, 0x0b59, 0x56d3, 0x3ec3, 0x241e, 0x3b90, 0x128e, 0x531e, 0x461a, + 0x4e00, 0x6974, 0x1700, 0x62c3, 0x0499, 0x4d16, 0x1cd0, 0x7b37, 0x3c59, 0x2b4a, + 0x2492, 0x6252, 0x41a6, 0x2bf1, 0x299b, 0x31bb, 0x5989, 0x1bcf, 0x4d73, 0x7295, + 0x0358, 0x0cb1, 0x270c, 0x0f90, 0x767a, 0x73b4, 0x4318, 0x5bd9, 0x3433, 0x48b2, + 0x0e74, 0x3484, 0x39f3, 0x205f, 0x356c, 0x0cfe, 0x5412, 0x10a3, 0x5e38, 0x7b81, + 0x488a, 0x0da5, 0x617b, 0x3a6c, 0x471f, 0x31fa, 0x5783, 0x611b, 0x07e1, 0x6023, + 0x19ce, 0x0583, 0x3b98, 0x3041, 0x4c99, 0x7319, 0x2d02, 0x14bd, 0x1767, 0x22a5, + 0x2878, 0x3d3d, 0x75b9, 0x2090, 0x3e7b, 0x2118, 0x3a8f, 0x7571, 0x57ed, 0x0dcd, + 0x5ed7, 0x496e, 0x39ab, 0x0f20, 0x3ff2, 0x4267, 0x7b96, 0x0a23, 0x65de, 0x08d9, + 0x6785, 0x6d92, 0x34e6, 0x17ea, 0x44b0, 0x10cb, 0x6d11, 0x0b9b, 0x78bd, 0x6fec, + 0x7a25, 0x49b8, 0x536d, 0x6e27, 0x6a8e, 0x5f7c, 0x4a77, 0x1497, 0x2ab9, 0x1de3, + 0x2d4a, 0x105f, 0x2625, 0x4f83, 0x3988, 0x7760, 0x28c4, 0x02bd, 0x3fdd, 0x543a, + 0x1f3c, 0x2b26, 0x79a5, 0x27bf, 0x0eef, 0x5191, 0x00e1, 0x03ef, 0x238e, 0x6eca, + 0x4e34, 0x3ccf, 0x1feb, 0x224a, 0x5fe0, 0x4769, 0x0d80, 0x5b78, 0x7452, 0x4bf2, + 0x05cb, 0x4add, 0x492f, 0x66fe, 0x5dc6, 0x6a03, 0x569f, 0x006b, 0x6714, 0x5cbe, + 0x643f, 0x567b, 0x6321, 0x7d2d, 0x70a6, 0x52d0, 0x3176, 0x69d1, 0x17a5, 0x5ae5, + 0x0261, 0x5b2a, 0x2c64, 0x4a9d, 0x20f1, 0x16c0, 0x6d6b, 0x2d8e, 0x375e, 0x41f0, + 0x63ad, 0x2db5, 0x6c8f, 0x2933, 0x0abf, 0x3594, 0x723d, 0x5577, 0x3ab4, 0x256a, + 0x34fd, 0x64b2, 0x1542, 0x0881, 0x0be7, 0x4503, 0x41e9, 0x4150, 0x7867, 0x1b53, + 0x1296, 0x1c9e, 0x3eab, 0x4300, 0x7a0d, 0x7811, 0x7829, 0x5ef9, 0x705a, 0x0d26, + 0x681c, 0x5010, 0x702a, 0x7bcb, 0x181a, 0x300b, 0x1029, 0x084b, 0x0829, 0x0a45, + 0x13d9, 0x01f1, 0x7378, 0x65f2, 0x76c2, 0x085f, 0x0652, 0x0de1, 0x5085, 0x06e0, + 0x1644, 0x0a8f, 0x456c, 0x0666, 0x7e35, 0x762e, 0x7654, 0x79e7, 0x1b6b, 0x2ca9, + 0x383a, 0x5c01, 0x4dce, 0x7edd, 0x7c21, 0x429c, 0x6560, 0x63a6, 0x4821, 0x24ce, + 0x1201, 0x267d, 0x0c1a, 0x5b9d, 0x72dd, 0x30ee, 0x5a26, 0x7102, 0x1c36, 0x387a, + 0x2199, 0x0f5c, 0x74fa, 0x3378, 0x2a16, 0x158c, 0x7e8f, 0x6b36, 0x1ac3, 0x6ab4, + 0x277f, 0x3614, 0x1449, 0x5274, 0x5895, 0x592d, 0x4bc0, 0x2ddc, 0x18cd, 0x61b7, + 0x6c03, 0x4340, 0x05a7, 0x1ec6, 0x5d54, 0x3d01, 0x4945, 0x055a, 0x6229, 0x345b, + 0x6429, 0x6213, 0x629a, 0x74b8, 0x0b72, 0x1dba, 0x2221, 0x2afd, 0x4fe7, 0x6489, + 0x69a8, 0x554e, 0x58fb, 0x62c8, 0x598e, 0x7576, 0x678a, 0x4ae2, 0x70ab, 0x3010, + 0x508a, 0x61bc, 0x629f, 0x61ea, 0x7eb4, 0x65c9, 0x334f, 0x1e9d, 0x10e2, 0x2855, + 0x5c52, 0x2b86, 0x59d1, 0x6f4c, 0x5520, 0x63d4, 0x71e8, 0x66a7, 0x2426, 0x48da, + 0x3e26, 0x50b3, 0x3f84, 0x55c1, 0x787f, 0x0703, 0x4c4d, 0x21c7, 0x32c9, 0x71b7, + 0x20c1, 0x47ac, 0x56ec, 0x61e5, 0x33ef, 0x0a7b, 0x44e1, 0x183d, 0x1f7b, 0x6938, + 0x5294, 0x7be0, 0x37d5, 0x1b31, 0x761a, 0x1659, 0x7eaf, 0x40a5, 0x1137, 0x62f1, + 0x5c21, 0x140f, 0x7e55, 0x25a6, 0x3af6, 0x5fa2, 0x54eb, 0x6baa, 0x3e46, 0x6750, + 0x21e7, 0x1343, 0x6bdf, 0x3785, 0x5007, 0x5b6f, 0x4611, 0x2087, 0x71ae, 0x5924, + 0x7596, 0x28f7, 0x44c7, 0x59b7, 0x6770, 0x4fcd, 0x7e75, 0x587b, 0x0638, 0x11e7, + 0x3a3b, 0x3d43, 0x6d17, 0x0d2c, 0x7e3b, 0x21cd, 0x7e5b, 0x022d, 0x1cb6, 0x3539, + 0x2ed5, 0x3a1b, 0x4c7f, 0x759f, 0x2981, 0x5e1e, 0x260b, 0x65c4, 0x0d66, 0x2374, + 0x0247, 0x3744, 0x0bcd, 0x080f, 0x09e7, 0x562c, 0x4cb9, 0x1e27, 0x3d5d, 0x6a4d, + 0x334a, 0x4217, 0x272c, 0x779c, 0x5730, 0x0e9c, 0x1c96, 0x3039, 0x48d2, 0x009f, + 0x12ae, 0x5492, 0x2e58, 0x2961, 0x53d8, 0x4157, 0x6567, 0x402e, 0x5e79, 0x70d4, + 0x1870, 0x07a7, 0x3a55, 0x02a6, 0x30d7, 0x4f47, 0x0281, 0x4f5e, 0x7d8d, 0x34ac, + 0x4a3d, 0x67b3, 0x2dae, 0x4027, 0x68da, 0x0998, 0x3ecb, 0x00a7, 0x0d46, 0x51f7, + 0x2bdb, 0x1e11, 0x2350, 0x4b0b, 0x6e47, 0x0227, 0x6d31, 0x2900, 0x2c34, 0x4090, + 0x4789, 0x1e98, 0x15f2, 0x79d3, 0x77ef, 0x050b, 0x23ae, 0x002f, 0x0401, 0x1d07, + 0x0d92, 0x6fca, 0x6aa0, 0x1a27, 0x10dd, 0x7495, 0x3770, 0x2cd2, 0x5af7, 0x64e8, + 0x1cb0, 0x60e0, 0x682e, 0x4fa9, 0x0e86, 0x0c9b, 0x0cc3, 0x0479, 0x3053, 0x1184, + 0x75cb, 0x6348, 0x5e72, 0x6c88, 0x5330, 0x2140, 0x2f01, 0x1b94, 0x4993, 0x546d, + 0x679c, 0x3863, 0x2b0f, 0x6104, 0x48ec, 0x6cd4, 0x32db, 0x4400, 0x159e, 0x715a, + 0x58a7, 0x4b6c, 0x4de0, 0x3122, 0x0678, 0x46cb, 0x0d3e, 0x3e1e, 0x4c91, 0x3ea3, + 0x21f9, 0x5c2a, 0x1421, 0x47f9, 0x53ea, 0x2850, 0x0eae, 0x6e6b, 0x00b9, 0x7b6e, + 0x6d43, 0x1001, 0x309e, 0x1931, 0x0ceb, 0x09ae, 0x77b6, 0x3c90, 0x5c4d, 0x7d54, + 0x45d8, 0x6631, 0x38c9, 0x75e1, 0x25d2, 0x42c5, 0x26d9, 0x05ff, 0x7072, 0x2165, + 0x1685, 0x0b39, 0x3290, 0x390b, 0x7c8a, 0x23ed, 0x3801, 0x7c4a, 0x1fae, 0x7dfc, + 0x2746, 0x4a62, 0x4e9f, 0x1a8a, 0x2c2b, 0x2978, 0x4bb7, 0x20b8, 0x3e72, 0x4df7, + 0x7449, 0x7021, 0x4477, 0x5a02, 0x04a1, 0x6a55, 0x0eb6, 0x5156, 0x136f, 0x7419, + 0x5950, 0x7f06, 0x5f41, 0x3533, 0x574a, 0x1418, 0x5753, 0x57b4, 0x36d4, 0x2b81, + 0x463d, 0x6b22, 0x17c8, 0x1090, 0x29d5, 0x3c20, 0x02d2, 0x26a6, 0x489f, 0x08b7, + 0x3364, 0x092c, 0x59cc, 0x0c65, 0x7a7b, 0x1737, 0x189c, 0x7cac, 0x2ecf, 0x717c, + 0x0e07, 0x3f46, 0x5f11, 0x7f85, 0x1d7f, 0x22cd, 0x2e84, 0x122a, 0x4eb4, 0x78f2, + 0x6b74, 0x0962, 0x6370, 0x281f, 0x4231, 0x2fd5, 0x4183, 0x226f, 0x2f6a, 0x2e6f, + 0x6a79, 0x3269, 0x2e96, 0x3139, 0x7cbe, 0x1d46, 0x4ce5, 0x0c43, 0x3675, 0x2f58, + 0x3d77, 0x35d9, 0x2fa3, 0x264b, 0x32a2, 0x1c0f, 0x75f3, 0x53b1, 0x6a67, 0x62fa, + 0x575c, 0x52f7, 0x4b7e, 0x6f47, 0x220b, 0x178f, 0x3065, 0x5bc6, 0x64fa, 0x4e66, + 0x5ddc, 0x6080, 0x3471, 0x28a0, 0x5223, 0x24f7, 0x551b, 0x393a, 0x3db0, 0x772e, + 0x307b, 0x3663, 0x1e41, 0x04d0, 0x3ef7, 0x252a, 0x7db9, 0x484a, 0x55f1, 0x58d6, + 0x4cd3, 0x1549, 0x2684, 0x7d94, 0x1b9b, 0x11b3, 0x67df, 0x6c36, 0x751f, 0x4f0c, + 0x43cc, 0x014c, 0x6da9, 0x339a, 0x11ac, 0x5a8e, 0x5646, 0x1259, 0x292c, 0x70cd, + 0x3234, 0x0b04, 0x4d1e, 0x3d65, 0x6e73, 0x6589, 0x14ed, 0x36a4, 0x7841, 0x4d4d, + 0x161e, 0x3a15, 0x2f91, 0x1140, 0x57bd, 0x132e, 0x0537, 0x63cf, 0x7a55, 0x1578, + 0x0a01, 0x204c, 0x6f08, 0x51b3, 0x468f, 0x08ed, 0x1bbc, 0x103d, 0x70ee, 0x5801, + 0x71e3, 0x6466, 0x6b95, 0x3403, 0x792b, 0x0df5, 0x4c79, 0x166d, 0x1d67, 0x1606, + 0x7042, 0x5355, 0x341b, 0x3b78, 0x2ebd, 0x2e40, 0x3822, 0x4c35, 0x1869, 0x0ab8, + 0x4421, 0x4539, 0x5e93, 0x67d8, 0x49f9, 0x3de2, 0x60a4, 0x54a9, 0x0747, 0x5815, + 0x50df, 0x068f, 0x5d06, 0x7ac8, 0x2306, 0x0940, 0x2f29, 0x7a69, 0x07c1, 0x4651, + 0x2452, 0x1a3b, 0x5c7e, 0x4595, 0x7ae9, 0x615b, 0x188a, 0x25af, 0x5f4a, 0x2e0e, + 0x5d78, 0x66a2, 0x6f78, 0x437d, 0x7981, 0x31a8, 0x5ce2, 0x043d, 0x6a19, 0x1981, + 0x623f, 0x3d9a, 0x3160, 0x0b5c, 0x2421, 0x3c5c, 0x6255, 0x767d, 0x5bdc, 0x488d, + 0x3a6f, 0x2d05, 0x22a8, 0x39ae, 0x426a, 0x78c0, 0x49bb, 0x398b, 0x02c0, 0x4e37, + 0x224d, 0x56a2, 0x5cc1, 0x2c67, 0x16c3, 0x3ab7, 0x64b5, 0x7a10, 0x5efc, 0x13dc, + 0x65f5, 0x7657, 0x2cac, 0x0c1d, 0x30f1, 0x1ac6, 0x3617, 0x5d57, 0x055d, 0x69ab, + 0x62cb, 0x3352, 0x2858, 0x3f87, 0x0706, 0x1f7e, 0x7be3, 0x3af9, 0x6bad, 0x7599, + 0x59ba, 0x7e5e, 0x353c, 0x0bd0, 0x562f, 0x48d5, 0x5495, 0x30da, 0x4f61, 0x2bde, + 0x4b0e, 0x23b1, 0x1d0a, 0x6831, 0x0c9e, 0x4996, 0x3866, 0x067b, 0x3e21, 0x6d46, + 0x1934, 0x26dc, 0x2168, 0x4ea2, 0x297b, 0x1372, 0x7f09, 0x29d8, 0x26a9, 0x0e0a, + 0x7f88, 0x4186, 0x2e72, 0x2fa6, 0x1c12, 0x64fd, 0x6083, 0x3efa, 0x484d, 0x43cf, + 0x339d, 0x14f0, 0x4d50, 0x6f0b, 0x08f0, 0x1d6a, 0x5358, 0x49fc, 0x54ac, 0x2455, + 0x4598, 0x5ce5, 0x1984, 0x22ab, 0x78c3, 0x5eff, 0x765a, 0x0709, 0x3afc, 0x4b11, + 0x6834, 0x7f0c, 0x0e0d, 0x4d53, 0x1d6d, 0x3aff, 0x7f0f, 0x030e, 0x76ee, 0x50ae, + 0x6de8, 0x2a45, 0x12c8, 0x7282, 0x030b, 0x27e3, 0x5a52, 0x76eb, 0x0f7d, 0x68f0, + 0x6581, 0x514e, 0x3f7f, 0x0180, 0x63f5, 0x1f08, 0x38a6, 0x635e, 0x42f8, 0x7311, + 0x50ab, 0x51ef, 0x5028, 0x6de5, 0x7943, 0x39d3, 0x6b62, 0x0888, 0x1208, 0x34b3, + 0x2f08, 0x5a95, 0x5e9a, 0x410f, 0x4048, 0x3bc7, 0x2a42, 0x2010, 0x6044, 0x12c5, + 0x6c2f, 0x4108, 0x7ce5, 0x46e2, 0x358d, 0x07a0, 0x1ef2, 0x661b, 0x1708, 0x421f, + 0x53f2, 0x0188, 0x727f, 0x7786, 0x5857, 0x0308, 0x6cb0, 0x5e18, 0x4171, 0x5fab, + 0x5959, 0x673b, 0x2ada, 0x55bc, 0x7ab4, 0x0f48, 0x1475, 0x73a1, 0x27e0, 0x0110, + 0x0f02, 0x5a4f, 0x14aa, 0x279d, 0x2dc8, 0x2dfa, 0x787a, 0x7553, 0x2072, 0x18eb, + 0x19e1, 0x43ba, 0x2605, 0x7136, 0x76e8, 0x3d1f, 0x3023, 0x0f7a, 0x71fb, 0x2d2a, + 0x750d, 0x712b, 0x42af, 0x72fb, 0x3a4e, 0x7236, 0x3798, 0x6ad2, 0x4041, 0x7518, + 0x68ed, 0x6005, 0x2041, 0x657e, 0x04c5, 0x4f01, 0x2fca, 0x7171, 0x35ce, 0x5bbb, + 0x7b63, 0x3117, 0x60d5, 0x1b89, 0x4a57, 0x42ba, 0x514b, 0x1085, 0x2bd3, 0x3f7c, + 0x2cfa, 0x7a05, 0x67cd, 0x1662, 0x4646, 0x319d, 0x7277, 0x06fe, 0x1367, 0x14e5, + 0x3bbc, 0x7306, 0x017d, 0x7396, 0x56b5, 0x63f2, 0x0570, 0x7341, 0x2319, 0x1c5f, + 0x4c48, 0x1100, 0x1997, 0x0e3d, 0x6510, 0x55df, 0x34c6, 0x7206, 0x1f05, 0x2a82, + 0x4cf8, 0x38a3, 0x7905, 0x789d, 0x7da7, 0x47ca, 0x3247, 0x7b19, 0x2400, 0x570a, + 0x448a, 0x689b, 0x15b1, 0x2d35, 0x635b, 0x6956, 0x0a3a, 0x42f5, 0x5b1f, 0x556c, + 0x4f78, 0x0b90, 0x6ebf, 0x69f8, 0x7b2c, 0x74d6, 0x7b3f, 0x4cc1, 0x7b76, 0x0f85, + 0x730e, 0x4963, 0x1832, 0x50a8, 0x647e, 0x65be, 0x2672, 0x7623, 0x6b2b, 0x1ebb, + 0x3739, 0x21c2, 0x259b, 0x5919, 0x029b, 0x302e, 0x51ec, 0x0500, 0x254c, 0x5025, + 0x31e7, 0x6ea8, 0x3600, 0x4581, 0x32c4, 0x2915, 0x41d2, 0x1aec, 0x0621, 0x5509, + 0x0d60, 0x37a3, 0x6de2, 0x2d70, 0x5ac7, 0x7940, 0x6856, 0x5fc0, 0x5211, 0x1155, + 0x0384, 0x5b0c, 0x5ca0, 0x40c3, 0x330d, 0x19f6, 0x0fbc, 0x6add, 0x39d0, 0x52b2, + 0x7bad, 0x6b5f, 0x2aa4, 0x1cd4, 0x2bf5, 0x5e3c, 0x31fe, 0x5edb, 0x0a27, 0x2629, + 0x543e, 0x5dca, 0x567f, 0x7241, 0x0885, 0x082d, 0x0863, 0x1205, 0x7106, 0x05ab, + 0x345f, 0x7eb8, 0x2b8a, 0x44e5, 0x1b35, 0x71b2, 0x4fd1, 0x024b, 0x1e2b, 0x3a59, + 0x34b0, 0x77f3, 0x6fce, 0x2f05, 0x6108, 0x00bd, 0x09b2, 0x274a, 0x20bc, 0x17cc, + 0x08bb, 0x4235, 0x326d, 0x3069, 0x28a4, 0x7523, 0x5a92, 0x0a05, 0x1041, 0x5e97, + 0x5819, 0x7985, 0x3d9e, 0x64b9, 0x0c21, 0x4f65, 0x499a, 0x33a1, 0x4a00, 0x12cc, + 0x68f4, 0x404c, 0x410c, 0x1479, 0x27a1, 0x4045, 0x4f05, 0x3bc0, 0x7345, 0x15b5, + 0x5570, 0x029f, 0x6eac, 0x0fc0, 0x1cd8, 0x1e2f, 0x00c1, 0x68f8, 0x3bc4, 0x03d1, + 0x474b, 0x2a3f, 0x73d6, 0x236e, 0x3ee5, 0x40ae, 0x36dd, 0x5b5a, 0x4abf, 0x47a7, + 0x6147, 0x5260, 0x03cd, 0x6010, 0x200d, 0x004d, 0x3ce3, 0x6041, 0x2105, 0x4747, + 0x432c, 0x4369, 0x56e7, 0x6199, 0x590f, 0x6891, 0x6ac8, 0x14db, 0x0241, 0x19ec, + 0x12c2, 0x5256, 0x156e, 0x6c2c, 0x2815, 0x1785, 0x6e61, 0x2136, 0x7df2, 0x6b18, + 0x30d0, 0x3aad, 0x452f, 0x4373, 0x2a3b, 0x43c5, 0x4105, 0x0f3e, 0x427e, 0x7ce2, + 0x1a4f, 0x73d2, 0x4b25, 0x4d91, 0x503c, 0x2c8b, 0x265f, 0x1bed, 0x7dcd, 0x3222, + 0x7086, 0x18f6, 0x46df, 0x24b0, 0x0841, 0x358a, 0x1055, 0x66f4, 0x4d0c, 0x37de, + 0x1099, 0x0dc3, 0x0a71, 0x61e0, 0x24c4, 0x4336, 0x236a, 0x207d, 0x079d, 0x79c9, + 0x55a3, 0x1eef, 0x2292, 0x3ee1, 0x2c4e, 0x6992, 0x33ea, 0x6689, 0x6f2e, 0x171e, + 0x0aeb, 0x119a, 0x6c6f, 0x7141, 0x6618, 0x2b68, 0x0a5d, 0x1705, 0x1bd4, 0x3a94, + 0x6d97, 0x05d0, 0x52d5, 0x181f, 0x06e5, 0x18d2, 0x74bd, 0x56f1, 0x40aa, 0x2610, + 0x421c, 0x478e, 0x749a, 0x53ef, 0x7d59, 0x36d9, 0x0c6a, 0x4b83, 0x393f, 0x053c, + 0x646b, 0x5d7d, 0x3c61, 0x5634, 0x6d4b, 0x76f3, 0x0185, 0x2adf, 0x7558, 0x727c, + 0x1105, 0x373e, 0x291a, 0x1b3a, 0x17d1, 0x4ac4, 0x619e, 0x0a76, 0x668e, 0x61a3, + 0x5b56, 0x3d2a, 0x7783, 0x1e7f, 0x28d9, 0x5854, 0x0dba, 0x4abb, 0x1eb2, 0x3194, + 0x44dc, 0x5b51, 0x1325, 0x52ee, 0x47f0, 0x57ab, 0x0bc7, 0x2e05, 0x0305, 0x6732, + 0x13f1, 0x6cad, 0x0905, 0x1522, 0x2f7f, 0x5127, 0x1a9f, 0x2588, 0x7bc2, 0x4be9, + 0x696b, 0x210f, 0x47a3, 0x2dd3, 0x5e15, 0x4087, 0x5e00, 0x416e, 0x3973, 0x6143, + 0x5399, 0x58be, 0x0461, 0x351b, 0x3726, 0x176d, 0x6ff2, 0x782f, 0x79ed, 0x7885, + 0x5fa8, 0x2356, 0x4faf, 0x5956, 0x3f4c, 0x7847, 0x160c, 0x7be9, 0x29de, 0x585d, + 0x3d25, 0x1838, 0x2d76, 0x4751, 0x525c, 0x755e, 0x6738, 0x020f, 0x0081, 0x2ad7, + 0x495b, 0x03c9, 0x369c, 0x7411, 0x1f76, 0x777e, 0x6a2f, 0x363d, 0x4e7c, 0x7a43, + 0x7809, 0x14b5, 0x55b9, 0x1e09, 0x0789, 0x7ab1, 0x1d2f, 0x700a, 0x0525, 0x3504, + 0x5ba4, 0x0288, 0x5474, 0x6db0, 0x3de9, 0x604b, 0x600c, 0x27a8, 0x0f45, 0x4010, + 0x4009, 0x1472, 0x0145, 0x2009, 0x23c8, 0x4c10, 0x2563, 0x4f40, 0x51d9, 0x05e9, + 0x2b52, 0x09ef, 0x1009, 0x5a5a, 0x739e, 0x0089, 0x0209, 0x27dd, 0x46a7, 0x0809, + 0x6ef6, 0x529d, 0x3c29, 0x28e2, 0x1e7a, 0x6933, 0x0429, 0x3ced, 0x0049, 0x0f0d, + 0x010d, 0x0011, 0x0003, 0x0eff, 0x6925, 0x3cdf, 0x07fb, 0x27cf, 0x528f, 0x28d4, + 0x4f32, 0x4c02, 0x1464, 0x1ffb, 0x09e1, 0x05db, 0x5a4c, 0x007b, 0x1dfb, 0x14a7, + 0x362f, 0x7a35, 0x03bb, 0x2ac9, 0x7403, 0x7770, 0x027a, 0x34f6, 0x7aa3, 0x6ffc, + 0x603d, 0x6da2, 0x279a, 0x4002, 0x4079, 0x2dc5, 0x4bdb, 0x2101, 0x1514, 0x6c9f, + 0x5119, 0x257a, 0x5b43, 0x3186, 0x5846, 0x4aad, 0x579d, 0x52e0, 0x2df7, 0x6724, + 0x2348, 0x7877, 0x175f, 0x7821, 0x6135, 0x4160, 0x58b0, 0x350d, 0x584f, 0x7bdb, + 0x5948, 0x7839, 0x4743, 0x182a, 0x7550, 0x0201, 0x79bb, 0x206f, 0x61d2, 0x4328, + 0x66e6, 0x357c, 0x37d0, 0x0db5, 0x2c7d, 0x4d83, 0x7cd4, 0x73c4, 0x3214, 0x1bdf, + 0x18e8, 0x24a2, 0x5248, 0x19de, 0x6883, 0x14cd, 0x4739, 0x6033, 0x435b, 0x618b, + 0x6b0a, 0x2128, 0x6c1e, 0x1777, 0x4365, 0x3a9f, 0x43b7, 0x0f30, 0x4780, 0x2602, + 0x18c4, 0x56e3, 0x3a86, 0x16f7, 0x05c2, 0x1811, 0x667b, 0x6984, 0x1ee1, 0x3ed3, + 0x118c, 0x1710, 0x7133, 0x2b5a, 0x2ad1, 0x76e5, 0x5d6f, 0x5626, 0x36cb, 0x53e1, + 0x4b75, 0x052e, 0x4ab6, 0x1b2c, 0x726e, 0x3730, 0x6195, 0x0a68, 0x3d1c, 0x1e71, + 0x04f2, 0x3020, 0x21b4, 0x590b, 0x65b0, 0x509a, 0x7615, 0x1ead, 0x69ea, 0x0b82, + 0x42e7, 0x555e, 0x4cb3, 0x74c8, 0x0f77, 0x4955, 0x2a74, 0x71f8, 0x0e2f, 0x55d1, + 0x7333, 0x63e4, 0x1c51, 0x10f2, 0x7b0b, 0x47bc, 0x3895, 0x788f, 0x688d, 0x56fc, + 0x2d27, 0x6948, 0x5ff7, 0x750a, 0x7228, 0x6ac4, 0x2d1c, 0x0f6c, 0x711d, 0x72ed, + 0x7545, 0x2dec, 0x5a41, 0x278f, 0x43ac, 0x18dd, 0x7128, 0x3d11, 0x1077, 0x42ac, + 0x3109, 0x1b7b, 0x4ef3, 0x6570, 0x7163, 0x5bad, 0x318f, 0x1654, 0x3f6e, 0x79f7, + 0x14d7, 0x06f0, 0x72f8, 0x7388, 0x77e5, 0x3a4b, 0x71a4, 0x023d, 0x059d, 0x11f7, + 0x7eaa, 0x44d7, 0x5ecd, 0x5e2e, 0x6b51, 0x1cc6, 0x5dbc, 0x261b, 0x7233, 0x081f, + 0x2d62, 0x3795, 0x1ade, 0x54fb, 0x6e9a, 0x5017, 0x4573, 0x2907, 0x5afe, 0x1147, + 0x7932, 0x5fb2, 0x19e8, 0x40b5, 0x6acf, 0x52a4, 0x146b, 0x403e, 0x3393, 0x12be, + 0x7977, 0x5e89, 0x64ab, 0x4f57, 0x17be, 0x273c, 0x2ef7, 0x00af, 0x305b, 0x4227, + 0x7515, 0x09f7, 0x03c3, 0x68ea, 0x0fb2, 0x1e21, 0x3bb2, 0x4037, 0x15a7, 0x0291, + 0x5b4c, 0x40a0, 0x2a31, 0x2360, 0x5252, 0x4799, 0x6002, 0x003f, 0x51a5, 0x203e, + 0x63c1, 0x156a, 0x3a07, 0x4d3f, 0x1132, 0x1320, 0x70bf, 0x124b, 0x338c, 0x5a80, + 0x3d57, 0x0af6, 0x657b, 0x3696, 0x251c, 0x04c2, 0x7720, 0x3655, 0x2892, 0x6072, + 0x24e9, 0x392c, 0x7d86, 0x153b, 0x483c, 0x58c8, 0x6c28, 0x11a5, 0x4efe, 0x013e, + 0x2261, 0x2fc7, 0x0954, 0x2811, 0x22bf, 0x7f77, 0x121c, 0x78e4, 0x0c57, 0x091e, + 0x2698, 0x08a9, 0x7c9e, 0x1729, 0x716e, 0x3f38, 0x263d, 0x35cb, 0x0c35, 0x2f4a, + 0x325b, 0x2e61, 0x312b, 0x1d38, 0x52e9, 0x62ec, 0x1c01, 0x53a3, 0x1781, 0x6f39, + 0x5bb8, 0x4e58, 0x0ff3, 0x7b60, 0x2842, 0x6e5d, 0x3e95, 0x3e10, 0x5c1c, 0x47eb, + 0x43f2, 0x6cc6, 0x3855, 0x60f6, 0x4b5e, 0x714c, 0x3114, 0x46bd, 0x4f9b, 0x60d2, + 0x2cc4, 0x64da, 0x6fbc, 0x1cf9, 0x1a19, 0x7487, 0x633a, 0x1176, 0x0c8d, 0x046b, + 0x2132, 0x6c7a, 0x1b86, 0x545f, 0x1a7c, 0x4a54, 0x7c3c, 0x7dee, 0x0b2b, 0x2157, + 0x38fd, 0x23df, 0x7d46, 0x3c82, 0x1923, 0x09a0, 0x75d3, 0x6623, 0x42b7, 0x05f1, + 0x740b, 0x5148, 0x59f4, 0x6a47, 0x20aa, 0x296a, 0x4de9, 0x7013, 0x57a6, 0x140a, + 0x7ef8, 0x3525, 0x6b14, 0x2b73, 0x1082, 0x3c12, 0x23a3, 0x2bd0, 0x48c7, 0x30cc, + 0x758b, 0x3aeb, 0x7e50, 0x0bc2, 0x5d49, 0x1ab8, 0x7649, 0x0c0f, 0x3344, 0x699d, + 0x3f79, 0x1f70, 0x39a0, 0x2cf7, 0x766f, 0x487f, 0x3d8c, 0x1973, 0x0b4e, 0x3c4e, + 0x5694, 0x4e29, 0x78b2, 0x397d, 0x3aa9, 0x2c59, 0x7a02, 0x13ce, 0x3dd4, 0x67ca, + 0x0aaa, 0x452b, 0x3b6a, 0x5347, 0x2e32, 0x4c27, 0x6458, 0x57f3, 0x08df, 0x102f, + 0x0de7, 0x33f5, 0x165f, 0x15f8, 0x1a2d, 0x4643, 0x0932, 0x7a5b, 0x5807, 0x549b, + 0x0681, 0x7aba, 0x2e00, 0x25a1, 0x4587, 0x614d, 0x436f, 0x6694, 0x319a, 0x042f, + 0x27d5, 0x7274, 0x50a0, 0x2a37, 0x4d45, 0x7efe, 0x3af1, 0x0300, 0x5cd7, 0x2447, + 0x1d5c, 0x49ee, 0x5ef1, 0x229d, 0x06fb, 0x4b03, 0x29ca, 0x1364, 0x26ce, 0x4e94, + 0x4988, 0x6823, 0x066d, 0x6d38, 0x64ef, 0x2f98, 0x0dfc, 0x4178, 0x43c1, 0x3eec, + 0x14e2, 0x6efd, 0x2002, 0x3bb9, 0x5a87, 0x4101, 0x39c5, 0x6dd7, 0x087a, 0x34a5, + 0x0172, 0x5140, 0x76dd, 0x68e2, 0x6350, 0x1efa, 0x7303, 0x51e1, 0x7778, 0x017a, + 0x660d, 0x4211, 0x40fa, 0x12b7, 0x46d4, 0x0792, 0x672d, 0x5f9d, 0x02fa, 0x5e0a, + 0x0f3a, 0x55ae, 0x7393, 0x0102, 0x3bf4, 0x56b2, 0x553b, 0x427a, 0x11d3, 0x5cd1, + 0x54e6, 0x13ec, 0x294c, 0x6265, 0x7970, 0x39be, 0x2726, 0x3c6c, 0x63ef, 0x6a29, + 0x0982, 0x056d, 0x7fad, 0x1f8e, 0x0faa, 0x5d67, 0x59ec, 0x6605, 0x4a36, 0x0be0, + 0x6f67, 0x7bf3, 0x7cde, 0x563f, 0x733e, 0x23c1, 0x7469, 0x2316, 0x01b5, 0x1a4b, + 0x7694, 0x7ad8, 0x2ba1, 0x60b4, 0x6c5c, 0x2e1e, 0x5c6d, 0x5c8e, 0x7c6a, 0x5d88, + 0x1c5c, 0x044d, 0x6e3f, 0x4c45, 0x2870, 0x7052, 0x32fb, 0x1879, 0x6cdd, 0x3df2, + 0x6ca8, 0x6ba5, 0x5f39, 0x1616, 0x73ce, 0x6476, 0x10fd, 0x469f, 0x3f1a, 0x1994, + 0x1da7, 0x4b21, 0x72af, 0x5cf5, 0x3e41, 0x0900, 0x7f59, 0x031e, 0x50ce, 0x6844, + 0x45b8, 0x76fe, 0x0e3a, 0x27f3, 0x5385, 0x650d, 0x3f9f, 0x26b9, 0x5ab5, 0x6093, + 0x3b56, 0x6f1b, 0x35ad, 0x1944, 0x0736, 0x29e8, 0x4d8d, 0x6d56, 0x55dc, 0x1d1a, + 0x1302, 0x34c3, 0x66c2, 0x5038, 0x5d26, 0x2f18, 0x139f, 0x2020, 0x0ad8, 0x6405, + 0x22f5, 0x51ff, 0x0ccb, 0x0190, 0x7203, 0x5a62, 0x3637, 0x1f02, 0x7fb5, 0x7796, + 0x0372, 0x07b0, 0x48f5, 0x6054, 0x151d, 0x674b, 0x2441, 0x5867, 0x2c87, 0x2aea, + 0x2a7f, 0x0120, 0x6edf, 0x4cf5, 0x3448, 0x265b, 0x59a3, 0x1d56, 0x21e2, 0x2f7a, + 0x6805, 0x5307, 0x4c68, 0x32b2, 0x572a, 0x4b8e, 0x38a0, 0x4e76, 0x49d0, 0x7902, + 0x071e, 0x5f21, 0x35ee, 0x6b84, 0x169a, 0x227f, 0x7256, 0x7a8b, 0x791a, 0x3f56, + 0x1be9, 0x0c75, 0x789a, 0x02e2, 0x0774, 0x7da4, 0x20dc, 0x7dc9, 0x33c1, 0x1bab, + 0x6665, 0x015c, 0x5590, 0x3dc0, 0x467e, 0x253a, 0x29b5, 0x394a, 0x47c7, 0x5dec, + 0x40e3, 0x3244, 0x4edc, 0x36b4, 0x31d5, 0x70dd, 0x32e4, 0x6db9, 0x5122, 0x133e, + 0x71d2, 0x7851, 0x321e, 0x0547, 0x7b16, 0x51c3, 0x1f52, 0x23fd, 0x6200, 0x7082, + 0x505c, 0x3811, 0x6bda, 0x1a9a, 0x3acd, 0x45e8, 0x2eac, 0x060f, 0x2c0b, 0x7d64, + 0x5707, 0x30ae, 0x395f, 0x4487, 0x3fb4, 0x7429, 0x41c0, 0x7031, 0x0a96, 0x2c3b, + 0x2cd9, 0x57c4, 0x340a, 0x5960, 0x18f2, 0x36e4, 0x6898, 0x3c30, 0x4c09, 0x15ae, + 0x1252, 0x46db, 0x626c, 0x4410, 0x44fc, 0x67ac, 0x33d7, 0x4809, 0x1858, 0x0d4e, + 0x0481, 0x53fa, 0x2d32, 0x1011, 0x7a3d, 0x6358, 0x1f96, 0x0e96, 0x6dd0, 0x5e82, + 0x4409, 0x547d, 0x2583, 0x3780, 0x49e8, 0x4fb9, 0x24ac, 0x74a5, 0x6953, 0x0411, + 0x01e3, 0x0a37, 0x2ffd, 0x083d, 0x0d18, 0x5eeb, 0x5002, 0x7bbd, 0x4142, 0x44f5, + 0x64a4, 0x0873, 0x1c90, 0x1b45, 0x42f2, 0x7803, 0x4a8f, 0x5b1c, 0x69c3, 0x5ad7, + 0x566d, 0x5cb0, 0x7d1f, 0x52c2, 0x2da7, 0x41e2, 0x16b2, 0x2d80, 0x3586, 0x2925, + 0x5569, 0x255c, 0x7752, 0x4f75, 0x1dd5, 0x1051, 0x6e19, 0x49aa, 0x5f6e, 0x1489, + 0x6d84, 0x08cb, 0x4259, 0x0a15, 0x10bd, 0x17dc, 0x0b8d, 0x6fde, 0x3cc1, 0x6ebc, + 0x5183, 0x03e1, 0x542c, 0x02af, 0x2b18, 0x27b1, 0x4be4, 0x5b6a, 0x223c, 0x475b, + 0x66f0, 0x4acf, 0x69f5, 0x005d, 0x2b3c, 0x7b29, 0x62b5, 0x4d08, 0x1280, 0x2410, + 0x460c, 0x6966, 0x16d9, 0x19a7, 0x314f, 0x2a92, 0x5e52, 0x1110, 0x74d3, 0x56c5, + 0x3712, 0x7b3c, 0x41ab, 0x4973, 0x7b9b, 0x6a08, 0x6444, 0x0a4a, 0x76c7, 0x1ecb, + 0x622e, 0x1842, 0x37da, 0x3749, 0x4cbe, 0x0510, 0x0d97, 0x7b73, 0x0cf0, 0x1095, + 0x48a4, 0x5bcb, 0x3476, 0x2051, 0x1bc1, 0x31ad, 0x6244, 0x2be3, 0x0ca3, 0x7287, + 0x0f82, 0x73a6, 0x14af, 0x730b, 0x0575, 0x3033, 0x31ec, 0x3a5e, 0x610d, 0x6015, + 0x210a, 0x2082, 0x2297, 0x3d2f, 0x0dbf, 0x7563, 0x4960, 0x0f12, 0x692a, 0x182f, + 0x61d7, 0x0a6d, 0x21b9, 0x06f5, 0x71a9, 0x479e, 0x63c6, 0x6f3e, 0x2847, 0x2b78, + 0x48cc, 0x6699, 0x50a5, 0x55b3, 0x5540, 0x647b, 0x1dac, 0x2aef, 0x344d, 0x054c, + 0x6205, 0x74aa, 0x3002, 0x4ad4, 0x62ba, 0x7568, 0x61dc, 0x61ae, 0x65bb, 0x1e8f, + 0x5b8f, 0x266f, 0x6398, 0x24c0, 0x5bf3, 0x2c9b, 0x7ecf, 0x428e, 0x06d2, 0x0dd3, + 0x65e4, 0x0851, 0x0658, 0x0a81, 0x7620, 0x79d9, 0x6aa6, 0x6b28, 0x336a, 0x157e, + 0x70f4, 0x30e0, 0x386c, 0x0f4e, 0x2dce, 0x591f, 0x3606, 0x5266, 0x4332, 0x61a9, + 0x1eb8, 0x3cf3, 0x0801, 0x3736, 0x65b6, 0x2366, 0x3a0d, 0x352b, 0x7591, 0x5e10, + 0x11d9, 0x586d, 0x59a9, 0x4fbf, 0x0d1e, 0x3d35, 0x21bf, 0x021f, 0x5f94, 0x2598, + 0x62e3, 0x1401, 0x1b23, 0x7bd2, 0x164b, 0x4097, 0x3777, 0x1335, 0x6b9c, 0x6742, + 0x2079, 0x5b61, 0x5916, 0x28e9, 0x4f39, 0x0298, 0x70c6, 0x0799, 0x2953, 0x5484, + 0x4149, 0x4020, 0x4209, 0x6a3f, 0x561e, 0x1e19, 0x0e8e, 0x778e, 0x302b, 0x0091, + 0x1e03, 0x51e9, 0x098a, 0x0099, 0x349e, 0x4f50, 0x67a5, 0x4019, 0x4082, 0x28f2, + 0x4afd, 0x0219, 0x79c5, 0x1e8a, 0x04fd, 0x0021, 0x00f3, 0x2549, 0x5b8a, 0x559f, + 0x5f8e, 0x29c4, 0x44c2, 0x5dfb, 0x4202, 0x33d0, 0x17b7, 0x016b, 0x12a8, 0x7dd8, + 0x5022, 0x0783, 0x3496, 0x31e4, 0x036a, 0x6dc8, 0x3baa, 0x36c3, 0x20a2, 0x40f2, + 0x68d3, 0x7860, 0x462c, 0x5131, 0x1eeb, 0x322d, 0x6ea5, 0x51d2, 0x4af4, 0x35fd, + 0x2233, 0x228e, 0x2438, 0x5f30, 0x71c9, 0x49df, 0x2a28, 0x3f65, 0x593f, 0x7265, + 0x7eef, 0x1bf8, 0x457e, 0x02f1, 0x6d29, 0x32c1, 0x75b1, 0x6814, 0x1355, 0x5739, + 0x5c33, 0x4e85, 0x4169, 0x59b2, 0x5742, 0x2f89, 0x3edd, 0x266a, 0x2912, 0x6eee, + 0x13bf, 0x41cf, 0x6393, 0x2c4a, 0x4e1a, 0x7438, 0x676b, 0x396e, 0x4870, 0x596f, + 0x3e61, 0x2ce8, 0x1964, 0x1901, 0x1ae9, 0x3c3f, 0x0bb3, 0x061e, 0x6bc5, 0x3adc, + 0x2bc1, 0x2c1a, 0x3330, 0x30bd, 0x0c00, 0x506b, 0x4ba6, 0x1aa9, 0x698e, 0x7091, + 0x5506, 0x1f61, 0x15e9, 0x0d5d, 0x6bfa, 0x33e6, 0x57e4, 0x0490, 0x4926, 0x1020, + 0x451c, 0x627b, 0x4466, 0x67bb, 0x5338, 0x46ea, 0x37a0, 0x4c18, 0x7aab, 0x6ddf, + 0x6f6f, 0x548c, 0x4634, 0x0ea5, 0x2202, 0x7a4c, 0x613e, 0x4fc8, 0x135e, 0x2592, + 0x6685, 0x24bb, 0x2d6d, 0x0420, 0x4e49, 0x5ac4, 0x5bee, 0x6f2a, 0x62dd, 0x26c8, + 0x7e70, 0x5394, 0x2f3b, 0x29f7, 0x25c1, 0x35bc, 0x2e52, 0x4d9c, 0x793d, 0x1d29, + 0x78d5, 0x6853, 0x3b11, 0x7f68, 0x2fb8, 0x45c7, 0x217a, 0x2802, 0x089a, 0x72be, + 0x38b8, 0x090f, 0x171a, 0x4b30, 0x5fbd, 0x3f29, 0x3687, 0x520e, 0x630c, 0x0ae7, + 0x123c, 0x0cda, 0x18ae, 0x5a71, 0x155b, 0x5d35, 0x308d, 0x202f, 0x4d30, 0x5047, + 0x1152, 0x1311, 0x391d, 0x0381, 0x38db, 0x6063, 0x04b3, 0x77a5, 0x142a, 0x3646, + 0x58b9, 0x5876, 0x5c3c, 0x152c, 0x1196, 0x2c96, 0x5b09, 0x012f, 0x5450, 0x5c9d, + 0x7eca, 0x6c6b, 0x1167, 0x7c79, 0x0633, 0x045c, 0x64cb, 0x76a3, 0x327f, 0x60c3, + 0x1cea, 0x1a5a, 0x40c0, 0x7478, 0x47dc, 0x330a, 0x6522, 0x3e01, 0x7b51, 0x7061, + 0x7635, 0x6e4e, 0x60e7, 0x1625, 0x1674, 0x6cb7, 0x713d, 0x73dd, 0x19f3, 0x46ae, + 0x05e2, 0x0fb9, 0x0afd, 0x6614, 0x3c73, 0x1f9d, 0x1b4c, 0x0991, 0x7ddf, 0x7c02, + 0x37f0, 0x4a45, 0x2148, 0x7ced, 0x6ada, 0x23d0, 0x7004, 0x39cd, 0x7bfb, 0x295b, + 0x5139, 0x2735, 0x4802, 0x6a38, 0x3516, 0x11e2, 0x4e8e, 0x13fb, 0x2b64, 0x4289, + 0x52af, 0x3c03, 0x1e62, 0x7baa, 0x06cd, 0x0a59, 0x1b1d, 0x4982, 0x3a36, 0x3721, + 0x5617, 0x1851, 0x2ef0, 0x76d6, 0x53d2, 0x37e9, 0x6b5c, 0x051f, 0x1802, 0x2aa1, + 0x3554, 0x16e8, 0x25f3, 0x5e61, 0x6548, 0x56d4, 0x3ec4, 0x128f, 0x531f, 0x6975, + 0x1701, 0x4d17, 0x1cd1, 0x2b4b, 0x2493, 0x2bf2, 0x299c, 0x1bd0, 0x4d74, 0x0cb2, + 0x270d, 0x73b5, 0x4319, 0x48b3, 0x0e75, 0x2060, 0x356d, 0x10a4, 0x5e39, 0x0da6, + 0x617c, 0x31fb, 0x5784, 0x6024, 0x19cf, 0x3042, 0x4c9a, 0x14be, 0x1768, 0x3d3e, + 0x75ba, 0x2119, 0x3a90, 0x0dce, 0x5ed8, 0x0f21, 0x3ff3, 0x0a24, 0x65df, 0x6d93, + 0x34e7, 0x10cc, 0x6d12, 0x6fed, 0x7a26, 0x6e28, 0x6a8f, 0x1498, 0x2aba, 0x1060, + 0x2626, 0x7761, 0x28c5, 0x543b, 0x1f3d, 0x27c0, 0x0ef0, 0x03f0, 0x238f, 0x3cd0, + 0x1fec, 0x476a, 0x0d81, 0x4bf3, 0x05cc, 0x66ff, 0x5dc7, 0x006c, 0x6715, 0x567c, + 0x6322, 0x52d1, 0x3177, 0x5ae6, 0x0262, 0x4a9e, 0x20f2, 0x2d8f, 0x375f, 0x2db6, + 0x6c90, 0x3595, 0x723e, 0x256b, 0x34fe, 0x0882, 0x0be8, 0x4151, 0x7868, 0x1c9f, + 0x3eac, 0x7812, 0x782a, 0x0d27, 0x681d, 0x7bcc, 0x181b, 0x084c, 0x082a, 0x01f2, + 0x7379, 0x0860, 0x0653, 0x06e1, 0x1645, 0x0667, 0x7e36, 0x79e8, 0x1b6c, 0x5c02, + 0x4dcf, 0x429d, 0x6561, 0x24cf, 0x1202, 0x5b9e, 0x72de, 0x7103, 0x1c37, 0x0f5d, + 0x74fb, 0x158d, 0x7e90, 0x6ab5, 0x2780, 0x5275, 0x5896, 0x2ddd, 0x18ce, 0x4341, + 0x05a8, 0x3d02, 0x4946, 0x345c, 0x642a, 0x74b9, 0x0b73, 0x2afe, 0x4fe8, 0x554f, + 0x58fc, 0x7577, 0x678b, 0x3011, 0x508b, 0x61eb, 0x7eb5, 0x1e9e, 0x10e3, 0x2b87, + 0x59d2, 0x63d5, 0x71e9, 0x48db, 0x3e27, 0x55c2, 0x7880, 0x21c8, 0x32ca, 0x47ad, + 0x56ed, 0x0a7c, 0x44e2, 0x6939, 0x5295, 0x1b32, 0x761b, 0x40a6, 0x1138, 0x1410, + 0x7e56, 0x5fa3, 0x54ec, 0x6751, 0x21e8, 0x3786, 0x5008, 0x2088, 0x71af, 0x28f8, + 0x44c8, 0x4fce, 0x7e76, 0x11e8, 0x3a3c, 0x0d2d, 0x7e3c, 0x022e, 0x1cb7, 0x3a1c, + 0x4c80, 0x5e1f, 0x260c, 0x2375, 0x0248, 0x0810, 0x09e8, 0x1e28, 0x3d5e, 0x4218, + 0x272d, 0x0e9d, 0x1c97, 0x00a0, 0x12af, 0x2962, 0x53d9, 0x402f, 0x5e7a, 0x07a8, + 0x3a56, 0x4f48, 0x0282, 0x34ad, 0x4a3e, 0x4028, 0x68db, 0x00a8, 0x0d47, 0x1e12, + 0x2351, 0x0228, 0x6d32, 0x4091, 0x478a, 0x79d4, 0x77f0, 0x0030, 0x0402, 0x6fcb, + 0x6aa1, 0x7496, 0x3771, 0x64e9, 0x1cb1, 0x4faa, 0x0e87, 0x047a, 0x3054, 0x6349, + 0x5e73, 0x2141, 0x2f02, 0x546e, 0x679d, 0x6105, 0x48ed, 0x4401, 0x159f, 0x4b6d, + 0x4de1, 0x46cc, 0x0d3f, 0x3ea4, 0x21fa, 0x47fa, 0x53eb, 0x6e6c, 0x00ba, 0x1002, + 0x309f, 0x09af, 0x77b7, 0x7d55, 0x45d9, 0x75e2, 0x25d3, 0x0600, 0x7073, 0x0b3a, + 0x3291, 0x23ee, 0x3802, 0x7dfd, 0x2747, 0x1a8b, 0x2c2c, 0x20b9, 0x3e73, 0x7022, + 0x4478, 0x6a56, 0x0eb7, 0x741a, 0x5951, 0x3534, 0x574b, 0x57b5, 0x36d5, 0x6b23, + 0x17c9, 0x3c21, 0x02d3, 0x08b8, 0x3365, 0x0c66, 0x7a7c, 0x7cad, 0x2ed0, 0x3f47, + 0x5f12, 0x22ce, 0x2e85, 0x78f3, 0x6b75, 0x2820, 0x4232, 0x2270, 0x2f6b, 0x326a, + 0x2e97, 0x1d47, 0x4ce6, 0x2f59, 0x3d78, 0x264c, 0x32a3, 0x53b2, 0x6a68, 0x52f8, + 0x4b7f, 0x1790, 0x3066, 0x4e67, 0x5ddd, 0x28a1, 0x5224, 0x393b, 0x3db1, 0x3664, + 0x1e42, 0x252b, 0x7dba, 0x58d7, 0x4cd4, 0x7d95, 0x1b9c, 0x6c37, 0x7520, 0x014d, + 0x6daa, 0x5a8f, 0x5647, 0x70ce, 0x3235, 0x3d66, 0x6e74, 0x36a5, 0x7842, 0x3a16, + 0x2f92, 0x132f, 0x0538, 0x1579, 0x0a02, 0x51b4, 0x4690, 0x103e, 0x70ef, 0x6467, + 0x6b96, 0x0df6, 0x4c7a, 0x1607, 0x7043, 0x3b79, 0x2ebe, 0x4c36, 0x186a, 0x453a, + 0x5e94, 0x3de3, 0x60a5, 0x5816, 0x50e0, 0x7ac9, 0x2307, 0x7a6a, 0x07c2, 0x1a3c, + 0x5c7f, 0x615c, 0x188b, 0x2e0f, 0x5d79, 0x437e, 0x7982, 0x043e, 0x6a1a, 0x3d9b, + 0x3161, 0x3c5d, 0x6256, 0x488e, 0x3a70, 0x39af, 0x426b, 0x398c, 0x02c1, 0x56a3, + 0x5cc2, 0x3ab8, 0x64b6, 0x13dd, 0x65f6, 0x0c1e, 0x30f2, 0x5d58, 0x055e, 0x3353, + 0x2859, 0x1f7f, 0x7be4, 0x759a, 0x59bb, 0x0bd1, 0x5630, 0x30db, 0x4f62, 0x23b2, + 0x1d0b, 0x4997, 0x3867, 0x6d47, 0x1935, 0x4ea3, 0x297c, 0x29d9, 0x26aa, 0x4187, + 0x2e73, 0x64fe, 0x6084, 0x43d0, 0x339e, 0x6f0c, 0x08f1, 0x49fd, 0x54ad, 0x5ce6, + 0x1985, 0x5f00, 0x765b, 0x4b12, 0x6835, 0x4d54, 0x1d6e, 0x030f, 0x76ef, 0x2a46, + 0x12c9, 0x27e4, 0x5a53, 0x68f1, 0x6582, 0x0181, 0x63f6, 0x635f, 0x42f9, 0x51f0, + 0x5029, 0x39d4, 0x6b63, 0x34b4, 0x2f09, 0x4110, 0x4049, 0x2011, 0x6045, 0x4109, + 0x7ce6, 0x07a1, 0x1ef3, 0x4220, 0x53f3, 0x7787, 0x5858, 0x5e19, 0x4172, 0x673c, + 0x2adb, 0x0f49, 0x1476, 0x0111, 0x0f03, 0x279e, 0x2dc9, 0x7554, 0x2073, 0x43bb, + 0x2606, 0x3d20, 0x3024, 0x2d2b, 0x750e, 0x72fc, 0x3a4f, 0x6ad3, 0x4042, 0x6006, + 0x2042, 0x4f02, 0x2fcb, 0x5bbc, 0x7b64, 0x1b8a, 0x4a58, 0x1086, 0x2bd4, 0x7a06, + 0x67ce, 0x319e, 0x7278, 0x14e6, 0x3bbd, 0x7397, 0x56b6, 0x7342, 0x231a, 0x1101, + 0x1998, 0x55e0, 0x34c7, 0x2a83, 0x4cf9, 0x789e, 0x7da8, 0x7b1a, 0x2401, 0x689c, + 0x15b2, 0x6957, 0x0a3b, 0x556d, 0x4f79, 0x69f9, 0x7b2d, 0x4cc2, 0x7b77, 0x4964, + 0x1833, 0x65bf, 0x2673, 0x1ebc, 0x373a, 0x591a, 0x029c, 0x0501, 0x254d, 0x6ea9, + 0x3601, 0x2916, 0x41d3, 0x550a, 0x0d61, 0x2d71, 0x5ac8, 0x5fc1, 0x5212, 0x5b0d, + 0x5ca1, 0x19f7, 0x0fbd, 0x52b3, 0x7bae, 0x1cd5, 0x2bf6, 0x5edc, 0x0a28, 0x5dcb, + 0x5680, 0x082e, 0x0864, 0x05ac, 0x3460, 0x44e6, 0x1b36, 0x024c, 0x1e2c, 0x77f4, + 0x6fcf, 0x00be, 0x09b3, 0x17cd, 0x08bc, 0x306a, 0x28a5, 0x0a06, 0x1042, 0x7986, + 0x3d9f, 0x4f66, 0x499b, 0x12cd, 0x68f5, 0x147a, 0x27a2, 0x3bc1, 0x7346, 0x02a0, + 0x6ead, 0x1e30, 0x00c2, 0x03d2, 0x474c, 0x236f, 0x3ee6, 0x5b5b, 0x4ac0, 0x5261, + 0x03ce, 0x004e, 0x3ce4, 0x4748, 0x432d, 0x619a, 0x5910, 0x14dc, 0x0242, 0x5257, + 0x156f, 0x1786, 0x6e62, 0x6b19, 0x30d1, 0x4374, 0x2a3c, 0x0f3f, 0x427f, 0x73d3, + 0x4b26, 0x2c8c, 0x2660, 0x3223, 0x7087, 0x24b1, 0x0842, 0x66f5, 0x4d0d, 0x0dc4, + 0x0a72, 0x4337, 0x236b, 0x79ca, 0x55a4, 0x3ee2, 0x2c4f, 0x668a, 0x6f2f, 0x119b, + 0x6c70, 0x2b69, 0x0a5e, 0x3a95, 0x6d98, 0x1820, 0x06e6, 0x56f2, 0x40ab, 0x478f, + 0x749b, 0x36da, 0x0c6b, 0x053d, 0x646c, 0x5635, 0x6d4c, 0x2ae0, 0x7559, 0x373f, + 0x291b, 0x4ac5, 0x619f, 0x61a4, 0x5b57, 0x1e80, 0x28da, 0x4abc, 0x1eb3, 0x5b52, + 0x1326, 0x57ac, 0x0bc8, 0x6733, 0x13f2, 0x1523, 0x2f80, 0x2589, 0x7bc3, 0x2110, + 0x47a4, 0x4088, 0x5e01, 0x6144, 0x539a, 0x351c, 0x3727, 0x7830, 0x79ee, 0x2357, + 0x4fb0, 0x7848, 0x160d, 0x585e, 0x3d26, 0x4752, 0x525d, 0x0210, 0x0082, 0x03ca, + 0x369d, 0x777f, 0x6a30, 0x7a44, 0x780a, 0x1e0a, 0x078a, 0x700b, 0x0526, 0x0289, + 0x5475, 0x604c, 0x600d, 0x4011, 0x400a, 0x200a, 0x23c9, 0x4f41, 0x51da, 0x09f0, + 0x100a, 0x008a, 0x020a, 0x080a, 0x6ef7, 0x28e3, 0x1e7b, 0x3cee, 0x004a, 0x0012, + 0x0004, 0x3ce0, 0x07fc, 0x28d5, 0x4f33, 0x1ffc, 0x09e2, 0x007c, 0x1dfc, 0x7a36, + 0x03bc, 0x7771, 0x027b, 0x6ffd, 0x603e, 0x4003, 0x407a, 0x2102, 0x1515, 0x257b, + 0x5b44, 0x4aae, 0x579e, 0x6725, 0x2349, 0x7822, 0x6136, 0x350e, 0x5850, 0x783a, + 0x4744, 0x0202, 0x79bc, 0x4329, 0x66e7, 0x0db6, 0x2c7e, 0x73c5, 0x3215, 0x24a3, + 0x5249, 0x14ce, 0x473a, 0x618c, 0x6b0b, 0x1778, 0x4366, 0x0f31, 0x4781, 0x56e4, + 0x3a87, 0x1812, 0x667c, 0x3ed4, 0x118d, 0x2b5b, 0x2ad2, 0x5627, 0x36cc, 0x052f, + 0x4ab7, 0x3731, 0x6196, 0x1e72, 0x04f3, 0x590c, 0x65b1, 0x1eae, 0x69eb, 0x555f, + 0x4cb4, 0x4956, 0x2a75, 0x55d2, 0x7334, 0x10f3, 0x7b0c, 0x7890, 0x688e, 0x6949, + 0x5ff8, 0x6ac5, 0x2d1d, 0x72ee, 0x7546, 0x2790, 0x43ad, 0x3d12, 0x1078, 0x1b7c, + 0x4ef4, 0x5bae, 0x3190, 0x79f8, 0x14d8, 0x7389, 0x77e6, 0x023e, 0x059e, 0x44d8, + 0x5ece, 0x1cc7, 0x5dbd, 0x0820, 0x2d63, 0x54fc, 0x6e9b, 0x2908, 0x5aff, 0x5fb3, + 0x19e9, 0x52a5, 0x146c, 0x12bf, 0x7978, 0x4f58, 0x17bf, 0x00b0, 0x305c, 0x09f8, + 0x03c4, 0x1e22, 0x3bb3, 0x0292, 0x5b4d, 0x2361, 0x5253, 0x0040, 0x51a6, 0x156b, + 0x3a08, 0x1321, 0x70c0, 0x5a81, 0x3d58, 0x3697, 0x251d, 0x3656, 0x2893, 0x392d, + 0x7d87, 0x58c9, 0x6c29, 0x013f, 0x2262, 0x2812, 0x22c0, 0x78e5, 0x0c58, 0x08aa, + 0x7c9f, 0x3f39, 0x263e, 0x2f4b, 0x325c, 0x1d39, 0x52ea, 0x53a4, 0x1782, 0x4e59, + 0x0ff4, 0x6e5e, 0x3e96, 0x47ec, 0x43f3, 0x60f7, 0x4b5f, 0x46be, 0x4f9c, 0x64db, + 0x6fbd, 0x7488, 0x633b, 0x046c, 0x2133, 0x5460, 0x1a7d, 0x7def, 0x0b2c, 0x23e0, + 0x7d47, 0x09a1, 0x75d4, 0x05f2, 0x740c, 0x6a48, 0x20ab, 0x7014, 0x57a7, 0x3526, + 0x6b15, 0x3c13, 0x23a4, 0x30cd, 0x758c, 0x0bc3, 0x5d4a, 0x0c10, 0x3345, 0x1f71, + 0x39a1, 0x4880, 0x3d8d, 0x3c4f, 0x5695, 0x397e, 0x3aaa, 0x13cf, 0x3dd5, 0x452c, + 0x3b6b, 0x4c28, 0x6459, 0x1030, 0x0de8, 0x15f9, 0x1a2e, 0x7a5c, 0x5808, 0x7abb, + 0x2e01, 0x614e, 0x4370, 0x0430, 0x27d6, 0x2a38, 0x4d46, 0x0301, 0x5cd8, 0x49ef, + 0x5ef2, 0x4b04, 0x29cb, 0x4e95, 0x4989, 0x6d39, 0x64f0, 0x4179, 0x43c2, 0x6efe, + 0x2003, 0x4102, 0x39c6, 0x34a6, 0x0173, 0x68e3, 0x6351, 0x51e2, 0x7779, 0x4212, + 0x40fb, 0x0793, 0x672e, 0x5e0b, 0x0f3b, 0x0103, 0x3bf5, 0x427b, 0x11d4, 0x13ed, + 0x294d, 0x39bf, 0x2727, 0x6a2a, 0x0983, 0x1f8f, 0x0fab, 0x6606, 0x4a37, 0x7bf4, + 0x7cdf, 0x23c2, 0x746a, 0x1a4c, 0x7695, 0x60b5, 0x6c5d, 0x5c8f, 0x7c6b, 0x044e, + 0x6e40, 0x7053, 0x32fc, 0x3df3, 0x6ca9, 0x1617, 0x73cf, 0x46a0, 0x3f1b, 0x4b22, + 0x72b0, 0x0901, 0x7f5a, 0x6845, 0x45b9, 0x27f4, 0x5386, 0x26ba, 0x5ab6, 0x6f1c, + 0x35ae, 0x29e9, 0x4d8e, 0x1d1b, 0x1303, 0x5039, 0x5d27, 0x2021, 0x0ad9, 0x5200, + 0x0ccc, 0x5a63, 0x3638, 0x7797, 0x0373, 0x6055, 0x151e, 0x5868, 0x2c88, 0x0121, + 0x6ee0, 0x265c, 0x59a4, 0x2f7b, 0x6806, 0x32b3, 0x572b, 0x4e77, 0x49d1, 0x5f22, + 0x35ef, 0x2280, 0x7257, 0x3f57, 0x1bea, 0x02e3, 0x0775, 0x7dca, 0x33c2, 0x015d, + 0x5591, 0x253b, 0x29b6, 0x5ded, 0x40e4, 0x36b5, 0x31d6, 0x6dba, 0x5123, 0x7852, + 0x321f, 0x51c4, 0x1f53, 0x7083, 0x505d, 0x1a9b, 0x3ace, 0x0610, 0x2c0c, 0x30af, + 0x3960, 0x742a, 0x41c1, 0x2c3c, 0x2cda, 0x5961, 0x18f3, 0x3c31, 0x4c0a, 0x46dc, + 0x626d, 0x67ad, 0x33d8, 0x0d4f, 0x0482, 0x1012, 0x7a3e, 0x0e97, 0x6dd1, 0x547e, + 0x2584, 0x4fba, 0x24ad, 0x0412, 0x01e4, 0x083e, 0x0d19, 0x7bbe, 0x4143, 0x0874, + 0x1c91, 0x7804, 0x4a90, 0x5ad8, 0x566e, 0x52c3, 0x2da8, 0x2d81, 0x3587, 0x255d, + 0x7753, 0x1052, 0x6e1a, 0x148a, 0x6d85, 0x0a16, 0x10be, 0x6fdf, 0x3cc2, 0x03e2, + 0x542d, 0x27b2, 0x4be5, 0x475c, 0x66f1, 0x005e, 0x2b3d, 0x4d09, 0x1281, 0x6967, + 0x16da, 0x2a93, 0x5e53, 0x56c6, 0x3713, 0x4974, 0x7b9c, 0x0a4b, 0x76c8, 0x1843, + 0x37db, 0x0511, 0x0d98, 0x1096, 0x48a5, 0x2052, 0x1bc2, 0x2be4, 0x0ca4, 0x73a7, + 0x14b0, 0x3034, 0x31ed, 0x6016, 0x210b, 0x3d30, 0x0dc0, 0x0f13, 0x692b, 0x0a6e, + 0x21ba, 0x479f, 0x63c7, 0x2b79, 0x48cd, 0x55b4, 0x5541, 0x2af0, 0x344e, 0x74ab, + 0x3003, 0x7569, 0x61dd, 0x1e90, 0x5b90, 0x24c1, 0x5bf4, 0x428f, 0x06d3, 0x0852, + 0x0659, 0x79da, 0x6aa7, 0x157f, 0x70f5, 0x0f4f, 0x2dcf, 0x5267, 0x4333, 0x3cf4, + 0x0802, 0x2367, 0x3a0e, 0x5e11, 0x11da, 0x4fc0, 0x0d1f, 0x0220, 0x5f95, 0x1402, + 0x1b24, 0x4098, 0x3778, 0x6743, 0x207a, 0x28ea, 0x4f3a, 0x079a, 0x2954, 0x4021, + 0x420a, 0x1e1a, 0x0e8f, 0x0092, 0x1e04, 0x009a, 0x349f, 0x401a, 0x4083, 0x021a, + 0x79c6, 0x0022, 0x00f4, 0x55a0, 0x5f8f, 0x5dfc, 0x4203, 0x016c, 0x12a9, 0x0784, + 0x3497, 0x6dc9, 0x3bab, 0x40f3, 0x68d4, 0x5132, 0x1eec, 0x51d3, 0x4af5, 0x228f, + 0x2439, 0x49e0, 0x2a29, 0x7266, 0x7ef0, 0x02f2, 0x6d2a, 0x6815, 0x1356, 0x4e86, + 0x416a, 0x2f8a, 0x3ede, 0x6eef, 0x13c0, 0x2c4b, 0x4e1b, 0x396f, 0x4871, 0x2ce9, + 0x1965, 0x3c40, 0x0bb4, 0x3add, 0x2bc2, 0x30be, 0x0c01, 0x1aaa, 0x698f, 0x1f62, + 0x15ea, 0x33e7, 0x57e5, 0x1021, 0x451d, 0x67bc, 0x5339, 0x4c19, 0x7aac, 0x548d, + 0x4635, 0x7a4d, 0x613f, 0x2593, 0x6686, 0x0421, 0x4e4a, 0x6f2b, 0x62de, 0x5395, + 0x2f3c, 0x35bd, 0x2e53, 0x1d2a, 0x78d6, 0x7f69, 0x2fb9, 0x2803, 0x089b, 0x0910, + 0x171b, 0x3f2a, 0x3688, 0x0ae8, 0x123d, 0x5a72, 0x155c, 0x2030, 0x4d31, 0x1312, + 0x391e, 0x6064, 0x04b4, 0x3647, 0x58ba, 0x152d, 0x1197, 0x0130, 0x5451, 0x6c6c, + 0x1168, 0x045d, 0x64cc, 0x60c4, 0x1ceb, 0x7479, 0x47dd, 0x3e02, 0x7b52, 0x6e4f, + 0x60e8, 0x6cb8, 0x713e, 0x46af, 0x05e3, 0x6615, 0x3c74, 0x0992, 0x7de0, 0x4a46, + 0x2149, 0x23d1, 0x7005, 0x295c, 0x513a, 0x6a39, 0x3517, 0x13fc, 0x2b65, 0x3c04, + 0x1e63, 0x0a5a, 0x1b1e, 0x3722, 0x5618, 0x76d7, 0x53d3, 0x0520, 0x1803, 0x16e9, + 0x25f4, 0x56d5, 0x3ec5, 0x6976, 0x1702, 0x2b4c, 0x2494, 0x1bd1, 0x4d75, 0x73b6, + 0x431a, 0x2061, 0x356e, 0x0da7, 0x617d, 0x6025, 0x19d0, 0x14bf, 0x1769, 0x211a, + 0x3a91, 0x0f22, 0x3ff4, 0x6d94, 0x34e8, 0x6fee, 0x7a27, 0x1499, 0x2abb, 0x7762, + 0x28c6, 0x27c1, 0x0ef1, 0x3cd1, 0x1fed, 0x4bf4, 0x05cd, 0x006d, 0x6716, 0x52d2, + 0x3178, 0x4a9f, 0x20f3, 0x2db7, 0x6c91, 0x256c, 0x34ff, 0x4152, 0x7869, 0x7813, + 0x782b, 0x7bcd, 0x181c, 0x01f3, 0x737a, 0x06e2, 0x1646, 0x79e9, 0x1b6d, 0x429e, + 0x6562, 0x5b9f, 0x72df, 0x0f5e, 0x74fc, 0x6ab6, 0x2781, 0x2dde, 0x18cf, 0x3d03, + 0x4947, 0x74ba, 0x0b74, 0x5550, 0x58fd, 0x3012, 0x508c, 0x1e9f, 0x10e4, 0x63d6, + 0x71ea, 0x55c3, 0x7881, 0x47ae, 0x56ee, 0x693a, 0x5296, 0x40a7, 0x1139, 0x5fa4, + 0x54ed, 0x3787, 0x5009, 0x28f9, 0x44c9, 0x11e9, 0x3a3d, 0x022f, 0x1cb8, 0x5e20, + 0x260d, 0x0811, 0x09e9, 0x4219, 0x272e, 0x00a1, 0x12b0, 0x4030, 0x5e7b, 0x4f49, + 0x0283, 0x4029, 0x68dc, 0x1e13, 0x2352, 0x4092, 0x478b, 0x0031, 0x0403, 0x7497, + 0x3772, 0x4fab, 0x0e88, 0x634a, 0x5e74, 0x546f, 0x679e, 0x4402, 0x15a0, 0x46cd, + 0x0d40, 0x47fb, 0x53ec, 0x1003, 0x30a0, 0x7d56, 0x45da, 0x0601, 0x7074, 0x23ef, + 0x3803, 0x1a8c, 0x2c2d, 0x7023, 0x4479, 0x741b, 0x5952, 0x57b6, 0x36d6, 0x3c22, + 0x02d4, 0x0c67, 0x7a7d, 0x3f48, 0x5f13, 0x78f4, 0x6b76, 0x2271, 0x2f6c, 0x1d48, + 0x4ce7, 0x264d, 0x32a4, 0x52f9, 0x4b80, 0x4e68, 0x5dde, 0x393c, 0x3db2, 0x252c, + 0x7dbb, 0x7d96, 0x1b9d, 0x014e, 0x6dab, 0x70cf, 0x3236, 0x36a6, 0x7843, 0x1330, + 0x0539, 0x51b5, 0x4691, 0x6468, 0x6b97, 0x1608, 0x7044, 0x4c37, 0x186b, 0x3de4, + 0x60a6, 0x7aca, 0x2308, 0x1a3d, 0x5c80, 0x2e10, 0x5d7a, 0x043f, 0x6a1b, 0x3c5e, + 0x6257, 0x39b0, 0x426c, 0x56a4, 0x5cc3, 0x13de, 0x65f7, 0x5d59, 0x055f, 0x1f80, + 0x7be5, 0x0bd2, 0x5631, 0x23b3, 0x1d0c, 0x6d48, 0x1936, 0x29da, 0x26ab, 0x64ff, + 0x6085, 0x6f0d, 0x08f2, 0x5ce7, 0x1986, 0x4b13, 0x6836, 0x0310, 0x76f0, 0x27e5, + 0x5a54, 0x0182, 0x63f7, 0x51f1, 0x502a, 0x34b5, 0x2f0a, 0x2012, 0x6046, 0x07a2, + 0x1ef4, 0x7788, 0x5859, 0x673d, 0x2adc, 0x0112, 0x0f04, 0x7555, 0x2074, 0x3d21, + 0x3025, 0x72fd, 0x3a50, 0x6007, 0x2043, 0x5bbd, 0x7b65, 0x1087, 0x2bd5, 0x319f, + 0x7279, 0x7398, 0x56b7, 0x1102, 0x1999, 0x2a84, 0x4cfa, 0x7b1b, 0x2402, 0x6958, + 0x0a3c, 0x69fa, 0x7b2e, 0x4965, 0x1834, 0x1ebd, 0x373b, 0x0502, 0x254e, 0x2917, + 0x41d4, 0x2d72, 0x5ac9, 0x5b0e, 0x5ca2, 0x52b4, 0x7baf, 0x5edd, 0x0a29, 0x082f, + 0x0865, 0x44e7, 0x1b37, 0x77f5, 0x6fd0, 0x17ce, 0x08bd, 0x0a07, 0x1043, 0x4f67, + 0x499c, 0x147b, 0x27a3, 0x02a1, 0x6eae, 0x03d3, 0x474d, 0x5b5c, 0x4ac1, 0x004f, + 0x3ce5, 0x619b, 0x5911, 0x5258, 0x1570, 0x6b1a, 0x30d2, 0x0f40, 0x4280, 0x2c8d, + 0x2661, 0x24b2, 0x0843, 0x0dc5, 0x0a73, 0x79cb, 0x55a5, 0x668b, 0x6f30, 0x2b6a, + 0x0a5f, 0x1821, 0x06e7, 0x4790, 0x749c, 0x053e, 0x646d, 0x2ae1, 0x755a, 0x4ac6, + 0x61a0, 0x1e81, 0x28db, 0x5b53, 0x1327, 0x6734, 0x13f3, 0x258a, 0x7bc4, 0x4089, + 0x5e02, 0x351d, 0x3728, 0x2358, 0x4fb1, 0x585f, 0x3d27, 0x0211, 0x0083, 0x7780, + 0x6a31, 0x1e0b, 0x078b, 0x028a, 0x5476, 0x4012, 0x400b, 0x4f42, 0x51db, 0x008b, + 0x020b, 0x28e4, 0x1e7c, 0x0013, 0x0005, 0x28d6, 0x4f34, 0x007d, 0x1dfd, 0x7772, + 0x027c, 0x4004, 0x407b, 0x257c, 0x5b45, 0x6726, 0x234a, 0x350f, 0x5851, 0x0203, + 0x79bd, 0x0db7, 0x2c7f, 0x24a4, 0x524a, 0x618d, 0x6b0c, 0x0f32, 0x4782, 0x1813, + 0x667d, 0x2b5c, 0x2ad3, 0x0530, 0x4ab8, 0x1e73, 0x04f4, 0x1eaf, 0x69ec, 0x4957, + 0x2a76, 0x10f4, 0x7b0d, 0x694a, 0x5ff9, 0x72ef, 0x7547, 0x3d13, 0x1079, 0x5baf, + 0x3191, 0x738a, 0x77e7, 0x44d9, 0x5ecf, 0x0821, 0x2d64, 0x2909, 0x5b00, 0x52a6, + 0x146d, 0x4f59, 0x17c0, 0x09f9, 0x03c5, 0x0293, 0x5b4e, 0x0041, 0x51a7, 0x1322, + 0x70c1, 0x3698, 0x251e, 0x392e, 0x7d88, 0x0140, 0x2263, 0x78e6, 0x0c59, 0x3f3a, + 0x263f, 0x1d3a, 0x52eb, 0x4e5a, 0x0ff5, 0x47ed, 0x43f4, 0x46bf, 0x4f9d, 0x7489, + 0x633c, 0x5461, 0x1a7e, 0x23e1, 0x7d48, 0x05f3, 0x740d, 0x7015, 0x57a8, 0x3c14, + 0x23a5, 0x0bc4, 0x5d4b, 0x1f72, 0x39a2, 0x3c50, 0x5696, 0x13d0, 0x3dd6, 0x4c29, + 0x645a, 0x15fa, 0x1a2f, 0x7abc, 0x2e02, 0x0431, 0x27d7, 0x0302, 0x5cd9, 0x4b05, + 0x29cc, 0x6d3a, 0x64f1, 0x6eff, 0x2004, 0x34a7, 0x0174, 0x51e3, 0x777a, 0x0794, + 0x672f, 0x0104, 0x3bf6, 0x13ee, 0x294e, 0x6a2b, 0x0984, 0x6607, 0x4a38, 0x23c3, + 0x746b, 0x60b6, 0x6c5e, 0x044f, 0x6e41, 0x3df4, 0x6caa, 0x46a1, 0x3f1c, 0x0902, + 0x7f5b, 0x27f5, 0x5387, 0x6f1d, 0x35af, 0x1d1c, 0x1304, 0x2022, 0x0ada, 0x5a64, + 0x3639, 0x6056, 0x151f, 0x0122, 0x6ee1, 0x2f7c, 0x6807, 0x4e78, 0x49d2, 0x2281, + 0x7258, 0x02e4, 0x0776, 0x015e, 0x5592, 0x5dee, 0x40e5, 0x6dbb, 0x5124, 0x51c5, + 0x1f54, 0x1a9c, 0x3acf, 0x30b0, 0x3961, 0x2c3d, 0x2cdb, 0x3c32, 0x4c0b, 0x67ae, + 0x33d9, 0x1013, 0x7a3f, 0x547f, 0x2585, 0x0413, 0x01e5, 0x7bbf, 0x4144, 0x7805, + 0x4a91, 0x52c4, 0x2da9, 0x255e, 0x7754, 0x148b, 0x6d86, 0x6fe0, 0x3cc3, 0x27b3, + 0x4be6, 0x005f, 0x2b3e, 0x6968, 0x16db, 0x56c7, 0x3714, 0x0a4c, 0x76c9, 0x0512, + 0x0d99, 0x2053, 0x1bc3, 0x73a8, 0x14b1, 0x6017, 0x210c, 0x0f14, 0x692c, 0x47a0, + 0x63c8, 0x55b5, 0x5542, 0x74ac, 0x3004, 0x1e91, 0x5b91, 0x4290, 0x06d4, 0x79db, + 0x6aa8, 0x0f50, 0x2dd0, 0x3cf5, 0x0803, 0x5e12, 0x11db, 0x0221, 0x5f96, 0x4099, + 0x3779, 0x28eb, 0x4f3b, 0x4022, 0x420b, 0x0093, 0x1e05, 0x401b, 0x4084, 0x0023, + 0x00f5, 0x5dfd, 0x4204, 0x0785, 0x3498, 0x40f4, 0x68d5, 0x51d4, 0x4af6, 0x49e1, + 0x2a2a, 0x02f3, 0x6d2b, 0x4e87, 0x416b, 0x6ef0, 0x13c1, 0x3970, 0x4872, 0x3c41, + 0x0bb5, 0x30bf, 0x0c02, 0x1f63, 0x15eb, 0x1022, 0x451e, 0x4c1a, 0x7aad, 0x7a4e, + 0x6140, 0x0422, 0x4e4b, 0x5396, 0x2f3d, 0x1d2b, 0x78d7, 0x2804, 0x089c, 0x3f2b, + 0x3689, 0x5a73, 0x155d, 0x1313, 0x391f, 0x3648, 0x58bb, 0x0131, 0x5452, 0x045e, + 0x64cd, 0x747a, 0x47de, 0x6e50, 0x60e9, 0x46b0, 0x05e4, 0x0993, 0x7de1, 0x23d2, + 0x7006, 0x6a3a, 0x3518, 0x3c05, 0x1e64, 0x3723, 0x5619, 0x0521, 0x1804, 0x56d6, + 0x3ec6, 0x2b4d, 0x2495, 0x73b7, 0x431b, 0x0da8, 0x617e, 0x14c0, 0x176a, 0x0f23, + 0x3ff5, 0x6fef, 0x7a28, 0x7763, 0x28c7, 0x3cd2, 0x1fee, 0x006e, 0x6717, 0x4aa0, + 0x20f4, 0x256d, 0x3500, 0x7814, 0x782c, 0x01f4, 0x737b, 0x79ea, 0x1b6e, 0x5ba0, + 0x72e0, 0x6ab7, 0x2782, 0x3d04, 0x4948, 0x5551, 0x58fe, 0x1ea0, 0x10e5, 0x55c4, + 0x7882, 0x693b, 0x5297, 0x5fa5, 0x54ee, 0x28fa, 0x44ca, 0x0230, 0x1cb9, 0x0812, + 0x09ea, 0x00a2, 0x12b1, 0x4f4a, 0x0284, 0x1e14, 0x2353, 0x0032, 0x0404, 0x4fac, + 0x0e89, 0x5470, 0x679f, 0x46ce, 0x0d41, 0x1004, 0x30a1, 0x0602, 0x7075, 0x1a8d, + 0x2c2e, 0x741c, 0x5953, 0x3c23, 0x02d5, 0x3f49, 0x5f14, 0x2272, 0x2f6d, 0x264e, + 0x32a5, 0x4e69, 0x5ddf, 0x252d, 0x7dbc, 0x014f, 0x6dac, 0x36a7, 0x7844, 0x51b6, + 0x4692, 0x1609, 0x7045, 0x3de5, 0x60a7, 0x1a3e, 0x5c81, 0x0440, 0x6a1c, 0x39b1, + 0x426d, 0x13df, 0x65f8, 0x1f81, 0x7be6, 0x23b4, 0x1d0d, 0x29db, 0x26ac, 0x6f0e, + 0x08f3, 0x4b14, 0x6837, 0x27e6, 0x5a55, 0x51f2, 0x502b, 0x2013, 0x6047, 0x7789, + 0x585a, 0x0113, 0x0f05, 0x3d22, 0x3026, 0x6008, 0x2044, 0x1088, 0x2bd6, 0x7399, + 0x56b8, 0x2a85, 0x4cfb, 0x6959, 0x0a3d, 0x4966, 0x1835, 0x0503, 0x254f, 0x2d73, + 0x5aca, 0x52b5, 0x7bb0, 0x0830, 0x0866, 0x77f6, 0x6fd1, 0x0a08, 0x1044, 0x147c, + 0x27a4, 0x03d4, 0x474e, 0x0050, 0x3ce6, 0x5259, 0x1571, 0x0f41, 0x4281, 0x24b3, + 0x0844, 0x79cc, 0x55a6, 0x2b6b, 0x0a60, 0x4791, 0x749d, 0x2ae2, 0x755b, 0x1e82, + 0x28dc, 0x6735, 0x13f4, 0x408a, 0x5e03, 0x2359, 0x4fb2, 0x0212, 0x0084, 0x1e0c, + 0x078c, 0x4013, 0x400c, 0x008c, 0x020c, 0x0014, 0x0006, 0x007e, 0x1dfe, 0x4005, + 0x407c, 0x6727, 0x234b, 0x0204, 0x79be, 0x24a5, 0x524b, 0x0f33, 0x4783, 0x2b5d, + 0x2ad4, 0x1e74, 0x04f5, 0x4958, 0x2a77, 0x694b, 0x5ffa, 0x3d14, 0x107a, 0x738b, + 0x77e8, 0x0822, 0x2d65, 0x52a7, 0x146e, 0x09fa, 0x03c6, 0x0042, 0x51a8, 0x3699, + 0x251f, 0x0141, 0x2264, 0x3f3b, 0x2640, 0x4e5b, 0x0ff6, 0x46c0, 0x4f9e, 0x5462, + 0x1a7f, 0x05f4, 0x740e, 0x3c15, 0x23a6, 0x1f73, 0x39a3, 0x13d1, 0x3dd7, 0x15fb, + 0x1a30, 0x0432, 0x27d8, 0x4b06, 0x29cd, 0x6f00, 0x2005, 0x51e4, 0x777b, 0x0105, + 0x3bf7, 0x6a2c, 0x0985, 0x23c4, 0x746c, 0x0450, 0x6e42, 0x46a2, 0x3f1d, 0x27f6, + 0x5388, 0x1d1d, 0x1305, 0x5a65, 0x363a, 0x0123, 0x6ee2, 0x4e79, 0x49d3, 0x02e5, + 0x0777, 0x5def, 0x40e6, 0x51c6, 0x1f55, 0x30b1, 0x3962, 0x3c33, 0x4c0c, 0x1014, + 0x7a40, 0x0414, 0x01e6, 0x7806, 0x4a92, 0x255f, 0x7755, 0x6fe1, 0x3cc4, 0x0060, + 0x2b3f, 0x56c8, 0x3715, 0x0513, 0x0d9a, 0x73a9, 0x14b2, 0x0f15, 0x692d, 0x55b6, + 0x5543, 0x1e92, 0x5b92, 0x79dc, 0x6aa9, 0x3cf6, 0x0804, 0x0222, 0x5f97, 0x28ec, + 0x4f3c, 0x0094, 0x1e06, 0x0024, 0x00f6, 0x0786, 0x3499, 0x51d5, 0x4af7, 0x02f4, + 0x6d2c, 0x6ef1, 0x13c2, 0x3c42, 0x0bb6, 0x1f64, 0x15ec, 0x4c1b, 0x7aae, 0x0423, + 0x4e4c, 0x1d2c, 0x78d8, 0x3f2c, 0x368a, 0x1314, 0x3920, 0x0132, 0x5453, 0x747b, + 0x47df, 0x46b1, 0x05e5, 0x23d3, 0x7007, 0x3c06, 0x1e65, 0x0522, 0x1805, 0x2b4e, + 0x2496, 0x0da9, 0x617f, 0x0f24, 0x3ff6, 0x7764, 0x28c8, 0x006f, 0x6718, 0x256e, + 0x3501, 0x01f5, 0x737c, 0x5ba1, 0x72e1, 0x3d05, 0x4949, 0x1ea1, 0x10e6, 0x693c, + 0x5298, 0x28fb, 0x44cb, 0x0813, 0x09eb, 0x4f4b, 0x0285, 0x0033, 0x0405, 0x5471, + 0x67a0, 0x1005, 0x30a2, 0x1a8e, 0x2c2f, 0x3c24, 0x02d6, 0x2273, 0x2f6e, 0x4e6a, + 0x5de0, 0x0150, 0x6dad, 0x51b7, 0x4693, 0x3de6, 0x60a8, 0x0441, 0x6a1d, 0x13e0, + 0x65f9, 0x23b5, 0x1d0e, 0x6f0f, 0x08f4, 0x27e7, 0x5a56, 0x2014, 0x6048, 0x0114, + 0x0f06, 0x6009, 0x2045, 0x739a, 0x56b9, 0x695a, 0x0a3e, 0x0504, 0x2550, 0x52b6, + 0x7bb1, 0x77f7, 0x6fd2, 0x147d, 0x27a5, 0x0051, 0x3ce7, 0x0f42, 0x4282, 0x79cd, + 0x55a7, 0x4792, 0x749e, 0x1e83, 0x28dd, 0x408b, 0x5e04, 0x0213, 0x0085, 0x4014, + 0x400d, 0x0015, 0x0007, 0x4006, 0x407d, 0x0205, 0x79bf, 0x0f34, 0x4784, 0x1e75, + 0x04f6, 0x694c, 0x5ffb, 0x738c, 0x77e9, 0x52a8, 0x146f, 0x0043, 0x51a9, 0x0142, + 0x2265, 0x4e5c, 0x0ff7, 0x5463, 0x1a80, 0x3c16, 0x23a7, 0x13d2, 0x3dd8, 0x0433, + 0x27d9, 0x6f01, 0x2006, 0x0106, 0x3bf8, 0x23c5, 0x746d, 0x46a3, 0x3f1e, 0x1d1e, + 0x1306, 0x0124, 0x6ee3, 0x02e6, 0x0778, 0x51c7, 0x1f56, 0x3c34, 0x4c0d, 0x0415, + 0x01e7, 0x2560, 0x7756, 0x0061, 0x2b40, 0x0514, 0x0d9b, 0x0f16, 0x692e, 0x1e93, + 0x5b93, 0x3cf7, 0x0805, 0x28ed, 0x4f3d, 0x0025, 0x00f7, 0x51d6, 0x4af8, 0x6ef2, + 0x13c3, 0x1f65, 0x15ed, 0x0424, 0x4e4d, 0x3f2d, 0x368b, 0x0133, 0x5454, 0x46b2, + 0x05e6, 0x3c07, 0x1e66, 0x2b4f, 0x2497, 0x0f25, 0x3ff7, 0x0070, 0x6719, 0x01f6, + 0x737d, 0x3d06, 0x494a, 0x693d, 0x5299, 0x0814, 0x09ec, 0x0034, 0x0406, 0x1006, + 0x30a3, 0x3c25, 0x02d7, 0x4e6b, 0x5de1, 0x51b8, 0x4694, 0x0442, 0x6a1e, 0x23b6, + 0x1d0f, 0x27e8, 0x5a57, 0x0115, 0x0f07, 0x739b, 0x56ba, 0x0505, 0x2551, 0x77f8, + 0x6fd3, 0x0052, 0x3ce8, 0x79ce, 0x55a8, 0x1e84, 0x28de, 0x0214, 0x0086, 0x0016, + 0x0008, 0x0206, 0x79c0, 0x1e76, 0x04f7, 0x738d, 0x77ea, 0x0044, 0x51aa, 0x4e5d, + 0x0ff8, 0x3c17, 0x23a8, 0x0434, 0x27da, 0x0107, 0x3bf9, 0x46a4, 0x3f1f, 0x0125, + 0x6ee4, 0x51c8, 0x1f57, 0x0416, 0x01e8, 0x0062, 0x2b41, 0x0f17, 0x692f, 0x3cf8, + 0x0806, 0x0026, 0x00f8, 0x6ef3, 0x13c4, 0x0425, 0x4e4e, 0x0134, 0x5455, 0x3c08, + 0x1e67, 0x0f26, 0x3ff8, 0x01f7, 0x737e, 0x693e, 0x529a, 0x0035, 0x0407, 0x3c26, + 0x02d8, 0x51b9, 0x4695, 0x23b7, 0x1d10, 0x0116, 0x0f08, 0x0506, 0x2552, 0x0053, + 0x3ce9, 0x1e85, 0x28df, 0x0017, 0x0009, 0x1e77, 0x04f8, 0x0045, 0x51ab, 0x3c18, + 0x23a9, 0x0108, 0x3bfa, 0x0126, 0x6ee5, 0x0417, 0x01e9, 0x0f18, 0x6930, 0x0027, + 0x00f9, 0x0426, 0x4e4f, 0x3c09, 0x1e68, 0x01f8, 0x737f, 0x0036, 0x0408, 0x51ba, + 0x4696, 0x0117, 0x0f09, 0x0054, 0x3cea, 0x0018, 0x000a, 0x0046, 0x51ac, 0x0109, + 0x3bfb, 0x0418, 0x01ea, 0x0028, 0x00fa, 0x3c0a, 0x1e69, 0x0037, 0x0409, 0x0118, + 0x0f0a, 0x0019, 0x000b, 0x010a, 0x3bfc, 0x0029, 0x00fb, 0x0038, 0x040a, 0x001a, + 0x000c, 0x002a, 0x00fc, 0x001b, 0x000d, 0x001c, 0x000e, +] diff --git a/feo3boy/src/apu/lfsr7.txt b/feo3boy/src/apu/lfsr7.txt new file mode 100644 index 0000000..d3adcb5 --- /dev/null +++ b/feo3boy/src/apu/lfsr7.txt @@ -0,0 +1,15 @@ +[ + 0x0080, 0x4040, 0x6060, 0x7070, 0x7878, 0x7c7c, 0x7e7e, 0x3f3f, 0x5fdf, 0x6fef, + 0x77f7, 0x7bfb, 0x7dfd, 0x3ebe, 0x1f1f, 0x4fcf, 0x67e7, 0x73f3, 0x79f9, 0x3cbc, + 0x5e5e, 0x2f2f, 0x57d7, 0x6beb, 0x75f5, 0x3aba, 0x1d1d, 0x0e8e, 0x0707, 0x43c3, + 0x61e1, 0x30b0, 0x5858, 0x6c6c, 0x7676, 0x3b3b, 0x5ddd, 0x2eae, 0x1717, 0x4bcb, + 0x65e5, 0x32b2, 0x1919, 0x0c8c, 0x4646, 0x2323, 0x51d1, 0x28a8, 0x5454, 0x6a6a, + 0x3535, 0x1a9a, 0x0d0d, 0x0686, 0x0303, 0x41c1, 0x20a0, 0x5050, 0x6868, 0x7474, + 0x7a7a, 0x3d3d, 0x1e9e, 0x0f0f, 0x47c7, 0x63e3, 0x71f1, 0x38b8, 0x5c5c, 0x6e6e, + 0x3737, 0x5bdb, 0x6ded, 0x36b6, 0x1b1b, 0x4dcd, 0x26a6, 0x1313, 0x49c9, 0x24a4, + 0x5252, 0x2929, 0x1494, 0x4a4a, 0x2525, 0x1292, 0x0909, 0x0484, 0x4242, 0x2121, + 0x1090, 0x4848, 0x6464, 0x7272, 0x3939, 0x1c9c, 0x4e4e, 0x2727, 0x53d3, 0x69e9, + 0x34b4, 0x5a5a, 0x2d2d, 0x1696, 0x0b0b, 0x45c5, 0x22a2, 0x1111, 0x0888, 0x4444, + 0x6262, 0x3131, 0x1898, 0x4c4c, 0x6666, 0x3333, 0x59d9, 0x2cac, 0x5656, 0x2b2b, + 0x55d5, 0x2aaa, 0x1515, 0x0a8a, 0x0505, 0x0282, 0x0101, +] diff --git a/feo3boy/src/apu/lfsr7_rev.txt b/feo3boy/src/apu/lfsr7_rev.txt new file mode 100644 index 0000000..0c32aad --- /dev/null +++ b/feo3boy/src/apu/lfsr7_rev.txt @@ -0,0 +1,15 @@ +[ + 0x0000, 0x007e, 0x007d, 0x0036, 0x0057, 0x007c, 0x0035, 0x001c, 0x006c, 0x0056, + 0x007b, 0x0068, 0x002b, 0x0034, 0x001b, 0x003f, 0x005a, 0x006b, 0x0055, 0x004d, + 0x0052, 0x007a, 0x0067, 0x0026, 0x0070, 0x002a, 0x0033, 0x004a, 0x005f, 0x001a, + 0x003e, 0x000e, 0x0038, 0x0059, 0x006a, 0x002d, 0x004f, 0x0054, 0x004c, 0x0061, + 0x002f, 0x0051, 0x0079, 0x0077, 0x0075, 0x0066, 0x0025, 0x0015, 0x001f, 0x006f, + 0x0029, 0x0073, 0x0064, 0x0032, 0x0049, 0x0046, 0x0043, 0x005e, 0x0019, 0x0023, + 0x0013, 0x003d, 0x000d, 0x0007, 0x0001, 0x0037, 0x0058, 0x001d, 0x006d, 0x0069, + 0x002c, 0x0040, 0x005b, 0x004e, 0x0053, 0x0027, 0x0071, 0x004b, 0x0060, 0x000f, + 0x0039, 0x002e, 0x0050, 0x0062, 0x0030, 0x0078, 0x0076, 0x0016, 0x0020, 0x0074, + 0x0065, 0x0047, 0x0044, 0x0024, 0x0014, 0x0008, 0x0002, 0x001e, 0x006e, 0x0041, + 0x005c, 0x0028, 0x0072, 0x0010, 0x003a, 0x0063, 0x0031, 0x0017, 0x0021, 0x0048, + 0x0045, 0x0009, 0x0003, 0x0042, 0x005d, 0x0011, 0x003b, 0x0018, 0x0022, 0x000a, + 0x0004, 0x0012, 0x003c, 0x000b, 0x0005, 0x000c, 0x0006, +] diff --git a/feo3boy/src/apu/lfsr_tables.py b/feo3boy/src/apu/lfsr_tables.py new file mode 100755 index 0000000..f178329 --- /dev/null +++ b/feo3boy/src/apu/lfsr_tables.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 + +def lfsr_next15(lfsr): + """Compute the next value of the LFSR in 15 bit mode.""" + bit = (~((lfsr & 0b01) ^ ((lfsr & 0b10) >> 1))) & 1 + return ((lfsr & 0x7fff) | (bit << 15)) >> 1 + +def lfsr_next7(lfsr): + """Compute the next value of the LFSR in 7 bit mode. + + This will also set bits in the 15-bit space. + """ + bit = (~((lfsr & 0b01) ^ ((lfsr & 0b10) >> 1))) & 1 + return ((lfsr & 0x7f7f) | (bit << 15) | (bit << 7)) >> 1 + +def build_pattern(lfsr_next, *, match_bits = 0x7fff): + """Compute the pattern for the given LFSR function. + + `match_bits` is the set of bits that are used to determine whether the sequence has looped. + + Once the pattern loops, the `lfsr_next` function will be reapplied to the first several patterns + until it reaches the first pattern where reapplying does not change the pattern. + + The reason we have reapplication is because when we want to switch from 7-bit mode to 15-bit + mode, we need to reconstruct the 8 extra bits of the 15-bit pattern. Those upper 8 bits are + predictable: they're identical to the 7 bits of the 7 bit pattern + the last bit that was + shifted out of the 7 bit pattern. That last bit shifted out is the problem, since it's gone and + we would have to look backwards through the pattern to find it again. So instead, we just make + sure that the 7 bit pattern values include the correct upper 8 bits of 15 bit mode. + + When first starting in 7-bit mode, the upper bits of 15-bit mode will not match what is in the + table, since there are multiple 15-bit patterns that have the same 7-bit value. To cover this + case, the LFSR based on the table must save the upper bits of the 15-bit mode whenever it + switches to 7-bit mode and then reapply them (with appropriate shift) when switching back. This + reapplication covers those first 8 cycles of 7-bit mode where the 15-bit pattern is not fully + predictable. + """ + pat = [] + seen = set() + lfsr = 0 + while True: + if (lfsr & match_bits) not in seen: + seen.add(lfsr & match_bits) + pat.append(lfsr) + lfsr = lfsr_next(lfsr) + else: + break + + reapplied_times = 0 + for i in range(len(pat)): + if pat[i] & match_bits != lfsr & match_bits: + raise ValueError(f"Mismatched pattern: {pat[i]:x} and lfsr: {lfsr:x} at {i}") + if pat[i] == lfsr: + break + reapplied_times += 1 + pat[i] = lfsr + lfsr = lfsr_next(lfsr) + + print(f"Pat len: {len(pat)}, first: {pat[0]}, last: {pat[-1]}, reapplied: {reapplied_times}") + return pat + +def print_sequence(seq, *, file=None, per_line=10): + """Print a sequence of numbers in lines of 10 values in hex format separated by commas.""" + print('[', file = file) + for line_start in range(0, len(seq), per_line): + print(' ', file = file, end = '') + for idx in range(line_start, min(line_start + per_line, len(seq))): + print(f"0x{seq[idx]:0>4x}", end = ", ", file = file) + print(file = file) + print(']', file = file) + +def build_inverse_lookup(pat, *, match_bits = 0x7fff): + """Compute the index lookup table for the given pattern.""" + # Create a dictionary mapping from the values in the pattern (truncated to the relevant part to + # match) to the index where they appear in the pattern. + rev = {pat[i] & match_bits: i for i in range(len(pat))} + # Turn the reverse mapping into a table by effectively sorting it. We do this by selecting the + # pattern-index mapping values in order. + rev = [rev[i] for i in range(len(pat))] + # validate the reverse mapping. + for i in range(len(pat)): + if (pat[rev[i]] & match_bits) != i: + raise ValueError(f"Pattern value at {i} doesn't match from reverse lookup mapping") + return rev + +def main(): + pat15 = build_pattern(lfsr_next15) + rev15 = build_inverse_lookup(pat15) + pat7 = build_pattern(lfsr_next7, match_bits = 0x007f) + rev7 = build_inverse_lookup(pat7, match_bits = 0x007f) + + with open('lfsr15.txt', 'w') as lfsr15: + print_sequence(pat15, file=lfsr15) + with open('lfsr15_rev.txt', 'w') as lfsr15_rev: + print_sequence(rev15, file=lfsr15_rev) + + with open('lfsr7.txt', 'w') as lfsr7: + print_sequence(pat7, file=lfsr7) + with open('lfsr7_rev.txt', 'w') as lfsr7_rev: + print_sequence(rev7, file=lfsr7_rev) + + +if __name__ == '__main__': + main() diff --git a/feo3boy/src/clock/mod.rs b/feo3boy/src/clock/mod.rs index 34b453f..6b4f21e 100644 --- a/feo3boy/src/clock/mod.rs +++ b/feo3boy/src/clock/mod.rs @@ -95,7 +95,7 @@ impl SystemClock { /// normal speed). pub fn current_cycle_fixed_cycles(&self) -> DCycleIter { DCycleIter { - cycles: self.dcycle..self.dcycle + self.speed.dcycles_per_mcycle(), + cycles: self.dcycle.as_u64()..self.dcycle.as_u64() + self.speed.dcycles_per_mcycle(), } } @@ -134,7 +134,7 @@ pub struct DCycleIter { impl DCycleIter { /// Get an interator over the time ranges covered by the d-cycles in this iterator, /// assuming the cycles are a count from the start of emulator time. - fn time_ranges(self) -> DCycleTimeRanges { + pub fn time_ranges(self) -> DCycleTimeRanges { let start = DCycle::new(self.cycles.start).duration(); DCycleTimeRanges { cycles: self, @@ -236,14 +236,12 @@ impl FusedIterator for DCycleTimeRanges {} /// Type for tracking what clock cycle a value is changed at. /// -/// Values with change tracking are considered equal if have the same dirty state and were -/// updated at the same cycle. +/// Values with change tracking are considered equal if have the same value and were updated at the +/// same cycle. #[derive(Debug, Clone, Eq, PartialEq)] pub struct TimedChangeTracker { /// Snapshot of the clock when the tracked value was last updated. changed_at: ClockSnapshot, - /// If the current value has been modified without updating the clock-cycle. - is_dirty: bool, /// The value stored. value: T, } @@ -254,7 +252,6 @@ impl TimedChangeTracker { pub const fn new(initial: T) -> Self { Self { changed_at: ClockSnapshot::new(), - is_dirty: false, value: initial, } } @@ -268,10 +265,6 @@ impl TimedChangeTracker { /// Get the clock snapshot from when the value was last changed. pub const fn changed_snapshot(&self) -> &ClockSnapshot { - debug_assert!( - !self.is_dirty, - "Value was changed but changed_cycle was not updated." - ); &self.changed_at } @@ -291,56 +284,22 @@ impl TimedChangeTracker { self.changed_snapshot().elapsed_fixed_cycles() } - /// If the value was changed but the `changed_cycle` has not been updated, this sets - /// the `changed_cycle`. This should be called whenever the value might have been - /// written, before advancing the system clock, otherwise the changed at times will - /// become incorrect. - pub fn update_if_dirty(&mut self, now: ClockSnapshot) { - if self.is_dirty { - self.changed_at = now; - self.is_dirty = false; - } - } - - /// Set the value, marking the time as dirty. After this `update_if_dirty` must be - /// called to make sure the `changed_cycle` is accurate. Returns the previously stored - /// value. - /// - /// Calling this method always marks the value as dirty regardless of whether the - /// value has actually changed or the two values are equal. - pub fn set(&mut self, value: T) -> T { - self.is_dirty = true; - mem::replace(&mut self.value, value) - } - - /// Set the value and update the `changed_time` to the given value of `now`. - /// - /// Calling this method always marks the value as dirty regardless of whether the - /// value has actually changed or the two values are equal. - pub fn set_now(&mut self, value: T, now: ClockSnapshot) -> T { + /// Set the value, with the given modification time. + pub fn set(&mut self, now: ClockSnapshot, value: T) -> T { self.changed_at = now; - // We have to clear dirty because its possible this is called after the value was - // changed wiht `set` but before `update_if_dirty`. - self.is_dirty = false; mem::replace(&mut self.value, value) } /// Set the value without updating the change tracker. This allows a new value to be /// set without affecting the time tracking, for example if there are two possible /// sources of changes and you only want to record times for one of them. - /// - /// In debug mode this will panic if the changed_cycle is dirty. pub fn set_untracked(&mut self, value: T) -> T { - debug_assert!( - !self.is_dirty, - "Value was previously changed without updating last_changed_cycle" - ); mem::replace(&mut self.value, value) } } -// The derived default would result in the correct MCycle::ZERO and is_dirty: false, but -// doing it this way is more explicit. +// The derived default would result in the correct MCycle::ZERO, but doing it this way is more +// explicit. impl Default for TimedChangeTracker { #[inline] fn default() -> Self { diff --git a/feo3boy/src/gb.rs b/feo3boy/src/gb.rs index eea5c45..731f9c9 100644 --- a/feo3boy/src/gb.rs +++ b/feo3boy/src/gb.rs @@ -1,7 +1,7 @@ use std::io::{self, Read, Write}; use std::marker::PhantomData; -use crate::apu::{self, ApuContext, ApuRegs, ApuState}; +use crate::apu::{self, ApuContext, ApuRegs, ApuState, Sample}; use crate::clock::{SystemClock, SystemClockContext}; use crate::gbz80core::direct_executor::DirectExecutor; use crate::gbz80core::direct_executor_v2::DirectExecutorV2; @@ -129,7 +129,13 @@ impl Gb { /// Set the sample rate for the APU. #[inline] pub fn set_sample_rate(&mut self, sample_rate: u32) { - self.apu.set_output_sample_rate(sample_rate); + self.apu + .set_output_sample_rate(self.clock.elapsed_fixed_cycles(), sample_rate); + } + + /// Take the next audio sample if available. + pub fn consume_audio_sample(&mut self) -> Option { + self.apu.consume_output_sample() } /// Returns true if the PPU finished a frame this tick and the display is ready to be @@ -182,14 +188,13 @@ impl ExecutorContext for Gb { } fn yield1m(&mut self) { - self.mmu.update_if_dirty(self.clock.snapshot()); // TODO: run background processing while yielded. // Continue processing serial while yielded. input::update(self); serial::tick(self, 4); // apu must update before timer to catch falling edges from CPU writes apu::tick(self); - ppu::tick(self, 4); + ppu::tick(self); self.clock.advance1m(); // Within each m-cycle, the timer tick must run before the CPU tick. diff --git a/feo3boy/src/gbz80core/externdefs.rs b/feo3boy/src/gbz80core/externdefs.rs index 59fc6e9..4c7b1d1 100644 --- a/feo3boy/src/gbz80core/externdefs.rs +++ b/feo3boy/src/gbz80core/externdefs.rs @@ -3,6 +3,7 @@ use std::mem; use crate::gbz80core::ExecutorContext; +use crate::memdev::{ReadCtx, WriteCtx}; use crate::{interrupts::Interrupts, memdev::RootMemDevice}; use feo3boy_opcodes::{ @@ -68,14 +69,16 @@ pub(super) fn write_reg16(ctx: &mut impl ExecutorContext, reg: Reg16, val: u16) /// stack. #[inline] pub(super) fn read_mem(ctx: &mut impl ExecutorContext, addr: u16) -> u8 { - ctx.mem().read(addr) + let readctx = ReadCtx::new(ctx.clock().snapshot()); + ctx.mem().read(&readctx, addr) } /// Pop a 16 bit value from the stack and use it as the address, then pop an 8 bit /// value from the stack and wite it to that address. #[inline] pub(super) fn write_mem(ctx: &mut impl ExecutorContext, addr: u16, val: u8) { - ctx.mem_mut().write(addr, val) + let writectx = WriteCtx::new(ctx.clock().snapshot()); + ctx.mem_mut().write(&writectx, addr, val) } /// Fetches the flags register onto the microcode stack, diff --git a/feo3boy/src/gbz80core/mod.rs b/feo3boy/src/gbz80core/mod.rs index ce81bb2..4c6c76b 100644 --- a/feo3boy/src/gbz80core/mod.rs +++ b/feo3boy/src/gbz80core/mod.rs @@ -1,8 +1,9 @@ pub use feo3boy_opcodes::gbz80types::Flags; +use crate::clock::{SystemClock, SystemClockContext}; use crate::gbz80core::executor::ExecutorState; -use crate::interrupts::{InterruptContext, MemInterrupts}; -use crate::memdev::{MemContext, MemDevice, RootExtend}; +use crate::interrupts::{InterruptContext, InterruptFlags, Interrupts}; +use crate::memdev::{MemContext, MemDevice, ReadCtx, RootExtend, RootMemDevice, WriteCtx}; pub mod direct_executor; pub mod direct_executor_v2; @@ -178,244 +179,111 @@ pub trait CpuContext { // Utility implementations of CpuContext. ///////////////////////////////////////// -/// Allows a tuple of Gbz80State and any MemDevice to be used as an [`ExecutorContext`]. -impl ExecutorContext for (Gbz80State, M) { - type State = (); - - #[inline] - fn executor(&self) -> &Self::State { - &() - } - - #[inline] - fn executor_mut(&mut self) -> &mut Self::State { - // since () is a ZST, we can just return our address as a mutable pointer to one. - assert!(std::mem::size_of::<()>() == 0); - unsafe { std::mem::transmute(self) } - } - - /// With just a Gbz80State and arbitrary MemDevice, yielding actually does nothing. - #[inline] - fn yield1m(&mut self) {} -} - -impl CpuContext for (Gbz80State, M) { - #[inline] - fn cpu(&self) -> &Gbz80State { - &self.0 - } - - #[inline] - fn cpu_mut(&mut self) -> &mut Gbz80State { - &mut self.0 - } -} - -impl MemContext for (Gbz80State, M) { - type Mem = RootExtend; - - #[inline] - fn mem(&self) -> &Self::Mem { - RootExtend::wrap_ref(&self.1) - } - - #[inline] - fn mem_mut(&mut self) -> &mut Self::Mem { - RootExtend::wrap_mut(&mut self.1) - } +/// A Gb for testing which takes arbitrary memory using RootExtend. +#[derive(Default, Clone)] +pub(crate) struct TestGb { + pub cpu: Gbz80State, + pub state: S, + pub clock: SystemClock, + pub mem: M, } -impl InterruptContext for (Gbz80State, M) { - type Interrupts = MemInterrupts>; - - #[inline] - fn interrupts(&self) -> &Self::Interrupts { - MemInterrupts::wrap_ref(self.mem()) - } - - #[inline] - fn interrupts_mut(&mut self) -> &mut Self::Interrupts { - MemInterrupts::wrap_mut(self.mem_mut()) +impl TestGb { + /// Create a new `TestGb` with the given memory and executor state. + fn new(mem: M, state: S) -> Self { + Self { + cpu: Gbz80State::default(), + state, + clock: SystemClock::new(), + mem, + } } } -/// Allows a tuple of references to Gbz80State and any MemDevice to be used as CpuContext. -impl ExecutorContext for (&mut Gbz80State, &mut M) { - type State = (); +/// Allows a tuple of Gbz80State and any MemDevice to be used as an [`ExecutorContext`]. +impl ExecutorContext for TestGb { + type State = S; #[inline] fn executor(&self) -> &Self::State { - &() + &self.state } #[inline] fn executor_mut(&mut self) -> &mut Self::State { - // since () is a ZST, we can just return our address as a mutable pointer to one. - assert!(std::mem::size_of::<()>() == 0); - unsafe { std::mem::transmute(self) } + &mut self.state } - /// With just a Gbz80State and arbitrary MemDevice, yielding actually does nothing. #[inline] - fn yield1m(&mut self) {} + fn yield1m(&mut self) { + self.clock.advance1m(); + } } -impl CpuContext for (&mut Gbz80State, &mut M) { +impl CpuContext for TestGb { #[inline] fn cpu(&self) -> &Gbz80State { - self.0 + &self.cpu } #[inline] fn cpu_mut(&mut self) -> &mut Gbz80State { - self.0 + &mut self.cpu } } -impl MemContext for (&mut Gbz80State, &mut M) { +impl MemContext for TestGb { type Mem = RootExtend; #[inline] fn mem(&self) -> &Self::Mem { - RootExtend::wrap_ref(self.1) + RootExtend::wrap_ref(&self.mem) } #[inline] fn mem_mut(&mut self) -> &mut Self::Mem { - RootExtend::wrap_mut(self.1) - } -} - -impl InterruptContext for (&mut Gbz80State, &mut M) { - type Interrupts = MemInterrupts>; - - #[inline] - fn interrupts(&self) -> &Self::Interrupts { - MemInterrupts::wrap_ref(self.mem()) - } - - #[inline] - fn interrupts_mut(&mut self) -> &mut Self::Interrupts { - MemInterrupts::wrap_mut(self.mem_mut()) - } -} - -/// Allows a tuple of Gbz80State and any MemDevice and [`ExecutorState`] to be used as an -/// [`ExecutorContext`]. -impl ExecutorContext for (Gbz80State, M, S) { - type State = S; - - #[inline] - fn executor(&self) -> &Self::State { - &self.2 - } - - #[inline] - fn executor_mut(&mut self) -> &mut Self::State { - &mut self.2 + RootExtend::wrap_mut(&mut self.mem) } - - /// With just a Gbz80State and arbitrary MemDevice, yielding actually does nothing. - #[inline] - fn yield1m(&mut self) {} } -impl CpuContext for (Gbz80State, M, S) { - #[inline] - fn cpu(&self) -> &Gbz80State { - &self.0 - } - - #[inline] - fn cpu_mut(&mut self) -> &mut Gbz80State { - &mut self.0 +impl SystemClockContext for TestGb { + fn clock(&self) -> &SystemClock { + &self.clock } } -impl MemContext for (Gbz80State, M, S) { - type Mem = RootExtend; - - #[inline] - fn mem(&self) -> &Self::Mem { - RootExtend::wrap_ref(&self.1) - } - - #[inline] - fn mem_mut(&mut self) -> &mut Self::Mem { - RootExtend::wrap_mut(&mut self.1) - } -} - -impl InterruptContext for (Gbz80State, M, S) { - type Interrupts = MemInterrupts>; +impl InterruptContext for TestGb { + type Interrupts = Self; #[inline] fn interrupts(&self) -> &Self::Interrupts { - MemInterrupts::wrap_ref(self.mem()) + self } #[inline] fn interrupts_mut(&mut self) -> &mut Self::Interrupts { - MemInterrupts::wrap_mut(self.mem_mut()) - } -} - -/// Allows a tuple of references to [`Gbz80State`] and any [`MemDevice`] and -/// [`ExecutorState`] to be used as CpuContext. -impl ExecutorContext for (&mut Gbz80State, &mut M, &mut S) { - type State = S; - - #[inline] - fn executor(&self) -> &Self::State { - &self.2 - } - - #[inline] - fn executor_mut(&mut self) -> &mut Self::State { - &mut self.2 + self } - - /// With just a Gbz80State and arbitrary MemDevice, yielding actually does nothing. - #[inline] - fn yield1m(&mut self) {} } -impl CpuContext for (&mut Gbz80State, &mut M, &mut S) { - #[inline] - fn cpu(&self) -> &Gbz80State { - self.0 +impl Interrupts for TestGb { + fn queued(&self) -> InterruptFlags { + let ctx = ReadCtx::new(self.clock.snapshot()); + InterruptFlags::from_bits_truncate(self.mem().read_byte(&ctx, 0xff0f)) } - #[inline] - fn cpu_mut(&mut self) -> &mut Gbz80State { - self.0 + fn set_queued(&mut self, flags: InterruptFlags) { + let ctx = WriteCtx::new(self.clock.snapshot()); + self.mem_mut().write_byte(&ctx, 0xff0f, flags.bits()); } -} - -impl MemContext for (&mut Gbz80State, &mut M, &mut S) { - type Mem = RootExtend; - #[inline] - fn mem(&self) -> &Self::Mem { - RootExtend::wrap_ref(self.1) - } - - #[inline] - fn mem_mut(&mut self) -> &mut Self::Mem { - RootExtend::wrap_mut(self.1) - } -} - -impl InterruptContext for (&mut Gbz80State, &mut M, &mut S) { - type Interrupts = MemInterrupts>; - - #[inline] - fn interrupts(&self) -> &Self::Interrupts { - MemInterrupts::wrap_ref(self.mem()) + fn enabled(&self) -> InterruptFlags { + let ctx = ReadCtx::new(self.clock.snapshot()); + InterruptFlags::from_bits_truncate(self.mem().read_byte(&ctx, 0xffff)) } - #[inline] - fn interrupts_mut(&mut self) -> &mut Self::Interrupts { - MemInterrupts::wrap_mut(self.mem_mut()) + fn set_enabled(&mut self, flags: InterruptFlags) { + let ctx = WriteCtx::new(self.clock.snapshot()); + self.mem_mut().write_byte(&ctx, 0xffff, flags.bits()); } } diff --git a/feo3boy/src/interrupts.rs b/feo3boy/src/interrupts.rs index 1b331e5..dfe64fc 100644 --- a/feo3boy/src/interrupts.rs +++ b/feo3boy/src/interrupts.rs @@ -1,8 +1,6 @@ -use std::mem; - use bitflags::bitflags; -use crate::memdev::{MemDevice, RootMemDevice}; +use crate::memdev::MemDevice; bitflags! { /// Available set of interrupt flags. @@ -93,47 +91,3 @@ pub trait Interrupts { self.enabled() & self.queued() } } - -/// Wraps a mem-device, providing access to memory-mapped interrupt registers by reading -/// and writing memory. This type assumes the interrupt vector is memory mapped at 0xff0f, -/// and the interrupt enable register is memory mapped at 0xffff. -#[repr(transparent)] -pub struct MemInterrupts(M); - -impl MemInterrupts { - /// Convert a reference to a memory device into a mem-interrupts accessor. - #[inline] - pub fn wrap_ref<'a>(device: &'a M) -> &'a MemInterrupts { - // This is safe because MemInterrupts is repr(transparent) so the layout of - // MemInterupts is the same as M and because the returned reference has the - // same lifetime as the passed reference, so memory safety rules for M are upheld. - unsafe { mem::transmute(device) } - } - - /// Convert a mutable reference to a memory device into a mem-interrupts accessor. - #[inline] - pub fn wrap_mut<'a>(device: &'a mut M) -> &'a mut MemInterrupts { - // This is safe because MemInterrupts is repr(transparent) so the layout of - // MemInterupts is the same as M and because the returned reference has the - // same lifetime as the passed reference, so memory safety rules for M are upheld. - unsafe { mem::transmute(device) } - } -} - -impl Interrupts for MemInterrupts { - fn queued(&self) -> InterruptFlags { - InterruptFlags::from_bits_truncate(self.0.read_byte(0xff0f)) - } - - fn set_queued(&mut self, flags: InterruptFlags) { - self.0.write_byte(0xff0f, flags.bits()); - } - - fn enabled(&self) -> InterruptFlags { - InterruptFlags::from_bits_truncate(self.0.read_byte(0xffff)) - } - - fn set_enabled(&mut self, flags: InterruptFlags) { - self.0.write_byte(0xffff, flags.bits()); - } -} diff --git a/feo3boy/src/macros.rs b/feo3boy/src/macros.rs index 0264a5b..751d17e 100644 --- a/feo3boy/src/macros.rs +++ b/feo3boy/src/macros.rs @@ -11,90 +11,87 @@ macro_rules! memdev_fields { impl $crate::memdev::MemDevice for $ty { const LEN: usize = $len; - fn read_byte_relative(&self, addr: $crate::memdev::RelativeAddr) -> u8 { + fn read_byte_relative(&self, ctx: &$crate::memdev::ReadCtx, addr: $crate::memdev::RelativeAddr) -> u8 { $crate::dispatch_memdev_byte!($ty, addr, |addr| { - $($start $(..= $end)? => $crate::memdev_fields!(@reader: self, addr, $target),)* + $($start $(..= $end)? => $crate::memdev_fields!(@reader: self, ctx, addr, $target),)* }) } - fn read_bytes_relative(&self, addr: $crate::memdev::RelativeAddr, data: &mut [u8]) { + fn read_bytes_relative(&self, ctx: &$crate::memdev::ReadCtx, addr: $crate::memdev::RelativeAddr, data: &mut [u8]) { $crate::dispatch_memdev_bytes!($ty, addr, data, |addr, mut data| { $($start $(..= $end)? => { - $crate::memdev_fields!(@range_reader: self, addr, data, $target) + $crate::memdev_fields!(@range_reader: self, ctx, addr, data, $target) },)* }) } - fn write_byte_relative(&mut self, addr: $crate::memdev::RelativeAddr, val: u8) { + fn write_byte_relative(&mut self, ctx: &$crate::memdev::WriteCtx, addr: $crate::memdev::RelativeAddr, val: u8) { $crate::dispatch_memdev_byte!($ty, addr, |addr| { - $($start $(..= $end)? => $crate::memdev_fields!(@writer: self, addr, val, $target),)* + $($start $(..= $end)? => $crate::memdev_fields!(@writer: self, ctx, addr, val, $target),)* }) } - fn write_bytes_relative(&mut self, addr: $crate::memdev::RelativeAddr, data: &[u8]) { + fn write_bytes_relative(&mut self, ctx: &$crate::memdev::WriteCtx, addr: $crate::memdev::RelativeAddr, data: &[u8]) { $crate::dispatch_memdev_bytes!($ty, addr, data, |addr, ref data| { $($start $(..= $end)? => { - $crate::memdev_fields!(@range_writer: self, addr, data, $target) + $crate::memdev_fields!(@range_writer: self, ctx, addr, data, $target) },)* }); } } }; - (@reader: $self:ident, $addr:expr, $target:ident) => { - $crate::memdev::MemDevice::read_byte_relative(&$self.$target, $addr) + (@reader: $self:ident, $ctx:ident, $addr:expr, $target:ident) => { + $crate::memdev::MemDevice::read_byte_relative(&$self.$target, $ctx, $addr) }; - (@reader: $self:ident, $addr:expr, $target:literal) => { + (@reader: $self:ident, $ctx:ident, $addr:expr, $target:literal) => { $target }; - (@reader: $self:ident, $addr:expr, { $target:ident, skip_over: $skip:literal }) => { - $crate::memdev_fields!(@reader: $self, $addr.skip_over($skip), $target) + (@reader: $self:ident, $ctx:ident, $addr:expr, { $target:ident, skip_over: $skip:literal }) => { + $crate::memdev_fields!(@reader: $self, $ctx, $addr.skip_over($skip), $target) }; - (@reader: $self:ident, $addr:expr, { $target:ident, readonly }) => { - $crate::memdev_fields!(@reader: $self, $addr, $target) + (@reader: $self:ident, $ctx:ident, $addr:expr, { $target:ident, readonly }) => { + $crate::memdev_fields!(@reader: $self, $ctx, $addr, $target) }; - (@range_reader: $self:ident, $addr:expr, $data:ident, $target:ident) => { - $crate::memdev::MemDevice::read_bytes_relative(&$self.$target, $addr, $data) + (@range_reader: $self:ident, $ctx:ident, $addr:expr, $data:ident, $target:ident) => { + $crate::memdev::MemDevice::read_bytes_relative(&$self.$target, $ctx, $addr, $data) }; - (@range_reader: $self:ident, $addr:expr, $data:ident, $target:literal) => { + (@range_reader: $self:ident, $ctx:ident, $addr:expr, $data:ident, $target:literal) => { $data.fill($target) }; - (@range_reader: $self:ident, $addr:expr, $data:ident, { $target:ident, skip_over: $skip:literal }) => { - $crate::memdev_fields!(@range_reader: $self, $addr.skip_over($skip), $data, $target) + (@range_reader: $self:ident, $ctx:ident, $addr:expr, $data:ident, { $target:ident, skip_over: $skip:literal }) => { + $crate::memdev_fields!(@range_reader: $self, $ctx, $addr.skip_over($skip), $data, $target) }; - (@range_reader: $self:ident, $addr:expr, $data:ident, { $target:ident, readonly }) => { - $crate::memdev_fields!(@range_reader: $self, $addr, $data, $target) + (@range_reader: $self:ident, $ctx:ident, $addr:expr, $data:ident, { $target:ident, readonly }) => { + $crate::memdev_fields!(@range_reader: $self, $ctx, $addr, $data, $target) }; - (@writer: $self:ident, $addr:expr, $val:ident, $target:ident) => { - $crate::memdev::MemDevice::write_byte_relative(&mut $self.$target, $addr, $val) + (@writer: $self:ident, $ctx:ident, $addr:expr, $val:ident, $target:ident) => { + $crate::memdev::MemDevice::write_byte_relative(&mut $self.$target, $ctx, $addr, $val) }; - (@writer: $self:ident, $addr:expr, $val:ident, $target:literal) => { + (@writer: $self:ident, $ctx:ident, $addr:expr, $val:ident, $target:literal) => { () }; - (@writer: $self:ident, $addr:expr, $val:ident, { $target:ident, skip_over: $skip:literal }) => { - $crate::memdev_fields!(@writer: $self, $addr.skip_over($skip), $val, $target) + (@writer: $self:ident, $ctx:ident, $addr:expr, $val:ident, { $target:ident, skip_over: $skip:literal }) => { + $crate::memdev_fields!(@writer: $self, $ctx, $addr.skip_over($skip), $val, $target) }; - (@writer: $self:ident, $addr:expr, $val:ident, { $target:ident, readonly }) => { + (@writer: $self:ident, $ctx:ident, $addr:expr, $val:ident, { $target:ident, readonly }) => { () }; - (@range_writer: $self:ident, $addr:expr, $data:ident, $target:ident) => { - $crate::memdev::MemDevice::write_bytes_relative(&mut $self.$target, $addr, $data) + (@range_writer: $self:ident, $ctx:ident, $addr:expr, $data:ident, $target:ident) => { + $crate::memdev::MemDevice::write_bytes_relative(&mut $self.$target, $ctx, $addr, $data) }; - (@range_writer: $self:ident, $addr:expr, $data:ident, $target:literal) => { + (@range_writer: $self:ident, $ctx:ident, $addr:expr, $data:ident, $target:literal) => { () }; - (@range_writer: $self:ident, $addr:expr, $data:ident, { $target:ident, skip_over: $skip:literal }) => { - $crate::memdev_fields!(@range_writer: $self, $addr.skip_over($skip), $data, $target) + (@range_writer: $self:ident, $ctx:ident, $addr:expr, $data:ident, { $target:ident, skip_over: $skip:literal }) => { + $crate::memdev_fields!(@range_writer: $self, $ctx, $addr.skip_over($skip), $data, $target) }; - (@range_writer: $self:ident, $addr:expr, $data:ident, { $target:ident, readonly }) => { + (@range_writer: $self:ident, $ctx:ident, $addr:expr, $data:ident, { $target:ident, readonly }) => { () }; - - (@addr: $base:ident, $addr:tt) => { $base.offset_by($addr) }; - (@addr: $base:ident, $addr:literal..=$end:literal) => { $base.offset_by($addr) }; } /// Dispatch a memdev read/write for a single byte to handlers based on their address. @@ -327,21 +324,33 @@ macro_rules! check_addr { #[macro_export] macro_rules! memdev_bytes_from_byte { ($dev:ty) => { - fn read_bytes_relative(&self, addr: $crate::memdev::RelativeAddr, data: &mut [u8]) { + fn read_bytes_relative( + &self, + ctx: &$crate::memdev::ReadCtx, + addr: $crate::memdev::RelativeAddr, + data: &mut [u8], + ) { $crate::check_addr!($dev, addr, data.len()); for (offset, dest) in data.iter_mut().enumerate() { *dest = $crate::memdev::MemDevice::read_byte_relative( self, + ctx, addr.move_forward_by(offset as u16), ); } } - fn write_bytes_relative(&mut self, addr: $crate::memdev::RelativeAddr, data: &[u8]) { + fn write_bytes_relative( + &mut self, + ctx: &$crate::memdev::WriteCtx, + addr: $crate::memdev::RelativeAddr, + data: &[u8], + ) { $crate::check_addr!($dev, addr, data.len()); for (offset, source) in data.iter().enumerate() { $crate::memdev::MemDevice::write_byte_relative( self, + ctx, addr.move_forward_by(offset as u16), *source, ); diff --git a/feo3boy/src/memdev.rs b/feo3boy/src/memdev.rs index 359adbb..c1794d7 100644 --- a/feo3boy/src/memdev.rs +++ b/feo3boy/src/memdev.rs @@ -8,7 +8,7 @@ use thiserror::Error; pub use feo3boy_memdev_derive::MemDevice; use crate::apu::ApuRegs; -use crate::clock::ClockSnapshot; +use crate::clock::{ClockSnapshot, SystemClockContext}; use crate::input::ButtonRegister; use crate::interrupts::{InterruptContext, InterruptEnable, InterruptFlags, Interrupts}; use crate::ppu::PpuRegs; @@ -153,6 +153,56 @@ impl fmt::Display for RelativeAddr { } } +/// Context about a memory read. +/// +/// Currently, this contains only the time that the read occurs at, which allows memory to implement +/// some lazy-evaluation features. +/// +/// In the future, it may also contain information such as the device that caused the read, which +/// could be used to help implement certain "simultaneous" read operations. +#[derive(Debug, Clone)] +pub struct ReadCtx { + /// Access time. The clock snapshot where the read is occurring. + atime: ClockSnapshot, +} + +impl ReadCtx { + /// Create a new [`ReadCtx`] with the given access time. + pub const fn new(atime: ClockSnapshot) -> Self { + Self { atime } + } + + /// Get the time of the memory access. + pub const fn atime(&self) -> &ClockSnapshot { + &self.atime + } +} + +/// Context about a memory write. +/// +/// Currently, this contains only the time that the write occurs at, which allows memory to +/// implement some lazy-evaluation features. +/// +/// In the future, it may also contain information such as the device that caused the write, which +/// could be used to help implement certain "simultaneous" write operations. +#[derive(Debug, Clone)] +pub struct WriteCtx { + /// Access time. The clock snapshot where the read is occurring. + atime: ClockSnapshot, +} + +impl WriteCtx { + /// Create a new [`WriteCtx`] with the given access time. + pub const fn new(atime: ClockSnapshot) -> Self { + Self { atime } + } + + /// Get the time of the memory access. + pub const fn atime(&self) -> &ClockSnapshot { + &self.atime + } +} + /// Provides access to system memory. pub trait MemDevice { /// Length of this MemDevice in bytes. Reads and writes must always be within the device. @@ -161,73 +211,83 @@ pub trait MemDevice { /// Read a typed value from this MemDevice. The value must be fully contained within /// this device. #[inline] - fn read_relative_into(&self, addr: RelativeAddr, dest: &mut V) + fn read_relative_into(&self, ctx: &ReadCtx, addr: RelativeAddr, dest: &mut V) where V: MemValue, { struct Reader<'a, M: ?Sized> { + ctx: &'a ReadCtx, addr: RelativeAddr, device: &'a M, } impl<'a, M: MemDevice + ?Sized> MemSource for Reader<'a, M> { #[inline(always)] fn get_byte(self) -> u8 { - self.device.read_byte_relative(self.addr) + self.device.read_byte_relative(self.ctx, self.addr) } #[inline(always)] fn get_bytes(self, data: &mut [u8]) { - self.device.read_bytes_relative(self.addr, data) + self.device.read_bytes_relative(self.ctx, self.addr, data) } } - dest.copy_from_mem(Reader { addr, device: self }) + dest.copy_from_mem(Reader { + ctx, + addr, + device: self, + }) } /// Read a typed value from this MemDevice. The value must be fully contained within /// this device. #[inline] - fn read_relative(&self, addr: RelativeAddr) -> V + fn read_relative(&self, ctx: &ReadCtx, addr: RelativeAddr) -> V where V: MemValue + Default, { let mut val = V::default(); - self.read_relative_into(addr, &mut val); + self.read_relative_into(ctx, addr, &mut val); val } /// Write a typed value into this MemDevice. The value must fit fully within the /// device. #[inline] - fn write_relative_from(&mut self, addr: RelativeAddr, source: &V) + fn write_relative_from(&mut self, ctx: &WriteCtx, addr: RelativeAddr, source: &V) where V: MemValue, { struct Writer<'a, M: ?Sized> { + ctx: &'a WriteCtx, addr: RelativeAddr, device: &'a mut M, } impl<'a, M: MemDevice + ?Sized> MemDest for Writer<'a, M> { #[inline(always)] fn set_byte(self, val: u8) { - self.device.write_byte_relative(self.addr, val) + self.device.write_byte_relative(self.ctx, self.addr, val) } #[inline(always)] fn set_bytes(self, data: &[u8]) { - self.device.write_bytes_relative(self.addr, data) + self.device.write_bytes_relative(self.ctx, self.addr, data) } } - source.copy_to_mem(Writer { addr, device: self }) + source.copy_to_mem(Writer { + ctx, + addr, + device: self, + }) } /// Write a typed value into this MemDevice. The value must fit fully within the /// device. #[inline] - fn write_relative(&mut self, addr: RelativeAddr, val: V) + fn write_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, val: V) where V: MemValue, { - self.write_relative_from(addr, &val) + self.write_relative_from(ctx, addr, &val) } /// Read the byte at the specified address. @@ -236,15 +296,15 @@ pub trait MemDevice { /// slice. A mem device may override this method if it can provide a more efficient /// single-byte read. #[inline] - fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { + fn read_byte_relative(&self, ctx: &ReadCtx, addr: RelativeAddr) -> u8 { let mut out = 0; - self.read_bytes_relative(addr, slice::from_mut(&mut out)); + self.read_bytes_relative(ctx, addr, slice::from_mut(&mut out)); out } /// Read a range of bytes into a slice. The read bytes must not wrap past the end of /// the device. - fn read_bytes_relative(&self, addr: RelativeAddr, data: &mut [u8]); + fn read_bytes_relative(&self, ctx: &ReadCtx, addr: RelativeAddr, data: &mut [u8]); /// Write the byte at the sepcified address. /// @@ -252,13 +312,13 @@ pub trait MemDevice { /// slice. A mem device may override this method if it can provide a more efficient /// single-byte write. #[inline] - fn write_byte_relative(&mut self, addr: RelativeAddr, data: u8) { - self.write_bytes_relative(addr, slice::from_ref(&data)); + fn write_byte_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, data: u8) { + self.write_bytes_relative(ctx, addr, slice::from_ref(&data)); } /// Write a range of bytes into memory from a slice. The slice must not exceed the /// length of the MemDevice. - fn write_bytes_relative(&mut self, addr: RelativeAddr, data: &[u8]); + fn write_bytes_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, data: &[u8]); } /// A [`MemDevice`] which exists at the root. Root MemDevices can read ranges that wrap @@ -274,93 +334,103 @@ pub trait RootMemDevice: MemDevice { /// Read a typed value from this MemDevice. The value must be fully contained within /// this device. #[inline] - fn read_into(&self, addr: u16, dest: &mut V) + fn read_into(&self, ctx: &ReadCtx, addr: u16, dest: &mut V) where V: MemValue, { struct Reader<'a, M: ?Sized> { + ctx: &'a ReadCtx, addr: u16, device: &'a M, } impl<'a, M: RootMemDevice + ?Sized> MemSource for Reader<'a, M> { #[inline(always)] fn get_byte(self) -> u8 { - self.device.read_byte(self.addr) + self.device.read_byte(self.ctx, self.addr) } #[inline(always)] fn get_bytes(self, data: &mut [u8]) { - self.device.read_bytes(self.addr, data) + self.device.read_bytes(self.ctx, self.addr, data) } } - dest.copy_from_mem(Reader { addr, device: self }) + dest.copy_from_mem(Reader { + ctx, + addr, + device: self, + }) } /// Read a typed value from this MemDevice. The value must be fully contained within /// this device. #[inline] - fn read(&self, addr: u16) -> V + fn read(&self, ctx: &ReadCtx, addr: u16) -> V where V: MemValue + Default, { let mut val = V::default(); - self.read_into(addr, &mut val); + self.read_into(ctx, addr, &mut val); val } /// Write a typed value into this MemDevice. The value must fit fully within the /// device. #[inline] - fn write_from(&mut self, addr: u16, source: &V) + fn write_from(&mut self, ctx: &WriteCtx, addr: u16, source: &V) where V: MemValue, { struct Writer<'a, M: ?Sized> { + ctx: &'a WriteCtx, addr: u16, device: &'a mut M, } impl<'a, M: RootMemDevice + ?Sized> MemDest for Writer<'a, M> { #[inline(always)] fn set_byte(self, val: u8) { - self.device.write_byte(self.addr, val) + self.device.write_byte(self.ctx, self.addr, val) } #[inline(always)] fn set_bytes(self, data: &[u8]) { - self.device.write_bytes(self.addr, data) + self.device.write_bytes(self.ctx, self.addr, data) } } - source.copy_to_mem(Writer { addr, device: self }) + source.copy_to_mem(Writer { + ctx, + addr, + device: self, + }) } /// Write a typed value into this MemDevice. The value must fit fully within the /// device. #[inline] - fn write(&mut self, addr: u16, val: V) + fn write(&mut self, ctx: &WriteCtx, addr: u16, val: V) where V: MemValue, { - self.write_from(addr, &val) + self.write_from(ctx, addr, &val) } /// Read a single byte from memory. #[inline] - fn read_byte(&self, addr: u16) -> u8 { - self.read_byte_relative(addr.into()) + fn read_byte(&self, ctx: &ReadCtx, addr: u16) -> u8 { + self.read_byte_relative(ctx, addr.into()) } /// Read a range of bytes, wrapping at the ends of memory. - fn read_bytes(&self, mut addr: u16, mut data: &mut [u8]) { + fn read_bytes(&self, ctx: &ReadCtx, mut addr: u16, mut data: &mut [u8]) { const SIZE: usize = u16::MAX as usize + 1; loop { let len = SIZE - addr as usize; if len < data.len() { let (current, rest) = data.split_at_mut(len); - self.read_bytes_relative(addr.into(), current); + self.read_bytes_relative(ctx, addr.into(), current); data = rest; addr = 0; } else { - self.read_bytes_relative(addr.into(), data); + self.read_bytes_relative(ctx, addr.into(), data); break; } } @@ -368,22 +438,22 @@ pub trait RootMemDevice: MemDevice { /// Write a single byte to memory. #[inline] - fn write_byte(&mut self, addr: u16, val: u8) { - self.write_byte_relative(addr.into(), val); + fn write_byte(&mut self, ctx: &WriteCtx, addr: u16, val: u8) { + self.write_byte_relative(ctx, addr.into(), val); } /// Write a range of bytes, wrapping at the ends of memory. - fn write_bytes(&mut self, mut addr: u16, mut data: &[u8]) { + fn write_bytes(&mut self, ctx: &WriteCtx, mut addr: u16, mut data: &[u8]) { const SIZE: usize = u16::MAX as usize + 1; loop { let len = SIZE - addr as usize; if len < data.len() { let (current, rest) = data.split_at(len); - self.write_bytes_relative(addr.into(), current); + self.write_bytes_relative(ctx, addr.into(), current); data = rest; addr = 0; } else { - self.write_bytes_relative(addr.into(), data); + self.write_bytes_relative(ctx, addr.into(), data); break; } } @@ -474,7 +544,9 @@ impl MemValue for u16 { } /// Context trait for accessing memory. -pub trait MemContext { +/// +/// Extends SystemClockContext because memory reads and writes require a clock. +pub trait MemContext: SystemClockContext { /// Type of MemDevice in this context. type Mem: RootMemDevice; @@ -507,6 +579,10 @@ impl RootExtend { // RootExtend has the same memory representation (`repr(transparent)`) as `M` so // it is safe to transmute the reference with the same lifetime while preserving // mutability. + // + // Since RootExtend also has no difference in drop behavior from `M`, this is even safe if + // the resulting reference is used to replace the value stored in the original location, as + // such a replacement will effectively just wrap one mem device and unwrap the other. unsafe { mem::transmute(m) } } } @@ -514,21 +590,21 @@ impl RootExtend { impl MemDevice for RootExtend { const LEN: usize = u16::MAX as usize + 1; - fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { + fn read_byte_relative(&self, ctx: &ReadCtx, addr: RelativeAddr) -> u8 { assert!( addr.relative() == addr.raw(), "Using RootExtend with offset address {}", addr ); if addr.index() < M::LEN { - self.0.read_byte_relative(addr) + self.0.read_byte_relative(ctx, addr) } else { check_addr!(RootExtend, addr); 0xff } } - fn read_bytes_relative(&self, addr: RelativeAddr, mut data: &mut [u8]) { + fn read_bytes_relative(&self, ctx: &ReadCtx, addr: RelativeAddr, mut data: &mut [u8]) { assert!( addr.relative() == addr.raw(), "Using RootExtend with offset address {}", @@ -537,7 +613,7 @@ impl MemDevice for RootExtend { check_addr!(RootExtend, addr, data.len()); if addr.index() < M::LEN { let num_read = (M::LEN - addr.index()).min(data.len()); - self.0.read_bytes_relative(addr, &mut data[..num_read]); + self.0.read_bytes_relative(ctx, addr, &mut data[..num_read]); data = &mut data[num_read..]; } // Fill any bytes not covered by the actuall data with 0xff. check_addr ensures @@ -545,20 +621,20 @@ impl MemDevice for RootExtend { data.fill(0xff); } - fn write_byte_relative(&mut self, addr: RelativeAddr, data: u8) { + fn write_byte_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, data: u8) { assert!( addr.relative() == addr.raw(), "Using RootExtend with offset address {}", addr ); if addr.index() < M::LEN { - self.0.write_byte_relative(addr, data) + self.0.write_byte_relative(ctx, addr, data) } else { check_addr!(RootExtend, addr); } } - fn write_bytes_relative(&mut self, addr: RelativeAddr, data: &[u8]) { + fn write_bytes_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, data: &[u8]) { assert!( addr.relative() == addr.raw(), "Using RootExtend with offset address {}", @@ -567,7 +643,7 @@ impl MemDevice for RootExtend { check_addr!(RootExtend, addr, data.len()); if addr.index() < M::LEN { let num_written = (M::LEN - addr.index()).min(data.len()); - self.0.write_bytes_relative(addr, &data[..num_written]); + self.0.write_bytes_relative(ctx, addr, &data[..num_written]); } } } @@ -603,38 +679,38 @@ impl MemDevice for ReadOnly { const LEN: usize = M::LEN; #[inline] - fn read_bytes_relative(&self, addr: RelativeAddr, data: &mut [u8]) { - self.0.read_bytes_relative(addr, data); + fn read_bytes_relative(&self, ctx: &ReadCtx, addr: RelativeAddr, data: &mut [u8]) { + self.0.read_bytes_relative(ctx, addr, data); } #[inline] - fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { - self.0.read_byte_relative(addr) + fn read_byte_relative(&self, ctx: &ReadCtx, addr: RelativeAddr) -> u8 { + self.0.read_byte_relative(ctx, addr) } #[inline] - fn write_bytes_relative(&mut self, addr: RelativeAddr, data: &[u8]) { + fn write_bytes_relative(&mut self, _ctx: &WriteCtx, addr: RelativeAddr, data: &[u8]) { check_addr!(ReadOnly, addr, data.len()); } #[inline] - fn write_byte_relative(&mut self, addr: RelativeAddr, _data: u8) { + fn write_byte_relative(&mut self, _ctx: &WriteCtx, addr: RelativeAddr, _data: u8) { check_addr!(ReadOnly, addr); } } impl RootMemDevice for ReadOnly { - fn read_byte(&self, addr: u16) -> u8 { - self.0.read_byte(addr) + fn read_byte(&self, ctx: &ReadCtx, addr: u16) -> u8 { + self.0.read_byte(ctx, addr) } - fn read_bytes(&self, addr: u16, data: &mut [u8]) { - self.0.read_bytes(addr, data) + fn read_bytes(&self, ctx: &ReadCtx, addr: u16, data: &mut [u8]) { + self.0.read_bytes(ctx, addr, data) } - fn write_byte(&mut self, _addr: u16, _val: u8) {} + fn write_byte(&mut self, _ctx: &WriteCtx, _addr: u16, _val: u8) {} - fn write_bytes(&mut self, _addr: u16, _data: &[u8]) {} + fn write_bytes(&mut self, _ctx: &WriteCtx, _addr: u16, _data: &[u8]) {} } /// A rom which does bounds checks, but contains no actual memory (always returns 0xff, @@ -644,12 +720,12 @@ pub struct NullRom; impl MemDevice for NullRom { const LEN: usize = N; - fn read_bytes_relative(&self, addr: RelativeAddr, data: &mut [u8]) { + fn read_bytes_relative(&self, _ctx: &ReadCtx, addr: RelativeAddr, data: &mut [u8]) { check_addr!(NullRom, addr, data.len()); data.fill(0xff); } - fn write_bytes_relative(&mut self, addr: RelativeAddr, data: &[u8]) { + fn write_bytes_relative(&mut self, _ctx: &WriteCtx, addr: RelativeAddr, data: &[u8]) { check_addr!(NullRom, addr, data.len()); } } @@ -749,21 +825,21 @@ impl Default for BiosEnable { impl MemDevice for [u8; N] { const LEN: usize = N; - fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { + fn read_byte_relative(&self, _ctx: &ReadCtx, addr: RelativeAddr) -> u8 { match self.get(addr.index()) { Some(val) => *val, None => panic!("Address {} out of range for {} byte memory array", addr, N), } } - fn write_byte_relative(&mut self, addr: RelativeAddr, value: u8) { + fn write_byte_relative(&mut self, _ctx: &WriteCtx, addr: RelativeAddr, value: u8) { match self.get_mut(addr.index()) { Some(val) => *val = value, None => panic!("Address {} out of range for {} byte memory array", addr, N), } } - fn read_bytes_relative(&self, addr: RelativeAddr, data: &mut [u8]) { + fn read_bytes_relative(&self, _ctx: &ReadCtx, addr: RelativeAddr, data: &mut [u8]) { match self.get(addr.range(data.len())) { Some(vals) => data.copy_from_slice(vals), None => panic!( @@ -775,7 +851,7 @@ impl MemDevice for [u8; N] { } } - fn write_bytes_relative(&mut self, addr: RelativeAddr, data: &[u8]) { + fn write_bytes_relative(&mut self, _ctx: &WriteCtx, addr: RelativeAddr, data: &[u8]) { match self.get_mut(addr.range(data.len())) { Some(vals) => vals.copy_from_slice(data), None => panic!( @@ -789,11 +865,11 @@ impl MemDevice for [u8; N] { } impl RootMemDevice for [u8; 0x10000] { - fn read_byte(&self, addr: u16) -> u8 { + fn read_byte(&self, _ctx: &ReadCtx, addr: u16) -> u8 { self[addr as usize] } - fn write_byte(&mut self, addr: u16, val: u8) { + fn write_byte(&mut self, _ctx: &WriteCtx, addr: u16, val: u8) { self[addr as usize] = val; } } @@ -854,35 +930,35 @@ impl CustomDefault for MaskableMem { impl MemDevice for MaskableMem { const LEN: usize = M::LEN; - fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { + fn read_byte_relative(&self, ctx: &ReadCtx, addr: RelativeAddr) -> u8 { if !self.masked { - self.device.read_byte_relative(addr) + self.device.read_byte_relative(ctx, addr) } else { check_addr!(MaskableMem, addr); 0xff } } - fn write_byte_relative(&mut self, addr: RelativeAddr, value: u8) { + fn write_byte_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, value: u8) { if !self.masked { - self.device.write_byte_relative(addr, value) + self.device.write_byte_relative(ctx, addr, value) } else { check_addr!(MaskableMem, addr); } } - fn read_bytes_relative(&self, addr: RelativeAddr, data: &mut [u8]) { + fn read_bytes_relative(&self, ctx: &ReadCtx, addr: RelativeAddr, data: &mut [u8]) { if !self.masked { - self.device.read_bytes_relative(addr, data) + self.device.read_bytes_relative(ctx, addr, data) } else { check_addr!(MaskableMem, addr, data.len()); data.fill(0xff); } } - fn write_bytes_relative(&mut self, addr: RelativeAddr, data: &[u8]) { + fn write_bytes_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, data: &[u8]) { if !self.masked { - self.device.write_bytes_relative(addr, data) + self.device.write_bytes_relative(ctx, addr, data) } else { check_addr!(MaskableMem, addr, data.len()); } @@ -909,19 +985,19 @@ impl MemDevice for u8 { const LEN: usize = 1; #[inline] - fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { + fn read_byte_relative(&self, _ctx: &ReadCtx, addr: RelativeAddr) -> u8 { check_addr!(u8, addr); *self } #[inline] - fn write_byte_relative(&mut self, addr: RelativeAddr, val: u8) { + fn write_byte_relative(&mut self, _ctx: &WriteCtx, addr: RelativeAddr, val: u8) { check_addr!(u8, addr); *self = val; } #[inline] - fn read_bytes_relative(&self, addr: RelativeAddr, data: &mut [u8]) { + fn read_bytes_relative(&self, _ctx: &ReadCtx, addr: RelativeAddr, data: &mut [u8]) { check_addr!(u8, addr, data.len()); match data.first_mut() { Some(out) => *out = *self, @@ -930,7 +1006,7 @@ impl MemDevice for u8 { } #[inline] - fn write_bytes_relative(&mut self, addr: RelativeAddr, data: &[u8]) { + fn write_bytes_relative(&mut self, _ctx: &WriteCtx, addr: RelativeAddr, data: &[u8]) { check_addr!(u8, addr, data.len()); match data.first() { Some(val) => *self = *val, @@ -945,45 +1021,45 @@ impl MemDevice for Box { const LEN: usize = D::LEN; #[inline] - fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { - (**self).read_byte_relative(addr) + fn read_byte_relative(&self, ctx: &ReadCtx, addr: RelativeAddr) -> u8 { + (**self).read_byte_relative(ctx, addr) } #[inline] - fn write_byte_relative(&mut self, addr: RelativeAddr, value: u8) { - (**self).write_byte_relative(addr, value) + fn write_byte_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, value: u8) { + (**self).write_byte_relative(ctx, addr, value) } #[inline] - fn read_bytes_relative(&self, addr: RelativeAddr, data: &mut [u8]) { - (**self).read_bytes_relative(addr, data) + fn read_bytes_relative(&self, ctx: &ReadCtx, addr: RelativeAddr, data: &mut [u8]) { + (**self).read_bytes_relative(ctx, addr, data) } #[inline] - fn write_bytes_relative(&mut self, addr: RelativeAddr, data: &[u8]) { - (**self).write_bytes_relative(addr, data) + fn write_bytes_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, data: &[u8]) { + (**self).write_bytes_relative(ctx, addr, data) } } impl RootMemDevice for Box { #[inline] - fn read_byte(&self, addr: u16) -> u8 { - (**self).read_byte(addr) + fn read_byte(&self, ctx: &ReadCtx, addr: u16) -> u8 { + (**self).read_byte(ctx, addr) } #[inline] - fn read_bytes(&self, addr: u16, data: &mut [u8]) { - (**self).read_bytes(addr, data) + fn read_bytes(&self, ctx: &ReadCtx, addr: u16, data: &mut [u8]) { + (**self).read_bytes(ctx, addr, data) } #[inline] - fn write_byte(&mut self, addr: u16, data: u8) { - (**self).write_byte(addr, data) + fn write_byte(&mut self, ctx: &WriteCtx, addr: u16, data: u8) { + (**self).write_byte(ctx, addr, data) } #[inline] - fn write_bytes(&mut self, addr: u16, data: &[u8]) { - (**self).write_bytes(addr, data) + fn write_bytes(&mut self, ctx: &WriteCtx, addr: u16, data: &[u8]) { + (**self).write_bytes(ctx, addr, data) } } @@ -1009,11 +1085,6 @@ impl MemMappedIo { pub fn new() -> Self { Default::default() } - - pub fn update_if_dirty(&mut self, now: ClockSnapshot) { - self.timer_regs.update_if_dirty(now); - self.apu_regs.update_if_dirty(now); - } } memdev_fields!(MemMappedIo, len: 0x80, { @@ -1044,23 +1115,23 @@ pub struct AllRam(Box<[u8; 0x10000]>); impl RootMemDevice for AllRam { #[inline] - fn read_byte(&self, addr: u16) -> u8 { - self.0.read_byte(addr) + fn read_byte(&self, ctx: &ReadCtx, addr: u16) -> u8 { + self.0.read_byte(ctx, addr) } #[inline] - fn read_bytes(&self, addr: u16, data: &mut [u8]) { - self.0.read_bytes(addr, data) + fn read_bytes(&self, ctx: &ReadCtx, addr: u16, data: &mut [u8]) { + self.0.read_bytes(ctx, addr, data) } #[inline] - fn write_byte(&mut self, addr: u16, val: u8) { - self.0.write_byte(addr, val); + fn write_byte(&mut self, ctx: &WriteCtx, addr: u16, val: u8) { + self.0.write_byte(ctx, addr, val); } #[inline] - fn write_bytes(&mut self, addr: u16, data: &[u8]) { - self.0.write_bytes(addr, data) + fn write_bytes(&mut self, ctx: &WriteCtx, addr: u16, data: &[u8]) { + self.0.write_bytes(ctx, addr, data) } } @@ -1120,11 +1191,6 @@ impl GbMmu { interrupt_enable: InterruptEnable(InterruptFlags::empty()), } } - - /// Update the modified-times of any memory locations that require time-tracking. - pub fn update_if_dirty(&mut self, now: ClockSnapshot) { - self.io.update_if_dirty(now); - } } impl Default for GbMmu { @@ -1136,7 +1202,7 @@ impl Default for GbMmu { impl MemDevice for GbMmu { const LEN: usize = u16::MAX as usize + 1; - fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { + fn read_byte_relative(&self, ctx: &ReadCtx, addr: RelativeAddr) -> u8 { assert!( addr.relative() == addr.raw(), "Using Root MMU with offset address {}", @@ -1144,24 +1210,24 @@ impl MemDevice for GbMmu { ); trace!("Read from MMU address {:#x}", addr.raw); dispatch_memdev_byte!(GbMmu, addr, |addr| { - 0x0..=0xff if self.io.bios_enable.enabled() => self.bios.read_byte_relative(addr), - 0x0..=0x7fff => self.cart.read_byte_relative(addr), - 0x8000..=0x9fff => self.vram.read_byte_relative(addr), + 0x0..=0xff if self.io.bios_enable.enabled() => self.bios.read_byte_relative(ctx, addr), + 0x0..=0x7fff => self.cart.read_byte_relative(ctx, addr), + 0x8000..=0x9fff => self.vram.read_byte_relative(ctx, addr), // Cartridge ram uses the same device as cartridge rom, so skip over the // number of bytes in Cart ROM when reading from Cart RAM. - 0xa000..=0xbfff => self.cart.read_byte_relative(addr.skip_over(0x8000)), - 0xc000..=0xdfff => self.wram.read_byte_relative(addr), - 0xe000..=0xfdff => self.wram.read_byte_relative(addr), - 0xfe00..=0xfe9f => self.oam.read_byte_relative(addr), + 0xa000..=0xbfff => self.cart.read_byte_relative(ctx, addr.skip_over(0x8000)), + 0xc000..=0xdfff => self.wram.read_byte_relative(ctx, addr), + 0xe000..=0xfdff => self.wram.read_byte_relative(ctx, addr), + 0xfe00..=0xfe9f => self.oam.read_byte_relative(ctx, addr), // Unmapped portion above sprite information, always returns 0xff. 0xfea0..=0xfeff => 0xff, - 0xff00..=0xff7f => self.io.read_byte_relative(addr), - 0xff80..=0xfffe => self.zram.read_byte_relative(addr), - 0xffff => self.interrupt_enable.read_byte_relative(addr), + 0xff00..=0xff7f => self.io.read_byte_relative(ctx, addr), + 0xff80..=0xfffe => self.zram.read_byte_relative(ctx, addr), + 0xffff => self.interrupt_enable.read_byte_relative(ctx, addr), }) } - fn read_bytes_relative(&self, addr: RelativeAddr, data: &mut [u8]) { + fn read_bytes_relative(&self, ctx: &ReadCtx, addr: RelativeAddr, data: &mut [u8]) { assert!( addr.relative() == addr.raw(), "Using Root MMU with offset address {}", @@ -1169,24 +1235,24 @@ impl MemDevice for GbMmu { ); trace!("Read {} bytes from MMU address {:#x}", data.len(), addr.raw); dispatch_memdev_bytes!(GbMmu, addr, data, |addr, mut data| { - 0x0..=0xff if self.io.bios_enable.enabled() => self.bios.read_bytes_relative(addr, data), - 0x0..=0x7fff => self.cart.read_bytes_relative(addr, data), - 0x8000..=0x9fff => self.vram.read_bytes_relative(addr, data), + 0x0..=0xff if self.io.bios_enable.enabled() => self.bios.read_bytes_relative(ctx, addr, data), + 0x0..=0x7fff => self.cart.read_bytes_relative(ctx, addr, data), + 0x8000..=0x9fff => self.vram.read_bytes_relative(ctx, addr, data), // Cartridge ram uses the same device as cartridge rom, so skip over the // number of bytes in Cart ROM when reading from Cart RAM. - 0xa000..=0xbfff => self.cart.read_bytes_relative(addr.skip_over(0x8000), data), - 0xc000..=0xdfff => self.wram.read_bytes_relative(addr, data), - 0xe000..=0xfdff => self.wram.read_bytes_relative(addr, data), - 0xfe00..=0xfe9f => self.oam.read_bytes_relative(addr, data), + 0xa000..=0xbfff => self.cart.read_bytes_relative(ctx, addr.skip_over(0x8000), data), + 0xc000..=0xdfff => self.wram.read_bytes_relative(ctx, addr, data), + 0xe000..=0xfdff => self.wram.read_bytes_relative(ctx, addr, data), + 0xfe00..=0xfe9f => self.oam.read_bytes_relative(ctx, addr, data), // Unmapped portion above sprite information, always returns 0xff. 0xfea0..=0xfeff => data.fill(0xff), - 0xff00..=0xff7f => self.io.read_bytes_relative(addr, data), - 0xff80..=0xfffe => self.zram.read_bytes_relative(addr, data), - 0xffff => self.interrupt_enable.read_bytes_relative(addr, data), + 0xff00..=0xff7f => self.io.read_bytes_relative(ctx, addr, data), + 0xff80..=0xfffe => self.zram.read_bytes_relative(ctx, addr, data), + 0xffff => self.interrupt_enable.read_bytes_relative(ctx, addr, data), }); } - fn write_byte_relative(&mut self, addr: RelativeAddr, value: u8) { + fn write_byte_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, value: u8) { assert!( addr.relative() == addr.raw(), "Using Root MMU with offset address {}", @@ -1195,24 +1261,24 @@ impl MemDevice for GbMmu { trace!("Write at MMU address {:#x}", addr.raw); // Address guaranteed to be in range since we cover the whole memory space. dispatch_memdev_byte!(GbMmu, addr, |addr| { - 0x0..=0xff if self.io.bios_enable.enabled() => self.bios.write_byte_relative(addr, value), - 0x0..=0x7fff => self.cart.write_byte_relative(addr, value), - 0x8000..=0x9fff => self.vram.write_byte_relative(addr, value), + 0x0..=0xff if self.io.bios_enable.enabled() => self.bios.write_byte_relative(ctx, addr, value), + 0x0..=0x7fff => self.cart.write_byte_relative(ctx, addr, value), + 0x8000..=0x9fff => self.vram.write_byte_relative(ctx, addr, value), // Cartridge ram uses the same device as cartridge rom, so skip over the // number of bytes in Cart ROM when reading from Cart RAM. - 0xa000..=0xbfff => self.cart.write_byte_relative(addr.skip_over(0x8000), value), - 0xc000..=0xdfff => self.wram.write_byte_relative(addr, value), - 0xe000..=0xfdff => self.wram.write_byte_relative(addr, value), - 0xfe00..=0xfe9f => self.oam.write_byte_relative(addr, value), + 0xa000..=0xbfff => self.cart.write_byte_relative(ctx, addr.skip_over(0x8000), value), + 0xc000..=0xdfff => self.wram.write_byte_relative(ctx, addr, value), + 0xe000..=0xfdff => self.wram.write_byte_relative(ctx, addr, value), + 0xfe00..=0xfe9f => self.oam.write_byte_relative(ctx, addr, value), // Unmapped portion above sprite information. 0xfea0..=0xfeff => (), - 0xff00..=0xff7f => self.io.write_byte_relative(addr, value), - 0xff80..=0xfffe => self.zram.write_byte_relative(addr, value), - 0xffff => self.interrupt_enable.write_byte_relative(addr, value), + 0xff00..=0xff7f => self.io.write_byte_relative(ctx, addr, value), + 0xff80..=0xfffe => self.zram.write_byte_relative(ctx, addr, value), + 0xffff => self.interrupt_enable.write_byte_relative(ctx, addr, value), }) } - fn write_bytes_relative(&mut self, addr: RelativeAddr, data: &[u8]) { + fn write_bytes_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, data: &[u8]) { assert!( addr.relative() == addr.raw(), "Using Root MMU with offset address {}", @@ -1220,38 +1286,26 @@ impl MemDevice for GbMmu { ); trace!("Write {} bytes at MMU address {:#x}", data.len(), addr.raw); dispatch_memdev_bytes!(GbMmu, addr, data, |addr, ref data| { - 0x0..=0xff if self.io.bios_enable.enabled() => self.bios.write_bytes_relative(addr, data), - 0x0..=0x7fff => self.cart.write_bytes_relative(addr, data), - 0x8000..=0x9fff => self.vram.write_bytes_relative(addr, data), + 0x0..=0xff if self.io.bios_enable.enabled() => self.bios.write_bytes_relative(ctx, addr, data), + 0x0..=0x7fff => self.cart.write_bytes_relative(ctx, addr, data), + 0x8000..=0x9fff => self.vram.write_bytes_relative(ctx, addr, data), // Cartridge ram uses the same device as cartridge rom, so skip over the // number of bytes in Cart ROM when reading from Cart RAM. - 0xa000..=0xbfff => self.cart.write_bytes_relative(addr.skip_over(0x8000), data), - 0xc000..=0xdfff => self.wram.write_bytes_relative(addr, data), - 0xe000..=0xfdff => self.wram.write_bytes_relative(addr, data), - 0xfe00..=0xfe9f => self.oam.write_bytes_relative(addr, data), + 0xa000..=0xbfff => self.cart.write_bytes_relative(ctx, addr.skip_over(0x8000), data), + 0xc000..=0xdfff => self.wram.write_bytes_relative(ctx, addr, data), + 0xe000..=0xfdff => self.wram.write_bytes_relative(ctx, addr, data), + 0xfe00..=0xfe9f => self.oam.write_bytes_relative(ctx, addr, data), // Unmapped portion above sprite information, always returns 0. 0xfea0..=0xfeff => {}, - 0xff00..=0xff7f => self.io.write_bytes_relative(addr, data), - 0xff80..=0xfffe => self.zram.write_bytes_relative(addr, data), - 0xffff => self.interrupt_enable.write_bytes_relative(addr, data), + 0xff00..=0xff7f => self.io.write_bytes_relative(ctx, addr, data), + 0xff80..=0xfffe => self.zram.write_bytes_relative(ctx, addr, data), + 0xffff => self.interrupt_enable.write_bytes_relative(ctx, addr, data), }); } } impl RootMemDevice for GbMmu {} -impl MemContext for GbMmu { - type Mem = Self; - - fn mem(&self) -> &Self::Mem { - self - } - - fn mem_mut(&mut self) -> &mut Self::Mem { - self - } -} - impl InterruptContext for GbMmu { type Interrupts = Self; @@ -1324,18 +1378,20 @@ mod tests { let len = rng.sample(&len_dist); input_buf.resize(len, 0u8); rng.fill(&mut input_buf[..]); + let readctx = ReadCtx::new(ClockSnapshot::default()); + let writectx = WriteCtx::new(ClockSnapshot::default()); for (i, &val) in input_buf.iter().enumerate() { - mmu_individual.write_byte(addr.wrapping_add(i as u16), val); + mmu_individual.write_byte(&writectx, addr.wrapping_add(i as u16), val); } - mmu_slice.write_bytes(addr, &input_buf); + mmu_slice.write_bytes(&writectx, addr, &input_buf); output_buf_individual.resize(len, 0u8); for (i, res) in output_buf_individual.iter_mut().enumerate() { - *res = mmu_individual.read_byte(addr.wrapping_add(i as u16)); + *res = mmu_individual.read_byte(&readctx, addr.wrapping_add(i as u16)); } output_buf_slice.resize(len, 0u8); - mmu_slice.read_bytes(addr, &mut output_buf_slice); + mmu_slice.read_bytes(&readctx, addr, &mut output_buf_slice); assert_eq!(output_buf_individual, output_buf_slice); } diff --git a/feo3boy/src/memdev/cartridge.rs b/feo3boy/src/memdev/cartridge.rs index 3160276..bd67095 100644 --- a/feo3boy/src/memdev/cartridge.rs +++ b/feo3boy/src/memdev/cartridge.rs @@ -8,6 +8,8 @@ use std::slice; use log::warn; use thiserror::Error; +use crate::memdev::{ReadCtx, WriteCtx}; + use super::{MemDevice, NullRom, ReadOnly, RelativeAddr}; /// Length of a cartridge in bytes, counting both ram and rom. This counts the number of @@ -317,39 +319,39 @@ impl TryFrom> for Cartridge { impl MemDevice for Cartridge { const LEN: usize = CART_MEMDEV_LEN; - fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { + fn read_byte_relative(&self, ctx: &ReadCtx, addr: RelativeAddr) -> u8 { match self { - Cartridge::None => NullRom::.read_byte_relative(addr), - Cartridge::RomOnly(ref cart) => cart.read_byte_relative(addr), - Cartridge::Mbc1(ref cart) => cart.read_byte_relative(addr), - Cartridge::Mbc3(ref cart) => cart.read_byte_relative(addr), + Cartridge::None => NullRom::.read_byte_relative(ctx, addr), + Cartridge::RomOnly(ref cart) => cart.read_byte_relative(ctx, addr), + Cartridge::Mbc1(ref cart) => cart.read_byte_relative(ctx, addr), + Cartridge::Mbc3(ref cart) => cart.read_byte_relative(ctx, addr), } } - fn read_bytes_relative(&self, addr: RelativeAddr, data: &mut [u8]) { + fn read_bytes_relative(&self, ctx: &ReadCtx, addr: RelativeAddr, data: &mut [u8]) { match self { - Cartridge::None => NullRom::.read_bytes_relative(addr, data), - Cartridge::RomOnly(ref cart) => cart.read_bytes_relative(addr, data), - Cartridge::Mbc1(ref cart) => cart.read_bytes_relative(addr, data), - Cartridge::Mbc3(ref cart) => cart.read_bytes_relative(addr, data), + Cartridge::None => NullRom::.read_bytes_relative(ctx, addr, data), + Cartridge::RomOnly(ref cart) => cart.read_bytes_relative(ctx, addr, data), + Cartridge::Mbc1(ref cart) => cart.read_bytes_relative(ctx, addr, data), + Cartridge::Mbc3(ref cart) => cart.read_bytes_relative(ctx, addr, data), } } - fn write_byte_relative(&mut self, addr: RelativeAddr, value: u8) { + fn write_byte_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, value: u8) { match self { - Cartridge::None => NullRom::.write_byte_relative(addr, value), - Cartridge::RomOnly(ref mut cart) => cart.write_byte_relative(addr, value), - Cartridge::Mbc1(ref mut cart) => cart.write_byte_relative(addr, value), - Cartridge::Mbc3(ref mut cart) => cart.write_byte_relative(addr, value), + Cartridge::None => NullRom::.write_byte_relative(ctx, addr, value), + Cartridge::RomOnly(ref mut cart) => cart.write_byte_relative(ctx, addr, value), + Cartridge::Mbc1(ref mut cart) => cart.write_byte_relative(ctx, addr, value), + Cartridge::Mbc3(ref mut cart) => cart.write_byte_relative(ctx, addr, value), } } - fn write_bytes_relative(&mut self, addr: RelativeAddr, data: &[u8]) { + fn write_bytes_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, data: &[u8]) { match self { - Cartridge::None => NullRom::.write_bytes_relative(addr, data), - Cartridge::RomOnly(ref mut cart) => cart.write_bytes_relative(addr, data), - Cartridge::Mbc1(ref mut cart) => cart.write_bytes_relative(addr, data), - Cartridge::Mbc3(ref mut cart) => cart.write_bytes_relative(addr, data), + Cartridge::None => NullRom::.write_bytes_relative(ctx, addr, data), + Cartridge::RomOnly(ref mut cart) => cart.write_bytes_relative(ctx, addr, data), + Cartridge::Mbc1(ref mut cart) => cart.write_bytes_relative(ctx, addr, data), + Cartridge::Mbc3(ref mut cart) => cart.write_bytes_relative(ctx, addr, data), } } } @@ -432,45 +434,45 @@ impl RomOnly { impl MemDevice for RomOnly { const LEN: usize = CART_MEMDEV_LEN; - fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { + fn read_byte_relative(&self, ctx: &ReadCtx, addr: RelativeAddr) -> u8 { dispatch_memdev_byte!(RomOnly, addr, |addr| { - 0..=0x3fff => self.rom_banks[0].read_byte_relative(addr), - 0x4000..=0x7fff => self.rom_banks[1].read_byte_relative(addr), + 0..=0x3fff => self.rom_banks[0].read_byte_relative(ctx, addr), + 0x4000..=0x7fff => self.rom_banks[1].read_byte_relative(ctx, addr), 0x8000..=0x9fff => match self.ram_bank { - Some(ref ram) => ram.read_byte_relative(addr), + Some(ref ram) => ram.read_byte_relative(ctx, addr), None => 0xff, }, }) } - fn read_bytes_relative(&self, addr: RelativeAddr, data: &mut [u8]) { + fn read_bytes_relative(&self, ctx: &ReadCtx, addr: RelativeAddr, data: &mut [u8]) { dispatch_memdev_bytes!(RomOnly, addr, data, |addr, mut data| { - 0..=0x3fff => self.rom_banks[0].read_bytes_relative(addr, data), - 0x4000..=0x7fff => self.rom_banks[1].read_bytes_relative(addr, data), + 0..=0x3fff => self.rom_banks[0].read_bytes_relative(ctx, addr, data), + 0x4000..=0x7fff => self.rom_banks[1].read_bytes_relative(ctx, addr, data), 0x8000..=0x9fff => match self.ram_bank { - Some(ref ram) => ram.read_bytes_relative(addr, data), + Some(ref ram) => ram.read_bytes_relative(ctx, addr, data), None => data.fill(0xff), }, }) } - fn write_byte_relative(&mut self, addr: RelativeAddr, value: u8) { + fn write_byte_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, value: u8) { dispatch_memdev_byte!(RomOnly, addr, |addr| { - 0..=0x3fff => self.rom_banks[0].write_byte_relative(addr, value), - 0x4000..=0x7fff => self.rom_banks[1].write_byte_relative(addr, value), + 0..=0x3fff => self.rom_banks[0].write_byte_relative(ctx, addr, value), + 0x4000..=0x7fff => self.rom_banks[1].write_byte_relative(ctx, addr, value), 0x8000..=0x9fff => match self.ram_bank { - Some(ref mut ram) => ram.write_byte_relative(addr, value), + Some(ref mut ram) => ram.write_byte_relative(ctx, addr, value), None => {} }, }) } - fn write_bytes_relative(&mut self, addr: RelativeAddr, data: &[u8]) { + fn write_bytes_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, data: &[u8]) { dispatch_memdev_bytes!(RomOnly, addr, data, |addr, ref data| { - 0..=0x3fff => self.rom_banks[0].write_bytes_relative(addr, data), - 0x4000..=0x7fff => self.rom_banks[1].write_bytes_relative(addr, data), + 0..=0x3fff => self.rom_banks[0].write_bytes_relative(ctx, addr, data), + 0x4000..=0x7fff => self.rom_banks[1].write_bytes_relative(ctx, addr, data), 0x8000..=0x9fff => match self.ram_bank { - Some(ref mut ram) => ram.write_bytes_relative(addr, data), + Some(ref mut ram) => ram.write_bytes_relative(ctx, addr, data), None => {}, }, }) @@ -480,30 +482,20 @@ impl MemDevice for RomOnly { impl SaveData for RomOnly { fn write_save_data(&self, mut writer: impl Write) -> Result<(), io::Error> { if self.save_ram { - match self.ram_bank { - Some(ref ram_bank) => match writer.write_all(ram_bank.as_ref()) { - Ok(_) => Ok(()), - Err(e) => return Err(e), - }, - None => Ok(()), + if let Some(ref ram_bank) = self.ram_bank { + writer.write_all(ram_bank.as_ref())?; } - } else { - Ok(()) } + Ok(()) } fn load_save_data(&mut self, mut reader: impl Read) -> Result<(), io::Error> { if self.save_ram { - match self.ram_bank { - Some(ref mut ram_bank) => match reader.read_exact(ram_bank.as_mut()) { - Ok(_) => Ok(()), - Err(e) => return Err(e), - }, - None => Ok(()), + if let Some(ref mut ram_bank) = self.ram_bank { + reader.read_exact(ram_bank.as_mut())?; } - } else { - Ok(()) } + Ok(()) } fn has_save_data(&self) -> bool { @@ -697,29 +689,29 @@ impl Mbc1Rom { impl MemDevice for Mbc1Rom { const LEN: usize = CART_MEMDEV_LEN; - fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { + fn read_byte_relative(&self, ctx: &ReadCtx, addr: RelativeAddr) -> u8 { dispatch_memdev_byte!(Mbc1Rom, addr, |addr| { - 0..=0x3fff => self.lower_bank().read_byte_relative(addr), - 0x4000..=0x7fff => self.upper_bank().read_byte_relative(addr), + 0..=0x3fff => self.lower_bank().read_byte_relative(ctx, addr), + 0x4000..=0x7fff => self.upper_bank().read_byte_relative(ctx, addr), 0x8000..=0x9fff => match self.ram_bank() { - Some(bank) => bank.read_byte_relative(addr), + Some(bank) => bank.read_byte_relative(ctx, addr), None => 0xff, }, }) } - fn read_bytes_relative(&self, addr: RelativeAddr, data: &mut [u8]) { + fn read_bytes_relative(&self, ctx: &ReadCtx, addr: RelativeAddr, data: &mut [u8]) { dispatch_memdev_bytes!(Mbc1Rom, addr, data, |addr, mut data| { - 0..=0x3fff => self.lower_bank().read_bytes_relative(addr, data), - 0x4000..=0x7fff => self.upper_bank().read_bytes_relative(addr, data), + 0..=0x3fff => self.lower_bank().read_bytes_relative(ctx, addr, data), + 0x4000..=0x7fff => self.upper_bank().read_bytes_relative(ctx, addr, data), 0x8000..=0x9fff => match self.ram_bank() { - Some(bank) => bank.read_bytes_relative(addr, data), + Some(bank) => bank.read_bytes_relative(ctx, addr, data), None => data.fill(0xff), }, }) } - fn write_byte_relative(&mut self, addr: RelativeAddr, value: u8) { + fn write_byte_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, value: u8) { dispatch_memdev_byte!(Mbc1Rom, addr, |addr| { 0x0000..=0x1fff => self.ram_enable = (value & 0xF) == 0xA, // Set the low-order bits of the rom-bank selection from the lower 5 bits of the @@ -731,13 +723,13 @@ impl MemDevice for Mbc1Rom { // Change between basic and advanced banking mode. 0x6000..=0x7fff => self.advanced_banking_mode = (value & 1) != 0, 0x8000..=0x9fff => match self.ram_bank_mut() { - Some(bank) => bank.write_byte_relative(addr, value), + Some(bank) => bank.write_byte_relative(ctx, addr, value), None => {} }, }) } - fn write_bytes_relative(&mut self, addr: RelativeAddr, data: &[u8]) { + fn write_bytes_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, data: &[u8]) { // Writes to the control registers are applied as if all the bytes in the range // were written in order, so only the last byte in each control register range // counts. @@ -768,7 +760,7 @@ impl MemDevice for Mbc1Rom { self.advanced_banking_mode = (value & 1) != 0; }, 0x8000..=0x9fff => match self.ram_bank_mut() { - Some(bank) => bank.write_bytes_relative(addr, data), + Some(bank) => bank.write_bytes_relative(ctx, addr, data), None => {} }, }) @@ -778,17 +770,14 @@ impl MemDevice for Mbc1Rom { impl SaveData for Mbc1Rom { fn write_save_data(&self, mut writer: impl Write) -> Result<(), io::Error> { for ram_bank in &self.ram_banks { - match writer.write_all(ram_bank) { - Ok(_) => (), - Err(e) => return Err(e), - } + writer.write_all(ram_bank)?; } Ok(()) } fn load_save_data(&mut self, mut reader: impl Read) -> Result<(), io::Error> { for ram_bank in &mut self.ram_banks { - reader.read_exact(ram_bank)? + reader.read_exact(ram_bank)?; } Ok(()) } @@ -997,31 +986,31 @@ impl Mbc3Rom { impl MemDevice for Mbc3Rom { const LEN: usize = CART_MEMDEV_LEN; - fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { + fn read_byte_relative(&self, ctx: &ReadCtx, addr: RelativeAddr) -> u8 { dispatch_memdev_byte!(Mbc3Rom, addr, |addr| { - 0..=0x3fff => self.lower_bank().read_byte_relative(addr), - 0x4000..=0x7fff => self.upper_bank().read_byte_relative(addr), + 0..=0x3fff => self.lower_bank().read_byte_relative(ctx, addr), + 0x4000..=0x7fff => self.upper_bank().read_byte_relative(ctx, addr), 0x8000..=0x9fff => match self.ram_rtc_bank() { - RamOrRtc::Ram(bank) => bank.read_byte_relative(addr), + RamOrRtc::Ram(bank) => bank.read_byte_relative(ctx, addr), RamOrRtc::Rtc(reg) => *reg, RamOrRtc::None => 0xff, }, }) } - fn read_bytes_relative(&self, addr: RelativeAddr, data: &mut [u8]) { + fn read_bytes_relative(&self, ctx: &ReadCtx, addr: RelativeAddr, data: &mut [u8]) { dispatch_memdev_bytes!(Mbc3Rom, addr, data, |addr, mut data| { - 0..=0x3fff => self.lower_bank().read_bytes_relative(addr, data), - 0x4000..=0x7fff => self.upper_bank().read_bytes_relative(addr, data), + 0..=0x3fff => self.lower_bank().read_bytes_relative(ctx, addr, data), + 0x4000..=0x7fff => self.upper_bank().read_bytes_relative(ctx, addr, data), 0x8000..=0x9fff => match self.ram_rtc_bank() { - RamOrRtc::Ram(bank) => bank.read_bytes_relative(addr, data), + RamOrRtc::Ram(bank) => bank.read_bytes_relative(ctx, addr, data), RamOrRtc::Rtc(reg) => data.fill(*reg), RamOrRtc::None => data.fill(0xff), }, }) } - fn write_byte_relative(&mut self, addr: RelativeAddr, value: u8) { + fn write_byte_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, value: u8) { dispatch_memdev_byte!(Mbc3Rom, addr, |addr| { 0x0000..=0x1fff => self.ram_enable = (value & 0xF) == 0xA, // Set the low-order bits of the rom-bank selection from the lower 5 bits of the @@ -1033,14 +1022,14 @@ impl MemDevice for Mbc3Rom { // Change between basic and advanced banking mode. 0x6000..=0x7fff => self.rtc_latch = (value & 1) != 0, 0x8000..=0x9fff => match self.ram_rtc_bank_mut() { - RamOrRtc::Ram(bank) => bank.write_byte_relative(addr, value), + RamOrRtc::Ram(bank) => bank.write_byte_relative(ctx, addr, value), RamOrRtc::Rtc(reg) => *reg = value, RamOrRtc::None => {} }, }) } - fn write_bytes_relative(&mut self, addr: RelativeAddr, data: &[u8]) { + fn write_bytes_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, data: &[u8]) { dispatch_memdev_bytes!(Mbc3Rom, addr, data, |addr, ref data| { 0x0000..=0x1fff => { let value = data.last() @@ -1068,7 +1057,7 @@ impl MemDevice for Mbc3Rom { self.rtc_latch = (value & 1) != 0 }, 0x8000..=0x9fff => match self.ram_rtc_bank_mut() { - RamOrRtc::Ram(bank) => bank.write_bytes_relative(addr, data), + RamOrRtc::Ram(bank) => bank.write_bytes_relative(ctx, addr, data), RamOrRtc::Rtc(reg) => { let value = data.last() .expect("dispatch_range should never provide an empty range"); @@ -1083,17 +1072,14 @@ impl MemDevice for Mbc3Rom { impl SaveData for Mbc3Rom { fn write_save_data(&self, mut writer: impl Write) -> Result<(), io::Error> { for ram_bank in &self.ram_banks { - match writer.write_all(ram_bank) { - Ok(_) => (), - Err(e) => return Err(e), - } + writer.write_all(ram_bank)?; } Ok(()) } fn load_save_data(&mut self, mut reader: impl Read) -> Result<(), io::Error> { for ram_bank in &mut self.ram_banks { - reader.read_exact(ram_bank)? + reader.read_exact(ram_bank)?; } Ok(()) } @@ -1107,6 +1093,8 @@ impl SaveData for Mbc3Rom { mod tests { use std::mem; + use crate::clock::ClockSnapshot; + use super::*; use rand::distributions::Uniform; @@ -1150,6 +1138,9 @@ mod tests { assert_eq!(cart_individual, cart_slice); + let readctx = ReadCtx::new(ClockSnapshot::default()); + let writectx = WriteCtx::new(ClockSnapshot::default()); + for _ in 0..0x80000 { let len = rng.sample(&len_dist); @@ -1159,21 +1150,21 @@ mod tests { rng.fill(&mut input_buf[..]); for (i, &val) in input_buf.iter().enumerate() { - cart_individual.write_byte_relative(addr.move_forward_by(i as u16), val); + cart_individual.write_byte_relative(&writectx, addr.move_forward_by(i as u16), val); } - cart_slice.write_bytes_relative(addr, &input_buf); + cart_slice.write_bytes_relative(&writectx, addr, &input_buf); output_buf_individual.resize(len, 0u8); for (i, res) in output_buf_individual.iter_mut().enumerate() { - *res = cart_individual.read_byte_relative(addr.move_forward_by(i as u16)); + *res = cart_individual.read_byte_relative(&readctx, addr.move_forward_by(i as u16)); } output_buf_slice.resize(len, 0u8); - cart_slice.read_bytes_relative(addr, &mut output_buf_slice); + cart_slice.read_bytes_relative(&readctx, addr, &mut output_buf_slice); assert_eq!(output_buf_individual, output_buf_slice); } - // To reduce the test runtime, only compare the final result. + // To reduce the test runtime, only compare the full state at the end. assert_eq!(cart_individual, cart_slice); } @@ -1211,6 +1202,9 @@ mod tests { assert_eq!(cart_individual, cart_slice); + let readctx = ReadCtx::new(ClockSnapshot::default()); + let writectx = WriteCtx::new(ClockSnapshot::default()); + for _ in 0..0x80000 { let len = rng.sample(&len_dist); @@ -1220,21 +1214,21 @@ mod tests { rng.fill(&mut input_buf[..]); for (i, &val) in input_buf.iter().enumerate() { - cart_individual.write_byte_relative(addr.move_forward_by(i as u16), val); + cart_individual.write_byte_relative(&writectx, addr.move_forward_by(i as u16), val); } - cart_slice.write_bytes_relative(addr, &input_buf); + cart_slice.write_bytes_relative(&writectx, addr, &input_buf); output_buf_individual.resize(len, 0u8); for (i, res) in output_buf_individual.iter_mut().enumerate() { - *res = cart_individual.read_byte_relative(addr.move_forward_by(i as u16)); + *res = cart_individual.read_byte_relative(&readctx, addr.move_forward_by(i as u16)); } output_buf_slice.resize(len, 0u8); - cart_slice.read_bytes_relative(addr, &mut output_buf_slice); + cart_slice.read_bytes_relative(&readctx, addr, &mut output_buf_slice); assert_eq!(output_buf_individual, output_buf_slice); } - // To reduce the test runtime, only compare the final result. + // To reduce the test runtime, only compare full state at the end. assert_eq!(cart_individual, cart_slice); } @@ -1272,6 +1266,9 @@ mod tests { assert_eq!(cart_individual, cart_slice); + let readctx = ReadCtx::new(ClockSnapshot::default()); + let writectx = WriteCtx::new(ClockSnapshot::default()); + for _ in 0..0x80000 { let len = rng.sample(&len_dist); @@ -1281,21 +1278,21 @@ mod tests { rng.fill(&mut input_buf[..]); for (i, &val) in input_buf.iter().enumerate() { - cart_individual.write_byte_relative(addr.move_forward_by(i as u16), val); + cart_individual.write_byte_relative(&writectx, addr.move_forward_by(i as u16), val); } - cart_slice.write_bytes_relative(addr, &input_buf); + cart_slice.write_bytes_relative(&writectx, addr, &input_buf); output_buf_individual.resize(len, 0u8); for (i, res) in output_buf_individual.iter_mut().enumerate() { - *res = cart_individual.read_byte_relative(addr.move_forward_by(i as u16)); + *res = cart_individual.read_byte_relative(&readctx, addr.move_forward_by(i as u16)); } output_buf_slice.resize(len, 0u8); - cart_slice.read_bytes_relative(addr, &mut output_buf_slice); + cart_slice.read_bytes_relative(&readctx, addr, &mut output_buf_slice); assert_eq!(output_buf_individual, output_buf_slice); } - // To reduce the test runtime, only compare the final result. + // To reduce the test runtime, only compare the full state at the end. assert_eq!(cart_individual, cart_slice); } } diff --git a/feo3boy/src/ppu.rs b/feo3boy/src/ppu.rs index e827f5d..aa403d4 100644 --- a/feo3boy/src/ppu.rs +++ b/feo3boy/src/ppu.rs @@ -1,7 +1,9 @@ use crate::bits::BitGroup; +use crate::clock::TCycle; use crate::interrupts::{InterruptContext, InterruptFlags, Interrupts}; use crate::memdev::{ - MemContext, MemDevice, MemSource, MemValue, Oam, RelativeAddr, RootMemDevice, Vram, + MemContext, MemDevice, MemSource, MemValue, Oam, ReadCtx, RelativeAddr, RootMemDevice, Vram, + WriteCtx, }; use bitflags::bitflags; use log::{debug, trace}; @@ -213,11 +215,11 @@ impl MemValue for Object { //} impl Oam { - fn get_object(&self, i: u16) -> Object { + fn get_object(&self, ctx: &ReadCtx, i: u16) -> Object { trace!("getting attributes for object: {}", i); let base_index = i * 4; self.bypass() - .read_relative(OAM_BEGIN.move_forward_by(base_index)) + .read_relative(ctx, OAM_BEGIN.move_forward_by(base_index)) } } @@ -261,10 +263,11 @@ fn get_tile_line( let line_address = (base_address + 16 * tile_offset + 2 * line as i16) as u16; + let readctx = ReadCtx::new(ctx.clock().snapshot()); let [low_bits, high_bits]: [u8; 2] = ctx .vram() .bypass() - .read_relative(VRAM_BEGIN.move_forward_by(line_address)); + .read_relative(&readctx, VRAM_BEGIN.move_forward_by(line_address)); let mut output = [0; 8]; @@ -365,25 +368,25 @@ impl MemDevice for DmaState { const LEN: usize = 1; #[inline] - fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { + fn read_byte_relative(&self, _ctx: &ReadCtx, addr: RelativeAddr) -> u8 { check_addr!(DmaState, addr); self.read_inner() } #[inline] - fn read_bytes_relative(&self, addr: RelativeAddr, data: &mut [u8]) { + fn read_bytes_relative(&self, _ctx: &ReadCtx, addr: RelativeAddr, data: &mut [u8]) { check_addr!(DmaState, addr, data.len()); data[0] = self.read_inner(); } #[inline] - fn write_byte_relative(&mut self, addr: RelativeAddr, data: u8) { + fn write_byte_relative(&mut self, _ctx: &WriteCtx, addr: RelativeAddr, data: u8) { check_addr!(DmaState, addr); self.write_inner(data); } #[inline] - fn write_bytes_relative(&mut self, addr: RelativeAddr, data: &[u8]) { + fn write_bytes_relative(&mut self, _ctx: &WriteCtx, addr: RelativeAddr, data: &[u8]) { check_addr!(DmaState, addr, data.len()); self.write_inner(data[0]); } @@ -427,7 +430,7 @@ pub struct PpuState { truecolor_palette: [(u8, u8, u8); 4], visible_screen_buffer: ScreenBuffer, drawing_screen_buffer: ScreenBuffer, - scanline_ticks: u64, + scanline_ticks: TCycle, object_cursor: u16, scanline_x: u8, fetcher_x: u16, @@ -436,6 +439,8 @@ pub struct PpuState { obj_fifo: VecDeque<(u8, ObjAttrs)>, bg_discard: u8, in_window: bool, + /// Cycle when PPU tick was last run. + last_tick_time: TCycle, } impl PpuState { @@ -451,7 +456,7 @@ impl PpuState { self.bg_fifo.clear(); self.obj_fifo.clear(); self.obj_queue.clear(); - self.scanline_ticks -= 456; + self.scanline_ticks -= SCANLINE_WIDTH; self.object_cursor = 0; self.scanline_x = 0; self.fetcher_x = 0; @@ -479,7 +484,7 @@ impl Default for PpuState { ], visible_screen_buffer: Box::new([(0xff, 0xff, 0xff); 23040]), drawing_screen_buffer: Box::new([(0xff, 0xff, 0xff); 23040]), - scanline_ticks: 0, + scanline_ticks: TCycle::ZERO, object_cursor: 0, scanline_x: 0, fetcher_x: 0, @@ -488,6 +493,7 @@ impl Default for PpuState { obj_fifo: VecDeque::new(), bg_discard: 0, in_window: false, + last_tick_time: TCycle::ZERO, } } } @@ -520,10 +526,11 @@ pub fn bg_tile_fetch(ctx: &mut impl PpuContext) { let tile_id_offset = ((tile_map_y as u16 / 8) & 0x1f) * 32 + ((ctx.ppu().fetcher_x + scroll_offset_x) & 0x1f); - let tile_id = ctx - .vram() - .bypass() - .read_byte_relative(VRAM_BEGIN.move_forward_by(tile_id_base + tile_id_offset)); + let readctx = ReadCtx::new(ctx.clock().snapshot()); + let tile_id = ctx.vram().bypass().read_byte_relative( + &readctx, + VRAM_BEGIN.move_forward_by(tile_id_base + tile_id_offset), + ); if ctx.ppu_regs().lcdc_y / 8 == 0 { debug!( @@ -552,26 +559,32 @@ pub fn bg_tile_fetch(ctx: &mut impl PpuContext) { ctx.ppu_mut().fetcher_x += 1; } -pub fn tick(ctx: &mut impl PpuContext, tcycles: u64) { - tick_dma(ctx, tcycles); +/// Number of ticks in a scanline. +const SCANLINE_WIDTH: TCycle = TCycle::new(456); + +pub fn tick(ctx: &mut impl PpuContext) { + tick_dma(ctx); + + let now = ctx.clock().elapsed_cycles().as_tcycle(); + let cycles = now - mem::replace(&mut ctx.ppu_mut().last_tick_time, now); if ctx .ppu_regs() .lcd_control .contains(LcdFlags::DISPLAY_ENABLE) { - ctx.ppu_mut().scanline_ticks += tcycles; + ctx.ppu_mut().scanline_ticks += cycles; let lcd_stat = ctx.ppu_regs().lcd_status; let mut lcdc_y = ctx.ppu_regs().lcdc_y; trace!("LCD is enabled."); trace!("LCD status {:b}", lcd_stat); trace!("Current scanline: {}", lcdc_y); - trace!("Scanline progress: {}", ctx.ppu().scanline_ticks); + trace!("Scanline progress: {}", ctx.ppu().scanline_ticks.as_u64()); match ctx.ppu_regs().lcd_status.mode() { LcdMode::HBlank => { trace!("HBlank"); - if ctx.ppu().scanline_ticks > 456 { + if ctx.ppu().scanline_ticks > SCANLINE_WIDTH { debug!("End of scan line {}", lcdc_y); ctx.ppu_mut().scanline_reset(); lcdc_y += 1; @@ -605,9 +618,9 @@ pub fn tick(ctx: &mut impl PpuContext, tcycles: u64) { } LcdMode::VBlank => { trace!("VBlank"); - if ctx.ppu().scanline_ticks > 456 { + if ctx.ppu().scanline_ticks > SCANLINE_WIDTH { debug!("End of scan line {}", lcdc_y); - ctx.ppu_mut().scanline_ticks -= 456; + ctx.ppu_mut().scanline_ticks -= SCANLINE_WIDTH; lcdc_y += 1; ctx.ppu_regs_mut().lcdc_y = lcdc_y; @@ -625,11 +638,12 @@ pub fn tick(ctx: &mut impl PpuContext, tcycles: u64) { LcdMode::OamScan => { trace!("OAMScan"); - let end_object = ctx.ppu().scanline_ticks as u16 / 2; + let end_object = (ctx.ppu().scanline_ticks.as_u64() / 2) as u16; + let readctx = ReadCtx::new(ctx.clock().snapshot()); for i in ctx.ppu().object_cursor..end_object { if ctx.ppu().obj_queue.len() < 10 && i < 40 { - let object = ctx.oam().get_object(i); + let object = ctx.oam().get_object(&readctx, i); let sprite_height = ctx.ppu_regs().lcd_control.sprite_height(); if lcdc_y + 16 >= object.y && lcdc_y + 16 < object.y + sprite_height { @@ -651,7 +665,7 @@ pub fn tick(ctx: &mut impl PpuContext, tcycles: u64) { ctx.ppu_mut().object_cursor = end_object; - if ctx.ppu().scanline_ticks > 80 { + if ctx.ppu().scanline_ticks > TCycle::new(80) { debug!("Video mode transition from 2 (OAMScan) to 3 (WriteScreen)"); ctx.vram_mut().mask(); ctx.ppu_regs_mut().lcd_status.set_mode(LcdMode::WriteScreen) @@ -660,7 +674,7 @@ pub fn tick(ctx: &mut impl PpuContext, tcycles: u64) { LcdMode::WriteScreen => { trace!("WriteScreen"); - for _i in 0..tcycles { + for _ in 0..cycles.as_u64() { if ctx.ppu_regs().lcd_control.contains(LcdFlags::WINDOW_ENABLE) && ctx.ppu().scanline_x + 7 == ctx.ppu_regs().window_x && lcdc_y >= ctx.ppu_regs().window_y @@ -719,7 +733,7 @@ pub fn tick(ctx: &mut impl PpuContext, tcycles: u64) { ctx.ppu_mut().scanline_x += 1; if ctx.ppu().scanline_x > 159 { - debug!("Video mode transition from 3 (WriteScreen) to 0 (HBlank) after {} cycles.", ctx.ppu().scanline_ticks - 80); + debug!("Video mode transition from 3 (WriteScreen) to 0 (HBlank) after {} cycles.", ctx.ppu().scanline_ticks.as_u64() - 80); ctx.oam_mut().unmask(); ctx.vram_mut().unmask(); ctx.ppu_regs_mut().lcd_status.set_mode(LcdMode::HBlank); @@ -736,18 +750,20 @@ pub fn tick(ctx: &mut impl PpuContext, tcycles: u64) { } /// Run one DMA tick. -fn tick_dma(ctx: &mut impl PpuContext, _tcycles: u64) { +fn tick_dma(ctx: &mut impl PpuContext) { if ctx.ppu_regs().dma.active() { + let readctx = ReadCtx::new(ctx.clock().snapshot()); + let writectx = WriteCtx::new(ctx.clock().snapshot()); // The address add can never overflow because "base" is always a multiple of 0x100 // and offset is always 0x00..0xa0, so essentially this address add is just // combining bits. let src = ctx.ppu_regs().dma.source_addr(); let dest = ctx.ppu_regs().dma.dest_addr(); - let byte = ctx.mem().read_byte(src); + let byte = ctx.mem().read_byte(&readctx, src); ctx.oam_mut() .bypass_mut() .bypass_mut() - .write_byte_relative(dest, byte); + .write_byte_relative(&writectx, dest, byte); ctx.ppu_regs_mut().dma.advance(); } } diff --git a/feo3boy/src/timer.rs b/feo3boy/src/timer.rs index c30747e..269557f 100644 --- a/feo3boy/src/timer.rs +++ b/feo3boy/src/timer.rs @@ -3,7 +3,7 @@ use std::mem; use crate::bits::BitGroup; use crate::clock::{ClockSnapshot, MCycle, SystemClockContext, TimedChangeTracker}; use crate::interrupts::{InterruptContext, InterruptFlags, Interrupts}; -use crate::memdev::{MemDevice, RelativeAddr}; +use crate::memdev::{MemDevice, ReadCtx, RelativeAddr, WriteCtx}; /// Represends sound and volume settings. #[derive(Default, Debug, Clone, Eq, PartialEq, Hash, MemDevice)] @@ -79,34 +79,27 @@ impl TimerCounter { } #[derive(Default, Debug, Clone, Eq, PartialEq)] -pub struct TimerDivider(TimedChangeTracker); +pub struct TimerDivider(TimedChangeTracker<()>); impl TimerDivider { - /// Update the timer divider based on the current m-cycle, and return the current - /// value. - fn update(&mut self, now: MCycle) -> u16 { - let ticks_since_update = now - self.0.changed_cycle(); - let new_divider = *self.0 as u64 + ticks_since_update.as_tcycle().as_u64(); - self.0.set_untracked(new_divider as u16); - *self.0 - } - - /// Clear the divider to zero and reset the modification time (mark dirty). - fn reset(&mut self) { - self.0.set(0); + /// Clear the divider to zero and reset the modification time. + fn reset(&mut self, now: ClockSnapshot) { + self.0.set(now, ()); } /// Get the portion of the divider register that is visible to the CPU. - fn cpu_visible_portion(&self) -> u8 { + fn cpu_visible_portion(&self, ctx: &ReadCtx) -> u8 { // Is this faster than using a shift or division? Do all those options optimize to // the same thing? No idea! - let [_, high] = self.0.to_le_bytes(); + let [_, high] = self.value(ctx.atime().elapsed_cycles()).to_le_bytes(); high } - /// Get the raw value of the divider. - pub fn value(&self) -> u16 { - *self.0 + /// Get the raw value of the divider at the specified time. The time must be after the time when + /// the divider was last reset. + pub fn value(&self, now: MCycle) -> u16 { + debug_assert!(self.0.changed_cycle() <= now); + (now - self.0.changed_cycle()).as_u64() as u16 } /// Get the clock cycle when the divider was last reset by the CPU. @@ -138,13 +131,6 @@ pub struct TimerRegs { } impl TimerRegs { - /// Update the divider and counter if they were written since the last time their - /// changed_times were updated. - pub fn update_if_dirty(&mut self, now: ClockSnapshot) { - self.divider.0.update_if_dirty(now); - self.timer_counter.0.update_if_dirty(now); - } - /// Set the value in the counter to the value in the `timer_mod`. fn reset_counter_to_mod(&mut self) { self.timer_counter.0.set_untracked(self.timer_mod); @@ -175,26 +161,28 @@ impl Default for TimerRegs { impl MemDevice for TimerRegs { const LEN: usize = 4; - fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { + fn read_byte_relative(&self, ctx: &ReadCtx, addr: RelativeAddr) -> u8 { match addr.relative() { - 0x00 => self.divider.cpu_visible_portion(), + 0x00 => self.divider.cpu_visible_portion(ctx), 0x01 => *self.timer_counter.0, 0x02 => self.timer_mod, - 0x03 => self.timer_control.read_byte_relative(addr.offset_by(0x03)), + 0x03 => self + .timer_control + .read_byte_relative(ctx, addr.offset_by(0x03)), _ => panic!("Address {} out of range for TimerRegs", addr), } } - fn write_byte_relative(&mut self, addr: RelativeAddr, val: u8) { + fn write_byte_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, val: u8) { match addr.relative() { - 0x00 => self.divider.reset(), + 0x00 => self.divider.reset(*ctx.atime()), 0x01 => { - self.timer_counter.0.set(val); + self.timer_counter.0.set(*ctx.atime(), val); } 0x02 => self.timer_mod = val, 0x03 => { self.timer_control - .write_byte_relative(addr.offset_by(0x03), val); + .write_byte_relative(ctx, addr.offset_by(0x03), val); self.timer_enable = self.timer_control.timer_enable(); self.selected_divider_bit = self.timer_control.divider_bit(); } @@ -308,13 +296,15 @@ pub fn tick(ctx: &mut impl TimerContext) { } } - let divider = ctx.timer_regs_mut().divider.update(now); - let &TimerRegs { - timer_enable, - selected_divider_bit, - .. - } = ctx.timer_regs(); - let divider_bit = timer_enable && (divider & selected_divider_bit) != 0; + let divider_bit = { + let &TimerRegs { + timer_enable, + selected_divider_bit, + ref divider, + .. + } = ctx.timer_regs(); + timer_enable && (divider.value(now) & selected_divider_bit) != 0 + }; let old_divider_bit = mem::replace(&mut ctx.timer_mut().old_divider_bit, divider_bit); if old_divider_bit && !divider_bit { From 7431f965bf310cb6bdec92067a5355e5e92933bb Mon Sep 17 00:00:00 2001 From: Zachary Stewart Date: Sat, 27 May 2023 12:42:36 -0400 Subject: [PATCH 4/6] Get everything building in the new system --- Cargo.lock | 546 ++++--- feo3boy-player/src/executor_selector.rs | 11 + feo3boy-player/src/main.rs | 2 +- feo3boy/benches/opcodes.rs | 24 +- feo3boy/src/apu.rs | 21 +- feo3boy/src/bits.rs | 4 +- feo3boy/src/clock/mod.rs | 2 +- feo3boy/src/gbz80core/direct_executor/args.rs | 40 +- feo3boy/src/gbz80core/direct_executor/mod.rs | 16 +- .../src/gbz80core/direct_executor/tests.rs | 1282 --------------- feo3boy/src/gbz80core/direct_executor_v2.rs | 1294 +-------------- feo3boy/src/gbz80core/executor_testing.rs | 1294 +++++++++++++++ .../src/gbz80core/microcode_executor/mod.rs | 8 +- .../src/gbz80core/microcode_executor/tests.rs | 1308 --------------- feo3boy/src/gbz80core/mod.rs | 41 +- feo3boy/src/gbz80core/stepping_executor.rs | 1427 +---------------- feo3boy/src/memdev.rs | 20 +- feo3boy/src/timer.rs | 20 + feo3boy/tests/asmtests.rs | 58 +- web-debugger/src/derefs.rs | 15 +- web-debugger/src/instrs.rs | 8 +- web-debugger/src/main.rs | 5 +- web-debugger/src/memview/mod.rs | 19 +- 23 files changed, 1831 insertions(+), 5634 deletions(-) delete mode 100644 feo3boy/src/gbz80core/direct_executor/tests.rs create mode 100644 feo3boy/src/gbz80core/executor_testing.rs delete mode 100644 feo3boy/src/gbz80core/microcode_executor/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 33d4ed4..e86ce9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ab_glyph" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe21446ad43aa56417a767f3e2f3d7c4ca522904de1dd640529a76e9c5c3b33c" +checksum = "5110f1c78cf582855d895ecd0746b653db010cec6d9f5575293f27934d980a39" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -46,9 +46,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] @@ -116,42 +116,51 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.2.6" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" dependencies = [ "anstyle", "anstyle-parse", + "anstyle-query", "anstyle-wincon", - "concolor-override", - "concolor-query", + "colorchoice", "is-terminal", "utf8parse", ] [[package]] name = "anstyle" -version = "0.3.5" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" [[package]] name = "anstyle-parse" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" dependencies = [ "utf8parse", ] +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "anstyle-wincon" -version = "0.2.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -208,7 +217,7 @@ dependencies = [ "cc", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.6.2", "object", "rustc-demangle", ] @@ -265,9 +274,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.0.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1" +checksum = "6776fc96284a0bb647b615056fc496d1fe1644a7ab01829818a6d91cae888b84" [[package]] name = "block" @@ -302,9 +311,9 @@ checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytemuck" @@ -381,9 +390,9 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] name = "ciborium" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" dependencies = [ "ciborium-io", "ciborium-ll", @@ -392,15 +401,15 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" [[package]] name = "ciborium-ll" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" dependencies = [ "ciborium-io", "half", @@ -408,9 +417,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" dependencies = [ "glob", "libc", @@ -419,9 +428,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.23" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "bitflags 1.3.2", "clap_lex 0.2.4", @@ -431,9 +440,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.2.0" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6efb5f0a41b5ef5b50c5da28c07609c20091df0c1fc33d418fa2a7e693c2b624" +checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc" dependencies = [ "clap_builder", "clap_derive", @@ -442,27 +451,27 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.2.0" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671fcaa5debda4b9a84aa7fde49c907c8986c0e6ab927e04217c9cb74e7c8bc9" +checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990" dependencies = [ "anstream", "anstyle", "bitflags 1.3.2", - "clap_lex 0.4.1", + "clap_lex 0.5.0", "strsim", ] [[package]] name = "clap_derive" -version = "4.2.0" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.16", ] [[package]] @@ -476,9 +485,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "codespan-reporting" @@ -490,6 +499,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "com-rs" version = "0.2.1" @@ -506,21 +521,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "concolor-override" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f" - -[[package]] -name = "concolor-query" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf" -dependencies = [ - "windows-sys", -] - [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -548,7 +548,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ - "core-foundation-sys 0.8.3", + "core-foundation-sys 0.8.4", "libc", ] @@ -560,9 +560,9 @@ checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "core-graphics" @@ -611,12 +611,12 @@ dependencies = [ [[package]] name = "cpal" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1241019dec4a73f874bdf6fe3467a8478b47cecaf79435f7acb01f107ab41b1" +checksum = "6d959d90e938c5493000514b446987c07aed46c668faaa7d34d6c7a67b1a578c" dependencies = [ "alsa", - "core-foundation-sys 0.8.3", + "core-foundation-sys 0.8.4", "coreaudio-rs", "dasp_sample", "jni 0.19.0", @@ -631,7 +631,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows", + "windows 0.46.0", ] [[package]] @@ -653,7 +653,7 @@ dependencies = [ "atty", "cast", "ciborium", - "clap 3.2.23", + "clap 3.2.25", "criterion-plot", "itertools", "lazy_static", @@ -681,9 +681,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", @@ -792,13 +792,13 @@ dependencies = [ [[package]] name = "errno" -version = "0.2.8" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -811,11 +811,20 @@ dependencies = [ "libc", ] +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + [[package]] name = "feo3boy" version = "0.1.0" dependencies = [ - "bitflags 2.0.2", + "bitflags 2.3.1", "criterion", "env_logger", "feo3boy-executor-generator", @@ -837,7 +846,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.16", ] [[package]] @@ -848,7 +857,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.16", ] [[package]] @@ -859,14 +868,14 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.16", ] [[package]] name = "feo3boy-opcodes" version = "0.1.0" dependencies = [ - "bitflags 2.0.2", + "bitflags 2.3.1", "feo3boy-microcode-generator", "proc-macro2", "quote", @@ -876,7 +885,7 @@ dependencies = [ name = "feo3boy-player" version = "0.1.0" dependencies = [ - "clap 4.2.0", + "clap 4.3.0", "cpal", "env_logger", "feo3boy", @@ -889,12 +898,12 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.7.1", ] [[package]] @@ -923,9 +932,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -937,9 +946,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -947,44 +956,44 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-io" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.16", ] [[package]] name = "futures-sink" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -1009,9 +1018,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", @@ -1212,9 +1221,9 @@ dependencies = [ [[package]] name = "gpu-alloc" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc59e5f710e310e76e6707f86c561dd646f69a8876da9131703b2f717de818d" +checksum = "22beaafc29b38204457ea030f6fb7a84c9e4dd1b86e311ba0542533453d87f62" dependencies = [ "bitflags 1.3.2", "gpu-alloc-types", @@ -1239,7 +1248,7 @@ dependencies = [ "log", "thiserror", "winapi", - "windows", + "windows 0.44.0", ] [[package]] @@ -1367,25 +1376,25 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "is-terminal" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1448,9 +1457,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" dependencies = [ "wasm-bindgen", ] @@ -1480,9 +1489,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.140" +version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "libloading" @@ -1496,9 +1505,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "lock_api" @@ -1599,6 +1608,16 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + [[package]] name = "mio" version = "0.8.6" @@ -1608,14 +1627,14 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] name = "naga" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eafe22a23b797c9bc227c6c896419b26b5bb88fa903417a3adaed08778850d5" +checksum = "6c3d4269bcb7d50121097702fde1afb75f4ea8083aeb7a55688dcf289a853271" dependencies = [ "bit-set", "bitflags 1.3.2", @@ -1764,9 +1783,9 @@ checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" [[package]] name = "objc2" -version = "0.3.0-beta.3.patch-leaks.2" +version = "0.3.0-beta.3.patch-leaks.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d9bb2ee6b71d02b1b3554ed600d267ee9a2796acc9fa43fb7748e13fe072dd" +checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" dependencies = [ "block2", "objc-sys", @@ -1837,14 +1856,11 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "orbclient" -version = "0.3.43" +version = "0.3.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "974465c5e83cf9df05c1e4137b271d29035c902e39e5ad4c1939837e22160af8" +checksum = "221d488cd70617f1bd599ed8ceb659df2147d9393717954d82a0f5e8032a6ab1" dependencies = [ - "cfg-if", - "redox_syscall 0.2.16", - "wasm-bindgen", - "web-sys", + "redox_syscall 0.3.5", ] [[package]] @@ -1855,9 +1871,9 @@ checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" [[package]] name = "owned_ttf_parser" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25e9fb15717794fae58ab55c26e044103aad13186fbb625893f9a3bbcc24228" +checksum = "706de7e2214113d63a8238d1910463cfce781129a6f263d13fdb09ff64355ba4" dependencies = [ "ttf-parser", ] @@ -1891,7 +1907,7 @@ dependencies = [ "libc", "redox_syscall 0.2.16", "smallvec", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -1908,22 +1924,22 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.16", ] [[package]] @@ -1951,9 +1967,9 @@ dependencies = [ [[package]] name = "pixels" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08f1233bd328cc8348a11681ac31bd1c41bd5098f3cf83f0a4871f1d8d5c6" +checksum = "a8a87d67cc8f4592a5313b37ce395e8965dd8284492a85f19478af941a4df257" dependencies = [ "bytemuck", "pollster", @@ -1965,9 +1981,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "plotters" @@ -1999,14 +2015,15 @@ dependencies = [ [[package]] name = "png" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" +checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa" dependencies = [ "bitflags 1.3.2", "crc32fast", + "fdeflate", "flate2", - "miniz_oxide", + "miniz_oxide 0.7.1", ] [[package]] @@ -2067,18 +2084,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.54" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534" +checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" dependencies = [ "unicode-ident", ] [[package]] name = "profiling" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74605f360ce573babfe43964cbe520294dcb081afbf8c108fc6e23036b4da2df" +checksum = "332cd62e95873ea4f41f3dfd6bbbfc5b52aec892d7e8d534197c4720a0bbbab2" [[package]] name = "prokio" @@ -2099,9 +2116,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" dependencies = [ "proc-macro2", ] @@ -2153,9 +2170,9 @@ checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" [[package]] name = "raw-window-handle" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f851a03551ceefd30132e447f07f96cb7011d6b658374f3aed847333adb5559" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" [[package]] name = "rayon" @@ -2199,9 +2216,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.3" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +checksum = "d1a59b5d8e97dee33696bf13c5ba8ab85341c002922fba050069326b9c498974" dependencies = [ "aho-corasick", "memchr", @@ -2210,9 +2227,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] name = "renderdoc-sys" @@ -2222,18 +2239,18 @@ checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" [[package]] name = "ringbuf" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ca10b9c9e53ac855a2d6953bce34cef6edbac32c4b13047a4d59d67299420a" +checksum = "79abed428d1fd2a128201cec72c5f6938e2da607c6f3745f769fabea399d950a" dependencies = [ "crossbeam-utils", ] [[package]] name = "rustc-demangle" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" @@ -2243,16 +2260,16 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.36.11" +version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ "bitflags 1.3.2", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2299,9 +2316,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "sctk-adwaita" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc56402866c717f54e48b122eb93c69f709bc5a6359c403598992fd92f017931" +checksum = "cda4e97be1fd174ccc2aae81c8b694e803fa99b34e8fd0f057a9d70698e3ed09" dependencies = [ "ab_glyph", "log", @@ -2312,9 +2329,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.159" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] @@ -2332,20 +2349,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.159" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.16", ] [[package]] name = "serde_json" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -2370,6 +2387,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +[[package]] +name = "simd-adler32" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" + [[package]] name = "slab" version = "0.4.8" @@ -2460,9 +2483,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.11" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e3787bb71465627110e7d87ed4faaa36c1f61042ee67badb9e2ef173accc40" +checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" dependencies = [ "proc-macro2", "quote", @@ -2501,14 +2524,14 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.11", + "syn 2.0.16", ] [[package]] name = "tiny-skia" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfef3412c6975196fdfac41ef232f910be2bb37b9dd3313a49a1a6bc815a5bdb" +checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67" dependencies = [ "arrayref", "arrayvec", @@ -2520,9 +2543,9 @@ dependencies = [ [[package]] name = "tiny-skia-path" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b5edac058fc98f51c935daea4d805b695b38e2f151241cad125ade2a2ac20d" +checksum = "adbfb5d3f3dd57a0e11d12f4f13d4ebbbc1b5c15b7ab0a156d030b21da5f677c" dependencies = [ "arrayref", "bytemuck", @@ -2541,20 +2564,20 @@ dependencies = [ [[package]] name = "tokio" -version = "1.27.0" +version = "1.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" dependencies = [ "autocfg", "pin-project-lite", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "tokio-stream" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -2563,15 +2586,15 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" [[package]] name = "toml_edit" -version = "0.19.8" +version = "0.19.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" dependencies = [ "indexmap", "toml_datetime", @@ -2592,29 +2615,29 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.16", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] [[package]] name = "ttf-parser" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633" +checksum = "44dcf002ae3b32cd25400d6df128c5babec3927cd1eb7ce813cfff20eb6c3746" [[package]] name = "ultraviolet" @@ -2627,9 +2650,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unicode-width" @@ -2679,9 +2702,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2689,24 +2712,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.16", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" dependencies = [ "cfg-if", "js-sys", @@ -2716,9 +2739,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2726,22 +2749,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.16", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" [[package]] name = "wayland-client" @@ -2839,9 +2862,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" dependencies = [ "js-sys", "wasm-bindgen", @@ -2949,9 +2972,9 @@ dependencies = [ [[package]] name = "wide" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b689b6c49d6549434bf944e6b0f39238cf63693cb7a147e9d887507fffa3b223" +checksum = "5cd0496a71f3cc6bc4bf0ed91346426a5099e93d89807e663162dc5a1069ff65" dependencies = [ "bytemuck", "safe_arch", @@ -3000,7 +3023,16 @@ version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +dependencies = [ + "windows-targets 0.42.2", ] [[package]] @@ -3009,7 +3041,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -3018,13 +3059,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -3033,47 +3089,89 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winit" -version = "0.28.3" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f504e8c117b9015f618774f8d58cd4781f5a479bc41079c064f974cbb253874" +checksum = "866db3f712fffba75d31bf0cdecf357c8aeafd158c5b7ab51dba2a2b2d47f196" dependencies = [ "android-activity", "bitflags 1.3.2", @@ -3100,7 +3198,7 @@ dependencies = [ "wayland-protocols", "wayland-scanner", "web-sys", - "windows-sys", + "windows-sys 0.45.0", "x11-dl", ] @@ -3115,9 +3213,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.4.1" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" dependencies = [ "memchr", ] @@ -3144,9 +3242,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.4" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" +checksum = "2d8f380ae16a37b30e6a2cf67040608071384b1450c189e61bea3ff57cde922d" [[package]] name = "yew" diff --git a/feo3boy-player/src/executor_selector.rs b/feo3boy-player/src/executor_selector.rs index bb42920..944bcd0 100644 --- a/feo3boy-player/src/executor_selector.rs +++ b/feo3boy-player/src/executor_selector.rs @@ -3,6 +3,7 @@ use std::marker::PhantomData; use std::mem; use clap::ValueEnum; +use feo3boy::clock::{SystemClock, SystemClockContext}; use feo3boy::gbz80core::direct_executor::DirectExecutor; use feo3boy::gbz80core::direct_executor_v2::DirectExecutorV2; use feo3boy::gbz80core::executor::{Executor, ExecutorConfig, ExecutorState}; @@ -192,3 +193,13 @@ where self.ctx.interrupts_mut() } } + +impl SystemClockContext for MultiExecutorWrapperContext +where + C: SystemClockContext, +{ + #[inline] + fn clock(&self) -> &SystemClock { + self.ctx.clock() + } +} diff --git a/feo3boy-player/src/main.rs b/feo3boy-player/src/main.rs index 10be5c9..71a603e 100644 --- a/feo3boy-player/src/main.rs +++ b/feo3boy-player/src/main.rs @@ -345,7 +345,7 @@ fn main() { write_serial_data(&mut gb, &mut stdout); gb.tick(); - if let Some(sample) = gb.apu.consume_output_sample() { + if let Some(sample) = gb.consume_audio_sample() { avg_sample_interval = if num_sample_intervals == 0.0 { sample_instant.elapsed().as_micros() as f64 } else { diff --git a/feo3boy/benches/opcodes.rs b/feo3boy/benches/opcodes.rs index e95105b..b0defa4 100644 --- a/feo3boy/benches/opcodes.rs +++ b/feo3boy/benches/opcodes.rs @@ -4,18 +4,18 @@ use feo3boy::gbz80core::direct_executor_v2::DirectExecutorV2; use feo3boy::gbz80core::executor::Executor; use feo3boy::gbz80core::microcode_executor::{MicrocodeExecutor, MicrocodeState}; use feo3boy::gbz80core::stepping_executor::{SteppingExecutor, SteppingExecutorState}; -use feo3boy::gbz80core::Gbz80State; -use feo3boy::memdev::{AllRam, RootMemDevice}; +use feo3boy::gbz80core::TestGb; +use feo3boy::memdev::AllRam; -fn run_until_halted(gb: &mut (Gbz80State, AllRam, E::State)) -> (u8, u8, u8, u8) { - while !gb.0.halted { +fn run_until_halted(gb: &mut TestGb) -> (u8, u8, u8, u8) { + while !gb.cpu.halted { E::run_single_instruction(gb); } ( - gb.1.read_byte(0xC000), - gb.1.read_byte(0xC001), - gb.1.read_byte(0xC002), - gb.1.read_byte(0xC003), + gb.mem[0xC000], + gb.mem[0xC001], + gb.mem[0xC002], + gb.mem[0xC003], ) } @@ -28,7 +28,7 @@ fn opcodes_benchmark(c: &mut Criterion) { b.iter_batched_ref( || { let mem = AllRam::from(fib); - (Gbz80State::default(), mem, ()) + TestGb::new(mem, ()) }, |gb| run_until_halted::(gb), BatchSize::LargeInput, @@ -38,7 +38,7 @@ fn opcodes_benchmark(c: &mut Criterion) { b.iter_batched_ref( || { let mem = AllRam::from(fib); - (Gbz80State::default(), mem, MicrocodeState::default()) + TestGb::new(mem, MicrocodeState::default()) }, |gb| run_until_halted::(gb), BatchSize::LargeInput, @@ -48,7 +48,7 @@ fn opcodes_benchmark(c: &mut Criterion) { b.iter_batched_ref( || { let mem = AllRam::from(fib); - (Gbz80State::default(), mem, ()) + TestGb::new(mem, ()) }, |gb| run_until_halted::(gb), BatchSize::LargeInput, @@ -58,7 +58,7 @@ fn opcodes_benchmark(c: &mut Criterion) { b.iter_batched_ref( || { let mem = AllRam::from(fib); - (Gbz80State::default(), mem, SteppingExecutorState::default()) + TestGb::new(mem, SteppingExecutorState::default()) }, |gb| run_until_halted::(gb), BatchSize::LargeInput, diff --git a/feo3boy/src/apu.rs b/feo3boy/src/apu.rs index d88bbe8..e95946d 100644 --- a/feo3boy/src/apu.rs +++ b/feo3boy/src/apu.rs @@ -104,7 +104,8 @@ impl ApuDiv { /// This method modifies the APU-DIV in-place, but also returns a copy of the updated /// value of `self` for convenience. fn tick_apu_div(&mut self) -> Self { - self.prev_counter = mem::replace(&mut self.counter, self.counter.wrapping_add(1)); + self.prev_counter = self.counter; + self.counter = self.counter.wrapping_add(1); *self } @@ -362,7 +363,7 @@ impl EnvelopeControl { const DAC_ENABLE: BitGroup = BitGroup(0b1111_1000); #[inline] - fn set(&mut self, val: u8) { + pub fn set(&mut self, val: u8) { self.0 = val; } @@ -854,7 +855,7 @@ impl WavetableChannel { } /// Update the dac_enabled value. - fn set_dac_enabled(&self, now: DCycle, val: u8) { + fn set_dac_enabled(&mut self, now: DCycle, val: u8) { let dac_enabled = Self::DAC_ENABLED.extract_bool(val); // If we are now turning off the dac (and the channel was active), we need to capture the // last-read sample into initial_sample for next time. @@ -977,7 +978,7 @@ impl WavetableChannel { impl MemDevice for WavetableChannel { const LEN: usize = 0x15; - fn read_byte_relative(&self, ctx: &ReadCtx, addr: RelativeAddr) -> u8 { + fn read_byte_relative(&self, _ctx: &ReadCtx, addr: RelativeAddr) -> u8 { dispatch_memdev_byte!(WavetableChannel, addr, |addr| { 0x00 => self.dac_enabled_reg(), 0x01 => 0xff, // length timer is write-only. @@ -1007,7 +1008,7 @@ impl MemDevice for WavetableChannel { } /// A trait for types which implement the LFSR used in the noise channel. -trait LinearFeedbackShiftRegister: Clone + fmt::Debug + Default + PartialEq + Eq { +pub trait LinearFeedbackShiftRegister: Clone + fmt::Debug + Default + PartialEq + Eq { /// Apply the 'increment/shift' operation of the LFSR the given number of times and return bit 0 /// at the end of the operation. The output is always either 0 or 1. fn increment_by_and_get_output(&mut self, increments: u64) -> u16; @@ -1024,7 +1025,7 @@ trait LinearFeedbackShiftRegister: Clone + fmt::Debug + Default + PartialEq + Eq /// Directly implements the LFSR by doing the actual shifts. #[derive(Clone, Debug, Default, PartialEq, Eq)] -struct DirectLinearFeedbackShiftRegister { +pub struct DirectLinearFeedbackShiftRegister { /// Whether to use the shorter width (7 bits) instead of 15 bits. short_width: bool, @@ -1083,7 +1084,7 @@ impl LinearFeedbackShiftRegister for DirectLinearFeedbackShiftRegister { /// Represents the LFSR used in the Noise channel. #[derive(Clone, Debug, Default, PartialEq, Eq)] -struct TabledLinearFeedbackShiftRegister { +pub struct TabledLinearFeedbackShiftRegister { /// Whether to use the shorter width (7 bits) instead of 15 bits. short_width: bool, @@ -1712,10 +1713,12 @@ pub fn tick(ctx: &mut impl ApuContext) { + mix.get_channel_mix_multiplier(ChannelMix::CH4) * ch4) } - ctx.apu_mut().output_sample = Some(Sample { + let unfiltered = Sample { left: blend_samples(channel_mix_left, vol_mul_left, ch1, ch2, ch3, ch4), right: blend_samples(channel_mix_right, vol_mul_right, ch1, ch2, ch3, ch4), - }) + }; + + ctx.apu_mut().output_sample = Some(ctx.apu_mut().hpf.filter_sample(unfiltered)); } } } diff --git a/feo3boy/src/bits.rs b/feo3boy/src/bits.rs index febc2cf..e65b26c 100644 --- a/feo3boy/src/bits.rs +++ b/feo3boy/src/bits.rs @@ -1,6 +1,6 @@ //! Utilites for working with bits and bitflags. -use bitflags::BitFlags; +use bitflags::Flags; /// A group of bits within a byte. Provides utilities for extracting and setting the /// selected bits. @@ -64,7 +64,7 @@ impl BitGroup { #[inline] pub fn apply_bits(self, dest: &mut B, val: u8) where - B: BitFlags, + B: Flags, { let mut bits = dest.bits(); self.apply(&mut bits, val); diff --git a/feo3boy/src/clock/mod.rs b/feo3boy/src/clock/mod.rs index 6b4f21e..886efa3 100644 --- a/feo3boy/src/clock/mod.rs +++ b/feo3boy/src/clock/mod.rs @@ -44,7 +44,7 @@ pub trait SystemClockContext { } /// Represents the system's actual clock. -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct SystemClock { /// Number of m-cycles elapsed since startup. /// diff --git a/feo3boy/src/gbz80core/direct_executor/args.rs b/feo3boy/src/gbz80core/direct_executor/args.rs index 177a1fd..b84a4d3 100644 --- a/feo3boy/src/gbz80core/direct_executor/args.rs +++ b/feo3boy/src/gbz80core/direct_executor/args.rs @@ -8,7 +8,7 @@ use feo3boy_opcodes::opcode::args::{AluOp, AluUnaryOp, ConditionCode, Operand16, use log::trace; use crate::gbz80core::direct_executor::{Ctx, Run, Runner}; -use crate::memdev::RootMemDevice; +use crate::gbz80core::externdefs; impl Runner for AluOp {} impl Runner for AluUnaryOp {} @@ -31,50 +31,50 @@ impl Run { Operand8::AddrHL => { let addr = ctx.cpu().regs.hl(); ctx.yield1m(); - ctx.mem().read_byte(addr) + externdefs::read_mem(ctx, addr) } Operand8::AddrBC => { let addr = ctx.cpu().regs.bc(); ctx.yield1m(); - ctx.mem().read_byte(addr) + externdefs::read_mem(ctx, addr) } Operand8::AddrDE => { let addr = ctx.cpu().regs.de(); ctx.yield1m(); - ctx.mem().read_byte(addr) + externdefs::read_mem(ctx, addr) } Operand8::AddrHLInc => { let addr = ctx.cpu().regs.hl(); ctx.yield1m(); ctx.cpu_mut().regs.set_hl(addr.wrapping_add(1)); - ctx.mem().read_byte(addr) + externdefs::read_mem(ctx, addr) } Operand8::AddrHLDec => { let addr = ctx.cpu().regs.hl(); ctx.yield1m(); ctx.cpu_mut().regs.set_hl(addr.wrapping_sub(1)); - ctx.mem().read_byte(addr) + externdefs::read_mem(ctx, addr) } Operand8::Immediate => { let addr = ctx.cpu_mut().regs.pc; ctx.cpu_mut().regs.pc = addr.wrapping_add(1); ctx.yield1m(); - ctx.mem().read_byte(addr) + externdefs::read_mem(ctx, addr) } Operand8::AddrImmediate => { let addr = Operand16::Immediate.runner().read(ctx); ctx.yield1m(); - ctx.mem().read_byte(addr) + externdefs::read_mem(ctx, addr) } Operand8::AddrRelC => { let addr = 0xFF00 + ctx.cpu().regs.c as u16; ctx.yield1m(); - ctx.mem().read_byte(addr) + externdefs::read_mem(ctx, addr) } Operand8::AddrRelImmediate => { let addr = 0xFF00 + Operand8::Immediate.runner().read(ctx) as u16; ctx.yield1m(); - ctx.mem().read_byte(addr) + externdefs::read_mem(ctx, addr) } } } @@ -93,45 +93,45 @@ impl Run { Operand8::AddrHL => { let addr = ctx.cpu().regs.hl(); ctx.yield1m(); - ctx.mem_mut().write_byte(addr, val) + externdefs::write_mem(ctx, addr, val) } Operand8::AddrBC => { let addr = ctx.cpu().regs.bc(); ctx.yield1m(); - ctx.mem_mut().write_byte(addr, val) + externdefs::write_mem(ctx, addr, val) } Operand8::AddrDE => { let addr = ctx.cpu().regs.de(); ctx.yield1m(); - ctx.mem_mut().write_byte(addr, val) + externdefs::write_mem(ctx, addr, val) } Operand8::AddrHLInc => { let addr = ctx.cpu().regs.hl(); ctx.yield1m(); ctx.cpu_mut().regs.set_hl(addr.wrapping_add(1)); - ctx.mem_mut().write_byte(addr, val) + externdefs::write_mem(ctx, addr, val) } Operand8::AddrHLDec => { let addr = ctx.cpu().regs.hl(); ctx.yield1m(); ctx.cpu_mut().regs.set_hl(addr.wrapping_sub(1)); - ctx.mem_mut().write_byte(addr, val) + externdefs::write_mem(ctx, addr, val) } Operand8::Immediate => panic!("Immediates cannot be used as store destinations"), Operand8::AddrImmediate => { let addr = Operand16::Immediate.runner().read(ctx); ctx.yield1m(); - ctx.mem_mut().write_byte(addr, val) + externdefs::write_mem(ctx, addr, val) } Operand8::AddrRelC => { let addr = 0xFF00 + ctx.cpu().regs.c as u16; ctx.yield1m(); - ctx.mem_mut().write_byte(addr, val) + externdefs::write_mem(ctx, addr, val) } Operand8::AddrRelImmediate => { let addr = 0xFF00 + Operand8::Immediate.runner().read(ctx) as u16; ctx.yield1m(); - ctx.mem_mut().write_byte(addr, val) + externdefs::write_mem(ctx, addr, val) } } } @@ -172,10 +172,10 @@ impl Run { let [low, high] = val.to_le_bytes(); let mut addr = Wrapping(Operand16::Immediate.runner().read(ctx)); ctx.yield1m(); - ctx.mem_mut().write_byte(addr.0.into(), low); + externdefs::write_mem(ctx, addr.0, low); addr += Wrapping(1u16); ctx.yield1m(); - ctx.mem_mut().write_byte(addr.0.into(), high); + externdefs::write_mem(ctx, addr.0, high); } } } diff --git a/feo3boy/src/gbz80core/direct_executor/mod.rs b/feo3boy/src/gbz80core/direct_executor/mod.rs index 05460a3..24e2774 100644 --- a/feo3boy/src/gbz80core/direct_executor/mod.rs +++ b/feo3boy/src/gbz80core/direct_executor/mod.rs @@ -12,10 +12,8 @@ use log::{debug, trace, warn}; use crate::gbz80core::executor::Executor; use crate::gbz80core::{externdefs, ExecutorContext}; use crate::interrupts::Interrupts; -use crate::memdev::RootMemDevice; mod args; -mod tests; /// Executor which evaluates GB Opcodes by decoding them, then matching on them and /// calling a function which implements them. This executor is not capable of pausing @@ -359,12 +357,12 @@ fn push_helper(ctx: &mut impl Ctx, val: u16) { ctx.yield1m(); let addr = ctx.cpu().regs.sp.wrapping_sub(1); ctx.cpu_mut().regs.sp = addr; - ctx.mem_mut().write_byte(addr, high); + externdefs::write_mem(ctx, addr, high); ctx.yield1m(); let addr = ctx.cpu().regs.sp.wrapping_sub(1); ctx.cpu_mut().regs.sp = addr; - ctx.mem_mut().write_byte(addr, low); + externdefs::write_mem(ctx, addr, low); } /// Pop helper, shared between pop and ret. Pops value from the stack, waiting 1m between each byte @@ -373,12 +371,12 @@ fn pop_helper(ctx: &mut impl Ctx) -> u16 { ctx.yield1m(); let addr = ctx.cpu().regs.sp; ctx.cpu_mut().regs.sp = addr.wrapping_add(1); - let low = ctx.mem().read_byte(addr); + let low = externdefs::read_mem(ctx, addr); ctx.yield1m(); let addr = ctx.cpu().regs.sp; ctx.cpu_mut().regs.sp = addr.wrapping_add(1); - let high = ctx.mem().read_byte(addr); + let high = externdefs::read_mem(ctx, addr); u16::from_le_bytes([low, high]) } @@ -499,3 +497,9 @@ impl Run { } } } + +executor_tests! { + tests, + crate::gbz80core::direct_executor::DirectExecutor, + crate::gbz80core::TestGb, ()> +} diff --git a/feo3boy/src/gbz80core/direct_executor/tests.rs b/feo3boy/src/gbz80core/direct_executor/tests.rs deleted file mode 100644 index a9bba7b..0000000 --- a/feo3boy/src/gbz80core/direct_executor/tests.rs +++ /dev/null @@ -1,1282 +0,0 @@ -//! Tests for the direct_executor -#![cfg(test)] - -use feo3boy_opcodes::gbz80types::Flags; - -use crate::gbz80core::Gbz80State; - -use super::*; - -fn init() { - let _ = env_logger::builder().is_test(true).try_init(); -} - -fn run_single_instruction(ctx: &mut impl Ctx) { - DirectExecutor::run_single_instruction(ctx) -} - -#[test] -fn test_loads_and_alu() { - init(); - - let mut cpu = Gbz80State::new(); - let mut testmem = [0u8; 0x10000]; - - testmem[0] = 0x3e; - testmem[1] = 0x80; - testmem[2] = 0x06; - testmem[3] = 0x01; - testmem[4] = 0x80; - testmem[5] = 0x0e; - testmem[6] = 0x85; - testmem[7] = 0x81; - - run_single_instruction(&mut (&mut cpu, &mut testmem)); - assert_eq!(cpu.regs.acc, 0x80, "{:?}", cpu.regs); - - run_single_instruction(&mut (&mut cpu, &mut testmem)); - assert_eq!(cpu.regs.b, 0x01, "{:?}", cpu.regs); - - run_single_instruction(&mut (&mut cpu, &mut testmem)); - assert_eq!(cpu.regs.acc, 0x81, "{:?}", cpu.regs); - - run_single_instruction(&mut (&mut cpu, &mut testmem)); - assert_eq!(cpu.regs.c, 0x85, "{:?}", cpu.regs); - - run_single_instruction(&mut (&mut cpu, &mut testmem)); - assert_eq!(cpu.regs.acc, 0x6, "{:?}", cpu.regs); - assert!(cpu.regs.flags.contains(Flags::CARRY), "{:?}", cpu.regs); -} - -#[test] -fn load8bit() { - init(); - - fn set_dest(inst: u8, ctx: &mut impl Ctx, val: u8) { - match inst { - 0x40..=0x47 => ctx.cpu_mut().regs.b = val, - 0x48..=0x4f => ctx.cpu_mut().regs.c = val, - 0x50..=0x57 => ctx.cpu_mut().regs.d = val, - 0x58..=0x5f => ctx.cpu_mut().regs.e = val, - 0x60..=0x67 => ctx.cpu_mut().regs.h = val, - 0x68..=0x6f => ctx.cpu_mut().regs.l = val, - 0x70..=0x77 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - 0x78..=0x7f => ctx.cpu_mut().regs.acc = val, - _ => unreachable!(), - } - } - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - 0x7 => ctx.cpu_mut().regs.acc = val, - _ => unreachable!(), - } - } - for inst in (0x40..=0x7f).filter(|&i| i != 0x76) { - for input in 0..=0xff { - let mut ctx = (Gbz80State::default(), [0u8; 0x10000]); - // Start with HL at a high number so any single-byte change to it will never - // collide with the instruction at 0x00. - ctx.0.regs.set_hl(0xAA); - // This step might overwrite H or L changing the destination address of LD (HL),H - // or LD (HL),L, but that's ok because we use HL in set_dest. - set_source(inst, &mut ctx, input); - ctx.1[0] = inst; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - set_dest(inst, &mut expected, input); - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn load8bit_immediate() { - init(); - - fn set_dest(inst: u8, ctx: &mut impl Ctx, val: u8) { - match inst { - 0x06 => ctx.cpu_mut().regs.b = val, - 0x0e => ctx.cpu_mut().regs.c = val, - 0x16 => ctx.cpu_mut().regs.d = val, - 0x1e => ctx.cpu_mut().regs.e = val, - 0x26 => ctx.cpu_mut().regs.h = val, - 0x2e => ctx.cpu_mut().regs.l = val, - 0x36 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - 0x3e => ctx.cpu_mut().regs.acc = val, - _ => unreachable!(), - } - } - for inst in (0x00..=0x30) - .step_by(0x10) - .flat_map(|high| [high + 0x06, high + 0x0e]) - { - for input in 0..=0xff { - let mut ctx = (Gbz80State::default(), [0u8; 3]); - // Start with HL at a high number so any single-byte change to it will never - // collide with the instruction at 0x00. - ctx.0.regs.set_hl(2); - ctx.1[0] = inst; - ctx.1[1] = input; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - set_dest(inst, &mut expected, input); - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn add8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0x80..=0x86 { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("add({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let (sum, wrapped) = v1.overflowing_add(v2); - expected.0.regs.acc = sum; - expected.0.regs.flags = Flags::empty(); - if sum == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) + (v2 & 0xf) > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - // A,u8 - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("add(c6) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = 0xc6; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let (sum, wrapped) = v1.overflowing_add(v2); - expected.0.regs.acc = sum; - expected.0.regs.flags = Flags::empty(); - if sum == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) + (v2 & 0xf) > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - // A,A - for v in 0..=0xff { - println!("add(87) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.1[0] = 0x87; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let (sum, wrapped) = v.overflowing_add(v); - expected.0.regs.acc = sum; - expected.0.regs.flags = Flags::empty(); - if sum == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped { - expected.0.regs.flags |= Flags::CARRY; - } - if (v & 0xf) + (v & 0xf) > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn adc8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - let carry = (existing_flags & 0x10) >> 4; - for inst in 0x88..=0x8e { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("adc({:02x}) {},{} {}", inst, v1, v2, carry); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let (sum1, wrapped1) = v1.overflowing_add(v2); - let (sum2, wrapped2) = sum1.overflowing_add(carry); - expected.0.regs.acc = sum2; - expected.0.regs.flags = Flags::empty(); - if sum2 == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped1 || wrapped2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) + (v2 & 0xf) + carry > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - // ADC A,u8 - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("adc(ce) {},{} {}", v1, v2, carry); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = 0xce; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let (sum1, wrapped1) = v1.overflowing_add(v2); - let (sum2, wrapped2) = sum1.overflowing_add(carry); - expected.0.regs.acc = sum2; - expected.0.regs.flags = Flags::empty(); - if sum2 == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped1 || wrapped2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) + (v2 & 0xf) + carry > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - // ADC A,A - for v in 0..=0xff { - println!("adc(8f) {},{} {}", v, v, carry); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.1[0] = 0x8f; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - if carry == 1 { - ctx.0.regs.flags |= Flags::CARRY; - } - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let (sum1, wrapped1) = v.overflowing_add(v); - let (sum2, wrapped2) = sum1.overflowing_add(carry); - expected.0.regs.acc = sum2; - expected.0.regs.flags = Flags::empty(); - if sum2 == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped1 || wrapped2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v & 0xf) + (v & 0xf) + carry > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn sub8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0x90..=0x96 { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("sub({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let diff = v1.wrapping_sub(v2); - expected.0.regs.acc = diff; - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("sub(d6) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = 0xd6; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let diff = v1.wrapping_sub(v2); - expected.0.regs.acc = diff; - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("sub(97) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.1[0] = 0x97; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.acc = 0; - expected.0.regs.flags = Flags::ZERO | Flags::SUB; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn sbc8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - let carry = (existing_flags & 0x10) >> 4; - for inst in 0x98..=0x9e { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("sbc({:02x}) {},{} {}", inst, v1, v2, carry); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let diff = v1.wrapping_sub(v2).wrapping_sub(carry); - expected.0.regs.acc = diff; - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 || (v1 == v2 && carry != 0) { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) || ((v1 & 0xf) == (v2 & 0xf) && carry != 0) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("sbc(de) {},{} {}", v1, v2, carry); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = 0xde; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let diff = v1.wrapping_sub(v2).wrapping_sub(carry); - expected.0.regs.acc = diff; - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 || (v1 == v2 && carry != 0) { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) || ((v1 & 0xf) == (v2 & 0xf) && carry != 0) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("sbc(9f) {},{} {}", v, v, carry); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.1[0] = 0x9f; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - if carry == 0 { - expected.0.regs.acc = 0; - expected.0.regs.flags = Flags::ZERO | Flags::SUB; - } else { - expected.0.regs.acc = 0xff; - expected.0.regs.flags = Flags::SUB | Flags::CARRY | Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn and8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0xa0..=0xa6 { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("and({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let res = v1 & v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::HALFCARRY; - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("and(e6) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = 0xe6; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let res = v1 & v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::HALFCARRY; - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("and(a7) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.1[0] = 0xa7; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.flags = Flags::HALFCARRY; - if v == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn xor8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0xa8..=0xae { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("xor({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let res = v1 ^ v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::empty(); - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("xor(ee) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = 0xee; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let res = v1 ^ v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::empty(); - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("and(af) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.1[0] = 0xaf; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.acc = 0; - expected.0.regs.flags = Flags::ZERO; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn or8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0xb0..=0xb6 { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("or({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let res = v1 | v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::empty(); - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("or(f6) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = 0xf6; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let res = v1 | v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::empty(); - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("or(b7) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.1[0] = 0xb7; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.flags = Flags::empty(); - if v == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn cp8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0xb8..=0xbe { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("cp({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let diff = v1.wrapping_sub(v2); - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("cp(fe) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = 0xfe; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let diff = v1.wrapping_sub(v2); - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("cp(bf) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.1[0] = 0xbf; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.flags = Flags::ZERO | Flags::SUB; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn relative_jumps() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for offset in -128i8..=127 { - let jnz = (flags & 0x80) == 0; - let jz = (flags & 0x80) != 0; - let jnc = (flags & 0x10) == 0; - let jc = (flags & 0x10) != 0; - let ops = [ - (0x18, true), - (0x20, jnz), - (0x28, jz), - (0x30, jnc), - (0x38, jc), - ]; - - for (opcode, should_jump) in ops { - println!("jr({:02x}) -> {} should: {}", opcode, offset, should_jump); - let mut ctx = (Gbz80State::default(), [0u8; 0x200]); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0x100] = opcode; - ctx.0.regs.pc = 0x100; - ctx.1[0x101] = offset as u8; - - let expected = { - let mut expected = ctx.clone(); - if should_jump { - expected.0.regs.pc = (0x102 + offset as i32) as u16; - } else { - expected.0.regs.pc = 0x102; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } -} - -#[test] -fn absolute_immediate_jumps() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for dest in 0x0000u16..=0xffff { - let jnz = (flags & 0x80) == 0; - let jz = (flags & 0x80) != 0; - let jnc = (flags & 0x10) == 0; - let jc = (flags & 0x10) != 0; - let ops = [ - (0xc3, true), - (0xc2, jnz), - (0xca, jz), - (0xd2, jnc), - (0xda, jc), - ]; - - for (opcode, should_jump) in ops { - println!("jp({:02x}) -> {:04x} should: {}", opcode, dest, should_jump); - let mut ctx = (Gbz80State::default(), [0u8; 3]); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0] = opcode; - let [low, high] = dest.to_le_bytes(); - ctx.1[1] = low; - ctx.1[2] = high; - - let expected = { - let mut expected = ctx.clone(); - if should_jump { - expected.0.regs.pc = dest; - } else { - expected.0.regs.pc = 3; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } -} - -#[test] -fn jump_hl() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for dest in 0x0000u16..=0xffff { - println!("jp(e9) -> {:04x}", dest); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0] = 0xe9; - ctx.0.regs.set_hl(dest); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = dest; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn rst() { - init(); - - let rsts = [ - (0xc7, 0x00), - (0xcf, 0x08), - (0xd7, 0x10), - (0xdf, 0x18), - (0xe7, 0x20), - (0xef, 0x28), - (0xf7, 0x30), - (0xff, 0x38), - ]; - - for (opcode, dest) in rsts { - let mut ctx = (Gbz80State::default(), [0u8; 0x200]); - ctx.1[0x101] = opcode; - ctx.0.regs.pc = 0x101; - ctx.0.regs.sp = 0x200; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = dest; - expected.0.regs.sp = 0x1fe; - // Pushed address should be 0x0102 (in little endian order), since that's - // the address of the instruction after RST. - expected.1[0x1fe] = 0x02; - expected.1[0x1ff] = 0x01; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } -} - -#[test] -fn returns() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for dest in 0x0000u16..=0xffff { - let jnz = (flags & 0x80) == 0; - let jz = (flags & 0x80) != 0; - let jnc = (flags & 0x10) == 0; - let jc = (flags & 0x10) != 0; - let ops = [ - (0xc9, true), - (0xc0, jnz), - (0xc8, jz), - (0xd0, jnc), - (0xd8, jc), - ]; - - for (opcode, should_jump) in ops { - println!( - "ret({:02x}) -> {:04x} should: {}", - opcode, dest, should_jump - ); - let mut ctx = (Gbz80State::default(), [0u8; 0x200]); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0x100] = opcode; - ctx.0.regs.pc = 0x100; - let [low, high] = dest.to_le_bytes(); - ctx.1[0x1fe] = low; - ctx.1[0x1ff] = high; - ctx.0.regs.sp = 0x1fe; - - let expected = { - let mut expected = ctx.clone(); - if should_jump { - expected.0.regs.sp = 0x200; - expected.0.regs.pc = dest; - } else { - expected.0.regs.pc = 0x101; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } -} - -#[test] -fn interrupt_return() { - init(); - - for dest in 0x0000u16..=0xffff { - println!("reti(0xd9) -> {:04x}", dest); - let mut ctx = (Gbz80State::default(), [0u8; 0x200]); - ctx.1[0x100] = 0xd9; - ctx.0.regs.pc = 0x100; - let [low, high] = dest.to_le_bytes(); - ctx.1[0x1fe] = low; - ctx.1[0x1ff] = high; - ctx.0.regs.sp = 0x1fe; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.sp = 0x200; - expected.0.regs.pc = dest; - expected.0.interrupt_master_enable.set(); - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } -} - -#[test] -fn calls() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for dest in 0x0000u16..=0xffff { - let jnz = (flags & 0x80) == 0; - let jz = (flags & 0x80) != 0; - let jnc = (flags & 0x10) == 0; - let jc = (flags & 0x10) != 0; - let ops = [ - (0xcd, true), - (0xc4, jnz), - (0xcc, jz), - (0xd4, jnc), - (0xdc, jc), - ]; - - for (opcode, should_jump) in ops { - println!( - "call({:02x}) -> {:04x} should: {}", - opcode, dest, should_jump - ); - let mut ctx = (Gbz80State::default(), [0u8; 0x200]); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0x100] = opcode; - ctx.0.regs.pc = 0x100; - let [low, high] = dest.to_le_bytes(); - ctx.1[0x101] = low; - ctx.1[0x102] = high; - ctx.0.regs.sp = 0x200; - - let expected = { - let mut expected = ctx.clone(); - if should_jump { - expected.0.regs.sp = 0x1fe; - // Return to 0x0103. - expected.1[0x1fe] = 0x03; - expected.1[0x1ff] = 0x01; - expected.0.regs.pc = dest; - } else { - expected.0.regs.pc = 0x103; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } -} diff --git a/feo3boy/src/gbz80core/direct_executor_v2.rs b/feo3boy/src/gbz80core/direct_executor_v2.rs index ece62eb..b247dce 100644 --- a/feo3boy/src/gbz80core/direct_executor_v2.rs +++ b/feo3boy/src/gbz80core/direct_executor_v2.rs @@ -28,1294 +28,8 @@ define_direct_executor!( ], ); -/// Tests for the direct_executor -#[cfg(test)] -mod tests { - - use feo3boy_opcodes::gbz80types::Flags; - - use crate::gbz80core::executor::Executor; - use crate::gbz80core::{ExecutorContext, Gbz80State}; - use crate::memdev::RootMemDevice; - - use super::*; - - /// Convenience type for an ExecutorContext with a unit state. - trait Ctx: ExecutorContext {} - - impl> Ctx for E {} - - fn init() { - let _ = env_logger::builder().is_test(true).try_init(); - } - - fn run_single_instruction(ctx: &mut impl Ctx) { - DirectExecutorV2::run_single_instruction(ctx) - } - - #[test] - fn test_loads_and_alu() { - init(); - - let mut cpu = Gbz80State::new(); - let mut testmem = [0u8; 0x10000]; - - testmem[0] = 0x3e; - testmem[1] = 0x80; - testmem[2] = 0x06; - testmem[3] = 0x01; - testmem[4] = 0x80; - testmem[5] = 0x0e; - testmem[6] = 0x85; - testmem[7] = 0x81; - - run_single_instruction(&mut (&mut cpu, &mut testmem)); - assert_eq!(cpu.regs.acc, 0x80, "{:?}", cpu.regs); - - run_single_instruction(&mut (&mut cpu, &mut testmem)); - assert_eq!(cpu.regs.b, 0x01, "{:?}", cpu.regs); - - run_single_instruction(&mut (&mut cpu, &mut testmem)); - assert_eq!(cpu.regs.acc, 0x81, "{:?}", cpu.regs); - - run_single_instruction(&mut (&mut cpu, &mut testmem)); - assert_eq!(cpu.regs.c, 0x85, "{:?}", cpu.regs); - - run_single_instruction(&mut (&mut cpu, &mut testmem)); - assert_eq!(cpu.regs.acc, 0x6, "{:?}", cpu.regs); - assert!(cpu.regs.flags.contains(Flags::CARRY), "{:?}", cpu.regs); - } - - #[test] - fn load8bit() { - init(); - - fn set_dest(inst: u8, ctx: &mut impl Ctx, val: u8) { - match inst { - 0x40..=0x47 => ctx.cpu_mut().regs.b = val, - 0x48..=0x4f => ctx.cpu_mut().regs.c = val, - 0x50..=0x57 => ctx.cpu_mut().regs.d = val, - 0x58..=0x5f => ctx.cpu_mut().regs.e = val, - 0x60..=0x67 => ctx.cpu_mut().regs.h = val, - 0x68..=0x6f => ctx.cpu_mut().regs.l = val, - 0x70..=0x77 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - 0x78..=0x7f => ctx.cpu_mut().regs.acc = val, - _ => unreachable!(), - } - } - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - 0x7 => ctx.cpu_mut().regs.acc = val, - _ => unreachable!(), - } - } - for inst in (0x40..=0x7f).filter(|&i| i != 0x76) { - for input in 0..=0xff { - let mut ctx = (Gbz80State::default(), [0u8; 0x10000]); - // Start with HL at a high number so any single-byte change to it will never - // collide with the instruction at 0x00. - ctx.0.regs.set_hl(0xAA); - // This step might overwrite H or L changing the destination address of LD (HL),H - // or LD (HL),L, but that's ok because we use HL in set_dest. - set_source(inst, &mut ctx, input); - ctx.1[0] = inst; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - set_dest(inst, &mut expected, input); - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn load8bit_immediate() { - init(); - - fn set_dest(inst: u8, ctx: &mut impl Ctx, val: u8) { - match inst { - 0x06 => ctx.cpu_mut().regs.b = val, - 0x0e => ctx.cpu_mut().regs.c = val, - 0x16 => ctx.cpu_mut().regs.d = val, - 0x1e => ctx.cpu_mut().regs.e = val, - 0x26 => ctx.cpu_mut().regs.h = val, - 0x2e => ctx.cpu_mut().regs.l = val, - 0x36 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - 0x3e => ctx.cpu_mut().regs.acc = val, - _ => unreachable!(), - } - } - for inst in (0x00..=0x30) - .step_by(0x10) - .flat_map(|high| [high + 0x06, high + 0x0e]) - { - for input in 0..=0xff { - let mut ctx = (Gbz80State::default(), [0u8; 3]); - // Start with HL at a high number so any single-byte change to it will never - // collide with the instruction at 0x00. - ctx.0.regs.set_hl(2); - ctx.1[0] = inst; - ctx.1[1] = input; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - set_dest(inst, &mut expected, input); - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn add8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0x80..=0x86 { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("add({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let (sum, wrapped) = v1.overflowing_add(v2); - expected.0.regs.acc = sum; - expected.0.regs.flags = Flags::empty(); - if sum == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) + (v2 & 0xf) > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - // A,u8 - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("add(c6) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = 0xc6; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let (sum, wrapped) = v1.overflowing_add(v2); - expected.0.regs.acc = sum; - expected.0.regs.flags = Flags::empty(); - if sum == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) + (v2 & 0xf) > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - // A,A - for v in 0..=0xff { - println!("add(87) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.1[0] = 0x87; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let (sum, wrapped) = v.overflowing_add(v); - expected.0.regs.acc = sum; - expected.0.regs.flags = Flags::empty(); - if sum == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped { - expected.0.regs.flags |= Flags::CARRY; - } - if (v & 0xf) + (v & 0xf) > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn adc8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - let carry = (existing_flags & 0x10) >> 4; - for inst in 0x88..=0x8e { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("adc({:02x}) {},{} {}", inst, v1, v2, carry); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let (sum1, wrapped1) = v1.overflowing_add(v2); - let (sum2, wrapped2) = sum1.overflowing_add(carry); - expected.0.regs.acc = sum2; - expected.0.regs.flags = Flags::empty(); - if sum2 == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped1 || wrapped2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) + (v2 & 0xf) + carry > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - // ADC A,u8 - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("adc(ce) {},{} {}", v1, v2, carry); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = 0xce; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let (sum1, wrapped1) = v1.overflowing_add(v2); - let (sum2, wrapped2) = sum1.overflowing_add(carry); - expected.0.regs.acc = sum2; - expected.0.regs.flags = Flags::empty(); - if sum2 == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped1 || wrapped2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) + (v2 & 0xf) + carry > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - // ADC A,A - for v in 0..=0xff { - println!("adc(8f) {},{} {}", v, v, carry); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.1[0] = 0x8f; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - if carry == 1 { - ctx.0.regs.flags |= Flags::CARRY; - } - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let (sum1, wrapped1) = v.overflowing_add(v); - let (sum2, wrapped2) = sum1.overflowing_add(carry); - expected.0.regs.acc = sum2; - expected.0.regs.flags = Flags::empty(); - if sum2 == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped1 || wrapped2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v & 0xf) + (v & 0xf) + carry > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn sub8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0x90..=0x96 { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("sub({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let diff = v1.wrapping_sub(v2); - expected.0.regs.acc = diff; - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("sub(d6) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = 0xd6; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let diff = v1.wrapping_sub(v2); - expected.0.regs.acc = diff; - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("sub(97) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.1[0] = 0x97; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.acc = 0; - expected.0.regs.flags = Flags::ZERO | Flags::SUB; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn sbc8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - let carry = (existing_flags & 0x10) >> 4; - for inst in 0x98..=0x9e { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("sbc({:02x}) {},{} {}", inst, v1, v2, carry); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let diff = v1.wrapping_sub(v2).wrapping_sub(carry); - expected.0.regs.acc = diff; - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 || (v1 == v2 && carry != 0) { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) || ((v1 & 0xf) == (v2 & 0xf) && carry != 0) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("sbc(de) {},{} {}", v1, v2, carry); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = 0xde; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let diff = v1.wrapping_sub(v2).wrapping_sub(carry); - expected.0.regs.acc = diff; - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 || (v1 == v2 && carry != 0) { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) || ((v1 & 0xf) == (v2 & 0xf) && carry != 0) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("sbc(9f) {},{} {}", v, v, carry); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.1[0] = 0x9f; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - if carry == 0 { - expected.0.regs.acc = 0; - expected.0.regs.flags = Flags::ZERO | Flags::SUB; - } else { - expected.0.regs.acc = 0xff; - expected.0.regs.flags = Flags::SUB | Flags::CARRY | Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn and8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0xa0..=0xa6 { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("and({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let res = v1 & v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::HALFCARRY; - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("and(e6) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = 0xe6; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let res = v1 & v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::HALFCARRY; - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("and(a7) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.1[0] = 0xa7; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.flags = Flags::HALFCARRY; - if v == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn xor8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0xa8..=0xae { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("xor({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let res = v1 ^ v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::empty(); - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("xor(ee) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = 0xee; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let res = v1 ^ v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::empty(); - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("and(af) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.1[0] = 0xaf; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.acc = 0; - expected.0.regs.flags = Flags::ZERO; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn or8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0xb0..=0xb6 { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("or({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let res = v1 | v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::empty(); - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("or(f6) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = 0xf6; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let res = v1 | v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::empty(); - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("or(b7) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.1[0] = 0xb7; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.flags = Flags::empty(); - if v == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn cp8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0xb8..=0xbe { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("cp({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let diff = v1.wrapping_sub(v2); - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("cp(fe) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2]); - ctx.1[0] = 0xfe; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let diff = v1.wrapping_sub(v2); - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("cp(bf) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.1[0] = 0xbf; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.flags = Flags::ZERO | Flags::SUB; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn relative_jumps() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for offset in -128i8..=127 { - let jnz = (flags & 0x80) == 0; - let jz = (flags & 0x80) != 0; - let jnc = (flags & 0x10) == 0; - let jc = (flags & 0x10) != 0; - let ops = [ - (0x18, true), - (0x20, jnz), - (0x28, jz), - (0x30, jnc), - (0x38, jc), - ]; - - for (opcode, should_jump) in ops { - println!("jr({:02x}) -> {} should: {}", opcode, offset, should_jump); - let mut ctx = (Gbz80State::default(), [0u8; 0x200]); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0x100] = opcode; - ctx.0.regs.pc = 0x100; - ctx.1[0x101] = offset as u8; - - let expected = { - let mut expected = ctx.clone(); - if should_jump { - expected.0.regs.pc = (0x102 + offset as i32) as u16; - } else { - expected.0.regs.pc = 0x102; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - } - - #[test] - fn absolute_immediate_jumps() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for dest in 0x0000u16..=0xffff { - let jnz = (flags & 0x80) == 0; - let jz = (flags & 0x80) != 0; - let jnc = (flags & 0x10) == 0; - let jc = (flags & 0x10) != 0; - let ops = [ - (0xc3, true), - (0xc2, jnz), - (0xca, jz), - (0xd2, jnc), - (0xda, jc), - ]; - - for (opcode, should_jump) in ops { - println!("jp({:02x}) -> {:04x} should: {}", opcode, dest, should_jump); - let mut ctx = (Gbz80State::default(), [0u8; 3]); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0] = opcode; - let [low, high] = dest.to_le_bytes(); - ctx.1[1] = low; - ctx.1[2] = high; - - let expected = { - let mut expected = ctx.clone(); - if should_jump { - expected.0.regs.pc = dest; - } else { - expected.0.regs.pc = 3; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - } - - #[test] - fn jump_hl() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for dest in 0x0000u16..=0xffff { - println!("jp(e9) -> {:04x}", dest); - let mut ctx = (Gbz80State::default(), [0u8; 1]); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0] = 0xe9; - ctx.0.regs.set_hl(dest); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = dest; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn rst() { - init(); - - let rsts = [ - (0xc7, 0x00), - (0xcf, 0x08), - (0xd7, 0x10), - (0xdf, 0x18), - (0xe7, 0x20), - (0xef, 0x28), - (0xf7, 0x30), - (0xff, 0x38), - ]; - - for (opcode, dest) in rsts { - let mut ctx = (Gbz80State::default(), [0u8; 0x200]); - ctx.1[0x101] = opcode; - ctx.0.regs.pc = 0x101; - ctx.0.regs.sp = 0x200; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = dest; - expected.0.regs.sp = 0x1fe; - // Pushed address should be 0x0102 (in little endian order), since that's - // the address of the instruction after RST. - expected.1[0x1fe] = 0x02; - expected.1[0x1ff] = 0x01; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - - #[test] - fn returns() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for dest in 0x0000u16..=0xffff { - let jnz = (flags & 0x80) == 0; - let jz = (flags & 0x80) != 0; - let jnc = (flags & 0x10) == 0; - let jc = (flags & 0x10) != 0; - let ops = [ - (0xc9, true), - (0xc0, jnz), - (0xc8, jz), - (0xd0, jnc), - (0xd8, jc), - ]; - - for (opcode, should_jump) in ops { - println!( - "ret({:02x}) -> {:04x} should: {}", - opcode, dest, should_jump - ); - let mut ctx = (Gbz80State::default(), [0u8; 0x200]); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0x100] = opcode; - ctx.0.regs.pc = 0x100; - let [low, high] = dest.to_le_bytes(); - ctx.1[0x1fe] = low; - ctx.1[0x1ff] = high; - ctx.0.regs.sp = 0x1fe; - - let expected = { - let mut expected = ctx.clone(); - if should_jump { - expected.0.regs.sp = 0x200; - expected.0.regs.pc = dest; - } else { - expected.0.regs.pc = 0x101; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - } - - #[test] - fn interrupt_return() { - init(); - - for dest in 0x0000u16..=0xffff { - println!("reti(0xd9) -> {:04x}", dest); - let mut ctx = (Gbz80State::default(), [0u8; 0x200]); - ctx.1[0x100] = 0xd9; - ctx.0.regs.pc = 0x100; - let [low, high] = dest.to_le_bytes(); - ctx.1[0x1fe] = low; - ctx.1[0x1ff] = high; - ctx.0.regs.sp = 0x1fe; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.sp = 0x200; - expected.0.regs.pc = dest; - expected.0.interrupt_master_enable.set(); - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - - #[test] - fn calls() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for dest in 0x0000u16..=0xffff { - let jnz = (flags & 0x80) == 0; - let jz = (flags & 0x80) != 0; - let jnc = (flags & 0x10) == 0; - let jc = (flags & 0x10) != 0; - let ops = [ - (0xcd, true), - (0xc4, jnz), - (0xcc, jz), - (0xd4, jnc), - (0xdc, jc), - ]; - - for (opcode, should_jump) in ops { - println!( - "call({:02x}) -> {:04x} should: {}", - opcode, dest, should_jump - ); - let mut ctx = (Gbz80State::default(), [0u8; 0x200]); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0x100] = opcode; - ctx.0.regs.pc = 0x100; - let [low, high] = dest.to_le_bytes(); - ctx.1[0x101] = low; - ctx.1[0x102] = high; - ctx.0.regs.sp = 0x200; - - let expected = { - let mut expected = ctx.clone(); - if should_jump { - expected.0.regs.sp = 0x1fe; - // Return to 0x0103. - expected.1[0x1fe] = 0x03; - expected.1[0x1ff] = 0x01; - expected.0.regs.pc = dest; - } else { - expected.0.regs.pc = 0x103; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - } +executor_tests! { + tests, + crate::gbz80core::direct_executor_v2::DirectExecutorV2, + crate::gbz80core::TestGb, ()> } diff --git a/feo3boy/src/gbz80core/executor_testing.rs b/feo3boy/src/gbz80core/executor_testing.rs new file mode 100644 index 0000000..351e07f --- /dev/null +++ b/feo3boy/src/gbz80core/executor_testing.rs @@ -0,0 +1,1294 @@ +/// Defines the common set of tests for the executors. +#[macro_export] +macro_rules! executor_tests { + ($name:ident, $exe:ty, $test_gb:ty) => { + #[cfg(test)] + mod $name { + use feo3boy_opcodes::gbz80types::Flags; + + use crate::gbz80core::executor::Executor; + + fn init() { + let _ = env_logger::builder().is_test(true).try_init(); + } + + fn run_single_instruction(gb: &mut $test_gb) { + <$exe>::run_single_instruction(gb) + } + + #[test] + fn test_loads_and_alu() { + init(); + + let mut gb = <$test_gb>::default(); + + gb.mem[0] = 0x3e; + gb.mem[1] = 0x80; + gb.mem[2] = 0x06; + gb.mem[3] = 0x01; + gb.mem[4] = 0x80; + gb.mem[5] = 0x0e; + gb.mem[6] = 0x85; + gb.mem[7] = 0x81; + + run_single_instruction(&mut gb); + assert_eq!(gb.cpu.regs.acc, 0x80, "{:?}", gb.cpu.regs); + + run_single_instruction(&mut gb); + assert_eq!(gb.cpu.regs.b, 0x01, "{:?}", gb.cpu.regs); + + run_single_instruction(&mut gb); + assert_eq!(gb.cpu.regs.acc, 0x81, "{:?}", gb.cpu.regs); + + run_single_instruction(&mut gb); + assert_eq!(gb.cpu.regs.c, 0x85, "{:?}", gb.cpu.regs); + + run_single_instruction(&mut gb); + assert_eq!(gb.cpu.regs.acc, 0x6, "{:?}", gb.cpu.regs); + assert!( + gb.cpu.regs.flags.contains(Flags::CARRY), + "{:?}", + gb.cpu.regs + ); + } + + #[test] + fn load8bit() { + init(); + + fn set_dest(inst: u8, gb: &mut $test_gb, val: u8) { + match inst { + 0x40..=0x47 => gb.cpu.regs.b = val, + 0x48..=0x4f => gb.cpu.regs.c = val, + 0x50..=0x57 => gb.cpu.regs.d = val, + 0x58..=0x5f => gb.cpu.regs.e = val, + 0x60..=0x67 => gb.cpu.regs.h = val, + 0x68..=0x6f => gb.cpu.regs.l = val, + 0x70..=0x77 => { + let dest = gb.cpu.regs.hl() as usize; + gb.mem[dest] = val; + } + 0x78..=0x7f => gb.cpu.regs.acc = val, + _ => unreachable!(), + } + } + fn set_source(inst: u8, gb: &mut $test_gb, val: u8) { + match (inst & 0xf) % 0x8 { + 0x0 => gb.cpu.regs.b = val, + 0x1 => gb.cpu.regs.c = val, + 0x2 => gb.cpu.regs.d = val, + 0x3 => gb.cpu.regs.e = val, + 0x4 => gb.cpu.regs.h = val, + 0x5 => gb.cpu.regs.l = val, + 0x6 => { + let dest = gb.cpu.regs.hl() as usize; + gb.mem[dest] = val; + } + 0x7 => gb.cpu.regs.acc = val, + _ => unreachable!(), + } + } + for inst in (0x40..=0x7f).filter(|&i| i != 0x76) { + for input in 0..=0xff { + let mut gb = <$test_gb>::default(); + // Start with HL at a high number so any single-byte change to it will never + // collide with the instruction at 0x00. + gb.cpu.regs.set_hl(0xAA); + // This step might overwrite H or L changing the destination address of LD (HL),H + // or LD (HL),L, but that's ok because we use HL in set_dest. + set_source(inst, &mut gb, input); + gb.mem[0] = inst; + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 1; + set_dest(inst, &mut expected, input); + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + + #[test] + fn load8bit_immediate() { + init(); + + fn set_dest(inst: u8, gb: &mut $test_gb, val: u8) { + match inst { + 0x06 => gb.cpu.regs.b = val, + 0x0e => gb.cpu.regs.c = val, + 0x16 => gb.cpu.regs.d = val, + 0x1e => gb.cpu.regs.e = val, + 0x26 => gb.cpu.regs.h = val, + 0x2e => gb.cpu.regs.l = val, + 0x36 => { + let dest = gb.cpu.regs.hl() as usize; + gb.mem[dest] = val; + } + 0x3e => gb.cpu.regs.acc = val, + _ => unreachable!(), + } + } + for inst in (0x00..=0x30) + .step_by(0x10) + .flat_map(|high| [high + 0x06, high + 0x0e]) + { + for input in 0..=0xff { + let mut gb = <$test_gb>::default(); + // Start with HL at a high number so any single-byte change to it will never + // collide with the instruction at 0x00. + gb.cpu.regs.set_hl(2); + gb.mem[0] = inst; + gb.mem[1] = input; + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 2; + set_dest(inst, &mut expected, input); + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + + #[test] + fn add8bit() { + init(); + + fn set_source(inst: u8, gb: &mut $test_gb, val: u8) { + match (inst & 0xf) % 0x8 { + 0x0 => gb.cpu.regs.b = val, + 0x1 => gb.cpu.regs.c = val, + 0x2 => gb.cpu.regs.d = val, + 0x3 => gb.cpu.regs.e = val, + 0x4 => gb.cpu.regs.h = val, + 0x5 => gb.cpu.regs.l = val, + 0x6 => { + let dest = gb.cpu.regs.hl() as usize; + gb.mem[dest] = val; + } + _ => unreachable!(), + } + } + for existing_flags in (0x00..=0xf0).step_by(0x10) { + for inst in 0x80..=0x86 { + for v1 in 0..=0xff { + for v2 in 0..=0xff { + println!("add({:02x}) {},{}", inst, v1, v2); + let mut gb = <$test_gb>::default(); + gb.mem[0] = inst; + gb.cpu.regs.acc = v1; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + gb.cpu.regs.set_hl(1); + set_source(inst, &mut gb, v2); + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 1; + let (sum, wrapped) = v1.overflowing_add(v2); + expected.cpu.regs.acc = sum; + expected.cpu.regs.flags = Flags::empty(); + if sum == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + if wrapped { + expected.cpu.regs.flags |= Flags::CARRY; + } + if (v1 & 0xf) + (v2 & 0xf) > 0xf { + expected.cpu.regs.flags |= Flags::HALFCARRY; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + // A,u8 + for v1 in 0..=0xff { + for v2 in 0..=0xff { + println!("add(c6) {},{}", v1, v2); + let mut gb = <$test_gb>::default(); + gb.mem[0] = 0xc6; + gb.cpu.regs.acc = v1; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + gb.mem[1] = v2; + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 2; + let (sum, wrapped) = v1.overflowing_add(v2); + expected.cpu.regs.acc = sum; + expected.cpu.regs.flags = Flags::empty(); + if sum == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + if wrapped { + expected.cpu.regs.flags |= Flags::CARRY; + } + if (v1 & 0xf) + (v2 & 0xf) > 0xf { + expected.cpu.regs.flags |= Flags::HALFCARRY; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + // A,A + for v in 0..=0xff { + println!("add(87) {},{}", v, v); + let mut gb = <$test_gb>::default(); + gb.mem[0] = 0x87; + gb.cpu.regs.acc = v; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 1; + let (sum, wrapped) = v.overflowing_add(v); + expected.cpu.regs.acc = sum; + expected.cpu.regs.flags = Flags::empty(); + if sum == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + if wrapped { + expected.cpu.regs.flags |= Flags::CARRY; + } + if (v & 0xf) + (v & 0xf) > 0xf { + expected.cpu.regs.flags |= Flags::HALFCARRY; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + + #[test] + fn adc8bit() { + init(); + + fn set_source(inst: u8, gb: &mut $test_gb, val: u8) { + match (inst & 0xf) % 0x8 { + 0x0 => gb.cpu.regs.b = val, + 0x1 => gb.cpu.regs.c = val, + 0x2 => gb.cpu.regs.d = val, + 0x3 => gb.cpu.regs.e = val, + 0x4 => gb.cpu.regs.h = val, + 0x5 => gb.cpu.regs.l = val, + 0x6 => { + let dest = gb.cpu.regs.hl() as usize; + gb.mem[dest] = val; + } + _ => unreachable!(), + } + } + for existing_flags in (0x00..=0xf0).step_by(0x10) { + let carry = (existing_flags & 0x10) >> 4; + for inst in 0x88..=0x8e { + for v1 in 0..=0xff { + for v2 in 0..=0xff { + println!("adc({:02x}) {},{} {}", inst, v1, v2, carry); + let mut gb = <$test_gb>::default(); + gb.mem[0] = inst; + gb.cpu.regs.acc = v1; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + gb.cpu.regs.set_hl(1); + set_source(inst, &mut gb, v2); + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 1; + let (sum1, wrapped1) = v1.overflowing_add(v2); + let (sum2, wrapped2) = sum1.overflowing_add(carry); + expected.cpu.regs.acc = sum2; + expected.cpu.regs.flags = Flags::empty(); + if sum2 == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + if wrapped1 || wrapped2 { + expected.cpu.regs.flags |= Flags::CARRY; + } + if (v1 & 0xf) + (v2 & 0xf) + carry > 0xf { + expected.cpu.regs.flags |= Flags::HALFCARRY; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + // ADC A,u8 + for v1 in 0..=0xff { + for v2 in 0..=0xff { + println!("adc(ce) {},{} {}", v1, v2, carry); + let mut gb = <$test_gb>::default(); + gb.mem[0] = 0xce; + gb.cpu.regs.acc = v1; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + gb.mem[1] = v2; + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 2; + let (sum1, wrapped1) = v1.overflowing_add(v2); + let (sum2, wrapped2) = sum1.overflowing_add(carry); + expected.cpu.regs.acc = sum2; + expected.cpu.regs.flags = Flags::empty(); + if sum2 == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + if wrapped1 || wrapped2 { + expected.cpu.regs.flags |= Flags::CARRY; + } + if (v1 & 0xf) + (v2 & 0xf) + carry > 0xf { + expected.cpu.regs.flags |= Flags::HALFCARRY; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + // ADC A,A + for v in 0..=0xff { + println!("adc(8f) {},{} {}", v, v, carry); + let mut gb = <$test_gb>::default(); + gb.mem[0] = 0x8f; + gb.cpu.regs.acc = v; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + if carry == 1 { + gb.cpu.regs.flags |= Flags::CARRY; + } + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 1; + let (sum1, wrapped1) = v.overflowing_add(v); + let (sum2, wrapped2) = sum1.overflowing_add(carry); + expected.cpu.regs.acc = sum2; + expected.cpu.regs.flags = Flags::empty(); + if sum2 == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + if wrapped1 || wrapped2 { + expected.cpu.regs.flags |= Flags::CARRY; + } + if (v & 0xf) + (v & 0xf) + carry > 0xf { + expected.cpu.regs.flags |= Flags::HALFCARRY; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + + #[test] + fn sub8bit() { + init(); + + fn set_source(inst: u8, gb: &mut $test_gb, val: u8) { + match (inst & 0xf) % 0x8 { + 0x0 => gb.cpu.regs.b = val, + 0x1 => gb.cpu.regs.c = val, + 0x2 => gb.cpu.regs.d = val, + 0x3 => gb.cpu.regs.e = val, + 0x4 => gb.cpu.regs.h = val, + 0x5 => gb.cpu.regs.l = val, + 0x6 => { + let dest = gb.cpu.regs.hl() as usize; + gb.mem[dest] = val; + } + _ => unreachable!(), + } + } + for existing_flags in (0x00..=0xf0).step_by(0x10) { + for inst in 0x90..=0x96 { + for v1 in 0..=0xff { + for v2 in 0..=0xff { + println!("sub({:02x}) {},{}", inst, v1, v2); + let mut gb = <$test_gb>::default(); + gb.mem[0] = inst; + gb.cpu.regs.acc = v1; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + gb.cpu.regs.set_hl(1); + set_source(inst, &mut gb, v2); + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 1; + let diff = v1.wrapping_sub(v2); + expected.cpu.regs.acc = diff; + expected.cpu.regs.flags = Flags::SUB; + if diff == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + if v1 < v2 { + expected.cpu.regs.flags |= Flags::CARRY; + } + if (v1 & 0xf) < (v2 & 0xf) { + expected.cpu.regs.flags |= Flags::HALFCARRY; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + for v1 in 0..=0xff { + for v2 in 0..=0xff { + println!("sub(d6) {},{}", v1, v2); + let mut gb = <$test_gb>::default(); + gb.mem[0] = 0xd6; + gb.cpu.regs.acc = v1; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + gb.mem[1] = v2; + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 2; + let diff = v1.wrapping_sub(v2); + expected.cpu.regs.acc = diff; + expected.cpu.regs.flags = Flags::SUB; + if diff == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + if v1 < v2 { + expected.cpu.regs.flags |= Flags::CARRY; + } + if (v1 & 0xf) < (v2 & 0xf) { + expected.cpu.regs.flags |= Flags::HALFCARRY; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + for v in 0..=0xff { + println!("sub(97) {},{}", v, v); + let mut gb = <$test_gb>::default(); + gb.mem[0] = 0x97; + gb.cpu.regs.acc = v; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 1; + expected.cpu.regs.acc = 0; + expected.cpu.regs.flags = Flags::ZERO | Flags::SUB; + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + + #[test] + fn sbc8bit() { + init(); + + fn set_source(inst: u8, gb: &mut $test_gb, val: u8) { + match (inst & 0xf) % 0x8 { + 0x0 => gb.cpu.regs.b = val, + 0x1 => gb.cpu.regs.c = val, + 0x2 => gb.cpu.regs.d = val, + 0x3 => gb.cpu.regs.e = val, + 0x4 => gb.cpu.regs.h = val, + 0x5 => gb.cpu.regs.l = val, + 0x6 => { + let dest = gb.cpu.regs.hl() as usize; + gb.mem[dest] = val; + } + _ => unreachable!(), + } + } + for existing_flags in (0x00..=0xf0).step_by(0x10) { + let carry = (existing_flags & 0x10) >> 4; + for inst in 0x98..=0x9e { + for v1 in 0..=0xff { + for v2 in 0..=0xff { + println!("sbc({:02x}) {},{} {}", inst, v1, v2, carry); + let mut gb = <$test_gb>::default(); + gb.mem[0] = inst; + gb.cpu.regs.acc = v1; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + gb.cpu.regs.set_hl(1); + set_source(inst, &mut gb, v2); + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 1; + let diff = v1.wrapping_sub(v2).wrapping_sub(carry); + expected.cpu.regs.acc = diff; + expected.cpu.regs.flags = Flags::SUB; + if diff == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + if v1 < v2 || (v1 == v2 && carry != 0) { + expected.cpu.regs.flags |= Flags::CARRY; + } + if (v1 & 0xf) < (v2 & 0xf) + || ((v1 & 0xf) == (v2 & 0xf) && carry != 0) + { + expected.cpu.regs.flags |= Flags::HALFCARRY; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + for v1 in 0..=0xff { + for v2 in 0..=0xff { + println!("sbc(de) {},{} {}", v1, v2, carry); + let mut gb = <$test_gb>::default(); + gb.mem[0] = 0xde; + gb.cpu.regs.acc = v1; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + gb.mem[1] = v2; + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 2; + let diff = v1.wrapping_sub(v2).wrapping_sub(carry); + expected.cpu.regs.acc = diff; + expected.cpu.regs.flags = Flags::SUB; + if diff == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + if v1 < v2 || (v1 == v2 && carry != 0) { + expected.cpu.regs.flags |= Flags::CARRY; + } + if (v1 & 0xf) < (v2 & 0xf) + || ((v1 & 0xf) == (v2 & 0xf) && carry != 0) + { + expected.cpu.regs.flags |= Flags::HALFCARRY; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + for v in 0..=0xff { + println!("sbc(9f) {},{} {}", v, v, carry); + let mut gb = <$test_gb>::default(); + gb.mem[0] = 0x9f; + gb.cpu.regs.acc = v; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 1; + if carry == 0 { + expected.cpu.regs.acc = 0; + expected.cpu.regs.flags = Flags::ZERO | Flags::SUB; + } else { + expected.cpu.regs.acc = 0xff; + expected.cpu.regs.flags = + Flags::SUB | Flags::CARRY | Flags::HALFCARRY; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + + #[test] + fn and8bit() { + init(); + + fn set_source(inst: u8, gb: &mut $test_gb, val: u8) { + match (inst & 0xf) % 0x8 { + 0x0 => gb.cpu.regs.b = val, + 0x1 => gb.cpu.regs.c = val, + 0x2 => gb.cpu.regs.d = val, + 0x3 => gb.cpu.regs.e = val, + 0x4 => gb.cpu.regs.h = val, + 0x5 => gb.cpu.regs.l = val, + 0x6 => { + let dest = gb.cpu.regs.hl() as usize; + gb.mem[dest] = val; + } + _ => unreachable!(), + } + } + for existing_flags in (0x00..=0xf0).step_by(0x10) { + for inst in 0xa0..=0xa6 { + for v1 in 0..=0xff { + for v2 in 0..=0xff { + println!("and({:02x}) {},{}", inst, v1, v2); + let mut gb = <$test_gb>::default(); + gb.mem[0] = inst; + gb.cpu.regs.acc = v1; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + gb.cpu.regs.set_hl(1); + set_source(inst, &mut gb, v2); + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 1; + let res = v1 & v2; + expected.cpu.regs.acc = res; + expected.cpu.regs.flags = Flags::HALFCARRY; + if res == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + for v1 in 0..=0xff { + for v2 in 0..=0xff { + println!("and(e6) {},{}", v1, v2); + let mut gb = <$test_gb>::default(); + gb.mem[0] = 0xe6; + gb.cpu.regs.acc = v1; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + gb.mem[1] = v2; + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 2; + let res = v1 & v2; + expected.cpu.regs.acc = res; + expected.cpu.regs.flags = Flags::HALFCARRY; + if res == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + for v in 0..=0xff { + println!("and(a7) {},{}", v, v); + let mut gb = <$test_gb>::default(); + gb.mem[0] = 0xa7; + gb.cpu.regs.acc = v; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 1; + expected.cpu.regs.flags = Flags::HALFCARRY; + if v == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + + #[test] + fn xor8bit() { + init(); + + fn set_source(inst: u8, gb: &mut $test_gb, val: u8) { + match (inst & 0xf) % 0x8 { + 0x0 => gb.cpu.regs.b = val, + 0x1 => gb.cpu.regs.c = val, + 0x2 => gb.cpu.regs.d = val, + 0x3 => gb.cpu.regs.e = val, + 0x4 => gb.cpu.regs.h = val, + 0x5 => gb.cpu.regs.l = val, + 0x6 => { + let dest = gb.cpu.regs.hl() as usize; + gb.mem[dest] = val; + } + _ => unreachable!(), + } + } + for existing_flags in (0x00..=0xf0).step_by(0x10) { + for inst in 0xa8..=0xae { + for v1 in 0..=0xff { + for v2 in 0..=0xff { + println!("xor({:02x}) {},{}", inst, v1, v2); + let mut gb = <$test_gb>::default(); + gb.mem[0] = inst; + gb.cpu.regs.acc = v1; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + gb.cpu.regs.set_hl(1); + set_source(inst, &mut gb, v2); + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 1; + let res = v1 ^ v2; + expected.cpu.regs.acc = res; + expected.cpu.regs.flags = Flags::empty(); + if res == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + for v1 in 0..=0xff { + for v2 in 0..=0xff { + println!("xor(ee) {},{}", v1, v2); + let mut gb = <$test_gb>::default(); + gb.mem[0] = 0xee; + gb.cpu.regs.acc = v1; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + gb.mem[1] = v2; + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 2; + let res = v1 ^ v2; + expected.cpu.regs.acc = res; + expected.cpu.regs.flags = Flags::empty(); + if res == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + for v in 0..=0xff { + println!("and(af) {},{}", v, v); + let mut gb = <$test_gb>::default(); + gb.mem[0] = 0xaf; + gb.cpu.regs.acc = v; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 1; + expected.cpu.regs.acc = 0; + expected.cpu.regs.flags = Flags::ZERO; + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + + #[test] + fn or8bit() { + init(); + + fn set_source(inst: u8, gb: &mut $test_gb, val: u8) { + match (inst & 0xf) % 0x8 { + 0x0 => gb.cpu.regs.b = val, + 0x1 => gb.cpu.regs.c = val, + 0x2 => gb.cpu.regs.d = val, + 0x3 => gb.cpu.regs.e = val, + 0x4 => gb.cpu.regs.h = val, + 0x5 => gb.cpu.regs.l = val, + 0x6 => { + let dest = gb.cpu.regs.hl() as usize; + gb.mem[dest] = val; + } + _ => unreachable!(), + } + } + for existing_flags in (0x00..=0xf0).step_by(0x10) { + for inst in 0xb0..=0xb6 { + for v1 in 0..=0xff { + for v2 in 0..=0xff { + println!("or({:02x}) {},{}", inst, v1, v2); + let mut gb = <$test_gb>::default(); + gb.mem[0] = inst; + gb.cpu.regs.acc = v1; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + gb.cpu.regs.set_hl(1); + set_source(inst, &mut gb, v2); + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 1; + let res = v1 | v2; + expected.cpu.regs.acc = res; + expected.cpu.regs.flags = Flags::empty(); + if res == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + for v1 in 0..=0xff { + for v2 in 0..=0xff { + println!("or(f6) {},{}", v1, v2); + let mut gb = <$test_gb>::default(); + gb.mem[0] = 0xf6; + gb.cpu.regs.acc = v1; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + gb.mem[1] = v2; + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 2; + let res = v1 | v2; + expected.cpu.regs.acc = res; + expected.cpu.regs.flags = Flags::empty(); + if res == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + for v in 0..=0xff { + println!("or(b7) {},{}", v, v); + let mut gb = <$test_gb>::default(); + gb.mem[0] = 0xb7; + gb.cpu.regs.acc = v; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 1; + expected.cpu.regs.flags = Flags::empty(); + if v == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + + #[test] + fn cp8bit() { + init(); + + fn set_source(inst: u8, gb: &mut $test_gb, val: u8) { + match (inst & 0xf) % 0x8 { + 0x0 => gb.cpu.regs.b = val, + 0x1 => gb.cpu.regs.c = val, + 0x2 => gb.cpu.regs.d = val, + 0x3 => gb.cpu.regs.e = val, + 0x4 => gb.cpu.regs.h = val, + 0x5 => gb.cpu.regs.l = val, + 0x6 => { + let dest = gb.cpu.regs.hl() as usize; + gb.mem[dest] = val; + } + _ => unreachable!(), + } + } + for existing_flags in (0x00..=0xf0).step_by(0x10) { + for inst in 0xb8..=0xbe { + for v1 in 0..=0xff { + for v2 in 0..=0xff { + println!("cp({:02x}) {},{}", inst, v1, v2); + let mut gb = <$test_gb>::default(); + gb.mem[0] = inst; + gb.cpu.regs.acc = v1; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + gb.cpu.regs.set_hl(1); + set_source(inst, &mut gb, v2); + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 1; + let diff = v1.wrapping_sub(v2); + expected.cpu.regs.flags = Flags::SUB; + if diff == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + if v1 < v2 { + expected.cpu.regs.flags |= Flags::CARRY; + } + if (v1 & 0xf) < (v2 & 0xf) { + expected.cpu.regs.flags |= Flags::HALFCARRY; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + for v1 in 0..=0xff { + for v2 in 0..=0xff { + println!("cp(fe) {},{}", v1, v2); + let mut gb = <$test_gb>::default(); + gb.mem[0] = 0xfe; + gb.cpu.regs.acc = v1; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + gb.mem[1] = v2; + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 2; + let diff = v1.wrapping_sub(v2); + expected.cpu.regs.flags = Flags::SUB; + if diff == 0 { + expected.cpu.regs.flags |= Flags::ZERO; + } + if v1 < v2 { + expected.cpu.regs.flags |= Flags::CARRY; + } + if (v1 & 0xf) < (v2 & 0xf) { + expected.cpu.regs.flags |= Flags::HALFCARRY; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + for v in 0..=0xff { + println!("cp(bf) {},{}", v, v); + let mut gb = <$test_gb>::default(); + gb.mem[0] = 0xbf; + gb.cpu.regs.acc = v; + gb.cpu.regs.flags = Flags::from_bits_truncate(existing_flags); + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = 1; + expected.cpu.regs.flags = Flags::ZERO | Flags::SUB; + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + + #[test] + fn relative_jumps() { + init(); + + for flags in (0x00..=0xf0).step_by(0x10) { + for offset in -128i8..=127 { + let jnz = (flags & 0x80) == 0; + let jz = (flags & 0x80) != 0; + let jnc = (flags & 0x10) == 0; + let jc = (flags & 0x10) != 0; + let ops = [ + (0x18, true), + (0x20, jnz), + (0x28, jz), + (0x30, jnc), + (0x38, jc), + ]; + + for (opcode, should_jump) in ops { + println!("jr({:02x}) -> {} should: {}", opcode, offset, should_jump); + let mut gb = <$test_gb>::default(); + gb.cpu.regs.flags = Flags::from_bits_truncate(flags); + gb.mem[0x100] = opcode; + gb.cpu.regs.pc = 0x100; + gb.mem[0x101] = offset as u8; + + let expected = { + let mut expected = gb.clone(); + if should_jump { + expected.cpu.regs.pc = (0x102 + offset as i32) as u16; + } else { + expected.cpu.regs.pc = 0x102; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + } + + #[test] + fn absolute_immediate_jumps() { + init(); + + for flags in (0x00..=0xf0).step_by(0x10) { + for dest in 0x0000u16..=0xffff { + let jnz = (flags & 0x80) == 0; + let jz = (flags & 0x80) != 0; + let jnc = (flags & 0x10) == 0; + let jc = (flags & 0x10) != 0; + let ops = [ + (0xc3, true), + (0xc2, jnz), + (0xca, jz), + (0xd2, jnc), + (0xda, jc), + ]; + + for (opcode, should_jump) in ops { + println!("jp({:02x}) -> {:04x} should: {}", opcode, dest, should_jump); + let mut gb = <$test_gb>::default(); + gb.cpu.regs.flags = Flags::from_bits_truncate(flags); + gb.mem[0] = opcode; + let [low, high] = dest.to_le_bytes(); + gb.mem[1] = low; + gb.mem[2] = high; + + let expected = { + let mut expected = gb.clone(); + if should_jump { + expected.cpu.regs.pc = dest; + } else { + expected.cpu.regs.pc = 3; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + } + + #[test] + fn jump_hl() { + init(); + + for flags in (0x00..=0xf0).step_by(0x10) { + for dest in 0x0000u16..=0xffff { + println!("jp(e9) -> {:04x}", dest); + let mut gb = <$test_gb>::default(); + gb.cpu.regs.flags = Flags::from_bits_truncate(flags); + gb.mem[0] = 0xe9; + gb.cpu.regs.set_hl(dest); + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = dest; + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + + #[test] + fn rst() { + init(); + + let rsts = [ + (0xc7, 0x00), + (0xcf, 0x08), + (0xd7, 0x10), + (0xdf, 0x18), + (0xe7, 0x20), + (0xef, 0x28), + (0xf7, 0x30), + (0xff, 0x38), + ]; + + for (opcode, dest) in rsts { + let mut gb = <$test_gb>::default(); + gb.mem[0x101] = opcode; + gb.cpu.regs.pc = 0x101; + gb.cpu.regs.sp = 0x200; + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.pc = dest; + expected.cpu.regs.sp = 0x1fe; + // Pushed address should be 0x0102 (in little endian order), since that's + // the address of the instruction after RST. + expected.mem[0x1fe] = 0x02; + expected.mem[0x1ff] = 0x01; + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + + #[test] + fn returns() { + init(); + + for flags in (0x00..=0xf0).step_by(0x10) { + for dest in 0x0000u16..=0xffff { + let jnz = (flags & 0x80) == 0; + let jz = (flags & 0x80) != 0; + let jnc = (flags & 0x10) == 0; + let jc = (flags & 0x10) != 0; + let ops = [ + (0xc9, true), + (0xc0, jnz), + (0xc8, jz), + (0xd0, jnc), + (0xd8, jc), + ]; + + for (opcode, should_jump) in ops { + println!( + "ret({:02x}) -> {:04x} should: {}", + opcode, dest, should_jump + ); + let mut gb = <$test_gb>::default(); + gb.cpu.regs.flags = Flags::from_bits_truncate(flags); + gb.mem[0x100] = opcode; + gb.cpu.regs.pc = 0x100; + let [low, high] = dest.to_le_bytes(); + gb.mem[0x1fe] = low; + gb.mem[0x1ff] = high; + gb.cpu.regs.sp = 0x1fe; + + let expected = { + let mut expected = gb.clone(); + if should_jump { + expected.cpu.regs.sp = 0x200; + expected.cpu.regs.pc = dest; + } else { + expected.cpu.regs.pc = 0x101; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + } + + #[test] + fn interrupt_return() { + init(); + + for dest in 0x0000u16..=0xffff { + println!("reti(0xd9) -> {:04x}", dest); + let mut gb = <$test_gb>::default(); + gb.mem[0x100] = 0xd9; + gb.cpu.regs.pc = 0x100; + let [low, high] = dest.to_le_bytes(); + gb.mem[0x1fe] = low; + gb.mem[0x1ff] = high; + gb.cpu.regs.sp = 0x1fe; + + let expected = { + let mut expected = gb.clone(); + expected.cpu.regs.sp = 0x200; + expected.cpu.regs.pc = dest; + expected.cpu.interrupt_master_enable.set(); + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + + #[test] + fn calls() { + init(); + + for flags in (0x00..=0xf0).step_by(0x10) { + for dest in 0x0000u16..=0xffff { + let jnz = (flags & 0x80) == 0; + let jz = (flags & 0x80) != 0; + let jnc = (flags & 0x10) == 0; + let jc = (flags & 0x10) != 0; + let ops = [ + (0xcd, true), + (0xc4, jnz), + (0xcc, jz), + (0xd4, jnc), + (0xdc, jc), + ]; + + for (opcode, should_jump) in ops { + println!( + "call({:02x}) -> {:04x} should: {}", + opcode, dest, should_jump + ); + let mut gb = <$test_gb>::default(); + gb.cpu.regs.flags = Flags::from_bits_truncate(flags); + gb.mem[0x100] = opcode; + gb.cpu.regs.pc = 0x100; + let [low, high] = dest.to_le_bytes(); + gb.mem[0x101] = low; + gb.mem[0x102] = high; + gb.cpu.regs.sp = 0x200; + + let expected = { + let mut expected = gb.clone(); + if should_jump { + expected.cpu.regs.sp = 0x1fe; + // Return to 0x0103. + expected.mem[0x1fe] = 0x03; + expected.mem[0x1ff] = 0x01; + expected.cpu.regs.pc = dest; + } else { + expected.cpu.regs.pc = 0x103; + } + expected + }; + + run_single_instruction(&mut gb); + assert_eq!(gb, expected); + } + } + } + } + } + }; +} diff --git a/feo3boy/src/gbz80core/microcode_executor/mod.rs b/feo3boy/src/gbz80core/microcode_executor/mod.rs index c314eef..7683f36 100644 --- a/feo3boy/src/gbz80core/microcode_executor/mod.rs +++ b/feo3boy/src/gbz80core/microcode_executor/mod.rs @@ -15,8 +15,6 @@ use once_cell::sync::Lazy; use crate::gbz80core::executor::{Executor, ExecutorState, PausePoint, SubInstructionExecutor}; use crate::gbz80core::{externdefs, ExecutorContext, InterruptMasterState}; -mod tests; - /// State of the microcode executor in the CPU. #[derive(Debug, Clone, PartialEq, Eq)] pub struct MicrocodeState { @@ -728,3 +726,9 @@ ctx_applier!([In1, In2, In3], [Out1, Out2, Out3]); ctx_applier!([In1], [Out1, Out2, Out3, Out4]); ctx_applier!([In1, In2], [Out1, Out2, Out3, Out4]); ctx_applier!([In1, In2, In3], [Out1, Out2, Out3, Out4]); + +executor_tests! { + tests, + crate::gbz80core::microcode_executor::MicrocodeExecutor, + crate::gbz80core::TestGb, crate::gbz80core::microcode_executor::MicrocodeState> +} diff --git a/feo3boy/src/gbz80core/microcode_executor/tests.rs b/feo3boy/src/gbz80core/microcode_executor/tests.rs deleted file mode 100644 index 4b41d33..0000000 --- a/feo3boy/src/gbz80core/microcode_executor/tests.rs +++ /dev/null @@ -1,1308 +0,0 @@ -//! Tests for the microcode executor. -#![cfg(test)] - -use feo3boy_opcodes::gbz80types::Flags; - -use crate::gbz80core::Gbz80State; -use crate::memdev::RootMemDevice; - -use super::*; - -fn init() { - let _ = env_logger::builder().is_test(true).try_init(); -} - -fn run_single_instruction(ctx: &mut impl Ctx) { - MicrocodeExecutor::run_single_instruction(ctx) -} - -#[test] -fn test_loads_and_alu() { - init(); - - let mut cpu = Gbz80State::new(); - let mut testmem = [0u8; 0x10000]; - let mut state = MicrocodeState::default(); - - testmem[0] = 0x3e; - testmem[1] = 0x80; - testmem[2] = 0x06; - testmem[3] = 0x01; - testmem[4] = 0x80; - testmem[5] = 0x0e; - testmem[6] = 0x85; - testmem[7] = 0x81; - - run_single_instruction(&mut (&mut cpu, &mut testmem, &mut state)); - assert_eq!(cpu.regs.acc, 0x80, "{:?}", cpu.regs); - - run_single_instruction(&mut (&mut cpu, &mut testmem, &mut state)); - assert_eq!(cpu.regs.b, 0x01, "{:?}", cpu.regs); - - run_single_instruction(&mut (&mut cpu, &mut testmem, &mut state)); - assert_eq!(cpu.regs.acc, 0x81, "{:?}", cpu.regs); - - run_single_instruction(&mut (&mut cpu, &mut testmem, &mut state)); - assert_eq!(cpu.regs.c, 0x85, "{:?}", cpu.regs); - - run_single_instruction(&mut (&mut cpu, &mut testmem, &mut state)); - assert_eq!(cpu.regs.acc, 0x6, "{:?}", cpu.regs); - assert!(cpu.regs.flags.contains(Flags::CARRY), "{:?}", cpu.regs); -} - -#[test] -fn load8bit() { - init(); - - fn set_dest(inst: u8, ctx: &mut impl Ctx, val: u8) { - match inst { - 0x40..=0x47 => ctx.cpu_mut().regs.b = val, - 0x48..=0x4f => ctx.cpu_mut().regs.c = val, - 0x50..=0x57 => ctx.cpu_mut().regs.d = val, - 0x58..=0x5f => ctx.cpu_mut().regs.e = val, - 0x60..=0x67 => ctx.cpu_mut().regs.h = val, - 0x68..=0x6f => ctx.cpu_mut().regs.l = val, - 0x70..=0x77 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - 0x78..=0x7f => ctx.cpu_mut().regs.acc = val, - _ => unreachable!(), - } - } - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - 0x7 => ctx.cpu_mut().regs.acc = val, - _ => unreachable!(), - } - } - for inst in (0x40..=0x7f).filter(|&i| i != 0x76) { - for input in 0..=0xff { - let mut ctx = ( - Gbz80State::default(), - [0u8; 0x10000], - MicrocodeState::default(), - ); - // Start with HL at a high number so any single-byte change to it will never - // collide with the instruction at 0x00. - ctx.0.regs.set_hl(0xAA); - // This step might overwrite H or L changing the destination address of LD (HL),H - // or LD (HL),L, but that's ok because we use HL in set_dest. - set_source(inst, &mut ctx, input); - ctx.1[0] = inst; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - set_dest(inst, &mut expected, input); - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn load8bit_immediate() { - init(); - - fn set_dest(inst: u8, ctx: &mut impl Ctx, val: u8) { - match inst { - 0x06 => ctx.cpu_mut().regs.b = val, - 0x0e => ctx.cpu_mut().regs.c = val, - 0x16 => ctx.cpu_mut().regs.d = val, - 0x1e => ctx.cpu_mut().regs.e = val, - 0x26 => ctx.cpu_mut().regs.h = val, - 0x2e => ctx.cpu_mut().regs.l = val, - 0x36 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - 0x3e => ctx.cpu_mut().regs.acc = val, - _ => unreachable!(), - } - } - for inst in (0x00..=0x30) - .step_by(0x10) - .flat_map(|high| [high + 0x06, high + 0x0e]) - { - for input in 0..=0xff { - let mut ctx = (Gbz80State::default(), [0u8; 3], MicrocodeState::default()); - // Start with HL at a high number so any single-byte change to it will never - // collide with the instruction at 0x00. - ctx.0.regs.set_hl(2); - ctx.1[0] = inst; - ctx.1[1] = input; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - set_dest(inst, &mut expected, input); - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn add8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0x80..=0x86 { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("add({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2], MicrocodeState::default()); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let (sum, wrapped) = v1.overflowing_add(v2); - expected.0.regs.acc = sum; - expected.0.regs.flags = Flags::empty(); - if sum == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) + (v2 & 0xf) > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - // A,u8 - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("add(c6) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2], MicrocodeState::default()); - ctx.1[0] = 0xc6; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let (sum, wrapped) = v1.overflowing_add(v2); - expected.0.regs.acc = sum; - expected.0.regs.flags = Flags::empty(); - if sum == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) + (v2 & 0xf) > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - // A,A - for v in 0..=0xff { - println!("add(87) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1], MicrocodeState::default()); - ctx.1[0] = 0x87; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let (sum, wrapped) = v.overflowing_add(v); - expected.0.regs.acc = sum; - expected.0.regs.flags = Flags::empty(); - if sum == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped { - expected.0.regs.flags |= Flags::CARRY; - } - if (v & 0xf) + (v & 0xf) > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn adc8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - let carry = (existing_flags & 0x10) >> 4; - for inst in 0x88..=0x8e { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("adc({:02x}) {},{} {}", inst, v1, v2, carry); - let mut ctx = (Gbz80State::default(), [0u8; 2], MicrocodeState::default()); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let (sum1, wrapped1) = v1.overflowing_add(v2); - let (sum2, wrapped2) = sum1.overflowing_add(carry); - expected.0.regs.acc = sum2; - expected.0.regs.flags = Flags::empty(); - if sum2 == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped1 || wrapped2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) + (v2 & 0xf) + carry > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - // ADC A,u8 - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("adc(ce) {},{} {}", v1, v2, carry); - let mut ctx = (Gbz80State::default(), [0u8; 2], MicrocodeState::default()); - ctx.1[0] = 0xce; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let (sum1, wrapped1) = v1.overflowing_add(v2); - let (sum2, wrapped2) = sum1.overflowing_add(carry); - expected.0.regs.acc = sum2; - expected.0.regs.flags = Flags::empty(); - if sum2 == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped1 || wrapped2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) + (v2 & 0xf) + carry > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - // ADC A,A - for v in 0..=0xff { - println!("adc(8f) {},{} {}", v, v, carry); - let mut ctx = (Gbz80State::default(), [0u8; 1], MicrocodeState::default()); - ctx.1[0] = 0x8f; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - if carry == 1 { - ctx.0.regs.flags |= Flags::CARRY; - } - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let (sum1, wrapped1) = v.overflowing_add(v); - let (sum2, wrapped2) = sum1.overflowing_add(carry); - expected.0.regs.acc = sum2; - expected.0.regs.flags = Flags::empty(); - if sum2 == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped1 || wrapped2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v & 0xf) + (v & 0xf) + carry > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn sub8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0x90..=0x96 { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("sub({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2], MicrocodeState::default()); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let diff = v1.wrapping_sub(v2); - expected.0.regs.acc = diff; - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("sub(d6) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2], MicrocodeState::default()); - ctx.1[0] = 0xd6; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let diff = v1.wrapping_sub(v2); - expected.0.regs.acc = diff; - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("sub(97) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1], MicrocodeState::default()); - ctx.1[0] = 0x97; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.acc = 0; - expected.0.regs.flags = Flags::ZERO | Flags::SUB; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn sbc8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - let carry = (existing_flags & 0x10) >> 4; - for inst in 0x98..=0x9e { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("sbc({:02x}) {},{} {}", inst, v1, v2, carry); - let mut ctx = (Gbz80State::default(), [0u8; 2], MicrocodeState::default()); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let diff = v1.wrapping_sub(v2).wrapping_sub(carry); - expected.0.regs.acc = diff; - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 || (v1 == v2 && carry != 0) { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) || ((v1 & 0xf) == (v2 & 0xf) && carry != 0) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("sbc(de) {},{} {}", v1, v2, carry); - let mut ctx = (Gbz80State::default(), [0u8; 2], MicrocodeState::default()); - ctx.1[0] = 0xde; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let diff = v1.wrapping_sub(v2).wrapping_sub(carry); - expected.0.regs.acc = diff; - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 || (v1 == v2 && carry != 0) { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) || ((v1 & 0xf) == (v2 & 0xf) && carry != 0) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("sbc(9f) {},{} {}", v, v, carry); - let mut ctx = (Gbz80State::default(), [0u8; 1], MicrocodeState::default()); - ctx.1[0] = 0x9f; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - if carry == 0 { - expected.0.regs.acc = 0; - expected.0.regs.flags = Flags::ZERO | Flags::SUB; - } else { - expected.0.regs.acc = 0xff; - expected.0.regs.flags = Flags::SUB | Flags::CARRY | Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn and8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0xa0..=0xa6 { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("and({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2], MicrocodeState::default()); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let res = v1 & v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::HALFCARRY; - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("and(e6) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2], MicrocodeState::default()); - ctx.1[0] = 0xe6; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let res = v1 & v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::HALFCARRY; - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("and(a7) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1], MicrocodeState::default()); - ctx.1[0] = 0xa7; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.flags = Flags::HALFCARRY; - if v == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn xor8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0xa8..=0xae { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("xor({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2], MicrocodeState::default()); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let res = v1 ^ v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::empty(); - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("xor(ee) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2], MicrocodeState::default()); - ctx.1[0] = 0xee; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let res = v1 ^ v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::empty(); - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("and(af) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1], MicrocodeState::default()); - ctx.1[0] = 0xaf; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.acc = 0; - expected.0.regs.flags = Flags::ZERO; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn or8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0xb0..=0xb6 { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("or({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2], MicrocodeState::default()); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let res = v1 | v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::empty(); - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("or(f6) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2], MicrocodeState::default()); - ctx.1[0] = 0xf6; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let res = v1 | v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::empty(); - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("or(b7) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1], MicrocodeState::default()); - ctx.1[0] = 0xb7; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.flags = Flags::empty(); - if v == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn cp8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0xb8..=0xbe { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("cp({:02x}) {},{}", inst, v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2], MicrocodeState::default()); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let diff = v1.wrapping_sub(v2); - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("cp(fe) {},{}", v1, v2); - let mut ctx = (Gbz80State::default(), [0u8; 2], MicrocodeState::default()); - ctx.1[0] = 0xfe; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let diff = v1.wrapping_sub(v2); - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("cp(bf) {},{}", v, v); - let mut ctx = (Gbz80State::default(), [0u8; 1], MicrocodeState::default()); - ctx.1[0] = 0xbf; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.flags = Flags::ZERO | Flags::SUB; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn relative_jumps() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for offset in -128i8..=127 { - let jnz = (flags & 0x80) == 0; - let jz = (flags & 0x80) != 0; - let jnc = (flags & 0x10) == 0; - let jc = (flags & 0x10) != 0; - let ops = [ - (0x18, true), - (0x20, jnz), - (0x28, jz), - (0x30, jnc), - (0x38, jc), - ]; - - for (opcode, should_jump) in ops { - println!("jr({:02x}) -> {} should: {}", opcode, offset, should_jump); - let mut ctx = ( - Gbz80State::default(), - [0u8; 0x200], - MicrocodeState::default(), - ); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0x100] = opcode; - ctx.0.regs.pc = 0x100; - ctx.1[0x101] = offset as u8; - - let expected = { - let mut expected = ctx.clone(); - if should_jump { - expected.0.regs.pc = (0x102 + offset as i32) as u16; - } else { - expected.0.regs.pc = 0x102; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } -} - -#[test] -fn absolute_immediate_jumps() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for dest in 0x0000u16..=0xffff { - let jnz = (flags & 0x80) == 0; - let jz = (flags & 0x80) != 0; - let jnc = (flags & 0x10) == 0; - let jc = (flags & 0x10) != 0; - let ops = [ - (0xc3, true), - (0xc2, jnz), - (0xca, jz), - (0xd2, jnc), - (0xda, jc), - ]; - - for (opcode, should_jump) in ops { - println!("jp({:02x}) -> {:04x} should: {}", opcode, dest, should_jump); - let mut ctx = (Gbz80State::default(), [0u8; 3], MicrocodeState::default()); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0] = opcode; - let [low, high] = dest.to_le_bytes(); - ctx.1[1] = low; - ctx.1[2] = high; - - let expected = { - let mut expected = ctx.clone(); - if should_jump { - expected.0.regs.pc = dest; - } else { - expected.0.regs.pc = 3; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } -} - -#[test] -fn jump_hl() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for dest in 0x0000u16..=0xffff { - println!("jp(e9) -> {:04x}", dest); - let mut ctx = (Gbz80State::default(), [0u8; 1], MicrocodeState::default()); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0] = 0xe9; - ctx.0.regs.set_hl(dest); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = dest; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } -} - -#[test] -fn rst() { - init(); - - let rsts = [ - (0xc7, 0x00), - (0xcf, 0x08), - (0xd7, 0x10), - (0xdf, 0x18), - (0xe7, 0x20), - (0xef, 0x28), - (0xf7, 0x30), - (0xff, 0x38), - ]; - - for (opcode, dest) in rsts { - let mut ctx = ( - Gbz80State::default(), - [0u8; 0x200], - MicrocodeState::default(), - ); - ctx.1[0x101] = opcode; - ctx.0.regs.pc = 0x101; - ctx.0.regs.sp = 0x200; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = dest; - expected.0.regs.sp = 0x1fe; - // Pushed address should be 0x0102 (in little endian order), since that's - // the address of the instruction after RST. - expected.1[0x1fe] = 0x02; - expected.1[0x1ff] = 0x01; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } -} - -#[test] -fn returns() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for dest in 0x0000u16..=0xffff { - let jnz = (flags & 0x80) == 0; - let jz = (flags & 0x80) != 0; - let jnc = (flags & 0x10) == 0; - let jc = (flags & 0x10) != 0; - let ops = [ - (0xc9, true), - (0xc0, jnz), - (0xc8, jz), - (0xd0, jnc), - (0xd8, jc), - ]; - - for (opcode, should_jump) in ops { - println!( - "ret({:02x}) -> {:04x} should: {}", - opcode, dest, should_jump - ); - let mut ctx = ( - Gbz80State::default(), - [0u8; 0x200], - MicrocodeState::default(), - ); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0x100] = opcode; - ctx.0.regs.pc = 0x100; - let [low, high] = dest.to_le_bytes(); - ctx.1[0x1fe] = low; - ctx.1[0x1ff] = high; - ctx.0.regs.sp = 0x1fe; - - let expected = { - let mut expected = ctx.clone(); - if should_jump { - expected.0.regs.sp = 0x200; - expected.0.regs.pc = dest; - } else { - expected.0.regs.pc = 0x101; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } -} - -#[test] -fn interrupt_return() { - init(); - - for dest in 0x0000u16..=0xffff { - println!("reti(0xd9) -> {:04x}", dest); - let mut ctx = ( - Gbz80State::default(), - [0u8; 0x200], - MicrocodeState::default(), - ); - ctx.1[0x100] = 0xd9; - ctx.0.regs.pc = 0x100; - let [low, high] = dest.to_le_bytes(); - ctx.1[0x1fe] = low; - ctx.1[0x1ff] = high; - ctx.0.regs.sp = 0x1fe; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.sp = 0x200; - expected.0.regs.pc = dest; - expected.0.interrupt_master_enable.set(); - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } -} - -#[test] -fn calls() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for dest in 0x0000u16..=0xffff { - let jnz = (flags & 0x80) == 0; - let jz = (flags & 0x80) != 0; - let jnc = (flags & 0x10) == 0; - let jc = (flags & 0x10) != 0; - let ops = [ - (0xcd, true), - (0xc4, jnz), - (0xcc, jz), - (0xd4, jnc), - (0xdc, jc), - ]; - - for (opcode, should_jump) in ops { - println!( - "call({:02x}) -> {:04x} should: {}", - opcode, dest, should_jump - ); - let mut ctx = ( - Gbz80State::default(), - [0u8; 0x200], - MicrocodeState::default(), - ); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0x100] = opcode; - ctx.0.regs.pc = 0x100; - let [low, high] = dest.to_le_bytes(); - ctx.1[0x101] = low; - ctx.1[0x102] = high; - ctx.0.regs.sp = 0x200; - - let expected = { - let mut expected = ctx.clone(); - if should_jump { - expected.0.regs.sp = 0x1fe; - // Return to 0x0103. - expected.1[0x1fe] = 0x03; - expected.1[0x1ff] = 0x01; - expected.0.regs.pc = dest; - } else { - expected.0.regs.pc = 0x103; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } -} diff --git a/feo3boy/src/gbz80core/mod.rs b/feo3boy/src/gbz80core/mod.rs index 4c6c76b..60c39bb 100644 --- a/feo3boy/src/gbz80core/mod.rs +++ b/feo3boy/src/gbz80core/mod.rs @@ -5,6 +5,9 @@ use crate::gbz80core::executor::ExecutorState; use crate::interrupts::{InterruptContext, InterruptFlags, Interrupts}; use crate::memdev::{MemContext, MemDevice, ReadCtx, RootExtend, RootMemDevice, WriteCtx}; +#[macro_use] +mod executor_testing; + pub mod direct_executor; pub mod direct_executor_v2; pub mod executor; @@ -179,9 +182,9 @@ pub trait CpuContext { // Utility implementations of CpuContext. ///////////////////////////////////////// -/// A Gb for testing which takes arbitrary memory using RootExtend. -#[derive(Default, Clone)] -pub(crate) struct TestGb { +/// A GameBoy that provides only a CPU and Memory. Intended primarily for testing +#[derive(Default, Clone, Debug, Eq, PartialEq)] +pub struct TestGb { pub cpu: Gbz80State, pub state: S, pub clock: SystemClock, @@ -190,7 +193,7 @@ pub(crate) struct TestGb { impl TestGb { /// Create a new `TestGb` with the given memory and executor state. - fn new(mem: M, state: S) -> Self { + pub fn new(mem: M, state: S) -> Self { Self { cpu: Gbz80State::default(), state, @@ -200,6 +203,36 @@ impl TestGb { } } +impl TestGb<[u8; N], S> { + /// Create a default `TestGb` with the given memory and executor state. + /// + /// Not all [u8; N] implement default, so this allows `::default` to work even in cases where it + /// otherwise wouldn't. In cases where default does exist, this shadows the trait method. + pub fn default() -> Self { + Self { + cpu: Gbz80State::default(), + state: S::default(), + clock: SystemClock::new(), + mem: [0u8; N], + } + } +} + +impl TestGb, S> { + /// Create a default `TestGb` with the given memory and executor state. + /// + /// Not all [u8; N] implement default, so this allows `::default` to work even in cases where it + /// otherwise wouldn't. In cases where default does exist, this shadows the trait method. + pub fn default() -> Self { + Self { + cpu: Gbz80State::default(), + state: S::default(), + clock: SystemClock::new(), + mem: Box::new([0u8; N]), + } + } +} + /// Allows a tuple of Gbz80State and any MemDevice to be used as an [`ExecutorContext`]. impl ExecutorContext for TestGb { type State = S; diff --git a/feo3boy/src/gbz80core/stepping_executor.rs b/feo3boy/src/gbz80core/stepping_executor.rs index 5a32cab..48427e4 100644 --- a/feo3boy/src/gbz80core/stepping_executor.rs +++ b/feo3boy/src/gbz80core/stepping_executor.rs @@ -40,1427 +40,8 @@ define_state_executor!( ], ); -/// Tests for the direct_executor -#[cfg(test)] -mod tests { - - use feo3boy_opcodes::gbz80types::Flags; - - use crate::gbz80core::executor::Executor; - use crate::gbz80core::{ExecutorContext, Gbz80State}; - use crate::memdev::RootMemDevice; - - use super::*; - - /// Convenience type for an ExecutorContext with a unit state. - trait Ctx: ExecutorContext {} - - impl> Ctx for E {} - - fn init() { - let _ = env_logger::builder().is_test(true).try_init(); - } - - fn run_single_instruction(ctx: &mut impl Ctx) { - SteppingExecutor::run_single_instruction(ctx) - } - - #[test] - fn test_loads_and_alu() { - init(); - - let mut cpu = Gbz80State::new(); - let mut testmem = [0u8; 0x10000]; - let mut state = SteppingExecutorState::default(); - - testmem[0] = 0x3e; - testmem[1] = 0x80; - testmem[2] = 0x06; - testmem[3] = 0x01; - testmem[4] = 0x80; - testmem[5] = 0x0e; - testmem[6] = 0x85; - testmem[7] = 0x81; - - run_single_instruction(&mut (&mut cpu, &mut testmem, &mut state)); - assert_eq!(cpu.regs.acc, 0x80, "{:?}", cpu.regs); - - run_single_instruction(&mut (&mut cpu, &mut testmem, &mut state)); - assert_eq!(cpu.regs.b, 0x01, "{:?}", cpu.regs); - - run_single_instruction(&mut (&mut cpu, &mut testmem, &mut state)); - assert_eq!(cpu.regs.acc, 0x81, "{:?}", cpu.regs); - - run_single_instruction(&mut (&mut cpu, &mut testmem, &mut state)); - assert_eq!(cpu.regs.c, 0x85, "{:?}", cpu.regs); - - run_single_instruction(&mut (&mut cpu, &mut testmem, &mut state)); - assert_eq!(cpu.regs.acc, 0x6, "{:?}", cpu.regs); - assert!(cpu.regs.flags.contains(Flags::CARRY), "{:?}", cpu.regs); - } - - #[test] - fn load8bit() { - init(); - - fn set_dest(inst: u8, ctx: &mut impl Ctx, val: u8) { - match inst { - 0x40..=0x47 => ctx.cpu_mut().regs.b = val, - 0x48..=0x4f => ctx.cpu_mut().regs.c = val, - 0x50..=0x57 => ctx.cpu_mut().regs.d = val, - 0x58..=0x5f => ctx.cpu_mut().regs.e = val, - 0x60..=0x67 => ctx.cpu_mut().regs.h = val, - 0x68..=0x6f => ctx.cpu_mut().regs.l = val, - 0x70..=0x77 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - 0x78..=0x7f => ctx.cpu_mut().regs.acc = val, - _ => unreachable!(), - } - } - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - 0x7 => ctx.cpu_mut().regs.acc = val, - _ => unreachable!(), - } - } - for inst in (0x40..=0x7f).filter(|&i| i != 0x76) { - for input in 0..=0xff { - let mut ctx = ( - Gbz80State::default(), - [0u8; 0x10000], - SteppingExecutorState::default(), - ); - // Start with HL at a high number so any single-byte change to it will never - // collide with the instruction at 0x00. - ctx.0.regs.set_hl(0xAA); - // This step might overwrite H or L changing the destination address of LD (HL),H - // or LD (HL),L, but that's ok because we use HL in set_dest. - set_source(inst, &mut ctx, input); - ctx.1[0] = inst; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - set_dest(inst, &mut expected, input); - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn load8bit_immediate() { - init(); - - fn set_dest(inst: u8, ctx: &mut impl Ctx, val: u8) { - match inst { - 0x06 => ctx.cpu_mut().regs.b = val, - 0x0e => ctx.cpu_mut().regs.c = val, - 0x16 => ctx.cpu_mut().regs.d = val, - 0x1e => ctx.cpu_mut().regs.e = val, - 0x26 => ctx.cpu_mut().regs.h = val, - 0x2e => ctx.cpu_mut().regs.l = val, - 0x36 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - 0x3e => ctx.cpu_mut().regs.acc = val, - _ => unreachable!(), - } - } - for inst in (0x00..=0x30) - .step_by(0x10) - .flat_map(|high| [high + 0x06, high + 0x0e]) - { - for input in 0..=0xff { - let mut ctx = ( - Gbz80State::default(), - [0u8; 3], - SteppingExecutorState::default(), - ); - // Start with HL at a high number so any single-byte change to it will never - // collide with the instruction at 0x00. - ctx.0.regs.set_hl(2); - ctx.1[0] = inst; - ctx.1[1] = input; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - set_dest(inst, &mut expected, input); - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn add8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0x80..=0x86 { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("add({:02x}) {},{}", inst, v1, v2); - let mut ctx = ( - Gbz80State::default(), - [0u8; 2], - SteppingExecutorState::default(), - ); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let (sum, wrapped) = v1.overflowing_add(v2); - expected.0.regs.acc = sum; - expected.0.regs.flags = Flags::empty(); - if sum == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) + (v2 & 0xf) > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - // A,u8 - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("add(c6) {},{}", v1, v2); - let mut ctx = ( - Gbz80State::default(), - [0u8; 2], - SteppingExecutorState::default(), - ); - ctx.1[0] = 0xc6; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let (sum, wrapped) = v1.overflowing_add(v2); - expected.0.regs.acc = sum; - expected.0.regs.flags = Flags::empty(); - if sum == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) + (v2 & 0xf) > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - // A,A - for v in 0..=0xff { - println!("add(87) {},{}", v, v); - let mut ctx = ( - Gbz80State::default(), - [0u8; 1], - SteppingExecutorState::default(), - ); - ctx.1[0] = 0x87; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let (sum, wrapped) = v.overflowing_add(v); - expected.0.regs.acc = sum; - expected.0.regs.flags = Flags::empty(); - if sum == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped { - expected.0.regs.flags |= Flags::CARRY; - } - if (v & 0xf) + (v & 0xf) > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn adc8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - let carry = (existing_flags & 0x10) >> 4; - for inst in 0x88..=0x8e { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("adc({:02x}) {},{} {}", inst, v1, v2, carry); - let mut ctx = ( - Gbz80State::default(), - [0u8; 2], - SteppingExecutorState::default(), - ); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let (sum1, wrapped1) = v1.overflowing_add(v2); - let (sum2, wrapped2) = sum1.overflowing_add(carry); - expected.0.regs.acc = sum2; - expected.0.regs.flags = Flags::empty(); - if sum2 == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped1 || wrapped2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) + (v2 & 0xf) + carry > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - // ADC A,u8 - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("adc(ce) {},{} {}", v1, v2, carry); - let mut ctx = ( - Gbz80State::default(), - [0u8; 2], - SteppingExecutorState::default(), - ); - ctx.1[0] = 0xce; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let (sum1, wrapped1) = v1.overflowing_add(v2); - let (sum2, wrapped2) = sum1.overflowing_add(carry); - expected.0.regs.acc = sum2; - expected.0.regs.flags = Flags::empty(); - if sum2 == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped1 || wrapped2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) + (v2 & 0xf) + carry > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - // ADC A,A - for v in 0..=0xff { - println!("adc(8f) {},{} {}", v, v, carry); - let mut ctx = ( - Gbz80State::default(), - [0u8; 1], - SteppingExecutorState::default(), - ); - ctx.1[0] = 0x8f; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - if carry == 1 { - ctx.0.regs.flags |= Flags::CARRY; - } - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let (sum1, wrapped1) = v.overflowing_add(v); - let (sum2, wrapped2) = sum1.overflowing_add(carry); - expected.0.regs.acc = sum2; - expected.0.regs.flags = Flags::empty(); - if sum2 == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if wrapped1 || wrapped2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v & 0xf) + (v & 0xf) + carry > 0xf { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn sub8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0x90..=0x96 { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("sub({:02x}) {},{}", inst, v1, v2); - let mut ctx = ( - Gbz80State::default(), - [0u8; 2], - SteppingExecutorState::default(), - ); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let diff = v1.wrapping_sub(v2); - expected.0.regs.acc = diff; - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("sub(d6) {},{}", v1, v2); - let mut ctx = ( - Gbz80State::default(), - [0u8; 2], - SteppingExecutorState::default(), - ); - ctx.1[0] = 0xd6; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let diff = v1.wrapping_sub(v2); - expected.0.regs.acc = diff; - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("sub(97) {},{}", v, v); - let mut ctx = ( - Gbz80State::default(), - [0u8; 1], - SteppingExecutorState::default(), - ); - ctx.1[0] = 0x97; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.acc = 0; - expected.0.regs.flags = Flags::ZERO | Flags::SUB; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn sbc8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - let carry = (existing_flags & 0x10) >> 4; - for inst in 0x98..=0x9e { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("sbc({:02x}) {},{} {}", inst, v1, v2, carry); - let mut ctx = ( - Gbz80State::default(), - [0u8; 2], - SteppingExecutorState::default(), - ); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let diff = v1.wrapping_sub(v2).wrapping_sub(carry); - expected.0.regs.acc = diff; - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 || (v1 == v2 && carry != 0) { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) || ((v1 & 0xf) == (v2 & 0xf) && carry != 0) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("sbc(de) {},{} {}", v1, v2, carry); - let mut ctx = ( - Gbz80State::default(), - [0u8; 2], - SteppingExecutorState::default(), - ); - ctx.1[0] = 0xde; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let diff = v1.wrapping_sub(v2).wrapping_sub(carry); - expected.0.regs.acc = diff; - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 || (v1 == v2 && carry != 0) { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) || ((v1 & 0xf) == (v2 & 0xf) && carry != 0) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("sbc(9f) {},{} {}", v, v, carry); - let mut ctx = ( - Gbz80State::default(), - [0u8; 1], - SteppingExecutorState::default(), - ); - ctx.1[0] = 0x9f; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - if carry == 0 { - expected.0.regs.acc = 0; - expected.0.regs.flags = Flags::ZERO | Flags::SUB; - } else { - expected.0.regs.acc = 0xff; - expected.0.regs.flags = Flags::SUB | Flags::CARRY | Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn and8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0xa0..=0xa6 { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("and({:02x}) {},{}", inst, v1, v2); - let mut ctx = ( - Gbz80State::default(), - [0u8; 2], - SteppingExecutorState::default(), - ); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let res = v1 & v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::HALFCARRY; - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("and(e6) {},{}", v1, v2); - let mut ctx = ( - Gbz80State::default(), - [0u8; 2], - SteppingExecutorState::default(), - ); - ctx.1[0] = 0xe6; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let res = v1 & v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::HALFCARRY; - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("and(a7) {},{}", v, v); - let mut ctx = ( - Gbz80State::default(), - [0u8; 1], - SteppingExecutorState::default(), - ); - ctx.1[0] = 0xa7; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.flags = Flags::HALFCARRY; - if v == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn xor8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0xa8..=0xae { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("xor({:02x}) {},{}", inst, v1, v2); - let mut ctx = ( - Gbz80State::default(), - [0u8; 2], - SteppingExecutorState::default(), - ); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let res = v1 ^ v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::empty(); - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("xor(ee) {},{}", v1, v2); - let mut ctx = ( - Gbz80State::default(), - [0u8; 2], - SteppingExecutorState::default(), - ); - ctx.1[0] = 0xee; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let res = v1 ^ v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::empty(); - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("and(af) {},{}", v, v); - let mut ctx = ( - Gbz80State::default(), - [0u8; 1], - SteppingExecutorState::default(), - ); - ctx.1[0] = 0xaf; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.acc = 0; - expected.0.regs.flags = Flags::ZERO; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn or8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0xb0..=0xb6 { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("or({:02x}) {},{}", inst, v1, v2); - let mut ctx = ( - Gbz80State::default(), - [0u8; 2], - SteppingExecutorState::default(), - ); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let res = v1 | v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::empty(); - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("or(f6) {},{}", v1, v2); - let mut ctx = ( - Gbz80State::default(), - [0u8; 2], - SteppingExecutorState::default(), - ); - ctx.1[0] = 0xf6; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let res = v1 | v2; - expected.0.regs.acc = res; - expected.0.regs.flags = Flags::empty(); - if res == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("or(b7) {},{}", v, v); - let mut ctx = ( - Gbz80State::default(), - [0u8; 1], - SteppingExecutorState::default(), - ); - ctx.1[0] = 0xb7; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.flags = Flags::empty(); - if v == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn cp8bit() { - init(); - - fn set_source(inst: u8, ctx: &mut impl Ctx, val: u8) { - match (inst & 0xf) % 0x8 { - 0x0 => ctx.cpu_mut().regs.b = val, - 0x1 => ctx.cpu_mut().regs.c = val, - 0x2 => ctx.cpu_mut().regs.d = val, - 0x3 => ctx.cpu_mut().regs.e = val, - 0x4 => ctx.cpu_mut().regs.h = val, - 0x5 => ctx.cpu_mut().regs.l = val, - 0x6 => { - let dest = ctx.cpu().regs.hl().into(); - ctx.mem_mut().write_byte(dest, val) - } - _ => unreachable!(), - } - } - for existing_flags in (0x00..=0xf0).step_by(0x10) { - for inst in 0xb8..=0xbe { - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("cp({:02x}) {},{}", inst, v1, v2); - let mut ctx = ( - Gbz80State::default(), - [0u8; 2], - SteppingExecutorState::default(), - ); - ctx.1[0] = inst; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.0.regs.set_hl(1); - set_source(inst, &mut ctx, v2); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - let diff = v1.wrapping_sub(v2); - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - for v1 in 0..=0xff { - for v2 in 0..=0xff { - println!("cp(fe) {},{}", v1, v2); - let mut ctx = ( - Gbz80State::default(), - [0u8; 2], - SteppingExecutorState::default(), - ); - ctx.1[0] = 0xfe; - ctx.0.regs.acc = v1; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - ctx.1[1] = v2; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 2; - let diff = v1.wrapping_sub(v2); - expected.0.regs.flags = Flags::SUB; - if diff == 0 { - expected.0.regs.flags |= Flags::ZERO; - } - if v1 < v2 { - expected.0.regs.flags |= Flags::CARRY; - } - if (v1 & 0xf) < (v2 & 0xf) { - expected.0.regs.flags |= Flags::HALFCARRY; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - for v in 0..=0xff { - println!("cp(bf) {},{}", v, v); - let mut ctx = ( - Gbz80State::default(), - [0u8; 1], - SteppingExecutorState::default(), - ); - ctx.1[0] = 0xbf; - ctx.0.regs.acc = v; - ctx.0.regs.flags = Flags::from_bits_truncate(existing_flags); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = 1; - expected.0.regs.flags = Flags::ZERO | Flags::SUB; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn relative_jumps() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for offset in -128i8..=127 { - let jnz = (flags & 0x80) == 0; - let jz = (flags & 0x80) != 0; - let jnc = (flags & 0x10) == 0; - let jc = (flags & 0x10) != 0; - let ops = [ - (0x18, true), - (0x20, jnz), - (0x28, jz), - (0x30, jnc), - (0x38, jc), - ]; - - for (opcode, should_jump) in ops { - println!("jr({:02x}) -> {} should: {}", opcode, offset, should_jump); - let mut ctx = ( - Gbz80State::default(), - [0u8; 0x200], - SteppingExecutorState::default(), - ); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0x100] = opcode; - ctx.0.regs.pc = 0x100; - ctx.1[0x101] = offset as u8; - - let expected = { - let mut expected = ctx.clone(); - if should_jump { - expected.0.regs.pc = (0x102 + offset as i32) as u16; - } else { - expected.0.regs.pc = 0x102; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - } - - #[test] - fn absolute_immediate_jumps() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for dest in 0x0000u16..=0xffff { - let jnz = (flags & 0x80) == 0; - let jz = (flags & 0x80) != 0; - let jnc = (flags & 0x10) == 0; - let jc = (flags & 0x10) != 0; - let ops = [ - (0xc3, true), - (0xc2, jnz), - (0xca, jz), - (0xd2, jnc), - (0xda, jc), - ]; - - for (opcode, should_jump) in ops { - println!("jp({:02x}) -> {:04x} should: {}", opcode, dest, should_jump); - let mut ctx = ( - Gbz80State::default(), - [0u8; 3], - SteppingExecutorState::default(), - ); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0] = opcode; - let [low, high] = dest.to_le_bytes(); - ctx.1[1] = low; - ctx.1[2] = high; - - let expected = { - let mut expected = ctx.clone(); - if should_jump { - expected.0.regs.pc = dest; - } else { - expected.0.regs.pc = 3; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - } - - #[test] - fn jump_hl() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for dest in 0x0000u16..=0xffff { - println!("jp(e9) -> {:04x}", dest); - let mut ctx = ( - Gbz80State::default(), - [0u8; 1], - SteppingExecutorState::default(), - ); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0] = 0xe9; - ctx.0.regs.set_hl(dest); - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = dest; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - - #[test] - fn rst() { - init(); - - let rsts = [ - (0xc7, 0x00), - (0xcf, 0x08), - (0xd7, 0x10), - (0xdf, 0x18), - (0xe7, 0x20), - (0xef, 0x28), - (0xf7, 0x30), - (0xff, 0x38), - ]; - - for (opcode, dest) in rsts { - let mut ctx = ( - Gbz80State::default(), - [0u8; 0x200], - SteppingExecutorState::default(), - ); - ctx.1[0x101] = opcode; - ctx.0.regs.pc = 0x101; - ctx.0.regs.sp = 0x200; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.pc = dest; - expected.0.regs.sp = 0x1fe; - // Pushed address should be 0x0102 (in little endian order), since that's - // the address of the instruction after RST. - expected.1[0x1fe] = 0x02; - expected.1[0x1ff] = 0x01; - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - - #[test] - fn returns() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for dest in 0x0000u16..=0xffff { - let jnz = (flags & 0x80) == 0; - let jz = (flags & 0x80) != 0; - let jnc = (flags & 0x10) == 0; - let jc = (flags & 0x10) != 0; - let ops = [ - (0xc9, true), - (0xc0, jnz), - (0xc8, jz), - (0xd0, jnc), - (0xd8, jc), - ]; - - for (opcode, should_jump) in ops { - println!( - "ret({:02x}) -> {:04x} should: {}", - opcode, dest, should_jump - ); - let mut ctx = ( - Gbz80State::default(), - [0u8; 0x200], - SteppingExecutorState::default(), - ); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0x100] = opcode; - ctx.0.regs.pc = 0x100; - let [low, high] = dest.to_le_bytes(); - ctx.1[0x1fe] = low; - ctx.1[0x1ff] = high; - ctx.0.regs.sp = 0x1fe; - - let expected = { - let mut expected = ctx.clone(); - if should_jump { - expected.0.regs.sp = 0x200; - expected.0.regs.pc = dest; - } else { - expected.0.regs.pc = 0x101; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - } - - #[test] - fn interrupt_return() { - init(); - - for dest in 0x0000u16..=0xffff { - println!("reti(0xd9) -> {:04x}", dest); - let mut ctx = ( - Gbz80State::default(), - [0u8; 0x200], - SteppingExecutorState::default(), - ); - ctx.1[0x100] = 0xd9; - ctx.0.regs.pc = 0x100; - let [low, high] = dest.to_le_bytes(); - ctx.1[0x1fe] = low; - ctx.1[0x1ff] = high; - ctx.0.regs.sp = 0x1fe; - - let expected = { - let mut expected = ctx.clone(); - expected.0.regs.sp = 0x200; - expected.0.regs.pc = dest; - expected.0.interrupt_master_enable.set(); - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - - #[test] - fn calls() { - init(); - - for flags in (0x00..=0xf0).step_by(0x10) { - for dest in 0x0000u16..=0xffff { - let jnz = (flags & 0x80) == 0; - let jz = (flags & 0x80) != 0; - let jnc = (flags & 0x10) == 0; - let jc = (flags & 0x10) != 0; - let ops = [ - (0xcd, true), - (0xc4, jnz), - (0xcc, jz), - (0xd4, jnc), - (0xdc, jc), - ]; - - for (opcode, should_jump) in ops { - println!( - "call({:02x}) -> {:04x} should: {}", - opcode, dest, should_jump - ); - let mut ctx = ( - Gbz80State::default(), - [0u8; 0x200], - SteppingExecutorState::default(), - ); - ctx.0.regs.flags = Flags::from_bits_truncate(flags); - ctx.1[0x100] = opcode; - ctx.0.regs.pc = 0x100; - let [low, high] = dest.to_le_bytes(); - ctx.1[0x101] = low; - ctx.1[0x102] = high; - ctx.0.regs.sp = 0x200; - - let expected = { - let mut expected = ctx.clone(); - if should_jump { - expected.0.regs.sp = 0x1fe; - // Return to 0x0103. - expected.1[0x1fe] = 0x03; - expected.1[0x1ff] = 0x01; - expected.0.regs.pc = dest; - } else { - expected.0.regs.pc = 0x103; - } - expected - }; - - run_single_instruction(&mut ctx); - assert_eq!(ctx, expected); - } - } - } - } +executor_tests! { + tests, + crate::gbz80core::stepping_executor::SteppingExecutor, + crate::gbz80core::TestGb, crate::gbz80core::stepping_executor::SteppingExecutorState> } diff --git a/feo3boy/src/memdev.rs b/feo3boy/src/memdev.rs index c1794d7..125c948 100644 --- a/feo3boy/src/memdev.rs +++ b/feo3boy/src/memdev.rs @@ -160,7 +160,7 @@ impl fmt::Display for RelativeAddr { /// /// In the future, it may also contain information such as the device that caused the read, which /// could be used to help implement certain "simultaneous" read operations. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct ReadCtx { /// Access time. The clock snapshot where the read is occurring. atime: ClockSnapshot, @@ -185,7 +185,7 @@ impl ReadCtx { /// /// In the future, it may also contain information such as the device that caused the write, which /// could be used to help implement certain "simultaneous" write operations. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct WriteCtx { /// Access time. The clock snapshot where the read is occurring. atime: ClockSnapshot, @@ -1153,6 +1153,22 @@ impl From<&[u8; N]> for AllRam { } } +impl Deref for AllRam { + type Target = [u8; 0x10000]; + + #[inline] + fn deref(&self) -> &Self::Target { + &*self.0 + } +} + +impl DerefMut for AllRam { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut *self.0 + } +} + /// MemoryDevice which configures the standard memory mapping of the real GameBoy. #[derive(Clone, Debug, Eq, PartialEq)] pub struct GbMmu { diff --git a/feo3boy/src/timer.rs b/feo3boy/src/timer.rs index 269557f..82563f6 100644 --- a/feo3boy/src/timer.rs +++ b/feo3boy/src/timer.rs @@ -76,6 +76,11 @@ impl TimerCounter { self.0.set_untracked(timer); overflow } + + /// Get the value of the timer counter. + pub fn value(&self) -> u8 { + *self.0 + } } #[derive(Default, Debug, Clone, Eq, PartialEq)] @@ -140,6 +145,21 @@ impl TimerRegs { pub fn divider(&self) -> &TimerDivider { &self.divider } + + /// Get the value of the timer counter register. + pub fn counter(&self) -> u8 { + self.timer_counter.value() + } + + /// Get the value of the timer mod register. + pub fn modulus(&self) -> u8 { + self.timer_mod + } + + /// Get the internal value of the timer control register (all bits, readable or otherwise). + pub fn control(&self) -> u8 { + self.timer_control.bits() + } } impl Default for TimerRegs { diff --git a/feo3boy/tests/asmtests.rs b/feo3boy/tests/asmtests.rs index 41c363e..6b87f8a 100644 --- a/feo3boy/tests/asmtests.rs +++ b/feo3boy/tests/asmtests.rs @@ -9,20 +9,20 @@ macro_rules! executor_tests { ($executor:ty, $modname:ident) => { mod $modname { use feo3boy::gbz80core::executor::Executor; - use feo3boy::gbz80core::Gbz80State; - use feo3boy::memdev::{AllRam, RootMemDevice}; + use feo3boy::gbz80core::TestGb; + use feo3boy::memdev::AllRam; use super::*; #[test] fn fibonacci() { - const OUTPUT: u16 = 0xC000; + const OUTPUT: usize = 0xC000; - let mut mem = AllRam::from(include_bytes!("fibonacci.bin")); - let mut cpu = Gbz80State::default(); - let mut state = <$executor as Executor>::State::default(); - while !cpu.halted { - <$executor>::run_single_instruction(&mut (&mut cpu, &mut mem, &mut state)); + let mem = AllRam::from(include_bytes!("fibonacci.bin")); + let state = <$executor as Executor>::State::default(); + let mut gb = TestGb::new(mem, state); + while !gb.cpu.halted { + <$executor>::run_single_instruction(&mut gb); } let (mut f1, mut f2) = (0, 1); @@ -37,19 +37,19 @@ macro_rules! executor_tests { if fib > u8::MAX as u32 { break; } - assert_eq!(mem.read_byte(OUTPUT + i as u16), fib as u8); + assert_eq!(gb.mem[OUTPUT + i], fib as u8); } } #[test] fn fibonacci16() { - const OUTPUT: u16 = 0xC000; + const OUTPUT: usize = 0xC000; - let mut mem = AllRam::from(include_bytes!("fibonacci16.bin")); - let mut cpu = Gbz80State::default(); - let mut state = <$executor as Executor>::State::default(); - while !cpu.halted { - <$executor>::run_single_instruction(&mut (&mut cpu, &mut mem, &mut state)); + let mem = AllRam::from(include_bytes!("fibonacci16.bin")); + let state = <$executor as Executor>::State::default(); + let mut gb = TestGb::new(mem, state); + while !gb.cpu.halted { + <$executor>::run_single_instruction(&mut gb); } let (mut f1, mut f2) = (0u32, 1); @@ -64,27 +64,28 @@ macro_rules! executor_tests { if fib > u16::MAX as u32 { break; } - let val: u16 = mem.read(OUTPUT + i as u16 * 2); + let val: u16 = + u16::from_le_bytes([gb.mem[OUTPUT + i * 2], gb.mem[OUTPUT + i * 2 + 1]]); assert_eq!(val, fib as u16); } } #[test] fn squares() { - const OUTPUT: u16 = 0xC000; + const OUTPUT: usize = 0xC000; - let mut mem = AllRam::from(include_bytes!("squares.bin")); - let mut cpu = Gbz80State::default(); - let mut state = <$executor as Executor>::State::default(); - while !cpu.halted { - <$executor>::run_single_instruction(&mut (&mut cpu, &mut mem, &mut state)); + let mem = AllRam::from(include_bytes!("squares.bin")); + let state = <$executor as Executor>::State::default(); + let mut gb = TestGb::new(mem, state); + while !gb.cpu.halted { + <$executor>::run_single_instruction(&mut gb); } for (i, square) in (1..).map(|x| x * x).enumerate() { if square > u8::MAX as u32 { break; } - assert_eq!(mem.read_byte(OUTPUT + i as u16), square as u8); + assert_eq!(gb.mem[OUTPUT + i], square as u8); } } @@ -140,7 +141,7 @@ mod test_roms { use feo3boy::gbz80core::executor::Executor; use feo3boy::gbz80core::microcode_executor::MicrocodeExecutor; use feo3boy::gbz80core::stepping_executor::SteppingExecutor; - use feo3boy::memdev::{BiosRom, Cartridge, RootMemDevice}; + use feo3boy::memdev::{BiosRom, Cartridge, ReadCtx, RootMemDevice}; use feo3boy_opcodes::opcode::Opcode; /// Checks if output matches the cpu_instrs output. Returns break when at the end of @@ -193,11 +194,12 @@ Passed all tests"; output_direct_v2.extend(gb_direct_v2.serial.stream.receive_bytes()); output_stepping.extend(gb_stepping.serial.stream.receive_bytes()); + let readctx = ReadCtx::new(gb_direct.clock().snapshot()); let ex_pc = gb_direct.cpustate.regs.pc; - let instr_direct = Opcode::decode(gb_direct.mmu.read_byte(ex_pc)); - let instr_microcode = Opcode::decode(gb_microcode.mmu.read_byte(ex_pc)); - let instr_direct_v2 = Opcode::decode(gb_direct_v2.mmu.read_byte(ex_pc)); - let instr_stepping = Opcode::decode(gb_stepping.mmu.read_byte(ex_pc)); + let instr_direct = Opcode::decode(gb_direct.mmu.read_byte(&readctx, ex_pc)); + let instr_microcode = Opcode::decode(gb_microcode.mmu.read_byte(&readctx, ex_pc)); + let instr_direct_v2 = Opcode::decode(gb_direct_v2.mmu.read_byte(&readctx, ex_pc)); + let instr_stepping = Opcode::decode(gb_stepping.mmu.read_byte(&readctx, ex_pc)); DirectExecutor::run_single_instruction(&mut *gb_direct); MicrocodeExecutor::run_single_instruction(&mut *gb_microcode); DirectExecutorV2::run_single_instruction(&mut *gb_direct_v2); diff --git a/web-debugger/src/derefs.rs b/web-debugger/src/derefs.rs index 5f9b09e..b6fee3a 100644 --- a/web-debugger/src/derefs.rs +++ b/web-debugger/src/derefs.rs @@ -1,5 +1,5 @@ use feo3boy::gb::Gb; -use feo3boy::memdev::RootMemDevice; +use feo3boy::memdev::{ReadCtx, RootMemDevice}; use yew::prelude::*; use crate::instrs::{Instr, InstrInfo}; @@ -22,13 +22,14 @@ pub struct ComputedDerefs { impl From<&Gb> for ComputedDerefs { fn from(gb: &Gb) -> Self { + let readctx = ReadCtx::new(gb.clock().snapshot()); Self { - pbc: gb.mmu.read(gb.cpustate.regs.bc()), - pde: gb.mmu.read(gb.cpustate.regs.de()), - phl: gb.mmu.read(gb.cpustate.regs.hl()), - pffc: gb.mmu.read(0xff00 + gb.cpustate.regs.c as u16), - psp: gb.mmu.read(gb.cpustate.regs.sp), - ppc: InstrInfo::fetch_at(&gb.mmu, gb.cpustate.regs.pc), + pbc: gb.mmu.read(&readctx, gb.cpustate.regs.bc()), + pde: gb.mmu.read(&readctx, gb.cpustate.regs.de()), + phl: gb.mmu.read(&readctx, gb.cpustate.regs.hl()), + pffc: gb.mmu.read(&readctx, 0xff00 + gb.cpustate.regs.c as u16), + psp: gb.mmu.read(&readctx, gb.cpustate.regs.sp), + ppc: InstrInfo::fetch_at(&gb.mmu, &readctx, gb.cpustate.regs.pc), } } } diff --git a/web-debugger/src/instrs.rs b/web-debugger/src/instrs.rs index 5b32526..50bd88d 100644 --- a/web-debugger/src/instrs.rs +++ b/web-debugger/src/instrs.rs @@ -1,4 +1,4 @@ -use feo3boy::memdev::RootMemDevice; +use feo3boy::memdev::{ReadCtx, RootMemDevice}; use feo3boy_opcodes::opcode::args::{ConditionCode, Operand16, Operand8}; use feo3boy_opcodes::opcode::{CBOpcode, Opcode}; use yew::prelude::*; @@ -14,10 +14,10 @@ pub struct InstrInfo { } impl InstrInfo { - pub fn fetch_at(mem: &impl RootMemDevice, addr: u16) -> Self { + pub fn fetch_at(mem: &impl RootMemDevice, ctx: &ReadCtx, addr: u16) -> Self { Self { - opcode: Opcode::decode(mem.read(addr)), - imm: mem.read(addr.wrapping_add(1)), + opcode: Opcode::decode(mem.read(ctx, addr)), + imm: mem.read(ctx, addr.wrapping_add(1)), } } diff --git a/web-debugger/src/main.rs b/web-debugger/src/main.rs index b39f6cf..83cd3b3 100644 --- a/web-debugger/src/main.rs +++ b/web-debugger/src/main.rs @@ -3,7 +3,7 @@ use std::num::NonZeroU32; use std::rc::Rc; use feo3boy::gb::Gb; -use feo3boy::memdev::{BiosRom, Cartridge}; +use feo3boy::memdev::{BiosRom, Cartridge, ReadCtx}; use gloo::storage::errors::StorageError; use gloo::storage::{LocalStorage, Storage}; use gloo::timers::callback::Timeout; @@ -368,6 +368,7 @@ impl Component for App { let delete_breakpoint = link.callback(|addr| Msg::DeleteBreakpoint { addr }); let toggle_breakpoint = link.callback(|(addr, new_enabled)| Msg::ToggleBreakpoint { addr, new_enabled }); + let readctx = ReadCtx::new(self.gb.clock().snapshot()); html! {
@@ -412,7 +413,7 @@ impl Component for App {
- +
diff --git a/web-debugger/src/memview/mod.rs b/web-debugger/src/memview/mod.rs index 522846e..791413f 100644 --- a/web-debugger/src/memview/mod.rs +++ b/web-debugger/src/memview/mod.rs @@ -3,7 +3,7 @@ use std::ops::RangeInclusive; use feo3boy::gb::Gb; use feo3boy::interrupts::InterruptFlags; -use feo3boy::memdev::{GbMmu, MemMappedIo}; +use feo3boy::memdev::{GbMmu, MemMappedIo, ReadCtx}; use owning_ref::RcRef; use yew::prelude::*; @@ -18,6 +18,7 @@ type GbMem = RcRef; #[derive(Properties, PartialEq)] pub struct Props { pub mem: GbMem, + pub readctx: ReadCtx, } pub enum Msg {} @@ -72,7 +73,7 @@ impl Component for Memview { {"Not Usable"} - +

{"\"Page Zero\" RAM"}

char { } } +type IoRef = RcRef; + #[derive(Properties, PartialEq)] pub struct ViewIoProps { - pub io: MemMappedIo, + pub io: IoRef, + pub readctx: ReadCtx, } #[function_component(ViewIo)] pub fn view_io(props: &ViewIoProps) -> Html { + let now = props.readctx.atime().elapsed_cycles(); html! {

{"Memory-Mapped IO"}

@@ -218,22 +223,22 @@ pub fn view_io(props: &ViewIoProps) -> Html {
{addr(0xff04)} {named("Divider")} - {hex16(props.io.timer_regs.divider)} + {hex16(props.io.timer_regs.divider().value(now))}
{addr(0xff05)} {named("Timer Accumulator")} - {hexbyte(props.io.timer_regs.timer)} + {hexbyte(props.io.timer_regs.counter())}
{addr(0xff06)} {named("Timer Modulo")} - {hexbyte(props.io.timer_regs.timer_mod)} + {hexbyte(props.io.timer_regs.modulus())}
{addr(0xff07)} {named("Timer Control")} - {hexbyte(props.io.timer_regs.timer_control.bits())} + {hexbyte(props.io.timer_regs.control())}
{addr_range(0xff08..=0xff0e)} From eb6794633442d9a7a3122e91407519c1402d94b2 Mon Sep 17 00:00:00 2001 From: Zachary Stewart Date: Sun, 28 May 2023 09:59:25 -0400 Subject: [PATCH 5/6] Get everything passing under the new system --- feo3boy/src/apu.rs | 29 ++++- feo3boy/src/gbz80core/direct_executor/mod.rs | 7 +- feo3boy/src/gbz80core/direct_executor_v2.rs | 7 +- feo3boy/src/gbz80core/executor_testing.rs | 101 ++++++++++++++++++ .../mod.rs => microcode_executor.rs} | 7 +- feo3boy/src/gbz80core/stepping_executor.rs | 7 +- feo3boy/src/macros.rs | 45 +++----- feo3boy/src/memdev.rs | 61 ++++++++++- feo3boy/src/timer.rs | 5 +- 9 files changed, 226 insertions(+), 43 deletions(-) rename feo3boy/src/gbz80core/{microcode_executor/mod.rs => microcode_executor.rs} (99%) diff --git a/feo3boy/src/apu.rs b/feo3boy/src/apu.rs index e95946d..fcee9fe 100644 --- a/feo3boy/src/apu.rs +++ b/feo3boy/src/apu.rs @@ -472,7 +472,7 @@ impl Envelope { /// This neatly explains why changing the wavelength value doesn't take effect /// immediately; the counter probably only resets on overflow or when the channel is /// triggered. -#[derive(Clone, Default, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Debug, Eq, PartialEq, Hash)] struct WavelengthCalculator { /// The latest wavelength setting. wavelength: u16, @@ -495,9 +495,31 @@ struct WavelengthCalculator initial_period: DCycle, } +impl Default + for WavelengthCalculator +{ + fn default() -> Self { + Self::new() + } +} + impl WavelengthCalculator { + /// Create a new WavelenghtCalculator with the wavelength set to zero. + const fn new() -> Self { + Self { + wavelength: 0, + wavelength_start_time: DCycle::ZERO, + initial_count: 0, + triggered_time: DCycle::ZERO, + // The period should always be reset when we trigger the wavelength for the first time, + // but starting with a non-zero value helps with certain unit tests and improves + // consistency. + initial_period: DCycle::new(2048 * PERIOD_MULTIPLIER), + } + } + /// Set the wavelength, recalcuating the start time and initial count. #[inline] fn set_wavelength(&mut self, now: DCycle, wavelength: u16) { @@ -571,6 +593,7 @@ impl self.initial_count = 0; self.wavelength_start_time = now; self.triggered_time = now; + self.initial_period = self.period(); } /// Compute the current duty step for the wavelength. @@ -597,7 +620,7 @@ impl /// Return true if we are in the first cycle. This is used by Channel 3 to determine if it /// should emit its old initial sample. fn is_first_duty_cycle(&self, now: DCycle) -> bool { - self.triggered_time + self.initial_period < now + now < self.triggered_time + self.initial_period } /// Get the duty step as-of 1 d-cycle ago, or none if we are in the first duty cycle. @@ -1000,7 +1023,7 @@ impl MemDevice for WavetableChannel { 0x04 => self.set_wavelength_high_and_control(ctx.atime().elapsed_fixed_cycles(), val), // Channel "samples" block. ApuRegs will remap this portion to the correct // offset. - 0x05 => self.set_sample_pair(addr, val), + 0x05..=0x15 => self.set_sample_pair(addr, val), }) } diff --git a/feo3boy/src/gbz80core/direct_executor/mod.rs b/feo3boy/src/gbz80core/direct_executor/mod.rs index 24e2774..6a33c2e 100644 --- a/feo3boy/src/gbz80core/direct_executor/mod.rs +++ b/feo3boy/src/gbz80core/direct_executor/mod.rs @@ -498,8 +498,11 @@ impl Run { } } +#[cfg(test)] +use crate::{gbz80core::TestGb, memdev::GrowRam}; + executor_tests! { tests, - crate::gbz80core::direct_executor::DirectExecutor, - crate::gbz80core::TestGb, ()> + DirectExecutor, + TestGb } diff --git a/feo3boy/src/gbz80core/direct_executor_v2.rs b/feo3boy/src/gbz80core/direct_executor_v2.rs index b247dce..40d4c9f 100644 --- a/feo3boy/src/gbz80core/direct_executor_v2.rs +++ b/feo3boy/src/gbz80core/direct_executor_v2.rs @@ -28,8 +28,11 @@ define_direct_executor!( ], ); +#[cfg(test)] +use crate::{gbz80core::TestGb, memdev::GrowRam}; + executor_tests! { tests, - crate::gbz80core::direct_executor_v2::DirectExecutorV2, - crate::gbz80core::TestGb, ()> + DirectExecutorV2, + TestGb } diff --git a/feo3boy/src/gbz80core/executor_testing.rs b/feo3boy/src/gbz80core/executor_testing.rs index 351e07f..c13f6fa 100644 --- a/feo3boy/src/gbz80core/executor_testing.rs +++ b/feo3boy/src/gbz80core/executor_testing.rs @@ -8,6 +8,9 @@ macro_rules! executor_tests { use crate::gbz80core::executor::Executor; + #[allow(unused)] + use super::*; + fn init() { let _ = env_logger::builder().is_test(true).try_init(); } @@ -103,6 +106,13 @@ macro_rules! executor_tests { let mut expected = gb.clone(); expected.cpu.regs.pc = 1; set_dest(inst, &mut expected, input); + expected.clock.advance1m(); + if (0x70..=0x77).contains(&inst) { + expected.clock.advance1m(); + } + if (inst & 0x0f) % 8 == 0x06 { + expected.clock.advance1m(); + } expected }; @@ -148,6 +158,11 @@ macro_rules! executor_tests { let mut expected = gb.clone(); expected.cpu.regs.pc = 2; set_dest(inst, &mut expected, input); + expected.clock.advance1m(); + expected.clock.advance1m(); + if inst == 0x36 { + expected.clock.advance1m(); + } expected }; @@ -203,6 +218,10 @@ macro_rules! executor_tests { if (v1 & 0xf) + (v2 & 0xf) > 0xf { expected.cpu.regs.flags |= Flags::HALFCARRY; } + expected.clock.advance1m(); + if (inst & 0x0f) % 8 == 0x06 { + expected.clock.advance1m(); + } expected }; @@ -236,6 +255,8 @@ macro_rules! executor_tests { if (v1 & 0xf) + (v2 & 0xf) > 0xf { expected.cpu.regs.flags |= Flags::HALFCARRY; } + expected.clock.advance1m(); + expected.clock.advance1m(); expected }; @@ -266,6 +287,7 @@ macro_rules! executor_tests { if (v & 0xf) + (v & 0xf) > 0xf { expected.cpu.regs.flags |= Flags::HALFCARRY; } + expected.clock.advance1m(); expected }; @@ -323,6 +345,10 @@ macro_rules! executor_tests { if (v1 & 0xf) + (v2 & 0xf) + carry > 0xf { expected.cpu.regs.flags |= Flags::HALFCARRY; } + expected.clock.advance1m(); + if (inst & 0x0f) % 8 == 0x06 { + expected.clock.advance1m(); + } expected }; @@ -357,6 +383,8 @@ macro_rules! executor_tests { if (v1 & 0xf) + (v2 & 0xf) + carry > 0xf { expected.cpu.regs.flags |= Flags::HALFCARRY; } + expected.clock.advance1m(); + expected.clock.advance1m(); expected }; @@ -391,6 +419,7 @@ macro_rules! executor_tests { if (v & 0xf) + (v & 0xf) + carry > 0xf { expected.cpu.regs.flags |= Flags::HALFCARRY; } + expected.clock.advance1m(); expected }; @@ -446,6 +475,10 @@ macro_rules! executor_tests { if (v1 & 0xf) < (v2 & 0xf) { expected.cpu.regs.flags |= Flags::HALFCARRY; } + expected.clock.advance1m(); + if (inst & 0x0f) % 8 == 0x06 { + expected.clock.advance1m(); + } expected }; @@ -478,6 +511,8 @@ macro_rules! executor_tests { if (v1 & 0xf) < (v2 & 0xf) { expected.cpu.regs.flags |= Flags::HALFCARRY; } + expected.clock.advance1m(); + expected.clock.advance1m(); expected }; @@ -497,6 +532,7 @@ macro_rules! executor_tests { expected.cpu.regs.pc = 1; expected.cpu.regs.acc = 0; expected.cpu.regs.flags = Flags::ZERO | Flags::SUB; + expected.clock.advance1m(); expected }; @@ -555,6 +591,10 @@ macro_rules! executor_tests { { expected.cpu.regs.flags |= Flags::HALFCARRY; } + expected.clock.advance1m(); + if (inst & 0x0f) % 8 == 0x06 { + expected.clock.advance1m(); + } expected }; @@ -589,6 +629,8 @@ macro_rules! executor_tests { { expected.cpu.regs.flags |= Flags::HALFCARRY; } + expected.clock.advance1m(); + expected.clock.advance1m(); expected }; @@ -614,6 +656,7 @@ macro_rules! executor_tests { expected.cpu.regs.flags = Flags::SUB | Flags::CARRY | Flags::HALFCARRY; } + expected.clock.advance1m(); expected }; @@ -663,6 +706,10 @@ macro_rules! executor_tests { if res == 0 { expected.cpu.regs.flags |= Flags::ZERO; } + expected.clock.advance1m(); + if (inst & 0x0f) % 8 == 0x06 { + expected.clock.advance1m(); + } expected }; @@ -689,6 +736,8 @@ macro_rules! executor_tests { if res == 0 { expected.cpu.regs.flags |= Flags::ZERO; } + expected.clock.advance1m(); + expected.clock.advance1m(); expected }; @@ -710,6 +759,7 @@ macro_rules! executor_tests { if v == 0 { expected.cpu.regs.flags |= Flags::ZERO; } + expected.clock.advance1m(); expected }; @@ -759,6 +809,10 @@ macro_rules! executor_tests { if res == 0 { expected.cpu.regs.flags |= Flags::ZERO; } + expected.clock.advance1m(); + if (inst & 0x0f) % 8 == 0x06 { + expected.clock.advance1m(); + } expected }; @@ -785,6 +839,8 @@ macro_rules! executor_tests { if res == 0 { expected.cpu.regs.flags |= Flags::ZERO; } + expected.clock.advance1m(); + expected.clock.advance1m(); expected }; @@ -804,6 +860,7 @@ macro_rules! executor_tests { expected.cpu.regs.pc = 1; expected.cpu.regs.acc = 0; expected.cpu.regs.flags = Flags::ZERO; + expected.clock.advance1m(); expected }; @@ -853,6 +910,10 @@ macro_rules! executor_tests { if res == 0 { expected.cpu.regs.flags |= Flags::ZERO; } + expected.clock.advance1m(); + if (inst & 0x0f) % 8 == 0x06 { + expected.clock.advance1m(); + } expected }; @@ -879,6 +940,8 @@ macro_rules! executor_tests { if res == 0 { expected.cpu.regs.flags |= Flags::ZERO; } + expected.clock.advance1m(); + expected.clock.advance1m(); expected }; @@ -900,6 +963,7 @@ macro_rules! executor_tests { if v == 0 { expected.cpu.regs.flags |= Flags::ZERO; } + expected.clock.advance1m(); expected }; @@ -954,6 +1018,10 @@ macro_rules! executor_tests { if (v1 & 0xf) < (v2 & 0xf) { expected.cpu.regs.flags |= Flags::HALFCARRY; } + expected.clock.advance1m(); + if (inst & 0x0f) % 8 == 0x06 { + expected.clock.advance1m(); + } expected }; @@ -985,6 +1053,8 @@ macro_rules! executor_tests { if (v1 & 0xf) < (v2 & 0xf) { expected.cpu.regs.flags |= Flags::HALFCARRY; } + expected.clock.advance1m(); + expected.clock.advance1m(); expected }; @@ -1003,6 +1073,7 @@ macro_rules! executor_tests { let mut expected = gb.clone(); expected.cpu.regs.pc = 1; expected.cpu.regs.flags = Flags::ZERO | Flags::SUB; + expected.clock.advance1m(); expected }; @@ -1040,7 +1111,10 @@ macro_rules! executor_tests { let expected = { let mut expected = gb.clone(); + expected.clock.advance1m(); + expected.clock.advance1m(); if should_jump { + expected.clock.advance1m(); expected.cpu.regs.pc = (0x102 + offset as i32) as u16; } else { expected.cpu.regs.pc = 0x102; @@ -1084,7 +1158,11 @@ macro_rules! executor_tests { let expected = { let mut expected = gb.clone(); + expected.clock.advance1m(); + expected.clock.advance1m(); + expected.clock.advance1m(); if should_jump { + expected.clock.advance1m(); expected.cpu.regs.pc = dest; } else { expected.cpu.regs.pc = 3; @@ -1114,6 +1192,7 @@ macro_rules! executor_tests { let expected = { let mut expected = gb.clone(); expected.cpu.regs.pc = dest; + expected.clock.advance1m(); expected }; @@ -1152,6 +1231,10 @@ macro_rules! executor_tests { // the address of the instruction after RST. expected.mem[0x1fe] = 0x02; expected.mem[0x1ff] = 0x01; + expected.clock.advance1m(); + expected.clock.advance1m(); + expected.clock.advance1m(); + expected.clock.advance1m(); expected }; @@ -1194,7 +1277,15 @@ macro_rules! executor_tests { let expected = { let mut expected = gb.clone(); + expected.clock.advance1m(); + expected.clock.advance1m(); if should_jump { + expected.clock.advance1m(); + expected.clock.advance1m(); + if opcode != 0xc9 { + // Unconditional return takes one fewer cycles. + expected.clock.advance1m(); + } expected.cpu.regs.sp = 0x200; expected.cpu.regs.pc = dest; } else { @@ -1229,6 +1320,10 @@ macro_rules! executor_tests { expected.cpu.regs.sp = 0x200; expected.cpu.regs.pc = dest; expected.cpu.interrupt_master_enable.set(); + expected.clock.advance1m(); + expected.clock.advance1m(); + expected.clock.advance1m(); + expected.clock.advance1m(); expected }; @@ -1271,7 +1366,13 @@ macro_rules! executor_tests { let expected = { let mut expected = gb.clone(); + expected.clock.advance1m(); + expected.clock.advance1m(); + expected.clock.advance1m(); if should_jump { + expected.clock.advance1m(); + expected.clock.advance1m(); + expected.clock.advance1m(); expected.cpu.regs.sp = 0x1fe; // Return to 0x0103. expected.mem[0x1fe] = 0x03; diff --git a/feo3boy/src/gbz80core/microcode_executor/mod.rs b/feo3boy/src/gbz80core/microcode_executor.rs similarity index 99% rename from feo3boy/src/gbz80core/microcode_executor/mod.rs rename to feo3boy/src/gbz80core/microcode_executor.rs index 7683f36..803890d 100644 --- a/feo3boy/src/gbz80core/microcode_executor/mod.rs +++ b/feo3boy/src/gbz80core/microcode_executor.rs @@ -727,8 +727,11 @@ ctx_applier!([In1], [Out1, Out2, Out3, Out4]); ctx_applier!([In1, In2], [Out1, Out2, Out3, Out4]); ctx_applier!([In1, In2, In3], [Out1, Out2, Out3, Out4]); +#[cfg(test)] +use crate::{gbz80core::TestGb, memdev::GrowRam}; + executor_tests! { tests, - crate::gbz80core::microcode_executor::MicrocodeExecutor, - crate::gbz80core::TestGb, crate::gbz80core::microcode_executor::MicrocodeState> + MicrocodeExecutor, + TestGb } diff --git a/feo3boy/src/gbz80core/stepping_executor.rs b/feo3boy/src/gbz80core/stepping_executor.rs index 48427e4..50b8767 100644 --- a/feo3boy/src/gbz80core/stepping_executor.rs +++ b/feo3boy/src/gbz80core/stepping_executor.rs @@ -40,8 +40,11 @@ define_state_executor!( ], ); +#[cfg(test)] +use crate::{gbz80core::TestGb, memdev::GrowRam}; + executor_tests! { tests, - crate::gbz80core::stepping_executor::SteppingExecutor, - crate::gbz80core::TestGb, crate::gbz80core::stepping_executor::SteppingExecutorState> + SteppingExecutor, + TestGb } diff --git a/feo3boy/src/macros.rs b/feo3boy/src/macros.rs index 751d17e..92bb202 100644 --- a/feo3boy/src/macros.rs +++ b/feo3boy/src/macros.rs @@ -98,36 +98,23 @@ macro_rules! memdev_fields { /// /// usage: /// ``` -/// # use feo3boy::memdev::{MemDevice, RelativeAddr}; -/// # use feo3boy::{dispatch_memdev_bytes, dispatch_memdev_byte}; +/// # use feo3boy::memdev::{MemDevice, RelativeAddr, ReadCtx, WriteCtx}; +/// # use feo3boy::{memdev_bytes_from_byte, dispatch_memdev_byte}; /// # struct MyType(u8, [u8; 3]); /// # impl MemDevice for MyType { /// # const LEN: usize = 5; -/// # fn read_bytes_relative(&self, addr: RelativeAddr, data: &mut [u8]) { -/// # dispatch_memdev_bytes!(MyType, addr, data, |addr, mut data| { -/// # 0x00 => data[0] = self.0.read_byte_relative(addr), -/// # 0x01..=0x04 => self.1.read_bytes_relative(addr, data), -/// # }) -/// # } -/// # -/// fn read_byte_relative(&self, addr: RelativeAddr) -> u8 { +/// # memdev_bytes_from_byte!(MyType); +/// fn read_byte_relative(&self, ctx: &ReadCtx, addr: RelativeAddr) -> u8 { /// dispatch_memdev_byte!(MyType, addr, |addr| { -/// 0x00 => self.0.read_byte_relative(addr), -/// 0x01..=0x04 => self.1.read_byte_relative(addr), +/// 0x00 => self.0.read_byte_relative(ctx, addr), +/// 0x01..=0x04 => self.1.read_byte_relative(ctx, addr), /// }) /// } -/// # -/// # fn write_bytes_relative(&mut self, addr: RelativeAddr, data: &[u8]) { -/// # dispatch_memdev_bytes!(MyType, addr, data, |addr, ref data| { -/// # 0x00 => self.0.write_byte_relative(addr, data[0]), -/// # 0x01..=0x04 => self.1.write_bytes_relative(addr, data), -/// # }) -/// # } /// -/// fn write_byte_relative(&mut self, addr: RelativeAddr, val: u8) { +/// fn write_byte_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, val: u8) { /// dispatch_memdev_byte!(MyType, addr, |addr| { -/// 0x00 => self.0.write_byte_relative(addr, val), -/// 0x01..=0x04 => self.1.write_byte_relative(addr, val), +/// 0x00 => self.0.write_byte_relative(ctx, addr, val), +/// 0x01..=0x04 => self.1.write_byte_relative(ctx, addr, val), /// }) /// } /// # } @@ -177,22 +164,22 @@ macro_rules! dispatch_memdev_byte { /// /// usage: /// ``` -/// # use feo3boy::memdev::{MemDevice, RelativeAddr}; +/// # use feo3boy::memdev::{MemDevice, RelativeAddr, ReadCtx, WriteCtx}; /// # use feo3boy::{dispatch_memdev_bytes, dispatch_memdev_byte}; /// # struct MyType(u8, [u8; 4]); /// # impl MemDevice for MyType { /// # const LEN: usize = 5; -/// fn read_bytes_relative(&self, addr: RelativeAddr, data: &mut [u8]) { +/// fn read_bytes_relative(&self, ctx: &ReadCtx, addr: RelativeAddr, data: &mut [u8]) { /// dispatch_memdev_bytes!(MyType, addr, data, |addr, mut data| { -/// 0x00 => data[0] = self.0.read_byte_relative(addr), -/// 0x01..=0x04 => self.1.read_bytes_relative(addr, data), +/// 0x00 => data[0] = self.0.read_byte_relative(ctx, addr), +/// 0x01..=0x04 => self.1.read_bytes_relative(ctx, addr, data), /// }) /// } /// -/// fn write_bytes_relative(&mut self, addr: RelativeAddr, data: &[u8]) { +/// fn write_bytes_relative(&mut self, ctx: &WriteCtx, addr: RelativeAddr, data: &[u8]) { /// dispatch_memdev_bytes!(MyType, addr, data, |addr, ref data| { -/// 0x00 => self.0.write_byte_relative(addr, data[0]), -/// 0x01..=0x04 => self.1.write_bytes_relative(addr, data), +/// 0x00 => self.0.write_byte_relative(ctx, addr, data[0]), +/// 0x01..=0x04 => self.1.write_bytes_relative(ctx, addr, data), /// }) /// } /// # } diff --git a/feo3boy/src/memdev.rs b/feo3boy/src/memdev.rs index 125c948..f360d6a 100644 --- a/feo3boy/src/memdev.rs +++ b/feo3boy/src/memdev.rs @@ -1,5 +1,5 @@ use std::convert::TryFrom; -use std::ops::{Deref, DerefMut, Range}; +use std::ops::{Deref, DerefMut, Range, Index, IndexMut}; use std::{fmt, mem, slice}; use log::trace; @@ -1169,6 +1169,65 @@ impl DerefMut for AllRam { } } +/// Simple memory device which works similarly to AllRam but allocates memory space only as-needed. +/// This is useful for small unit tests which want to do equality checks on the entire body of ram, +/// since those tests can be written to only use low parts of memory, and then the GrowRam will stay +/// fairly small, which makes the equality comparison much faster. +#[derive(Debug, Default, Clone, Eq)] +pub struct GrowRam(Vec); + +impl PartialEq for GrowRam { + fn eq(&self, other: &Self) -> bool { + if self.0.len() < other.0.len() { + let (overlap, extra) = other.0.split_at(self.0.len()); + self.0 == overlap && extra.iter().all(|&val| val == 0) + } else { + let (overlap, extra) = self.0.split_at(other.0.len()); + other.0 == overlap && extra.iter().all(|&val| val == 0) + } + } +} + +impl Index for GrowRam { + type Output = u8; + + fn index(&self, index: usize) -> &Self::Output { + assert!(index <= u16::MAX as usize, "address out of range"); + if index < self.0.len() { + &self.0[index] + } else { + &0 + } + } +} + +impl IndexMut for GrowRam { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + assert!(index <= u16::MAX as usize, "address out of range"); + if index >= self.0.len() { + self.0.resize(index + 1, 0); + } + &mut self.0[index] + } +} + +impl MemDevice for GrowRam { + const LEN: usize = 0x10000; + + fn read_byte_relative(&self, _ctx: &ReadCtx, addr: RelativeAddr) -> u8 { + // Since we have len 0x10000, we don't need to check addresses, they are always in range. + self[addr.index()] + } + + fn write_byte_relative(&mut self, _ctx: &WriteCtx, addr: RelativeAddr, data: u8) { + self[addr.index()] = data; + } + + memdev_bytes_from_byte!(GrowRam); +} + +impl RootMemDevice for GrowRam {} + /// MemoryDevice which configures the standard memory mapping of the real GameBoy. #[derive(Clone, Debug, Eq, PartialEq)] pub struct GbMmu { diff --git a/feo3boy/src/timer.rs b/feo3boy/src/timer.rs index 82563f6..2f5615c 100644 --- a/feo3boy/src/timer.rs +++ b/feo3boy/src/timer.rs @@ -104,7 +104,7 @@ impl TimerDivider { /// the divider was last reset. pub fn value(&self, now: MCycle) -> u16 { debug_assert!(self.0.changed_cycle() <= now); - (now - self.0.changed_cycle()).as_u64() as u16 + (now - self.0.changed_cycle()).as_tcycle().as_u64() as u16 } /// Get the clock cycle when the divider was last reset by the CPU. @@ -323,7 +323,8 @@ pub fn tick(ctx: &mut impl TimerContext) { ref divider, .. } = ctx.timer_regs(); - timer_enable && (divider.value(now) & selected_divider_bit) != 0 + let divider = divider.value(now); + timer_enable && (divider & selected_divider_bit) != 0 }; let old_divider_bit = mem::replace(&mut ctx.timer_mut().old_divider_bit, divider_bit); From b5290ae123fd0d4e182224db2a5ee33a38cfee87 Mon Sep 17 00:00:00 2001 From: Zachary Stewart Date: Sun, 28 May 2023 10:54:06 -0400 Subject: [PATCH 6/6] validate that the two LFSR implementations are equivalent --- feo3boy/src/apu.rs | 209 ++++++++++++++++++++++++++++----------------- 1 file changed, 133 insertions(+), 76 deletions(-) diff --git a/feo3boy/src/apu.rs b/feo3boy/src/apu.rs index fcee9fe..38344f7 100644 --- a/feo3boy/src/apu.rs +++ b/feo3boy/src/apu.rs @@ -1036,6 +1036,9 @@ pub trait LinearFeedbackShiftRegister: Clone + fmt::Debug + Default + PartialEq /// at the end of the operation. The output is always either 0 or 1. fn increment_by_and_get_output(&mut self, increments: u64) -> u16; + /// Get the current register state of the LFSR. + fn state(&self) -> u16; + /// Return true if the LFSR is using short mode. fn short(&self) -> bool; @@ -1092,6 +1095,10 @@ impl LinearFeedbackShiftRegister for DirectLinearFeedbackShiftRegister { self.value & 1 } + fn state(&self) -> u16 { + self.value + } + fn short(&self) -> bool { self.short_width } @@ -1157,94 +1164,91 @@ impl LinearFeedbackShiftRegister for TabledLinearFeedbackShiftRegister { } } + fn state(&self) -> u16 { + if self.short_width { + // Get the current value of the LFSR assuming we have shifted enough times that bits + // from 15 bit mode have all been shifted away. We will later reapply bits from 15 + // bit mode if necessary. + let base_value = if self.locked { + 0x7fff + } else { + lfsr::LFSR7_PATTERN[self.idx7()] + }; + + // Amount that saved_high_bits have been shifted down. + let amount_shifted = self.elapsed_increments - self.changed_increment; + // Figure out the actual value of the LFSR by reapplying the saved_high_bits. + if amount_shifted < 8 { + let reapplied = (self.saved_high_bits >> amount_shifted) & 0x7f80; + let reapply_mask = (0x7f80 >> amount_shifted) & 0x7f80; + (base_value & !reapply_mask) | (reapplied & reapply_mask) + } else { + base_value + } + } else { + if self.locked { + 0x7fff + } else { + lfsr::LFSR15_PATTERN[self.idx15()] + } + } + } + fn short(&self) -> bool { self.short_width } fn set_short(&mut self, short_width: bool) { if short_width != self.short_width { + let state = self.state(); if short_width { - if self.locked { - // If we were locked at 15 bits, we are still going to be locked at 7 bits, and - // don't need to change anything, but to be sure we convert back correctly - // later, we'll store the locked bit pattern in saved_high_bits and update the - // changed_increment. - self.changed_increment = self.elapsed_increments; - self.saved_high_bits = 0x7f80; - } else { - // Changing from wide to short, so we need to recalculate the index_offset such - // that the current value of `elapsed_increments` has the same value for the - // 7-bit secton as we have for the 15-bit section. - - // Get the current 15 bit value. - let value = lfsr::LFSR15_PATTERN[self.idx15()]; - - // Store information about the high bits from the 15 bit register so we can - // restore them later if needed. - self.changed_increment = self.elapsed_increments; - self.saved_high_bits = value & 0x7f80; - - // Get just the last bits of the 7-bit portion. This is the relevant part and is - // used in the reverse lookup table to figure out where we are in the 7-bit - // sequence. - let masked_lower_bits = value & 0x007f; - - match lfsr::LFSR7_REVERSE_LOOKUP.get(masked_lower_bits as usize) { - // If the index is out of bounds, the value is all 1 and will cause us to be - // locked in 7 bit mode. - None => { - self.locked = true; - } - Some(&idx7) => { - let pat_len = lfsr::LFSR7_PATTERN.len() as u64; - let idx7 = idx7 as u64; - // Find an index_offset which, when added to the current increment - // produces the correct current index into the LFSR7_PATTERN. - // - // Basically, we're trying to solve `index = elapsed + offset` but mod - // pat_len, which is `index - elapsed = offset`. Since `index` and - // `offset` are `% pat_len`, we adjust the formula to avoid underflow. - // - // Essentially, we add pat_len to idx7 to make sure it is large enough - // to subtract from, and then mod elapsed by `pat_len` to ensure it is - // smaller than `idx7 + pat_len`. This produces an index_offset which is - // somewhere in the range `1..(2 * pat_len)`. - // - // Since mod will be applied again when calculating the final index, we - // don't need to apply mod to index_offset again here. - self.index_offset = - idx7 + pat_len - (self.elapsed_increments % pat_len); - } - }; - } - } else { - // Changing from 7 to 15 can result in unlocking if done soon enough! - - // Get the current value of the LFSR assuming we have shifted enough times that bits - // from 15 bit mode have all been shifted away. We will later reapply bits from 15 - // bit mode if necessary. - let base_value = if self.locked { - 0x7fff - } else { - lfsr::LFSR7_PATTERN[self.idx7()] - }; - - // Amount that saved_high_bits have been shifted down. - let amount_shifted = self.elapsed_increments - self.changed_increment; - // Figure out the actual value of the LFSR by reapplying the saved_high_bits. - let value = if amount_shifted < 8 { - let reapplied = (self.saved_high_bits >> amount_shifted) & 0x7f8; - let reapply_mask = (0x7f8 >> amount_shifted) & 0x7f8; - (base_value & !reapply_mask) | (reapplied & reapply_mask) - } else { - base_value + // Store information about the high bits from the 15 bit register so we can + // restore them later if needed. + self.changed_increment = self.elapsed_increments; + self.saved_high_bits = state & 0x7f80; + + // Get just the last bits of the 7-bit portion. This is the relevant part and is + // used in the reverse lookup table to figure out where we are in the 7-bit + // sequence. + let masked_lower_bits = state & 0x007f; + + match lfsr::LFSR7_REVERSE_LOOKUP.get(masked_lower_bits as usize) { + // If the index is out of bounds, the value is all 1 and will cause us to be + // locked in 7 bit mode. + None => { + self.locked = true; + } + Some(&idx7) => { + // Changing from 15 to 7 can never unlock so we dont' need to reset the + // locked state here. + let pat_len = lfsr::LFSR7_PATTERN.len() as u64; + let idx7 = idx7 as u64; + // Find an index_offset which, when added to the current increment + // produces the correct current index into the LFSR7_PATTERN. + // + // Basically, we're trying to solve `index = elapsed + offset` but mod + // pat_len, which is `index - elapsed = offset`. Since `index` and + // `offset` are `% pat_len`, we adjust the formula to avoid underflow. + // + // Essentially, we add pat_len to idx7 to make sure it is large enough + // to subtract from, and then mod elapsed by `pat_len` to ensure it is + // smaller than `idx7 + pat_len`. This produces an index_offset which is + // somewhere in the range `1..(2 * pat_len)`. + // + // Since mod will be applied again when calculating the final index, we + // don't need to apply mod to index_offset again here. + self.index_offset = idx7 + pat_len - (self.elapsed_increments % pat_len); + debug_assert!(idx7 == self.idx7() as u64); + } }; - - match lfsr::LFSR15_REVERSE_LOOKUP.get(value as usize) { + } else { + match lfsr::LFSR15_REVERSE_LOOKUP.get(state as usize) { None => { self.locked = true; } Some(&idx15) => { + // Changing from 7 to 15 can unlock. + self.locked = false; let pat_len = lfsr::LFSR15_PATTERN.len() as u64; let idx15 = idx15 as u64; // Find an index_offset which, when added to the current increment @@ -1255,6 +1259,7 @@ impl LinearFeedbackShiftRegister for TabledLinearFeedbackShiftRegister { } } } + self.short_width = short_width; } } @@ -1746,3 +1751,55 @@ pub fn tick(ctx: &mut impl ApuContext) { } } } + +#[cfg(test)] +mod tests { + use super::*; + + use rand::distributions::Uniform; + use rand::{Rng, SeedableRng}; + use rand_pcg::Pcg64Mcg; + + #[test] + fn compare_lfsrs() { + let mut rng = + Pcg64Mcg::from_seed([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF]); + + let steps_before_change_dist = Uniform::new(0, 0x10000); + let steps_before_change_back_dist = Uniform::new(0, 0x100); + + for _ in 0..0x1000 { + let mut directlfsr = DirectLinearFeedbackShiftRegister::default(); + let mut tabledlfsr = TabledLinearFeedbackShiftRegister::default(); + assert_eq!(directlfsr.state(), tabledlfsr.state()); + + let steps_before_change = rng.sample(steps_before_change_dist); + let steps_before_change_back = rng.sample(steps_before_change_back_dist); + + for _ in 0..steps_before_change { + let directbit = directlfsr.increment_by_and_get_output(1); + let tabledbit = tabledlfsr.increment_by_and_get_output(1); + assert_eq!(directbit, tabledbit); + assert_eq!(directlfsr.state(), tabledlfsr.state()); + } + directlfsr.set_short(true); + tabledlfsr.set_short(true); + assert_eq!(directlfsr.state(), tabledlfsr.state()); + for _ in 0..steps_before_change_back { + let directbit = directlfsr.increment_by_and_get_output(1); + let tabledbit = tabledlfsr.increment_by_and_get_output(1); + assert_eq!(directbit, tabledbit); + assert_eq!(directlfsr.state(), tabledlfsr.state()); + } + directlfsr.set_short(false); + tabledlfsr.set_short(false); + assert_eq!(directlfsr.state(), tabledlfsr.state()); + for _ in 0..0x10000 { + let directbit = directlfsr.increment_by_and_get_output(1); + let tabledbit = tabledlfsr.increment_by_and_get_output(1); + assert_eq!(directbit, tabledbit); + assert_eq!(directlfsr.state(), tabledlfsr.state()); + } + } + } +}