diff --git a/battlesnake/src/bin/generate.rs b/battlesnake/src/bin/generate.rs new file mode 100644 index 0000000..1156810 --- /dev/null +++ b/battlesnake/src/bin/generate.rs @@ -0,0 +1,47 @@ +use std::{ + fs::File, + io::{self, BufWriter, Write}, +}; + +use az::Az; +use bytemuck::cast_slice; + +fn main() -> Result<(), io::Error> { + let mut chain = BufWriter::new(File::create("assets/chain")?); + + let mut indices = vec![0u32; (i32::MAX - 1).az::()]; + + let mut x = 1; + let mut count = 0; + + loop { + if count % 1_000_000 == 0 { + println!("current: {count:0>10}"); + } + let next = seedrand(x); + + indices[x.az::() - 1] = count; + chain.write_all(&next.to_ne_bytes())?; + + x = next; + count += 1; + if x == 1 { + break; + } + } + + File::create("assets/table")?.write_all(cast_slice(&indices))?; + + Ok(()) +} + +const fn seedrand(x: i32) -> i32 { + const A: i32 = 48_271; + const Q: i32 = 44_488; + const R: i32 = 3_399; + + let hi = x / Q; + let lo = x % Q; + let x = A * lo - R * hi; + if x < 0 { x + i32::MAX } else { x } +} diff --git a/battlesnake/src/bin/seed-cracker.rs b/battlesnake/src/bin/seed-cracker.rs new file mode 100644 index 0000000..8b9ce83 --- /dev/null +++ b/battlesnake/src/bin/seed-cracker.rs @@ -0,0 +1,1180 @@ +use std::{fs::File, path::PathBuf, sync::LazyLock}; + +use az::{Az, WrappingAs}; +use battlesnake::types::{Coord, Direction, wire::Request}; +use bytemuck::cast_slice; +use clap::Parser; +use flamer::flame; +use hashbrown::HashMap; +use log::{debug, error, info, trace, warn}; +use memmap2::{Mmap, MmapOptions}; + +type DynError = Box; + +#[derive(Debug, Parser)] +#[command(version, about)] +/// Try cracking the seed of a recorded game +struct Args { + /// The correct seed for validation + #[arg(long, short)] + seed: Option, + #[arg()] + /// The file to read the requests from + file: PathBuf, +} + +#[flame] +fn main() -> Result<(), DynError> { + env_logger::init(); + + let args = Args::parse(); + + let file = args.file; + debug!("Openeing {}", file.display()); + let file = File::open(file)?; + debug!("Parsing Json"); + let requests: Vec = serde_json::from_reader(file)?; + trace!("Got Requests: {requests:?}"); + + let _ = *CHAIN; + + let mut rand = SeedRand::new(0); + + if let Some(seed) = args.seed { + if validate(&mut rand, &requests, seed, true) { + println!("{seed} is the correct seed for this game"); + } else { + println!("{seed} is not the correct seed for this game"); + } + } else { + for seed in 1..i32::MAX { + if seed == 100 { + break; + } + if seed % 1_000_000 == 0 { + debug!("Checking {seed:>10}"); + } + if validate(&mut rand, &requests, i64::from(seed), false) { + println!("The correct seed is {seed}"); + break; + } + } + } + + flame::dump_html(File::create("flamegraph.html")?)?; + + Ok(()) +} + +#[allow(clippy::too_many_lines)] +#[flame] +fn validate(input_rand: &mut SeedRand, requests: &[Request], seed: i64, log: bool) -> bool { + if log { + info!("Checking starting positions"); + } + let request = &requests[0]; + let rand = get_rand(input_rand, seed, 0); + let mn = 1; + let md = (request.board.width - 1) / 2; + let mx = request.board.width - 2; + let mut corner_points = [ + Coord { x: mn, y: mn }, + Coord { x: mn, y: mx }, + Coord { x: mx, y: mn }, + Coord { x: mx, y: mx }, + ]; + let mut cardinal_points = [ + Coord { x: mn, y: md }, + Coord { x: md, y: mn }, + Coord { x: md, y: mx }, + Coord { x: mx, y: md }, + ]; + + rand.shuffle(corner_points.len(), |i, j| { + corner_points.swap(i, j); + }); + rand.shuffle(cardinal_points.len(), |i, j| { + cardinal_points.swap(i, j); + }); + + let mut start_points = Vec::with_capacity(corner_points.len() + cardinal_points.len()); + if rand.int_n(2) == 0 { + start_points.extend_from_slice(&corner_points); + start_points.extend_from_slice(&cardinal_points); + } else { + start_points.extend_from_slice(&cardinal_points); + start_points.extend_from_slice(&corner_points); + } + + for i in 0..request.board.snakes.len() { + if start_points[i] != request.board.snakes[i].head { + if log { + error!( + "Starting position is not the same: {} != {}", + start_points[i], request.board.snakes[i].head + ); + } + return false; + } + } + + if log { + info!("Checking food placement"); + } + for i in 1..requests.len() { + let previous_request = &requests[i - 1]; + let request = &requests[i]; + if previous_request.turn + 1 != request.turn { + if log { + warn!( + "nonconsecutive requests: {} + 1 != {}", + previous_request.turn, request.turn + ); + } + continue; + } + if log { + info!("Checking turn {}", request.turn); + } + let rand = get_rand(input_rand, seed, i64::from(request.turn - 1)); + let mut forged_request = request.clone(); + forged_request + .board + .food + .clone_from(&previous_request.board.food); + let heads: Vec<_> = forged_request + .board + .snakes + .iter() + .map(|snake| snake.head) + .collect(); + forged_request + .board + .food + .retain(|coord| !heads.contains(coord)); + let food_needed = check_food_needing_placement(rand, &forged_request); + if food_needed > 0 { + forged_request.board.food.append(&mut place_food_randomly( + rand, + food_needed, + &forged_request, + )); + } + if log { + debug!("snake head: {:?}", request.board.snakes[0].head); + } + if forged_request.board.food != request.board.food { + let forged: Vec<_> = forged_request + .board + .food + .iter() + .map(|coord| format!("{coord}")) + .collect(); + let forged = forged.join(", "); + let truth: Vec<_> = request + .board + .food + .iter() + .map(|coord| format!("{coord}")) + .collect(); + let truth = truth.join(", "); + if log { + error!( + "Food generation incorrect: \n\tgenerated: [{forged}]\n\t real: [{truth}]" + ); + } + return false; + } + } + + true +} + +#[must_use] +#[flame] +fn get_rand(rand: &mut SeedRand, seed: i64, turn: i64) -> &mut SeedRand { + rand.rand.seed(seed + turn); + rand +} + +#[flame] +fn check_food_needing_placement(rand: &mut impl Rand, request: &Request) -> usize { + let min_food = request.game.ruleset.settings.minimum_food as usize; + let food_spawn_chance = request.game.ruleset.settings.food_spawn_chance as usize; + let num_current_food = request.board.food.len(); + + if num_current_food < min_food { + return min_food - num_current_food; + } + if food_spawn_chance > 0 && (100 - rand.int_n(100)) < food_spawn_chance { + return 1; + } + + 0 +} + +#[flame] +fn place_food_randomly(rand: &mut impl Rand, n: usize, request: &Request) -> Vec { + let unoccupied_points = get_unoccupied_points(request); + place_food_randomly_at_positions(rand, n, unoccupied_points) +} + +#[flame] +fn place_food_randomly_at_positions( + rand: &mut impl Rand, + n: usize, + mut positions: Vec, +) -> Vec { + let n = n.min(positions.len()); + + rand.shuffle(positions.len(), |i, j| { + positions.swap(i, j); + }); + + let mut out = Vec::new(); + let mut i = 0; + while i < n { + out.push(positions[i]); + i += 1; + } + out +} + +#[flame] +fn get_unoccupied_points(request: &Request) -> Vec { + let possible_moves: Vec<_> = request + .board + .snakes + .iter() + .flat_map(|snake| { + enum_iterator::all::().map(|direction| snake.head.wrapping_apply(direction)) + }) + .collect(); + (0..request.board.width) + .flat_map(|x| (0..request.board.height).map(move |y| Coord { x, y })) + .filter(|tile| !request.board.food.contains(tile)) + .filter(|tile| { + !request + .board + .snakes + .iter() + .flat_map(|snake| snake.body.iter()) + .any(|snake| snake == tile) + }) + .filter(|tile| !possible_moves.contains(tile)) + .filter(|tile| !request.board.hazards.contains(tile)) + .collect() +} + +trait Rand { + /// Draw a random number in 0..n + #[allow(unused)] + fn int_n(&mut self, n: usize) -> usize; + /// Draw a random number in min..=max + #[allow(unused)] + fn range(&mut self, min: usize, max: usize) -> usize; + /// shuffle an array of n values using the swap function + #[allow(unused)] + fn shuffle(&mut self, n: usize, swap: impl FnMut(usize, usize)); +} + +#[derive(Debug, PartialEq, Eq, Clone)] +struct SeedRand { + rand: GoRand, +} + +impl SeedRand { + #[flame] + fn new(seed: i64) -> Self { + Self { + rand: GoRand::new(GoSource::new(seed)), + } + } +} + +impl Rand for SeedRand { + #[flame] + fn int_n(&mut self, n: usize) -> usize { + self.rand.int_n(n) + } + + #[flame] + fn range(&mut self, min: usize, max: usize) -> usize { + self.rand.int_n(max - min + 1) + min + } + + #[flame] + fn shuffle(&mut self, n: usize, swap: impl FnMut(usize, usize)) { + self.rand.shuffle(n, swap); + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +struct GoRand { + src: GoSource, +} + +impl GoRand { + #[flame] + const fn new(src: GoSource) -> Self { + Self { src } + } + + // Seed uses the provided seed value to initialize the generator to a deterministic state. + // Seed should not be called concurrently with any other [Rand] method. + #[flame] + fn seed(&mut self, seed: i64) { + self.src.seed(seed); + } + + /// returns a non-negative pseudo-random 63-bit integer + #[flame] + fn int63(&mut self) -> u64 { + self.src.int63() + } + + /// returns a non-negative pseudo-random 63-bit integer + #[flame] + fn int31(&mut self) -> u32 { + (self.src.int63() >> 32) as u32 + } + + /// Returns a non-negative pseudo-random number in the half-open interval [0,n). + /// + /// # Panics + /// if n <= 0 + #[flame] + fn int63_n(&mut self, n: u64) -> u64 { + assert!((n > 0), "invalid argument to int63_n"); + if n.is_power_of_two() { + // n is power of two, can mask + return self.int63() & (n - 1); + } + let max = (1 << 63) - 1 - (1 << 63) % n; + let mut v = self.int63(); + while v > max { + v = self.int63(); + } + v % n + } + + /// Returns a non-negative pseudo-random number in the half-open interval [0,n). + /// + /// # Panics + /// if n <= 0 + #[flame] + fn int31_n(&mut self, n: u32) -> u32 { + assert!((n > 0), "invalid argument to in32_n"); + if n.is_power_of_two() { + // n is power of two, can mask + return self.int31() & (n - 1); + } + let max = (1 << 31) - 1 - (1 << 31) % n; + let mut v = self.int31(); + while v > max { + v = self.int31(); + } + v % n + } + + #[flame] + fn uint32(&mut self) -> u32 { + (self.int63() >> 31).az() + } + + #[flame] + fn int31_n2(&mut self, n: u32) -> u32 { + let mut v = self.uint32(); + let mut prod = u64::from(v) * u64::from(n); + let mut low = prod.wrapping_as::(); + if low < n { + let thresh = ((-(n.az::())).wrapping_as::()) % n; + while low < thresh { + v = self.uint32(); + prod = u64::from(v) * u64::from(n); + low = prod.wrapping_as(); + } + } + (prod >> 32) as u32 + } + + /// Returns a non-negative pseudo-random number in the half-open interval [0,n). + /// + /// # Panics + /// if n <= 0 + #[flame] + fn int_n(&mut self, n: usize) -> usize { + assert!((n > 0), "invalid argument to int_n"); + if n < (1 << 31) { + self.int31_n(n.az()) as usize + } else { + self.int63_n(n as u64).az() + } + } + + #[allow(unused)] + #[flame] + fn int(&mut self) -> usize { + self.int63().az() + } + + // Shuffle pseudo-randomizes the order of elements. n is the number of elements. Shouffle + // panics if n < 0. swap swaps the elements with indices i and j. + #[flame] + fn shuffle(&mut self, n: usize, mut swap: impl FnMut(usize, usize)) { + // Fisher-Yates shuffle: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle + // Shuffle really ought not be called with n that doesn't fit in 32 bits. + // Not only will it take a very long time, but with 2³¹! possible permutations, + // there's no way that any PRNG can have a big enough internal state to + // generate even a minuscule percentage of the possible permutations. + // Nevertheless, the right API signature accepts an int n, so handle it as best we can. + let mut i = n.saturating_sub(1); + while i > (1 << 31) - 1 - 1 { + let j = self.int63_n((i + 1) as u64).az(); + swap(i, j); + i -= 1; + } + let mut i: u32 = i.az(); + while i > 0 { + let j = self.int31_n2(i + 1); + swap(i.az(), j.az()); + i -= 1; + } + } +} + +static CHAIN_RAW: LazyLock = LazyLock::new(|| unsafe { + MmapOptions::new() + .populate() + .map(&File::open("assets/chain").unwrap()) + .unwrap() +}); +static CHAIN: LazyLock<&[i32]> = LazyLock::new(|| cast_slice(&CHAIN_RAW)); +static TABLE_RAW: LazyLock = + LazyLock::new(|| unsafe { Mmap::map(&File::open("assets/table").unwrap()).unwrap() }); +static TABLE: LazyLock<&[u32]> = LazyLock::new(|| cast_slice(&TABLE_RAW)); + +#[derive(Debug, PartialEq, Eq, Clone)] +struct GoSource { + tap: u16, + feed: u16, + base_x_index: usize, + vec: HashMap, +} + +impl GoSource { + const LEN: u16 = 607; + const TAP: u16 = 273; + const MAX: u64 = 1 << 63; + const MASK: u64 = Self::MAX - 1; + + #[flame] + fn new(seed: i64) -> Self { + let seed: i32 = (seed % i64::from(i32::MAX)).az(); + let seed = match seed { + seed @ ..0 => seed + i32::MAX, + 0 => 89_482_311, + seed @ 1.. => seed, + }; + + let base_x_index = TABLE[seed.az::() - 1].az(); + debug_assert_eq!(CHAIN[base_x_index], seedrand(seed)); + + Self { + tap: 0, + feed: Self::LEN - Self::TAP, + base_x_index, + vec: HashMap::new(), + } + } +} + +impl GoSource { + #[flame] + fn int63(&mut self) -> u64 { + self.uint64() & Self::MASK + } + + #[allow(unused)] + #[flame] + fn seed(&mut self, seed: i64) { + let seed: i32 = (seed % i64::from(i32::MAX)).az(); + let seed = match seed { + seed @ ..0 => seed + i32::MAX, + 0 => 89_482_311, + seed @ 1.. => seed, + }; + + let base_x_index = TABLE[seed.az::() - 1].az(); + debug_assert_eq!(CHAIN[base_x_index], seedrand(seed)); + + self.tap = 0; + self.feed = Self::LEN - Self::TAP; + self.base_x_index = base_x_index; + self.vec.clear(); + } + + #[flame] + fn uint64(&mut self) -> u64 { + self.tap = self.tap.checked_sub(1).unwrap_or(Self::LEN - 1); + self.feed = self.feed.checked_sub(1).unwrap_or(Self::LEN - 1); + + let feed = self.feed; + let tap = self.tap; + + let feed = *self + .vec + .entry(feed) + .or_insert_with(|| seed_one(self.base_x_index, feed)); + let tap = *self + .vec + .entry(tap) + .or_insert_with(|| seed_one(self.base_x_index, tap)); + let x = feed.wrapping_add(tap); + self.vec.insert(self.feed, x); + x.wrapping_as() + } +} + +#[flame] +fn seed_one(base: usize, i: u16) -> i64 { + let i = usize::from(i); + let base = base + 20 + i * 3; + let len = CHAIN.len(); + + let (x1, x2, x3) = unsafe { + let x1 = *CHAIN.get_unchecked(base % len); + let x2 = *CHAIN.get_unchecked((base + 1) % len); + let x3 = *CHAIN.get_unchecked((base + 2) % len); + (x1, x2, x3) + }; + let u = i64::from(x1) << 40; + let u = u ^ i64::from(x2) << 20; + let u = u ^ i64::from(x3); + u ^ GoSource::COOKED[i] +} + +#[flame] +const fn seedrand(x: i32) -> i32 { + const A: i32 = 48_271; + const Q: i32 = 44_488; + const R: i32 = 3_399; + + let hi = x / Q; + let lo = x % Q; + let x = A * lo - R * hi; + if x < 0 { x + i32::MAX } else { x } +} + +impl GoSource { + #[allow(clippy::unreadable_literal)] + const COOKED: [i64; Self::LEN as usize] = [ + -4181792142133755926, + -4576982950128230565, + 1395769623340756751, + 5333664234075297259, + -6347679516498800754, + 9033628115061424579, + 7143218595135194537, + 4812947590706362721, + 7937252194349799378, + 5307299880338848416, + 8209348851763925077, + -7107630437535961764, + 4593015457530856296, + 8140875735541888011, + -5903942795589686782, + -603556388664454774, + -7496297993371156308, + 113108499721038619, + 4569519971459345583, + -4160538177779461077, + -6835753265595711384, + -6507240692498089696, + 6559392774825876886, + 7650093201692370310, + 7684323884043752161, + -8965504200858744418, + -2629915517445760644, + 271327514973697897, + -6433985589514657524, + 1065192797246149621, + 3344507881999356393, + -4763574095074709175, + 7465081662728599889, + 1014950805555097187, + -4773931307508785033, + -5742262670416273165, + 2418672789110888383, + 5796562887576294778, + 4484266064449540171, + 3738982361971787048, + -4699774852342421385, + 10530508058128498, + -589538253572429690, + -6598062107225984180, + 8660405965245884302, + 10162832508971942, + -2682657355892958417, + 7031802312784620857, + 6240911277345944669, + 831864355460801054, + -1218937899312622917, + 2116287251661052151, + 2202309800992166967, + 9161020366945053561, + 4069299552407763864, + 4936383537992622449, + 457351505131524928, + -8881176990926596454, + -6375600354038175299, + -7155351920868399290, + 4368649989588021065, + 887231587095185257, + -3659780529968199312, + -2407146836602825512, + 5616972787034086048, + -751562733459939242, + 1686575021641186857, + -5177887698780513806, + -4979215821652996885, + -1375154703071198421, + 5632136521049761902, + -8390088894796940536, + -193645528485698615, + -5979788902190688516, + -4907000935050298721, + -285522056888777828, + -2776431630044341707, + 1679342092332374735, + 6050638460742422078, + -2229851317345194226, + -1582494184340482199, + 5881353426285907985, + 812786550756860885, + 4541845584483343330, + -6497901820577766722, + 4980675660146853729, + -4012602956251539747, + -329088717864244987, + -2896929232104691526, + 1495812843684243920, + -2153620458055647789, + 7370257291860230865, + -2466442761497833547, + 4706794511633873654, + -1398851569026877145, + 8549875090542453214, + -9189721207376179652, + -7894453601103453165, + 7297902601803624459, + 1011190183918857495, + -6985347000036920864, + 5147159997473910359, + -8326859945294252826, + 2659470849286379941, + 6097729358393448602, + -7491646050550022124, + -5117116194870963097, + -896216826133240300, + -745860416168701406, + 5803876044675762232, + -787954255994554146, + -3234519180203704564, + -4507534739750823898, + -1657200065590290694, + 505808562678895611, + -4153273856159712438, + -8381261370078904295, + 572156825025677802, + 1791881013492340891, + 3393267094866038768, + -5444650186382539299, + 2352769483186201278, + -7930912453007408350, + -325464993179687389, + -3441562999710612272, + -6489413242825283295, + 5092019688680754699, + -227247482082248967, + 4234737173186232084, + 5027558287275472836, + 4635198586344772304, + -536033143587636457, + 5907508150730407386, + -8438615781380831356, + 972392927514829904, + -3801314342046600696, + -4064951393885491917, + -174840358296132583, + 2407211146698877100, + -1640089820333676239, + 3940796514530962282, + -5882197405809569433, + 3095313889586102949, + -1818050141166537098, + 5832080132947175283, + 7890064875145919662, + 8184139210799583195, + -8073512175445549678, + -7758774793014564506, + -4581724029666783935, + 3516491885471466898, + -8267083515063118116, + 6657089965014657519, + 5220884358887979358, + 1796677326474620641, + 5340761970648932916, + 1147977171614181568, + 5066037465548252321, + 2574765911837859848, + 1085848279845204775, + -5873264506986385449, + 6116438694366558490, + 2107701075971293812, + -7420077970933506541, + 2469478054175558874, + -1855128755834809824, + -5431463669011098282, + -9038325065738319171, + -6966276280341336160, + 7217693971077460129, + -8314322083775271549, + 7196649268545224266, + -3585711691453906209, + -5267827091426810625, + 8057528650917418961, + -5084103596553648165, + -2601445448341207749, + -7850010900052094367, + 6527366231383600011, + 3507654575162700890, + 9202058512774729859, + 1954818376891585542, + -2582991129724600103, + 8299563319178235687, + -5321504681635821435, + 7046310742295574065, + -2376176645520785576, + -7650733936335907755, + 8850422670118399721, + 3631909142291992901, + 5158881091950831288, + -6340413719511654215, + 4763258931815816403, + 6280052734341785344, + -4979582628649810958, + 2043464728020827976, + -2678071570832690343, + 4562580375758598164, + 5495451168795427352, + -7485059175264624713, + 553004618757816492, + 6895160632757959823, + -989748114590090637, + 7139506338801360852, + -672480814466784139, + 5535668688139305547, + 2430933853350256242, + -3821430778991574732, + -1063731997747047009, + -3065878205254005442, + 7632066283658143750, + 6308328381617103346, + 3681878764086140361, + 3289686137190109749, + 6587997200611086848, + 244714774258135476, + -5143583659437639708, + 8090302575944624335, + 2945117363431356361, + -8359047641006034763, + 3009039260312620700, + -793344576772241777, + 401084700045993341, + -1968749590416080887, + 4707864159563588614, + -3583123505891281857, + -3240864324164777915, + -5908273794572565703, + -3719524458082857382, + -5281400669679581926, + 8118566580304798074, + 3839261274019871296, + 7062410411742090847, + -8481991033874568140, + 6027994129690250817, + -6725542042704711878, + -2971981702428546974, + -7854441788951256975, + 8809096399316380241, + 6492004350391900708, + 2462145737463489636, + -8818543617934476634, + -5070345602623085213, + -8961586321599299868, + -3758656652254704451, + -8630661632476012791, + 6764129236657751224, + -709716318315418359, + -3403028373052861600, + -8838073512170985897, + -3999237033416576341, + -2920240395515973663, + -2073249475545404416, + 368107899140673753, + -6108185202296464250, + -6307735683270494757, + 4782583894627718279, + 6718292300699989587, + 8387085186914375220, + 3387513132024756289, + 4654329375432538231, + -292704475491394206, + -3848998599978456535, + 7623042350483453954, + 7725442901813263321, + 9186225467561587250, + -5132344747257272453, + -6865740430362196008, + 2530936820058611833, + 1636551876240043639, + -3658707362519810009, + 1452244145334316253, + -7161729655835084979, + -7943791770359481772, + 9108481583171221009, + -3200093350120725999, + 5007630032676973346, + 2153168792952589781, + 6720334534964750538, + -3181825545719981703, + 3433922409283786309, + 2285479922797300912, + 3110614940896576130, + -2856812446131932915, + -3804580617188639299, + 7163298419643543757, + 4891138053923696990, + 580618510277907015, + 1684034065251686769, + 4429514767357295841, + -8893025458299325803, + -8103734041042601133, + 7177515271653460134, + 4589042248470800257, + -1530083407795771245, + 143607045258444228, + 246994305896273627, + -8356954712051676521, + 6473547110565816071, + 3092379936208876896, + 2058427839513754051, + -4089587328327907870, + 8785882556301281247, + -3074039370013608197, + -637529855400303673, + 6137678347805511274, + -7152924852417805802, + 5708223427705576541, + -3223714144396531304, + 4358391411789012426, + 325123008708389849, + 6837621693887290924, + 4843721905315627004, + -3212720814705499393, + -3825019837890901156, + 4602025990114250980, + 1044646352569048800, + 9106614159853161675, + -8394115921626182539, + -4304087667751778808, + 2681532557646850893, + 3681559472488511871, + -3915372517896561773, + -2889241648411946534, + -6564663803938238204, + -8060058171802589521, + 581945337509520675, + 3648778920718647903, + -4799698790548231394, + -7602572252857820065, + 220828013409515943, + -1072987336855386047, + 4287360518296753003, + -4633371852008891965, + 5513660857261085186, + -2258542936462001533, + -8744380348503999773, + 8746140185685648781, + 228500091334420247, + 1356187007457302238, + 3019253992034194581, + 3152601605678500003, + -8793219284148773595, + 5559581553696971176, + 4916432985369275664, + -8559797105120221417, + -5802598197927043732, + 2868348622579915573, + -7224052902810357288, + -5894682518218493085, + 2587672709781371173, + -7706116723325376475, + 3092343956317362483, + -5561119517847711700, + 972445599196498113, + -1558506600978816441, + 1708913533482282562, + -2305554874185907314, + -6005743014309462908, + -6653329009633068701, + -483583197311151195, + 2488075924621352812, + -4529369641467339140, + -4663743555056261452, + 2997203966153298104, + 1282559373026354493, + 240113143146674385, + 8665713329246516443, + 628141331766346752, + -4651421219668005332, + -7750560848702540400, + 7596648026010355826, + -3132152619100351065, + 7834161864828164065, + 7103445518877254909, + 4390861237357459201, + -4780718172614204074, + -319889632007444440, + 622261699494173647, + -3186110786557562560, + -8718967088789066690, + -1948156510637662747, + -8212195255998774408, + -7028621931231314745, + 2623071828615234808, + -4066058308780939700, + -5484966924888173764, + -6683604512778046238, + -6756087640505506466, + 5256026990536851868, + 7841086888628396109, + 6640857538655893162, + -8021284697816458310, + -7109857044414059830, + -1689021141511844405, + -4298087301956291063, + -4077748265377282003, + -998231156719803476, + 2719520354384050532, + 9132346697815513771, + 4332154495710163773, + -2085582442760428892, + 6994721091344268833, + -2556143461985726874, + -8567931991128098309, + 59934747298466858, + -3098398008776739403, + -265597256199410390, + 2332206071942466437, + -7522315324568406181, + 3154897383618636503, + -7585605855467168281, + -6762850759087199275, + 197309393502684135, + -8579694182469508493, + 2543179307861934850, + 4350769010207485119, + -4468719947444108136, + -7207776534213261296, + -1224312577878317200, + 4287946071480840813, + 8362686366770308971, + 6486469209321732151, + -5605644191012979782, + -1669018511020473564, + 4450022655153542367, + -7618176296641240059, + -3896357471549267421, + -4596796223304447488, + -6531150016257070659, + -8982326463137525940, + -4125325062227681798, + -1306489741394045544, + -8338554946557245229, + 5329160409530630596, + 7790979528857726136, + 4955070238059373407, + -4304834761432101506, + -6215295852904371179, + 3007769226071157901, + -6753025801236972788, + 8928702772696731736, + 7856187920214445904, + -4748497451462800923, + 7900176660600710914, + -7082800908938549136, + -6797926979589575837, + -6737316883512927978, + 4186670094382025798, + 1883939007446035042, + -414705992779907823, + 3734134241178479257, + 4065968871360089196, + 6953124200385847784, + -7917685222115876751, + -7585632937840318161, + -5567246375906782599, + -5256612402221608788, + 3106378204088556331, + -2894472214076325998, + 4565385105440252958, + 1979884289539493806, + -6891578849933910383, + 3783206694208922581, + 8464961209802336085, + 2843963751609577687, + 3030678195484896323, + -4429654462759003204, + 4459239494808162889, + 402587895800087237, + 8057891408711167515, + 4541888170938985079, + 1042662272908816815, + -3666068979732206850, + 2647678726283249984, + 2144477441549833761, + -3417019821499388721, + -2105601033380872185, + 5916597177708541638, + -8760774321402454447, + 8833658097025758785, + 5970273481425315300, + 563813119381731307, + -6455022486202078793, + 1598828206250873866, + -4016978389451217698, + -2988328551145513985, + -6071154634840136312, + 8469693267274066490, + 125672920241807416, + -3912292412830714870, + -2559617104544284221, + -486523741806024092, + -4735332261862713930, + 5923302823487327109, + -9082480245771672572, + -1808429243461201518, + 7990420780896957397, + 4317817392807076702, + 3625184369705367340, + -6482649271566653105, + -3480272027152017464, + -3225473396345736649, + -368878695502291645, + -3981164001421868007, + -8522033136963788610, + 7609280429197514109, + 3020985755112334161, + -2572049329799262942, + 2635195723621160615, + 5144520864246028816, + -8188285521126945980, + 1567242097116389047, + 8172389260191636581, + -2885551685425483535, + -7060359469858316883, + -6480181133964513127, + -7317004403633452381, + 6011544915663598137, + 5932255307352610768, + 2241128460406315459, + -8327867140638080220, + 3094483003111372717, + 4583857460292963101, + 9079887171656594975, + -384082854924064405, + -3460631649611717935, + 4225072055348026230, + -7385151438465742745, + 3801620336801580414, + -399845416774701952, + -7446754431269675473, + 7899055018877642622, + 5421679761463003041, + 5521102963086275121, + -4975092593295409910, + 8735487530905098534, + -7462844945281082830, + -2080886987197029914, + -1000715163927557685, + -4253840471931071485, + -5828896094657903328, + 6424174453260338141, + 359248545074932887, + -5949720754023045210, + -2426265837057637212, + 3030918217665093212, + -9077771202237461772, + -3186796180789149575, + 740416251634527158, + -2142944401404840226, + 6951781370868335478, + 399922722363687927, + -8928469722407522623, + -1378421100515597285, + -8343051178220066766, + -3030716356046100229, + -8811767350470065420, + 9026808440365124461, + 6440783557497587732, + 4615674634722404292, + 539897290441580544, + 2096238225866883852, + 8751955639408182687, + -7316147128802486205, + 7381039757301768559, + 6157238513393239656, + -1473377804940618233, + 8629571604380892756, + 5280433031239081479, + 7101611890139813254, + 2479018537985767835, + 7169176924412769570, + -1281305539061572506, + -7865612307799218120, + 2278447439451174845, + 3625338785743880657, + 6477479539006708521, + 8976185375579272206, + -3712000482142939688, + 1326024180520890843, + 7537449876596048829, + 5464680203499696154, + 3189671183162196045, + 6346751753565857109, + -8982212049534145501, + -6127578587196093755, + -245039190118465649, + -6320577374581628592, + 7208698530190629697, + 7276901792339343736, + -7490986807540332668, + 4133292154170828382, + 2918308698224194548, + -7703910638917631350, + -3929437324238184044, + -4300543082831323144, + -6344160503358350167, + 5896236396443472108, + -758328221503023383, + -1894351639983151068, + -307900319840287220, + -6278469401177312761, + -2171292963361310674, + 8382142935188824023, + 9103922860780351547, + 4152330101494654406, + ]; +}