diff --git a/Cargo.lock b/Cargo.lock index a38bd0ab3a12f..f8cf79785253d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4273,9 +4273,11 @@ dependencies = [ "aptos-infallible", "aptos-logger", "aptos-sdk", + "aptos-types", "async-trait", "clap 4.4.14", "move-binary-format", + "move-core-types", "once_cell", "rand 0.7.3", "rand_core 0.5.1", diff --git a/aptos-move/aptos-gas-schedule/src/gas_schedule/transaction.rs b/aptos-move/aptos-gas-schedule/src/gas_schedule/transaction.rs index 5c51fa9ee314b..63a55d6c9253a 100644 --- a/aptos-move/aptos-gas-schedule/src/gas_schedule/transaction.rs +++ b/aptos-move/aptos-gas-schedule/src/gas_schedule/transaction.rs @@ -69,7 +69,7 @@ crate::gas_schedule::macros::define_gas_parameters!( [ max_transaction_size_in_bytes: NumBytes, "max_transaction_size_in_bytes", - 64 * 1024 + 90 * 1024 ], [ max_transaction_size_in_bytes_gov: NumBytes, diff --git a/crates/transaction-emitter-lib/src/emitter/mod.rs b/crates/transaction-emitter-lib/src/emitter/mod.rs index 78a140cc4f997..1045b2355b25f 100644 --- a/crates/transaction-emitter-lib/src/emitter/mod.rs +++ b/crates/transaction-emitter-lib/src/emitter/mod.rs @@ -44,7 +44,7 @@ use std::{ collections::{HashMap, HashSet}, str::FromStr, sync::{ - atomic::{AtomicBool, Ordering}, + atomic::{AtomicBool, AtomicU64, Ordering}, Arc, }, time::{Duration, Instant}, @@ -860,6 +860,7 @@ impl TxnEmitter { // traffic pattern to be correct. info!("Tx emitter creating workers"); let mut submission_workers = Vec::with_capacity(num_accounts); + let txn_counter: Arc = Arc::new(AtomicU64::new(0)); let all_clients = Arc::new(req.rest_clients.clone()); for index in 0..num_accounts { let main_client_index = index % all_clients.len(); @@ -867,7 +868,8 @@ impl TxnEmitter { let accounts = all_accounts.split_off(all_accounts.len() - 1); let stop = stop.clone(); let stats = Arc::clone(&stats); - let txn_generator = txn_generator_creator.create_transaction_generator(); + let txn_generator = + txn_generator_creator.create_transaction_generator(txn_counter.clone()); let worker_index = submission_workers.len(); let worker = SubmissionWorker::new( diff --git a/crates/transaction-emitter-lib/src/emitter/submission_worker.rs b/crates/transaction-emitter-lib/src/emitter/submission_worker.rs index be3dafccaeaf2..03163706f00b7 100644 --- a/crates/transaction-emitter-lib/src/emitter/submission_worker.rs +++ b/crates/transaction-emitter-lib/src/emitter/submission_worker.rs @@ -322,8 +322,12 @@ impl SubmissionWorker { accounts .into_iter() .flat_map(|account| { - self.txn_generator - .generate_transactions(account.borrow(), self.params.transactions_per_account) + self.txn_generator.generate_transactions( + account.borrow(), + self.params.transactions_per_account, + &Vec::new(), + false, + ) }) .collect() } diff --git a/crates/transaction-generator-lib/Cargo.toml b/crates/transaction-generator-lib/Cargo.toml index 9757a2b301fca..2f9a557303481 100644 --- a/crates/transaction-generator-lib/Cargo.toml +++ b/crates/transaction-generator-lib/Cargo.toml @@ -18,9 +18,11 @@ aptos-framework = { workspace = true } aptos-infallible = { workspace = true } aptos-logger = { workspace = true } aptos-sdk = { workspace = true } +aptos-types = { workspace = true } async-trait = { workspace = true } clap = { workspace = true } move-binary-format = { workspace = true } +move-core-types = { workspace = true } once_cell = { workspace = true } rand = { workspace = true } rand_core = { workspace = true } diff --git a/crates/transaction-generator-lib/src/account_generator.rs b/crates/transaction-generator-lib/src/account_generator.rs index 374f8c4562a39..a6821a1c7c1b9 100644 --- a/crates/transaction-generator-lib/src/account_generator.rs +++ b/crates/transaction-generator-lib/src/account_generator.rs @@ -9,7 +9,7 @@ use aptos_sdk::{ types::{transaction::SignedTransaction, LocalAccount}, }; use rand::{rngs::StdRng, SeedableRng}; -use std::sync::Arc; +use std::sync::{atomic::AtomicU64, Arc}; pub struct AccountGenerator { rng: StdRng, @@ -45,6 +45,8 @@ impl TransactionGenerator for AccountGenerator { &mut self, account: &LocalAccount, num_to_create: usize, + _history: &[String], + _market_maker: bool, ) -> Vec { let mut requests = Vec::with_capacity(num_to_create); let mut new_accounts = Vec::with_capacity(num_to_create); @@ -105,7 +107,10 @@ impl AccountGeneratorCreator { } impl TransactionGeneratorCreator for AccountGeneratorCreator { - fn create_transaction_generator(&self) -> Box { + fn create_transaction_generator( + &self, + _txn_counter: Arc, + ) -> Box { Box::new(AccountGenerator::new( StdRng::from_entropy(), self.txn_factory.clone(), diff --git a/crates/transaction-generator-lib/src/accounts_pool_wrapper.rs b/crates/transaction-generator-lib/src/accounts_pool_wrapper.rs index 1dee749e5f019..5be4cb41e9c73 100644 --- a/crates/transaction-generator-lib/src/accounts_pool_wrapper.rs +++ b/crates/transaction-generator-lib/src/accounts_pool_wrapper.rs @@ -1,10 +1,99 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -use crate::{ObjectPool, TransactionGenerator, TransactionGeneratorCreator}; -use aptos_sdk::types::{transaction::SignedTransaction, LocalAccount}; -use rand::{rngs::StdRng, SeedableRng}; -use std::sync::Arc; +use crate::{ObjectPool, BucketedAccountPool, TransactionGenerator, TransactionGeneratorCreator}; +use aptos_sdk::{ + types::{ + transaction::{SignedTransaction, TransactionPayload}, + LocalAccount, + }, + move_types::account_address::AccountAddress, +}; +use aptos_logger::info; +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::{ + collections::HashMap, + sync::{atomic::AtomicU64, Arc}, +}; + +pub struct BucketedAccountsPoolWrapperGenerator { + rng: StdRng, + generator: Box, + source_accounts_pool: Arc>, + destination_accounts_pool: Option>>, +} + +impl BucketedAccountsPoolWrapperGenerator { + pub fn new( + rng: StdRng, + generator: Box, + source_accounts_pool: Arc>, + destination_accounts_pool: Option>>, + ) -> Self { + Self { + rng, + generator, + source_accounts_pool, + destination_accounts_pool, + } + } +} + +impl TransactionGenerator for BucketedAccountsPoolWrapperGenerator { + fn generate_transactions( + &mut self, + _account: &LocalAccount, + num_to_create: usize, + history: &[String], + market_maker: bool, + ) -> Vec { + let mut accounts_to_use = + self.source_accounts_pool + .take_from_pool(num_to_create, true, &mut self.rng); + if accounts_to_use.is_empty() { + return Vec::new(); + } + let txns = accounts_to_use + .iter() + .flat_map(|account| self.generator.generate_transactions(account, num_to_create, history, market_maker)) + .collect(); + if let Some(destination_accounts_pool) = &self.destination_accounts_pool { + destination_accounts_pool.add_to_pool(accounts_to_use); + } + txns + } +} + +pub struct BucketedAccountsPoolWrapperCreator { + creator: Box, + source_accounts_pool: Arc>, + destination_accounts_pool: Option>>, +} + +impl BucketedAccountsPoolWrapperCreator { + pub fn new( + creator: Box, + source_accounts_pool: Arc>, + destination_accounts_pool: Option>>, + ) -> Self { + Self { + creator, + source_accounts_pool, + destination_accounts_pool, + } + } +} + +impl TransactionGeneratorCreator for BucketedAccountsPoolWrapperCreator { + fn create_transaction_generator(&self, txn_counter: Arc) -> Box { + Box::new(BucketedAccountsPoolWrapperGenerator::new( + StdRng::from_entropy(), + self.creator.create_transaction_generator(txn_counter), + self.source_accounts_pool.clone(), + self.destination_accounts_pool.clone(), + )) + } +} /// Wrapper that allows inner transaction generator to have unique accounts /// for all transactions (instead of having 5-20 transactions per account, as default) @@ -39,16 +128,21 @@ impl TransactionGenerator for AccountsPoolWrapperGenerator { &mut self, _account: &LocalAccount, num_to_create: usize, + _history: &[String], + _market_maker: bool, ) -> Vec { - let accounts_to_use = + let mut accounts_to_use = self.source_accounts_pool .take_from_pool(num_to_create, true, &mut self.rng); if accounts_to_use.is_empty() { return Vec::new(); } let txns = accounts_to_use - .iter() - .flat_map(|account| self.generator.generate_transactions(account, 1)) + .iter_mut() + .flat_map(|account| { + self.generator + .generate_transactions(account, 1, &Vec::new(), false) + }) .collect(); if let Some(destination_accounts_pool) = &self.destination_accounts_pool { destination_accounts_pool.add_to_pool(accounts_to_use); @@ -78,12 +172,453 @@ impl AccountsPoolWrapperCreator { } impl TransactionGeneratorCreator for AccountsPoolWrapperCreator { - fn create_transaction_generator(&self) -> Box { + fn create_transaction_generator( + &self, + txn_counter: Arc, + ) -> Box { Box::new(AccountsPoolWrapperGenerator::new( StdRng::from_entropy(), - self.creator.create_transaction_generator(), + self.creator.create_transaction_generator(txn_counter), + self.source_accounts_pool.clone(), + self.destination_accounts_pool.clone(), + )) + } +} + +pub struct AddHistoryWrapperGenerator { + rng: StdRng, + source_accounts_pool: Arc>, + destination_accounts_pool: Arc)>>, +} + +impl AddHistoryWrapperGenerator { + pub fn new( + rng: StdRng, + source_accounts_pool: Arc>, + destination_accounts_pool: Arc)>>, + ) -> Self { + Self { + rng, + source_accounts_pool, + destination_accounts_pool, + } + } +} + +impl TransactionGenerator for AddHistoryWrapperGenerator { + fn generate_transactions( + &mut self, + _account: &LocalAccount, + _num_to_create: usize, + _history: &[String], + _market_maker: bool, + ) -> Vec { + let length = self.source_accounts_pool.len(); + let all_source_accounts = + self.source_accounts_pool + .take_from_pool(length, true, &mut self.rng); + self.destination_accounts_pool.add_to_pool( + all_source_accounts + .into_iter() + .map(|account| (account, Vec::new())) + .collect(), + ); + vec![] + } +} + +pub struct AddHistoryWrapperCreator { + source_accounts_pool: Arc>, + destination_accounts_pool: Arc)>>, +} + +impl AddHistoryWrapperCreator { + pub fn new( + source_accounts_pool: Arc>, + destination_accounts_pool: Arc)>>, + ) -> Self { + Self { + source_accounts_pool, + destination_accounts_pool, + } + } +} + +impl TransactionGeneratorCreator for AddHistoryWrapperCreator { + fn create_transaction_generator( + &self, + _txn_counter: Arc, + ) -> Box { + Box::new(AddHistoryWrapperGenerator::new( + StdRng::from_entropy(), self.source_accounts_pool.clone(), self.destination_accounts_pool.clone(), )) } } + +pub struct MarketMakerPoolWrapperGenerator { + rng: StdRng, + generator: Box, + source_accounts_pool: Arc)>>, + market_makers: Vec<(LocalAccount, Vec)>, +} + +impl MarketMakerPoolWrapperGenerator { + pub fn new( + rng: StdRng, + generator: Box, + source_accounts_pool: Arc)>>, + ) -> Self { + Self { + rng, + generator, + source_accounts_pool, + market_makers: vec![], + } + } +} + +impl TransactionGenerator for MarketMakerPoolWrapperGenerator { + fn generate_transactions( + &mut self, + _account: &LocalAccount, + _num_to_create: usize, + _history: &[String], + _market_maker: bool, + ) -> Vec { + if self.market_makers.len() < 7 { + self.market_makers = + self.source_accounts_pool + .take_from_pool(7, true, &mut StdRng::from_entropy()); + } + if self.rng.gen_range(0, 1000) < 939 { + let rand = self.rng.gen_range(0, 1000); + if rand < 476 { + let txns = self.generator.generate_transactions( + &self.market_makers[0].0, + 1, + &self.market_makers[0].1, + true, + ); + for txn in txns.iter() { + if let TransactionPayload::EntryFunction(entry_function) = txn.payload() { + let function_name = entry_function.function().as_str(); + self.market_makers[0].1.push(function_name.to_string()); + } + } + return txns; + } else if rand < 733 { + let txns = self.generator.generate_transactions( + &self.market_makers[1].0, + 1, + &self.market_makers[1].1, + true, + ); + for txn in txns.iter() { + if let TransactionPayload::EntryFunction(entry_function) = txn.payload() { + let function_name = entry_function.function().as_str(); + self.market_makers[1].1.push(function_name.to_string()); + } + } + return txns; + } else if rand < 818 { + let txns = self.generator.generate_transactions( + &self.market_makers[2].0, + 1, + &self.market_makers[2].1, + true, + ); + for txn in txns.iter() { + if let TransactionPayload::EntryFunction(entry_function) = txn.payload() { + let function_name = entry_function.function().as_str(); + self.market_makers[2].1.push(function_name.to_string()); + } + } + return txns; + } else if rand < 871 { + let txns = self.generator.generate_transactions( + &self.market_makers[3].0, + 1, + &self.market_makers[3].1, + true, + ); + for txn in txns.iter() { + if let TransactionPayload::EntryFunction(entry_function) = txn.payload() { + let function_name = entry_function.function().as_str(); + self.market_makers[3].1.push(function_name.to_string()); + } + } + return txns; + } else if rand < 920 { + let txns = self.generator.generate_transactions( + &self.market_makers[4].0, + 1, + &self.market_makers[4].1, + true, + ); + for txn in txns.iter() { + if let TransactionPayload::EntryFunction(entry_function) = txn.payload() { + let function_name = entry_function.function().as_str(); + self.market_makers[4].1.push(function_name.to_string()); + } + } + return txns; + } else if rand < 962 { + let txns = self.generator.generate_transactions( + &self.market_makers[5].0, + 1, + &self.market_makers[5].1, + true, + ); + for txn in txns.iter() { + if let TransactionPayload::EntryFunction(entry_function) = txn.payload() { + let function_name = entry_function.function().as_str(); + self.market_makers[5].1.push(function_name.to_string()); + } + } + return txns; + } else if rand < 991 { + let txns = self.generator.generate_transactions( + &self.market_makers[6].0, + 1, + &self.market_makers[6].1, + true, + ); + for txn in txns.iter() { + if let TransactionPayload::EntryFunction(entry_function) = txn.payload() { + let function_name = entry_function.function().as_str(); + self.market_makers[6].1.push(function_name.to_string()); + } + } + return txns; + } + } + println!("MarketMakerPoolWrapperGenerator::generate_transactions: non-market-maker"); + let mut accounts_to_use = self + .source_accounts_pool + .take_from_pool(1, true, &mut self.rng); + if accounts_to_use.is_empty() { + println!( + "MarketMakerPoolWrapperGenerator::generate_transactions: accounts_to_use is empty" + ); + return Vec::new(); + } + let txns: Vec = accounts_to_use + .iter_mut() + .flat_map(|(account, history)| { + self.generator + .generate_transactions(account, 1, history, false) + }) + .collect(); + + let mut function_calls = HashMap::new(); + for txn in txns.iter() { + if let TransactionPayload::EntryFunction(entry_function) = txn.payload() { + let function_name = entry_function.function().as_str(); + function_calls.insert(txn.sender(), function_name.to_string()); + } + } + accounts_to_use = accounts_to_use + .into_iter() + .map(|(account, history)| { + if let Some(function_name) = function_calls.get(&account.address()) { + let mut history = history.clone(); + history.push(function_name.clone()); + (account, history) + } else { + (account, history) + } + }) + .collect(); + + self.source_accounts_pool.add_to_pool(accounts_to_use); + println!( + "MarketMakerPoolWrapperGenerator::source_pool_len {}", + self.source_accounts_pool.len() + ); + txns + } +} + +pub struct MarketMakerPoolWrapperCreator { + creator: Box, + source_accounts_pool: Arc)>>, +} + +impl MarketMakerPoolWrapperCreator { + pub fn new( + creator: Box, + source_accounts_pool: Arc)>>, + ) -> Self { + Self { + creator, + source_accounts_pool, + } + } +} + +impl TransactionGeneratorCreator for MarketMakerPoolWrapperCreator { + fn create_transaction_generator( + &self, + txn_counter: Arc, + ) -> Box { + Box::new(MarketMakerPoolWrapperGenerator::new( + StdRng::from_entropy(), + self.creator.create_transaction_generator(txn_counter), + self.source_accounts_pool.clone(), + )) + } +} + +pub struct ReuseAccountsPoolWrapperGenerator { + rng: StdRng, + generator: Box, + source_accounts_pool: Arc>, +} + +impl ReuseAccountsPoolWrapperGenerator { + pub fn new( + rng: StdRng, + generator: Box, + source_accounts_pool: Arc>, + ) -> Self { + Self { + rng, + generator, + source_accounts_pool, + } + } +} + +impl TransactionGenerator for ReuseAccountsPoolWrapperGenerator { + fn generate_transactions( + &mut self, + account: &LocalAccount, + num_to_create: usize, + _history: &[String], + _market_maker: bool, + ) -> Vec { + let mut accounts_to_use = + self.source_accounts_pool + .take_from_pool(account.address(), num_to_create, true, &mut self.rng); + if accounts_to_use.is_empty() { + info!("ReuseAccountsPoolWrapperGenerator::generate_transactions: accounts_to_use is empty num_to_create: {}", num_to_create); + return Vec::new(); + } + let txns = accounts_to_use + .iter_mut() + .flat_map(|account| { + self.generator + .generate_transactions(account, 1, &Vec::new(), false) + }) + .collect(); + + self.source_accounts_pool.add_to_bucket(account.address(), accounts_to_use); + txns + } +} + +pub struct ReuseAccountsPoolWrapperCreator { + creator: Box, + source_accounts_pool: Arc>, +} + +impl ReuseAccountsPoolWrapperCreator { + pub fn new( + creator: Box, + source_accounts_pool: Arc>, + ) -> Self { + Self { + creator, + source_accounts_pool, + } + } +} + +impl TransactionGeneratorCreator for ReuseAccountsPoolWrapperCreator { + fn create_transaction_generator( + &self, + txn_counter: Arc, + ) -> Box { + Box::new(ReuseAccountsPoolWrapperGenerator::new( + StdRng::from_entropy(), + self.creator.create_transaction_generator(txn_counter), + self.source_accounts_pool.clone(), + )) + } +} + +// pub struct BypassAccountsPoolWrapperGenerator { +// rng: StdRng, +// generator: Box, +// source_accounts_pool: Arc>, +// destination_accounts_pool: Option>>, +// } + +// impl BypassAccountsPoolWrapperGenerator { +// pub fn new( +// rng: StdRng, +// generator: Box, +// source_accounts_pool: Arc>, +// destination_accounts_pool: Option>>, +// ) -> Self { +// Self { +// rng, +// generator, +// source_accounts_pool, +// destination_accounts_pool, +// } +// } +// } + +// impl TransactionGenerator for BypassAccountsPoolWrapperGenerator { +// fn generate_transactions( +// &mut self, +// account: &LocalAccount, +// _num_to_create: usize, +// ) -> Vec { +// let accounts_to_use = +// self.source_accounts_pool +// .take_from_pool(self.source_accounts_pool.len(), true, &mut self.rng); +// if accounts_to_use.is_empty() { +// return Vec::new(); +// } +// if let Some(destination_accounts_pool) = &self.destination_accounts_pool { +// destination_accounts_pool.add_to_pool(accounts_to_use); +// } +// let txns = self.generator.generate_transactions(account, 1); +// txns +// } +// } + +// pub struct BypassAccountsPoolWrapperCreator { +// creator: Box, +// source_accounts_pool: Arc>, +// destination_accounts_pool: Option>>, +// } + +// impl BypassAccountsPoolWrapperCreator { +// pub fn new( +// creator: Box, +// source_accounts_pool: Arc>, +// destination_accounts_pool: Option>>, +// ) -> Self { +// Self { +// creator, +// source_accounts_pool, +// destination_accounts_pool, +// } +// } +// } + +// impl TransactionGeneratorCreator for BypassAccountsPoolWrapperCreator { +// fn create_transaction_generator(&self, txn_counter: Arc) -> Box { +// Box::new(BypassAccountsPoolWrapperGenerator::new( +// StdRng::from_entropy(), +// self.creator.create_transaction_generator(txn_counter), +// self.source_accounts_pool.clone(), +// self.destination_accounts_pool.clone(), +// )) +// } +// } diff --git a/crates/transaction-generator-lib/src/args.rs b/crates/transaction-generator-lib/src/args.rs index b19bf2651e8b4..f5924b14c69ac 100644 --- a/crates/transaction-generator-lib/src/args.rs +++ b/crates/transaction-generator-lib/src/args.rs @@ -36,6 +36,7 @@ pub enum TransactionTypeArg { ModifyGlobalResourceAggV2, ModifyGlobalFlagAggV2, ModifyGlobalBoundedAggV2, + EmitEvents, ModifyGlobalMilestoneAggV2, // Complex EntryPoints CreateObjects10, @@ -68,6 +69,29 @@ pub enum TransactionTypeArg { VectorPicture40, VectorPictureRead40, SmartTablePicture30KWith200Change, + EconiaBasic1Market, + EconiaMixed1Market, + EconiaMixed10Market, + EconiaMixed100Market, + EconiaMarket1Market, + EconiaMarket10Market, + EconiaMarket100Market, + EconiaBasic1MarketNoPublish, + EconiaMixed1MarketNoPublish, + EconiaMixed10MarketNoPublish, + EconiaMixed100MarketNoPublish, + EconiaBasic1MarketReuseAccounts, + EconiaMixed1MarketReuseAccounts, + EconiaMixed10MarketReuseAccounts, + EconiaMixed100MarketReuseAccounts, + EconiaMarket1MarketReuseAccounts, + EconiaMarket10MarketReuseAccounts, + EconiaMarket100MarketReuseAccounts, + EconiaMarket1MarketReuseAccountsNoPublish, + EconiaMarket10MarketReuseAccountsNoPublish, + EconiaMarket100MarketReuseAccountsNoPublish, + EconiaReal, + EconiaRealNoPublish, SmartTablePicture1MWith256Change, SmartTablePicture1BWith256Change, SmartTablePicture1MWith1KChangeExceedsLimit, @@ -150,6 +174,11 @@ impl TransactionTypeArg { data_length: Some(10 * 1024), }) }, + TransactionTypeArg::EmitEvents => TransactionType::CallCustomModules { + entry_point: EntryPoints::EmitEvents { count: 10000 }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, TransactionTypeArg::ModifyGlobalResource => call_custom_module(EntryPoints::IncGlobal), TransactionTypeArg::ModifyGlobalResourceAggV2 => { call_custom_module(EntryPoints::IncGlobalAggV2) @@ -318,6 +347,348 @@ impl TransactionTypeArg { num_points_per_txn: 1024, }) }, + TransactionTypeArg::EconiaBasic1Market => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Basic, + num_markets: 1, + reuse_accounts_for_orders: false, + publish_packages: true, + }, + // progress_type: WorkflowProgress::MoveByPhases, + progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaMixed1Market => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Mixed, + num_markets: 1, + reuse_accounts_for_orders: false, + publish_packages: true, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaMixed10Market => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Mixed, + num_markets: 10, + reuse_accounts_for_orders: false, + publish_packages: true, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaMixed100Market => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Mixed, + num_markets: 100, + reuse_accounts_for_orders: false, + publish_packages: true, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaMarket1Market => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Market, + num_markets: 100, + reuse_accounts_for_orders: false, + publish_packages: true, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaMarket10Market => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Market, + num_markets: 100, + reuse_accounts_for_orders: false, + publish_packages: true, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaMarket100Market => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Market, + num_markets: 100, + reuse_accounts_for_orders: false, + publish_packages: true, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaBasic1MarketNoPublish => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Basic, + num_markets: 1, + reuse_accounts_for_orders: false, + publish_packages: false, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaMixed1MarketNoPublish => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Mixed, + num_markets: 1, + reuse_accounts_for_orders: false, + publish_packages: false, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaMixed10MarketNoPublish => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Mixed, + num_markets: 10, + reuse_accounts_for_orders: false, + publish_packages: false, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaMixed100MarketNoPublish => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Mixed, + num_markets: 100, + reuse_accounts_for_orders: false, + publish_packages: false, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaBasic1MarketReuseAccounts => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Basic, + num_markets: 1, + reuse_accounts_for_orders: true, + publish_packages: true, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaMixed1MarketReuseAccounts => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Mixed, + num_markets: 1, + reuse_accounts_for_orders: true, + publish_packages: true, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaMixed10MarketReuseAccounts => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Mixed, + num_markets: 10, + reuse_accounts_for_orders: true, + publish_packages: true, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaMixed100MarketReuseAccounts => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Mixed, + num_markets: 100, + reuse_accounts_for_orders: true, + publish_packages: true, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaReal => TransactionType::Workflow { + workflow_kind: WorkflowKind::EconiaReal { + num_users: 600000, + publish_packages: true, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaRealNoPublish => TransactionType::Workflow { + workflow_kind: WorkflowKind::EconiaReal { + num_users: 600000, + publish_packages: false, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaMarket1MarketReuseAccounts => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Market, + num_markets: 1, + reuse_accounts_for_orders: true, + publish_packages: true, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaMarket10MarketReuseAccounts => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Market, + num_markets: 10, + reuse_accounts_for_orders: true, + publish_packages: true, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaMarket100MarketReuseAccounts => TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Market, + num_markets: 100, + reuse_accounts_for_orders: true, + publish_packages: true, + }, + // progress_type: WorkflowProgress::MoveByPhases, +progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + }, + TransactionTypeArg::EconiaMarket1MarketReuseAccountsNoPublish => { + TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 600000, + flow_type: crate::EconiaFlowType::Market, + num_markets: 1, + reuse_accounts_for_orders: true, + publish_packages: false, + }, + progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + } + }, + TransactionTypeArg::EconiaMarket10MarketReuseAccountsNoPublish => { + TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 200000, + flow_type: crate::EconiaFlowType::Market, + num_markets: 10, + reuse_accounts_for_orders: true, + publish_packages: false, + }, + progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + } + }, + TransactionTypeArg::EconiaMarket100MarketReuseAccountsNoPublish => { + TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 2000000, + flow_type: crate::EconiaFlowType::Market, + num_markets: 100, + reuse_accounts_for_orders: true, + publish_packages: false, + }, + progress_type: WorkflowProgress::WhenDone { + delay_between_stages_s: 60, + }, + num_modules: module_working_set_size, + use_account_pool: sender_use_account_pool, + } + }, TransactionTypeArg::DeserializeU256 => call_custom_module(EntryPoints::DeserializeU256), TransactionTypeArg::SimpleScript => call_custom_module(EntryPoints::SimpleScript), } diff --git a/crates/transaction-generator-lib/src/batch_transfer.rs b/crates/transaction-generator-lib/src/batch_transfer.rs index ff58614f9c908..242b3fb5f5fea 100644 --- a/crates/transaction-generator-lib/src/batch_transfer.rs +++ b/crates/transaction-generator-lib/src/batch_transfer.rs @@ -8,7 +8,7 @@ use aptos_sdk::{ types::{transaction::SignedTransaction, LocalAccount}, }; use rand::{rngs::StdRng, SeedableRng}; -use std::sync::Arc; +use std::sync::{atomic::AtomicU64, Arc}; pub struct BatchTransferTransactionGenerator { rng: StdRng, @@ -41,6 +41,8 @@ impl TransactionGenerator for BatchTransferTransactionGenerator { &mut self, account: &LocalAccount, num_to_create: usize, + _history: &[String], + _market_maker: bool, ) -> Vec { let mut requests = Vec::with_capacity(num_to_create); for _ in 0..num_to_create { @@ -85,7 +87,10 @@ impl BatchTransferTransactionGeneratorCreator { } impl TransactionGeneratorCreator for BatchTransferTransactionGeneratorCreator { - fn create_transaction_generator(&self) -> Box { + fn create_transaction_generator( + &self, + _txn_counter: Arc, + ) -> Box { Box::new(BatchTransferTransactionGenerator::new( StdRng::from_entropy(), self.batch_size, diff --git a/crates/transaction-generator-lib/src/bounded_batch_wrapper.rs b/crates/transaction-generator-lib/src/bounded_batch_wrapper.rs index c3b79dc621578..5e21f2cd34065 100644 --- a/crates/transaction-generator-lib/src/bounded_batch_wrapper.rs +++ b/crates/transaction-generator-lib/src/bounded_batch_wrapper.rs @@ -3,6 +3,7 @@ use crate::{TransactionGenerator, TransactionGeneratorCreator}; use aptos_sdk::types::{transaction::SignedTransaction, LocalAccount}; +use std::sync::{atomic::AtomicU64, Arc}; struct BoundedBatchWrapperTransactionGenerator { batch_size: usize, @@ -14,9 +15,15 @@ impl TransactionGenerator for BoundedBatchWrapperTransactionGenerator { &mut self, account: &LocalAccount, num_to_create: usize, + _history: &[String], + _market_maker: bool, ) -> Vec { - self.generator - .generate_transactions(account, num_to_create.min(self.batch_size)) + self.generator.generate_transactions( + account, + num_to_create.min(self.batch_size), + &Vec::new(), + false, + ) } } @@ -36,10 +43,15 @@ impl BoundedBatchWrapperTransactionGeneratorCreator { } impl TransactionGeneratorCreator for BoundedBatchWrapperTransactionGeneratorCreator { - fn create_transaction_generator(&self) -> Box { + fn create_transaction_generator( + &self, + txn_counter: Arc, + ) -> Box { Box::new(BoundedBatchWrapperTransactionGenerator { batch_size: self.batch_size, - generator: self.generator_creator.create_transaction_generator(), + generator: self + .generator_creator + .create_transaction_generator(txn_counter), }) } } diff --git a/crates/transaction-generator-lib/src/call_custom_modules.rs b/crates/transaction-generator-lib/src/call_custom_modules.rs index c2d06a9da2057..4c4b5a961bdc5 100644 --- a/crates/transaction-generator-lib/src/call_custom_modules.rs +++ b/crates/transaction-generator-lib/src/call_custom_modules.rs @@ -12,8 +12,11 @@ use aptos_sdk::{ types::{transaction::SignedTransaction, LocalAccount}, }; use async_trait::async_trait; -use rand::{rngs::StdRng, seq::SliceRandom, SeedableRng}; -use std::{borrow::Borrow, sync::Arc}; +use rand::{rngs::StdRng, SeedableRng}; +use std::{ + borrow::Borrow, + sync::{atomic::AtomicU64, Arc}, +}; // Fn + Send + Sync, as it will be called from multiple threads simultaneously // if you need any coordination, use Arc> fields @@ -23,7 +26,10 @@ pub type TransactionGeneratorWorker = dyn Fn( &LocalAccount, &TransactionFactory, &mut StdRng, - ) -> Option + u64, + &[String], + bool, + ) -> Vec + Send + Sync; @@ -48,7 +54,7 @@ pub trait UserModuleTransactionGenerator: Sync + Send { /// If you need to send any additional initialization transactions /// (like creating and funding additional accounts), you can do so by using provided txn_executor async fn create_generator_fn( - &self, + &mut self, root_account: &dyn RootAccountHandle, txn_factory: &TransactionFactory, txn_executor: &dyn ReliableTransactionSubmitter, @@ -61,6 +67,7 @@ pub struct CustomModulesDelegationGenerator { txn_factory: TransactionFactory, packages: Arc>, txn_generator: Arc, + txn_counter: Arc, } impl CustomModulesDelegationGenerator { @@ -69,12 +76,14 @@ impl CustomModulesDelegationGenerator { txn_factory: TransactionFactory, packages: Arc>, txn_generator: Arc, + txn_counter: Arc, ) -> Self { Self { rng, txn_factory, packages, txn_generator, + txn_counter, } } } @@ -83,24 +92,28 @@ impl TransactionGenerator for CustomModulesDelegationGenerator { fn generate_transactions( &mut self, account: &LocalAccount, - num_to_create: usize, + _num_to_create: usize, + history: &[String], + market_maker: bool, ) -> Vec { - let mut requests = Vec::with_capacity(num_to_create); + let mut all_requests = Vec::with_capacity(self.packages.len()); - for _ in 0..num_to_create { - let (package, publisher) = self.packages.choose(&mut self.rng).unwrap(); - let request = (self.txn_generator)( + for (package, publisher) in self.packages.iter() { + self.txn_counter + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let mut requests = (self.txn_generator)( account, package, publisher, &self.txn_factory, &mut self.rng, + self.txn_counter.load(std::sync::atomic::Ordering::Relaxed), + history, + market_maker, ); - if let Some(request) = request { - requests.push(request); - } + all_requests.append(&mut requests); } - requests + all_requests } } @@ -132,6 +145,7 @@ impl CustomModulesDelegationGeneratorCreator { num_modules: usize, package_name: &str, workload: &mut dyn UserModuleTransactionGenerator, + publish_packages: bool, ) -> Self { let mut packages = Self::publish_package( init_txn_factory.clone(), @@ -140,6 +154,7 @@ impl CustomModulesDelegationGeneratorCreator { num_modules, package_name, None, + publish_packages, ) .await; let worker = Self::create_worker( @@ -201,8 +216,12 @@ impl CustomModulesDelegationGeneratorCreator { num_modules: usize, package_name: &str, publisher_balance: Option, + publish_packages: bool, ) -> Vec<(Package, LocalAccount)> { - let mut rng = StdRng::from_entropy(); + let mut rng = StdRng::from_seed([ + 51, 25, 26, 63, 61, 14, 94, 14, 11, 18, 13, 43, 16, 13, 54, 72, 147, 18, 102, 97, 45, + 71, 35, 58, 28, 59, 8, 1, 5, 23, 98, 90, + ]); let mut requests_create = Vec::with_capacity(num_modules); let mut requests_publish = Vec::with_capacity(num_modules); let mut package_handler = PackageHandler::new(package_name); @@ -249,30 +268,38 @@ impl CustomModulesDelegationGeneratorCreator { .unwrap(); } - info!( - "Publishing {} copies of package {}", - requests_publish.len(), - package_name - ); - txn_executor - .execute_transactions(&requests_publish) - .await - .inspect_err(|err| error!("Failed to publish test package {}: {:#}", package_name, err)) - .unwrap(); + if publish_packages { + info!( + "Publishing {} copies of package {}", + requests_publish.len(), + package_name + ); + txn_executor + .execute_transactions(&requests_publish) + .await + .inspect_err(|err| { + error!("Failed to publish test package {}: {:#}", package_name, err) + }) + .unwrap(); - info!("Done publishing {} packages", packages.len()); + info!("Done publishing {} packages", packages.len()); + } packages } } impl TransactionGeneratorCreator for CustomModulesDelegationGeneratorCreator { - fn create_transaction_generator(&self) -> Box { + fn create_transaction_generator( + &self, + txn_counter: Arc, + ) -> Box { Box::new(CustomModulesDelegationGenerator::new( StdRng::from_entropy(), self.txn_factory.clone(), self.packages.clone(), self.txn_generator.clone(), + txn_counter, )) } } diff --git a/crates/transaction-generator-lib/src/econia_order_generator.rs b/crates/transaction-generator-lib/src/econia_order_generator.rs new file mode 100644 index 0000000000000..4939134551baa --- /dev/null +++ b/crates/transaction-generator-lib/src/econia_order_generator.rs @@ -0,0 +1,858 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 +use crate::{ + call_custom_modules::{TransactionGeneratorWorker, UserModuleTransactionGenerator}, + publishing::publish_util::Package, + ReliableTransactionSubmitter, RootAccountHandle, +}; +use aptos_sdk::{ + bcs, + move_types::account_address::AccountAddress, + transaction_builder::TransactionFactory, + types::{transaction::SignedTransaction, LocalAccount}, +}; +use aptos_types::transaction::{EntryFunction, TransactionPayload}; +use async_trait::async_trait; +use move_core_types::{ + ident_str, + identifier::Identifier, + language_storage::{ModuleId, StructTag, TypeTag}, +}; +use rand::{rngs::StdRng, Rng}; +use std::sync::Arc; +const BASE_COIN_TYPES: [&str; 104] = [ + "AAC", "ABC", "ACC", "ADC", "AEC", "AFC", "AGC", "AHC", "AIC", "AJC", "AKC", "ALC", "AMC", + "ANC", "AOC", "APC", "AQC", "ARC", "ASC", "ATC", "AUC", "AVC", "AWC", "AXC", "AYC", "AZC", + "BAC", "BBC", "BCC", "BDC", "BEC", "BFC", "BGC", "BHC", "BIC", "BJC", "BKC", "BLC", "BMC", + "BNC", "BOC", "BPC", "BQC", "BRC", "BSC", "BTC", "BUC", "BVC", "BWC", "BXC", "BYC", "BZC", + "CAC", "CBC", "CCC", "CDC", "CEC", "CFC", "CGC", "CHC", "CIC", "CJC", "CKC", "CLC", "CMC", + "CNC", "COC", "CPC", "CQC", "CRC", "CSC", "CTC", "CUC", "CVC", "CWC", "CXC", "CYC", "CZC", + "DAC", "DBC", "DCC", "DDC", "DEC", "DFC", "DGC", "DHC", "DIC", "DJC", "DKC", "DLC", "DMC", + "DNC", "DOC", "DPC", "DQC", "DRC", "DSC", "DTC", "DUC", "DVC", "DWC", "DXC", "DYC", "DZC", +]; +// const QUOTE_COIN_TYPES: [&str; 11] = ["QC", "QC", "QC", "QC", "QC", "QC", "QC", "QC", "QC", "QC", "QC"]; + +fn base_coin_type(market_id: u64) -> &'static str { + BASE_COIN_TYPES[(market_id - 1) as usize] +} + +fn quote_coin_type(_market_id: u64) -> &'static str { + "QC" +} + +const ASK: bool = true; +const BID: bool = false; + +/// Placeas a bid limit order. +pub fn place_bid_limit_order( + module_id: ModuleId, + size: u64, + price: u64, + market_id: u64, + publisher: AccountAddress, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + module_id, + ident_str!("place_bid_limit_order").to_owned(), + vec![ + TypeTag::Struct(Box::new(StructTag { + address: publisher, + module: Identifier::new(<&str as Into>>::into("assets")).unwrap(), + name: Identifier::new(<&str as Into>>::into(base_coin_type(market_id))) + .unwrap(), + type_args: vec![], + })), + TypeTag::Struct(Box::new(StructTag { + address: publisher, + module: Identifier::new(<&str as Into>>::into("assets")).unwrap(), + name: Identifier::new(<&str as Into>>::into(quote_coin_type(market_id))) + .unwrap(), + type_args: vec![], + })), + ], + vec![ + bcs::to_bytes(&size).unwrap(), + bcs::to_bytes(&price).unwrap(), + bcs::to_bytes(&market_id).unwrap(), + ], + )) +} + +/// Placeas an ask limit order. +pub fn place_ask_limit_order( + module_id: ModuleId, + size: u64, + price: u64, + market_id: u64, + publisher: AccountAddress, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + module_id, + ident_str!("place_ask_limit_order").to_owned(), + vec![ + TypeTag::Struct(Box::new(StructTag { + address: publisher, + module: Identifier::new(<&str as Into>>::into("assets")).unwrap(), + name: Identifier::new(<&str as Into>>::into(base_coin_type(market_id))) + .unwrap(), + type_args: vec![], + })), + TypeTag::Struct(Box::new(StructTag { + address: publisher, + module: Identifier::new(<&str as Into>>::into("assets")).unwrap(), + name: Identifier::new(<&str as Into>>::into(quote_coin_type(market_id))) + .unwrap(), + type_args: vec![], + })), + ], + vec![ + bcs::to_bytes(&size).unwrap(), + bcs::to_bytes(&price).unwrap(), + bcs::to_bytes(&market_id).unwrap(), + ], + )) +} + +/// Placeas a bid market order. +pub fn place_bid_market_order( + module_id: ModuleId, + size: u64, + market_id: u64, + publisher: AccountAddress, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + module_id, + ident_str!("place_bid_market_order").to_owned(), + vec![ + TypeTag::Struct(Box::new(StructTag { + address: publisher, + module: Identifier::new(<&str as Into>>::into("assets")).unwrap(), + name: Identifier::new(<&str as Into>>::into(base_coin_type(market_id))) + .unwrap(), + type_args: vec![], + })), + TypeTag::Struct(Box::new(StructTag { + address: publisher, + module: Identifier::new(<&str as Into>>::into("assets")).unwrap(), + name: Identifier::new(<&str as Into>>::into(quote_coin_type(market_id))) + .unwrap(), + type_args: vec![], + })), + ], + vec![ + bcs::to_bytes(&size).unwrap(), + bcs::to_bytes(&market_id).unwrap(), + ], + )) +} + +/// Placeas an ask market order. +pub fn place_ask_market_order( + module_id: ModuleId, + size: u64, + market_id: u64, + publisher: AccountAddress, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + module_id, + ident_str!("place_ask_market_order").to_owned(), + vec![ + TypeTag::Struct(Box::new(StructTag { + address: publisher, + module: Identifier::new(<&str as Into>>::into("assets")).unwrap(), + name: Identifier::new(<&str as Into>>::into(base_coin_type(market_id))) + .unwrap(), + type_args: vec![], + })), + TypeTag::Struct(Box::new(StructTag { + address: publisher, + module: Identifier::new(<&str as Into>>::into("assets")).unwrap(), + name: Identifier::new(<&str as Into>>::into(quote_coin_type(market_id))) + .unwrap(), + type_args: vec![], + })), + ], + vec![ + bcs::to_bytes(&size).unwrap(), + bcs::to_bytes(&market_id).unwrap(), + ], + )) +} + +/// Placeas a market order. +pub fn place_market_order( + module_id: ModuleId, + publisher: AccountAddress, + size: u64, + market_id: u64, + direction: bool, + self_matching_behavior: u8, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + module_id, + ident_str!("place_market_order").to_owned(), + vec![ + TypeTag::Struct(Box::new(StructTag { + address: publisher, + module: Identifier::new(<&str as Into>>::into("assets")).unwrap(), + name: Identifier::new(<&str as Into>>::into(base_coin_type(market_id))) + .unwrap(), + type_args: vec![], + })), + TypeTag::Struct(Box::new(StructTag { + address: publisher, + module: Identifier::new(<&str as Into>>::into("assets")).unwrap(), + name: Identifier::new(<&str as Into>>::into(quote_coin_type(market_id))) + .unwrap(), + type_args: vec![], + })), + ], + vec![ + bcs::to_bytes(&market_id).unwrap(), + bcs::to_bytes(&direction).unwrap(), + bcs::to_bytes(&size).unwrap(), + bcs::to_bytes(&self_matching_behavior).unwrap(), + ], + )) +} + +pub fn place_limit_order( + module_id: ModuleId, + publisher: AccountAddress, + size: u64, + price: u64, + market_id: u64, + direction: bool, + restriction: u8, + self_matching_behavior: u8, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + module_id, + ident_str!("place_limit_order").to_owned(), + vec![ + TypeTag::Struct(Box::new(StructTag { + address: publisher, + module: Identifier::new(<&str as Into>>::into("assets")).unwrap(), + name: Identifier::new(<&str as Into>>::into(base_coin_type(market_id))) + .unwrap(), + type_args: vec![], + })), + TypeTag::Struct(Box::new(StructTag { + address: publisher, + module: Identifier::new(<&str as Into>>::into("assets")).unwrap(), + name: Identifier::new(<&str as Into>>::into(quote_coin_type(market_id))) + .unwrap(), + type_args: vec![], + })), + ], + vec![ + bcs::to_bytes(&market_id).unwrap(), + bcs::to_bytes(&direction).unwrap(), + bcs::to_bytes(&size).unwrap(), + bcs::to_bytes(&price).unwrap(), + bcs::to_bytes(&restriction).unwrap(), + bcs::to_bytes(&self_matching_behavior).unwrap(), + ], + )) +} + +pub fn place_cancel_order(module_id: ModuleId) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + module_id, + ident_str!("place_cancel_order").to_owned(), + vec![], + vec![], + )) +} + +pub fn register_market(module_id: ModuleId, num_markets: u64) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + module_id, + ident_str!("register_multiple_markets").to_owned(), + vec![], + vec![bcs::to_bytes(&num_markets).unwrap()], + )) +} + +pub fn register_market_accounts( + module_id: ModuleId, + market_id: u64, + publisher: AccountAddress, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + module_id, + ident_str!("register_market_accounts").to_owned(), + vec![ + TypeTag::Struct(Box::new(StructTag { + address: publisher, + module: Identifier::new(<&str as Into>>::into("assets")).unwrap(), + name: Identifier::new(<&str as Into>>::into(base_coin_type(market_id))) + .unwrap(), + type_args: vec![], + })), + TypeTag::Struct(Box::new(StructTag { + address: publisher, + module: Identifier::new(<&str as Into>>::into("assets")).unwrap(), + name: Identifier::new(<&str as Into>>::into(quote_coin_type(market_id))) + .unwrap(), + type_args: vec![], + })), + ], + vec![bcs::to_bytes(&market_id).unwrap()], + )) +} + +pub fn deposit_coins( + module_id: ModuleId, + market_id: u64, + publisher: AccountAddress, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + module_id, + ident_str!("deposit_coins").to_owned(), + vec![ + TypeTag::Struct(Box::new(StructTag { + address: publisher, + module: Identifier::new(<&str as Into>>::into("assets")).unwrap(), + name: Identifier::new(<&str as Into>>::into(base_coin_type(market_id))) + .unwrap(), + type_args: vec![], + })), + TypeTag::Struct(Box::new(StructTag { + address: publisher, + module: Identifier::new(<&str as Into>>::into("assets")).unwrap(), + name: Identifier::new(<&str as Into>>::into(quote_coin_type(market_id))) + .unwrap(), + type_args: vec![], + })), + ], + vec![bcs::to_bytes(&market_id).unwrap()], + )) +} + +pub struct EconiaLimitOrderTransactionGenerator { + num_markets: Arc, + num_prev_transactions: Arc, +} + +impl EconiaLimitOrderTransactionGenerator { + pub fn new(num_markets: u64, num_prev_transactions: u64) -> Self { + Self { + num_markets: Arc::new(num_markets), + num_prev_transactions: Arc::new(num_prev_transactions), + } + } +} + +#[async_trait] +impl UserModuleTransactionGenerator for EconiaLimitOrderTransactionGenerator { + fn initialize_package( + &mut self, + _package: &Package, + _publisher: &mut LocalAccount, + _txn_factory: &TransactionFactory, + _rng: &mut StdRng, + ) -> Vec { + vec![] + } + + async fn create_generator_fn( + &mut self, + _root_account: &dyn RootAccountHandle, + _txn_factory: &TransactionFactory, + _txn_executor: &dyn ReliableTransactionSubmitter, + _rng: &mut StdRng, + ) -> Arc { + let num_markets = self.num_markets.clone(); + let num_prev_transactions = self.num_prev_transactions.clone(); + Arc::new( + move |account, + package, + publisher, + txn_factory, + rng, + txn_counter, + _prev_orders, + _market_maker| { + let mut requests = vec![]; + let market_id = account.address().into_bytes()[0] as u64 % *num_markets + 1; + let bid_size = rng.gen_range(4, 14); + let ask_size = rng.gen_range(4, 14); + + if txn_counter <= (*num_prev_transactions) + (*num_markets) * 100 + || txn_counter % 2 == 0 + { + let bid_price = rng.gen_range(1, 200); + let ask_price = rng.gen_range(201, 400); + + let bid_builder = txn_factory.payload(place_bid_limit_order( + package.get_module_id("txn_generator_utils"), + bid_size, + bid_price, + market_id, + publisher.address(), + )); + let ask_builder = txn_factory.payload(place_ask_limit_order( + package.get_module_id("txn_generator_utils"), + ask_size, + ask_price, + market_id, + publisher.address(), + )); + + requests.push(account.sign_with_transaction_builder(bid_builder)); + requests.push(account.sign_with_transaction_builder(ask_builder)); + } else { + let bid_builder = txn_factory.payload(place_bid_market_order( + package.get_module_id("txn_generator_utils"), + bid_size, + market_id, + publisher.address(), + )); + let ask_builder = txn_factory.payload(place_ask_market_order( + package.get_module_id("txn_generator_utils"), + ask_size, + market_id, + publisher.address(), + )); + + requests.push(account.sign_with_transaction_builder(bid_builder)); + requests.push(account.sign_with_transaction_builder(ask_builder)); + } + requests + }, + ) + } +} + +pub struct EconiaMarketOrderTransactionGenerator { + num_markets: Arc, + num_prev_transactions: Arc, +} + +impl EconiaMarketOrderTransactionGenerator { + pub fn new(num_markets: u64, num_prev_transactions: u64) -> Self { + Self { + num_markets: Arc::new(num_markets), + num_prev_transactions: Arc::new(num_prev_transactions), + } + } +} + +#[async_trait] +impl UserModuleTransactionGenerator for EconiaMarketOrderTransactionGenerator { + fn initialize_package( + &mut self, + _package: &Package, + _publisher: &mut LocalAccount, + _txn_factory: &TransactionFactory, + _rng: &mut StdRng, + ) -> Vec { + vec![] + } + + async fn create_generator_fn( + &mut self, + _root_account: &dyn RootAccountHandle, + _txn_factory: &TransactionFactory, + _txn_executor: &dyn ReliableTransactionSubmitter, + _rng: &mut StdRng, + ) -> Arc { + let num_markets = self.num_markets.clone(); + let num_prev_transactions = self.num_prev_transactions.clone(); + Arc::new( + move |account, + package, + publisher, + txn_factory, + rng, + txn_counter, + _prev_orders, + _market_maker| { + let market_id = account.address().into_bytes()[0] as u64 % *num_markets + 1; + if txn_counter <= (*num_prev_transactions) + (*num_markets) * 600 { + let bid_size = rng.gen_range(400000, 500000); + let ask_size = rng.gen_range(400000, 500000); + if rng.gen_range(0, 2) == 0 { + let bid_price = rng.gen_range(1000, 1500); + let bid_builder = txn_factory.payload(place_bid_limit_order( + package.get_module_id("txn_generator_utils"), + bid_size, + bid_price, + market_id, + publisher.address(), + )); + vec![account.sign_with_transaction_builder(bid_builder)] + } else { + let ask_price = rng.gen_range(1501, 2000); + let ask_builder = txn_factory.payload(place_ask_limit_order( + package.get_module_id("txn_generator_utils"), + ask_size, + ask_price, + market_id, + publisher.address(), + )); + vec![account.sign_with_transaction_builder(ask_builder)] + } + } else { + let bid_size = rng.gen_range(4, 10); + let ask_size = rng.gen_range(4, 10); + if rng.gen_range(0, 2) == 0 { + let bid_builder = txn_factory.payload(place_bid_market_order( + package.get_module_id("txn_generator_utils"), + bid_size, + market_id, + publisher.address(), + )); + vec![account.sign_with_transaction_builder(bid_builder)] + } else { + let ask_builder = txn_factory.payload(place_ask_market_order( + package.get_module_id("txn_generator_utils"), + ask_size, + market_id, + publisher.address(), + )); + vec![account.sign_with_transaction_builder(ask_builder)] + } + } + }, + ) + } +} + +pub struct EconiaRealOrderTransactionGenerator {} + +impl EconiaRealOrderTransactionGenerator { + pub fn new() -> Self { + Self {} + } +} + +impl Default for EconiaRealOrderTransactionGenerator { + fn default() -> Self { + Self::new() + } +} + +#[async_trait] +impl UserModuleTransactionGenerator for EconiaRealOrderTransactionGenerator { + fn initialize_package( + &mut self, + _package: &Package, + _publisher: &mut LocalAccount, + _txn_factory: &TransactionFactory, + _rng: &mut StdRng, + ) -> Vec { + vec![] + } + + async fn create_generator_fn( + &mut self, + _root_account: &dyn RootAccountHandle, + _txn_factory: &TransactionFactory, + _txn_executor: &dyn ReliableTransactionSubmitter, + _rng: &mut StdRng, + ) -> Arc { + Arc::new( + move |account, + package, + publisher, + txn_factory, + rng, + _txn_counter, + history, + market_maker| { + // println!("EconiaRealOrderTransactionGenerator. account: {}, history: {:?}", account.address(), history); + let size = rng.gen_range(4, 10000); + if market_maker || rng.gen_range(0, 1000) < 138 { + // Market makers always do only limit and cancel orders + // Non-market makers do limit orders with 13.8% probability + if market_maker { + let num_prev_limit_orders = history + .iter() + .map(|s| if s == "place_limit_order" { 1 } else { 0 }) + .sum::(); + let num_prev_cancel_orders = history + .iter() + .map(|s| if s == "place_cancel_order" { 1 } else { 0 }) + .sum::(); + if num_prev_limit_orders > num_prev_cancel_orders + && rng.gen_range(1, 101) <= 98 + { + // 98% probability + return vec![account.sign_with_transaction_builder( + txn_factory.payload(place_cancel_order( + package.get_module_id("txn_generator_utils"), + )), + )]; + } + } + + // Limit order + let market_id = if rng.gen_range(1, 1000) < 740 { + // Market 1 with 74% probability + 1 + } else { + 2 + }; + let rand = rng.gen_range(1, 10000); + let restriction = if rand < 88 { + // 0.88% probability + 0 + } else if rand < 176 { + // 0.88% probability + 2 + } else { + // 98.2% probability + 3 + }; + + let rand = rng.gen_range(1, 1000); + let self_matching_behavior = if rand < 8 { + // 0.8% probability + 3 + } else if rand < 295 { + // 2.87% probability + 2 + } else { + // 96.33% probability + 0 + }; + let (direction, price) = if rng.gen_range(1, 1000) < 546 { + // ASK with 54.6% probability + (ASK, rng.gen_range(13200, 13400)) + } else { + (BID, rng.gen_range(13000, 13200)) + }; + vec![account.sign_with_transaction_builder(txn_factory.payload( + place_limit_order( + package.get_module_id("txn_generator_utils"), + publisher.address(), + size, + price, + market_id, + direction, + restriction, + self_matching_behavior, + ), + ))] + } else { + let market_id = if rng.gen_range(1, 1000) < 885 { + // Market 1 with 88.5% probability + 1 + } else { + 2 + }; + let direction = if rng.gen_range(1, 1000) < 515 { + // ASK with 51.5% probability + ASK + } else { + BID + }; + let self_matching_behavior = if rng.gen_range(1, 1000) < 184 { + // 18.4% probability + 0 + } else { + // 81.6% probability + 3 + }; + vec![account.sign_with_transaction_builder(txn_factory.payload( + place_market_order( + package.get_module_id("txn_generator_utils"), + publisher.address(), + size, + market_id, + direction, + self_matching_behavior, + ), + ))] + } + }, + ) + } +} + +pub async fn register_econia_markets( + init_txn_factory: TransactionFactory, + packages: &mut Vec<(Package, LocalAccount)>, + txn_executor: &dyn ReliableTransactionSubmitter, + num_markets: u64, +) { + assert!(num_markets > 0, "num_markets must be greater than 0"); + assert!( + num_markets <= 104, + "num_markets must be less than or equal to 104" + ); + let mut requests = vec![]; + for (package, publisher) in packages { + let builder = init_txn_factory.payload(register_market( + package.get_module_id("txn_generator_utils"), + num_markets, + )); + requests.push(publisher.sign_with_transaction_builder(builder)); + } + txn_executor.execute_transactions(&requests).await.unwrap(); +} + +pub struct EconiaRegisterMarketUserTransactionGenerator { + num_markets: Arc, + bucket_users_into_markets: bool, +} + +impl EconiaRegisterMarketUserTransactionGenerator { + pub fn new(num_markets: u64, bucket_users_into_markets: bool) -> Self { + Self { + num_markets: Arc::new(num_markets), + bucket_users_into_markets, + } + } +} + +#[async_trait] +impl UserModuleTransactionGenerator for EconiaRegisterMarketUserTransactionGenerator { + fn initialize_package( + &mut self, + _package: &Package, + _publisher: &mut LocalAccount, + _txn_factory: &TransactionFactory, + _rng: &mut StdRng, + ) -> Vec { + vec![] + } + + async fn create_generator_fn( + &mut self, + _root_account: &dyn RootAccountHandle, + _txn_factory: &TransactionFactory, + _txn_executor: &dyn ReliableTransactionSubmitter, + _rng: &mut StdRng, + ) -> Arc { + let num_markets = self.num_markets.clone(); + if self.bucket_users_into_markets { + Arc::new( + move |account, + package, + publisher, + txn_factory, + _rng, + _txn_counter, + _prev_orders, + _market_maker| { + let market_id = account.address().into_bytes()[0] as u64 % *num_markets + 1; + let builder = txn_factory.payload(register_market_accounts( + package.get_module_id("txn_generator_utils"), + market_id, + publisher.address(), + )); + vec![account.sign_with_transaction_builder(builder)] + }, + ) + } else { + Arc::new( + move |account, + package, + publisher, + txn_factory, + _rng, + _txn_counter, + _prev_orders, + _market_maker| { + let mut requests = Vec::new(); + for market_id in 1..(*num_markets + 1) { + let builder = txn_factory.payload(register_market_accounts( + package.get_module_id("txn_generator_utils"), + market_id, + publisher.address(), + )); + requests.push(account.sign_with_transaction_builder(builder)) + } + requests + }, + ) + } + } +} + +pub struct EconiaDepositCoinsTransactionGenerator { + num_markets: Arc, + bucket_users_into_markets: bool, +} + +impl EconiaDepositCoinsTransactionGenerator { + pub fn new(num_markets: u64, bucket_users_into_markets: bool) -> Self { + Self { + num_markets: Arc::new(num_markets), + bucket_users_into_markets, + } + } +} + +#[async_trait] +impl UserModuleTransactionGenerator for EconiaDepositCoinsTransactionGenerator { + fn initialize_package( + &mut self, + _package: &Package, + _publisher: &mut LocalAccount, + _txn_factory: &TransactionFactory, + _rng: &mut StdRng, + ) -> Vec { + vec![] + } + + async fn create_generator_fn( + &mut self, + _root_account: &dyn RootAccountHandle, + _txn_factory: &TransactionFactory, + _txn_executor: &dyn ReliableTransactionSubmitter, + _rng: &mut StdRng, + ) -> Arc { + let num_markets = self.num_markets.clone(); + if self.bucket_users_into_markets { + Arc::new( + move |account, + package, + publisher, + txn_factory, + _rng, + _txn_counter, + _prev_orders, + _market_maker| { + let market_id = account.address().into_bytes()[0] as u64 % *num_markets + 1; + let builder = txn_factory.payload(deposit_coins( + package.get_module_id("txn_generator_utils"), + market_id, + publisher.address(), + )); + vec![account.sign_multi_agent_with_transaction_builder(vec![publisher], builder)] + }, + ) + } else { + Arc::new( + move |account, + package, + publisher, + txn_factory, + _rng, + _txn_counter, + _prev_orders, + _market_maker| { + let mut requests = Vec::new(); + for market_id in 1..(*num_markets + 1) { + let builder = txn_factory.payload(deposit_coins( + package.get_module_id("txn_generator_utils"), + market_id, + publisher.address(), + )); + requests.push( + account.sign_multi_agent_with_transaction_builder( + vec![publisher], + builder, + ), + ) + } + requests + }, + ) + } + } +} diff --git a/crates/transaction-generator-lib/src/entry_points.rs b/crates/transaction-generator-lib/src/entry_points.rs index 6c5f5a014243e..bf9c87a03689e 100644 --- a/crates/transaction-generator-lib/src/entry_points.rs +++ b/crates/transaction-generator-lib/src/entry_points.rs @@ -45,7 +45,7 @@ impl UserModuleTransactionGenerator for EntryPointTransactionGenerator { } async fn create_generator_fn( - &self, + &mut self, root_account: &dyn RootAccountHandle, txn_factory: &TransactionFactory, txn_executor: &dyn ReliableTransactionSubmitter, @@ -90,27 +90,36 @@ impl UserModuleTransactionGenerator for EntryPointTransactionGenerator { _ => None, }; - Arc::new(move |account, package, publisher, txn_factory, rng| { - let payload = entry_point.create_payload( - package.get_module_id(entry_point.module_name()), - Some(rng), - Some(&publisher.address()), - ); - let builder = txn_factory.payload(payload); + Arc::new( + move |account, + package, + publisher, + txn_factory, + rng, + _txn_counter, + _prev_orders, + _market_maker| { + let payload = entry_point.create_payload( + package.get_module_id(entry_point.module_name()), + Some(rng), + Some(&publisher.address()), + ); + let builder = txn_factory.payload(payload); - Some(match entry_point.multi_sig_additional_num() { - MultiSigConfig::None => account.sign_with_transaction_builder(builder), - MultiSigConfig::Random(_) => account.sign_multi_agent_with_transaction_builder( - additional_signers.as_ref().unwrap().iter().collect(), - builder, - ), - MultiSigConfig::Publisher => { - account.sign_multi_agent_with_transaction_builder(vec![publisher], builder) - }, - MultiSigConfig::FeePayerPublisher => { - account.sign_fee_payer_with_transaction_builder(vec![], publisher, builder) - }, - }) - }) + vec![match entry_point.multi_sig_additional_num() { + MultiSigConfig::None => account.sign_with_transaction_builder(builder), + MultiSigConfig::Random(_) => account.sign_multi_agent_with_transaction_builder( + additional_signers.as_ref().unwrap().iter().collect(), + builder, + ), + MultiSigConfig::Publisher => { + account.sign_multi_agent_with_transaction_builder(vec![publisher], builder) + }, + MultiSigConfig::FeePayerPublisher => { + account.sign_fee_payer_with_transaction_builder(vec![], publisher, builder) + }, + }] + }, + ) } } diff --git a/crates/transaction-generator-lib/src/lib.rs b/crates/transaction-generator-lib/src/lib.rs index f9094ba4266ab..5acee1ac1daf4 100644 --- a/crates/transaction-generator-lib/src/lib.rs +++ b/crates/transaction-generator-lib/src/lib.rs @@ -19,10 +19,11 @@ use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, sync::{ - atomic::{AtomicUsize, Ordering}, + atomic::{AtomicU64, AtomicUsize, Ordering}, Arc, }, time::Duration, + hash::Hash, }; mod account_generator; @@ -31,12 +32,14 @@ pub mod args; mod batch_transfer; mod bounded_batch_wrapper; mod call_custom_modules; +pub mod econia_order_generator; mod entry_points; mod p2p_transaction_generator; pub mod publish_modules; pub mod publishing; mod transaction_mix_generator; mod workflow_delegator; + use self::{ account_generator::AccountGeneratorCreator, call_custom_modules::CustomModulesDelegationGeneratorCreator, @@ -86,6 +89,13 @@ pub enum TransactionType { }, } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum EconiaFlowType { + Basic, + Mixed, + Market, +} + #[derive(Debug, Copy, Clone, ValueEnum, Default, Deserialize, Parser, Serialize)] pub enum AccountType { #[default] @@ -95,7 +105,25 @@ pub enum AccountType { #[derive(Debug, Copy, Clone)] pub enum WorkflowKind { - CreateMintBurn { count: usize, creation_balance: u64 }, + CreateMintBurn { + count: usize, + creation_balance: u64, + }, + // Places bid and ask limit orders at random price + Econia { + num_users: usize, + flow_type: EconiaFlowType, + num_markets: u64, + // If this is flag is set, the same accounts will be reused for placing multiple orders + reuse_accounts_for_orders: bool, + // Publish Econia package during the test + publish_packages: bool, + }, + EconiaReal { + num_users: usize, + // Publish Econia package during the test + publish_packages: bool, + }, } #[derive(Debug, Copy, Clone)] @@ -123,12 +151,17 @@ pub trait TransactionGenerator: Sync + Send { &mut self, account: &LocalAccount, num_to_create: usize, + history: &[String], + market_maker: bool, ) -> Vec; } #[async_trait] pub trait TransactionGeneratorCreator: Sync + Send { - fn create_transaction_generator(&self) -> Box; + fn create_transaction_generator( + &self, + txn_counter: Arc, + ) -> Box; } pub struct CounterState { @@ -340,6 +373,7 @@ pub async fn create_txn_generator_creator( &mut EntryPointTransactionGenerator { entry_point: *entry_point, }, + true, ) .await, ), @@ -367,7 +401,7 @@ pub async fn create_txn_generator_creator( &root_account, txn_executor, *num_modules, - use_account_pool.then(|| accounts_pool.clone()), + Some(accounts_pool.clone()), cur_phase.clone(), *progress_type, ) @@ -389,6 +423,107 @@ pub async fn create_txn_generator_creator( ) } +pub struct BucketedAccountPool { + pool: RwLock>>, + all_buckets: Arc>, + current_index: AtomicUsize, + object_to_bucket_map: RwLock>, +} + +impl BucketedAccountPool { + pub(crate) fn new(buckets: Arc>) -> Self { + let mut pool = HashMap::new(); + for bucket in buckets.iter() { + pool.insert(bucket.clone(), Vec::new()); + } + Self { + pool: RwLock::new(pool), + all_buckets: buckets, + current_index: AtomicUsize::new(0), + object_to_bucket_map: RwLock::new(HashMap::new()), + } + } + + pub(crate) fn add_to_bucket(&self, bucket: Bucket, mut addition: Vec) { + assert!(!addition.is_empty()); + let mut current = self.pool.write(); + let mut object_to_bucket_map = self.object_to_bucket_map.write(); + object_to_bucket_map.extend(addition.iter().map(|object| (object.address(), bucket.clone()))); + current + .entry(bucket) + .or_insert_with(Vec::new) + .append(&mut addition); + } + + + pub(crate) fn add_to_pool(&self, addition: Vec) { + assert!(!addition.is_empty()); + let mut current = self.pool.write(); + let mut object_to_bucket_map = self.object_to_bucket_map.write(); + for object in addition { + let current_index = self.current_index.load(Ordering::Relaxed); + let current_bucket = self.all_buckets[current_index].clone(); + let object_address = object.address(); + current + .entry(current_bucket.clone()) + .or_insert_with(Vec::new) + .append(&mut vec![object]); + self.current_index.store((current_index + 1) % self.all_buckets.len(), Ordering::Relaxed); + object_to_bucket_map.insert(object_address, current_bucket); + } + } + + pub(crate) fn take_from_pool( + &self, + bucket: Bucket, + needed: usize, + return_partial: bool, + rng: &mut StdRng, + ) -> Vec { + let mut current = self.pool.write(); + let num_in_pool = current.get_mut(&bucket).map_or(0, |v| v.len()); + if !return_partial && num_in_pool < needed { + sample!( + SampleRate::Duration(Duration::from_secs(10)), + warn!("Cannot fetch enough from shared pool, left in pool {}, needed {}", num_in_pool, needed); + ); + return Vec::new(); + } + let num_to_return = std::cmp::min(num_in_pool, needed); + let current_bucket = current.get_mut(&bucket).unwrap(); + let mut result = current_bucket + .drain((num_in_pool - num_to_return)..) + .collect::>(); + + if current_bucket.len() > num_to_return { + let start = rng.gen_range(0, current_bucket.len() - num_to_return); + current_bucket[start..start + num_to_return].swap_with_slice(&mut result); + } + result + } + + pub(crate) fn update_sequence_number( + &self, + object_address: &AccountAddress, + sequence_number: u64, + ) { + info!("Called update sequence number for {} {}", object_address, sequence_number); + let mut current = self.pool.write(); + if let Some(bucket) = self.object_to_bucket_map.read().get(object_address).and_then(|bucket| current.get_mut(bucket)) { + for object in bucket.iter_mut() { + if object.address() == *object_address { + if sequence_number < object.sequence_number() { + info!("Sequence number for {} decreased from {} to {}", object_address, object.sequence_number(), sequence_number); + object.set_sequence_number(sequence_number); + } else { + info!("Sequence number for {} not updated", object_address); + } + } + } + } + } +} + /// Simple object pool structure, that you can add and remove from multiple threads. /// Taking is done at random positions, but sequentially. /// Overflow replaces at random positions as well. @@ -396,7 +531,7 @@ pub async fn create_txn_generator_creator( /// It's efficient to lock the objects for short time - and replace /// in place, but its not a concurrent datastructure. pub struct ObjectPool { - pool: RwLock>, + pub pool: RwLock>, } impl ObjectPool { @@ -415,7 +550,7 @@ impl ObjectPool { } pub(crate) fn add_to_pool(&self, mut addition: Vec) { - assert!(!addition.is_empty()); + // assert!(!addition.is_empty()); let mut current = self.pool.write(); current.append(&mut addition); sample!( @@ -497,6 +632,9 @@ impl ObjectPool { pub(crate) fn len(&self) -> usize { self.pool.read().len() } + + // pub(crate) fn addresses(&self) -> Vec { + // } } impl ObjectPool { diff --git a/crates/transaction-generator-lib/src/p2p_transaction_generator.rs b/crates/transaction-generator-lib/src/p2p_transaction_generator.rs index 81c5ae7dc94b9..af20c1ac31da3 100644 --- a/crates/transaction-generator-lib/src/p2p_transaction_generator.rs +++ b/crates/transaction-generator-lib/src/p2p_transaction_generator.rs @@ -14,7 +14,7 @@ use rand::{ }; use std::{ cmp::{max, min}, - sync::Arc, + sync::{atomic::AtomicU64, Arc}, }; pub enum SamplingMode { @@ -260,6 +260,8 @@ impl TransactionGenerator for P2PTransactionGenerator { &mut self, account: &LocalAccount, num_to_create: usize, + _history: &[String], + _market_maker: bool, ) -> Vec { let mut requests = Vec::with_capacity(num_to_create); let invalid_size = if self.invalid_transaction_ratio != 0 { @@ -334,7 +336,10 @@ impl P2PTransactionGeneratorCreator { } impl TransactionGeneratorCreator for P2PTransactionGeneratorCreator { - fn create_transaction_generator(&self) -> Box { + fn create_transaction_generator( + &self, + _txn_counter: Arc, + ) -> Box { let rng = StdRng::from_entropy(); let sampler: Box> = match self.sampling_mode { SamplingMode::Basic => Box::new(BasicSampler::new()), diff --git a/crates/transaction-generator-lib/src/publish_modules.rs b/crates/transaction-generator-lib/src/publish_modules.rs index 4b1838dc85521..8938168759376 100644 --- a/crates/transaction-generator-lib/src/publish_modules.rs +++ b/crates/transaction-generator-lib/src/publish_modules.rs @@ -9,7 +9,7 @@ use aptos_sdk::{ types::{transaction::SignedTransaction, LocalAccount}, }; use rand::{rngs::StdRng, SeedableRng}; -use std::sync::Arc; +use std::sync::{atomic::AtomicU64, Arc}; pub struct PublishPackageGenerator { rng: StdRng, @@ -36,6 +36,8 @@ impl TransactionGenerator for PublishPackageGenerator { &mut self, account: &LocalAccount, num_to_create: usize, + _history: &[String], + _market_maker: bool, ) -> Vec { let mut requests = Vec::with_capacity(num_to_create); @@ -81,7 +83,10 @@ impl PublishPackageCreator { } impl TransactionGeneratorCreator for PublishPackageCreator { - fn create_transaction_generator(&self) -> Box { + fn create_transaction_generator( + &self, + _txn_counter: Arc, + ) -> Box { Box::new(PublishPackageGenerator::new( StdRng::from_entropy(), self.package_handler.clone(), diff --git a/crates/transaction-generator-lib/src/publishing/module_simple.rs b/crates/transaction-generator-lib/src/publishing/module_simple.rs index 0bc7b959fb408..5b109cf64355b 100644 --- a/crates/transaction-generator-lib/src/publishing/module_simple.rs +++ b/crates/transaction-generator-lib/src/publishing/module_simple.rs @@ -7,8 +7,10 @@ use aptos_framework::natives::code::{MoveOption, PackageMetadata}; use aptos_sdk::{ bcs, move_types::{ - account_address::AccountAddress, ident_str, identifier::Identifier, - language_storage::ModuleId, + account_address::AccountAddress, + ident_str, + identifier::Identifier, + language_storage::{ModuleId, StructTag, TypeTag}, }, types::{ serde_helper::bcs_utils::bcs_size_of_byte_array, @@ -267,6 +269,12 @@ pub enum EntryPoints { length: u64, num_points_per_txn: usize, }, + EconiaRegisterMarket, + EconiaRegisterMarketUser, + EconiaDepositCoins, + EconiaPlaceBidLimitOrder, + EconiaPlaceAskLimitOrder, + EconiaPlaceRandomLimitOrder, DeserializeU256, /// No-op script with dependencies in *::simple.move. The script has unreachable code that is /// there to slow down deserialization & verification, effectively making it more expensive to @@ -326,6 +334,12 @@ impl EntryPoints { | EntryPoints::VectorPictureRead { .. } | EntryPoints::InitializeSmartTablePicture | EntryPoints::SmartTablePicture { .. } => "complex", + EntryPoints::EconiaRegisterMarket + | EntryPoints::EconiaRegisterMarketUser + | EntryPoints::EconiaDepositCoins + | EntryPoints::EconiaPlaceBidLimitOrder + | EntryPoints::EconiaPlaceAskLimitOrder + | EntryPoints::EconiaPlaceRandomLimitOrder => "econia", EntryPoints::IncGlobalMilestoneAggV2 { .. } | EntryPoints::CreateGlobalMilestoneAggV2 { .. } => "aggregator_examples", EntryPoints::DeserializeU256 => "bcs_stream", @@ -386,6 +400,12 @@ impl EntryPoints { EntryPoints::InitializeSmartTablePicture | EntryPoints::SmartTablePicture { .. } => { "smart_table_picture" }, + EntryPoints::EconiaRegisterMarket + | EntryPoints::EconiaRegisterMarketUser + | EntryPoints::EconiaDepositCoins + | EntryPoints::EconiaPlaceBidLimitOrder + | EntryPoints::EconiaPlaceAskLimitOrder + | EntryPoints::EconiaPlaceRandomLimitOrder => "txn_generator_utils", EntryPoints::IncGlobalMilestoneAggV2 { .. } | EntryPoints::CreateGlobalMilestoneAggV2 { .. } => "counter_with_milestone", EntryPoints::DeserializeU256 => "bcs_stream", @@ -719,6 +739,192 @@ impl EntryPoints { bcs::to_bytes(&colors).unwrap(), // colors ]) }, + EntryPoints::EconiaRegisterMarket => { + TransactionPayload::EntryFunction(EntryFunction::new( + module_id, + ident_str!("register_market").to_owned(), + vec![ + TypeTag::Struct(Box::new(StructTag { + address: *other.expect("Must provide other"), + module: ident_str!("assets").to_owned(), + name: ident_str!("AAC").to_owned(), + type_args: vec![], + })), + TypeTag::Struct(Box::new(StructTag { + address: *other.expect("Must provide other"), + module: ident_str!("assets").to_owned(), + name: ident_str!("QC").to_owned(), + type_args: vec![], + })), + ], + vec![], + )) + }, + EntryPoints::EconiaRegisterMarketUser => { + TransactionPayload::EntryFunction(EntryFunction::new( + module_id, + ident_str!("register_market_accounts").to_owned(), + vec![ + TypeTag::Struct(Box::new(StructTag { + address: *other.expect("Must provide other"), + module: ident_str!("assets").to_owned(), + name: ident_str!("AAC").to_owned(), + type_args: vec![], + })), + TypeTag::Struct(Box::new(StructTag { + address: *other.expect("Must provide other"), + module: ident_str!("assets").to_owned(), + name: ident_str!("QC").to_owned(), + type_args: vec![], + })), + ], + vec![ + bcs::to_bytes(&1u64).unwrap(), //market id + ], + )) + }, + EntryPoints::EconiaDepositCoins => { + TransactionPayload::EntryFunction(EntryFunction::new( + module_id, + ident_str!("deposit_coins").to_owned(), + vec![ + TypeTag::Struct(Box::new(StructTag { + address: *other.expect("Must provide other"), + module: ident_str!("assets").to_owned(), + name: ident_str!("AAC").to_owned(), + type_args: vec![], + })), + TypeTag::Struct(Box::new(StructTag { + address: *other.expect("Must provide other"), + module: ident_str!("assets").to_owned(), + name: ident_str!("QC").to_owned(), + type_args: vec![], + })), + ], + vec![ + bcs::to_bytes(&1u64).unwrap(), // market id + ], + )) + }, + EntryPoints::EconiaPlaceBidLimitOrder => { + let rng: &mut StdRng = rng.expect("Must provide RNG"); + let size: u64 = rng.gen_range(4u64, 14u64); + let price: u64 = rng.gen_range(1u64, 30u64); + TransactionPayload::EntryFunction(EntryFunction::new( + module_id, + ident_str!("place_bid_limit_order").to_owned(), + vec![ + TypeTag::Struct(Box::new(StructTag { + address: *other.expect("Must provide other"), + module: ident_str!("assets").to_owned(), + name: ident_str!("AAC").to_owned(), + type_args: vec![], + })), + TypeTag::Struct(Box::new(StructTag { + address: *other.expect("Must provide other"), + module: ident_str!("assets").to_owned(), + name: ident_str!("QC").to_owned(), + type_args: vec![], + })), + ], + vec![ + bcs::to_bytes(&size).unwrap(), // size + bcs::to_bytes(&price).unwrap(), // amount + bcs::to_bytes(&1u64).unwrap(), // market id + ], + )) + }, + EntryPoints::EconiaPlaceAskLimitOrder => { + let rng: &mut StdRng = rng.expect("Must provide RNG"); + let size: u64 = rng.gen_range(4u64, 14u64); + let price: u64 = rng.gen_range(1u64, 30u64); + TransactionPayload::EntryFunction(EntryFunction::new( + module_id, + ident_str!("place_ask_limit_order").to_owned(), + vec![ + TypeTag::Struct(Box::new(StructTag { + address: *other.expect("Must provide other"), + module: ident_str!("assets").to_owned(), + name: ident_str!("AAC").to_owned(), + type_args: vec![], + })), + TypeTag::Struct(Box::new(StructTag { + address: *other.expect("Must provide other"), + module: ident_str!("assets").to_owned(), + name: ident_str!("QC").to_owned(), + type_args: vec![], + })), + ], + vec![ + bcs::to_bytes(&size).unwrap(), // size + bcs::to_bytes(&price).unwrap(), // amount + bcs::to_bytes(&1u64).unwrap(), //market id + ], + )) + }, + EntryPoints::EconiaPlaceRandomLimitOrder => { + let rng: &mut StdRng = rng.expect("Must provide RNG"); + let size: u64 = rng.gen_range(4u64, 14u64); + let price: u64 = rng.gen_range(1u64, 30u64); + let is_bid: bool = rng.gen(); + if is_bid { + TransactionPayload::EntryFunction(EntryFunction::new( + module_id, + ident_str!("place_bid_limit_order").to_owned(), + vec![ + TypeTag::Struct(Box::new(StructTag { + address: *other.expect("Must provide other"), + module: Identifier::new(<&str as Into>>::into("assets")) + .unwrap(), + name: Identifier::new(<&str as Into>>::into("AAC")) + .unwrap(), + type_args: vec![], + })), + TypeTag::Struct(Box::new(StructTag { + address: *other.expect("Must provide other"), + module: Identifier::new(<&str as Into>>::into("assets")) + .unwrap(), + name: Identifier::new(<&str as Into>>::into("QC")) + .unwrap(), + type_args: vec![], + })), + ], + vec![ + bcs::to_bytes(&size).unwrap(), // size + bcs::to_bytes(&price).unwrap(), // amount + bcs::to_bytes(&1u64).unwrap(), // market id + ], + )) + } else { + TransactionPayload::EntryFunction(EntryFunction::new( + module_id, + ident_str!("place_ask_limit_order").to_owned(), + vec![ + TypeTag::Struct(Box::new(StructTag { + address: *other.expect("Must provide other"), + module: Identifier::new(<&str as Into>>::into("assets")) + .unwrap(), + name: Identifier::new(<&str as Into>>::into("AAC")) + .unwrap(), + type_args: vec![], + })), + TypeTag::Struct(Box::new(StructTag { + address: *other.expect("Must provide other"), + module: Identifier::new(<&str as Into>>::into("assets")) + .unwrap(), + name: Identifier::new(<&str as Into>>::into("QC")) + .unwrap(), + type_args: vec![], + })), + ], + vec![ + bcs::to_bytes(&size).unwrap(), // size + bcs::to_bytes(&price).unwrap(), // amount + bcs::to_bytes(&1u64).unwrap(), // market id + ], + )) + } + }, EntryPoints::DeserializeU256 => { let rng: &mut StdRng = rng.expect("Must provide RNG"); let mut u256_bytes = [0u8; 32]; @@ -770,9 +976,11 @@ impl EntryPoints { EntryPoints::Nop5Signers => MultiSigConfig::Random(4), EntryPoints::ResourceGroupsGlobalWriteTag { .. } | EntryPoints::ResourceGroupsGlobalWriteAndReadTag { .. } => MultiSigConfig::Publisher, - EntryPoints::CoinInitAndMint | EntryPoints::FungibleAssetMint => { - MultiSigConfig::Publisher - }, + EntryPoints::TokenV2AmbassadorMint { .. } + | EntryPoints::TokenV2AmbassadorBurn + | EntryPoints::EconiaDepositCoins + | EntryPoints::CoinInitAndMint + | EntryPoints::FungibleAssetMint => MultiSigConfig::Publisher, EntryPoints::TokenV2AmbassadorMint { .. } | EntryPoints::TokenV2AmbassadorBurn => { MultiSigConfig::Publisher }, @@ -837,6 +1045,12 @@ impl EntryPoints { }, EntryPoints::InitializeSmartTablePicture => AutomaticArgs::Signer, EntryPoints::SmartTablePicture { .. } => AutomaticArgs::None, + EntryPoints::EconiaRegisterMarket + | EntryPoints::EconiaRegisterMarketUser + | EntryPoints::EconiaPlaceBidLimitOrder + | EntryPoints::EconiaPlaceAskLimitOrder + | EntryPoints::EconiaPlaceRandomLimitOrder => AutomaticArgs::Signer, + EntryPoints::EconiaDepositCoins => AutomaticArgs::SignerAndMultiSig, EntryPoints::DeserializeU256 => AutomaticArgs::None, EntryPoints::IncGlobalMilestoneAggV2 { .. } => AutomaticArgs::None, EntryPoints::CreateGlobalMilestoneAggV2 { .. } => AutomaticArgs::Signer, diff --git a/crates/transaction-generator-lib/src/publishing/publish_util.rs b/crates/transaction-generator-lib/src/publishing/publish_util.rs index 68924e485ada2..5cc08ccdb7c6d 100644 --- a/crates/transaction-generator-lib/src/publishing/publish_util.rs +++ b/crates/transaction-generator-lib/src/publishing/publish_util.rs @@ -327,13 +327,16 @@ fn publish_transaction_payload( metadata: &PackageMetadata, ) -> TransactionPayload { let metadata = bcs::to_bytes(metadata).expect("PackageMetadata must serialize"); + println!("metadata size: {:?}", metadata.len()); let mut code: Vec> = vec![]; - for (_, module) in modules { + for (name, module) in modules { let mut module_code: Vec = vec![]; module .serialize(&mut module_code) .expect("Module must serialize"); + println!("module code size name: {:?} {:?}", name, module_code.len()); code.push(module_code); } + println!("publishing transaction"); aptos_stdlib::code_publish_package_txn(metadata, code) } diff --git a/crates/transaction-generator-lib/src/publishing/raw_module_data.rs b/crates/transaction-generator-lib/src/publishing/raw_module_data.rs index 4deb1a72d1709..ab72d07ce9d14 100644 --- a/crates/transaction-generator-lib/src/publishing/raw_module_data.rs +++ b/crates/transaction-generator-lib/src/publishing/raw_module_data.rs @@ -15,6 +15,4526 @@ use once_cell::sync::Lazy; use std::collections::HashMap; +#[rustfmt::skip] +pub static PACKAGE_ECONIA_METADATA: Lazy> = Lazy::new(|| { + vec![ + 6, 69, 99, 111, 110, 105, 97, 1, 0, 0, 0, 0, 0, 0, 0, 0, 64, 69, + 48, 65, 50, 49, 50, 65, 66, 66, 56, 52, 70, 68, 55, 49, 48, 54, 57, 55, + 49, 70, 65, 55, 54, 68, 55, 48, 53, 52, 54, 52, 53, 52, 66, 53, 49, 49, + 48, 48, 48, 54, 67, 68, 55, 56, 50, 55, 70, 53, 69, 66, 68, 55, 66, 52, + 70, 57, 68, 70, 56, 57, 66, 67, 48, 252, 1, 31, 139, 8, 0, 0, 0, 0, + 0, 2, 255, 77, 144, 207, 78, 196, 32, 16, 198, 239, 60, 5, 193, 139, 30, 100, + 183, 235, 170, 39, 19, 61, 232, 73, 159, 160, 105, 54, 179, 48, 91, 73, 41, 67, + 128, 86, 141, 241, 221, 133, 182, 251, 39, 225, 50, 191, 249, 190, 143, 153, 169, 61, + 168, 14, 90, 108, 152, 131, 30, 249, 19, 23, 175, 138, 156, 1, 193, 70, 12, 209, + 144, 43, 104, 43, 55, 178, 18, 108, 240, 109, 0, 141, 59, 79, 214, 168, 159, 210, + 80, 212, 123, 72, 102, 111, 81, 48, 24, 210, 39, 133, 152, 113, 189, 100, 240, 119, + 216, 71, 126, 173, 113, 68, 75, 62, 199, 61, 227, 196, 109, 198, 50, 91, 111, 68, + 195, 88, 13, 90, 7, 140, 17, 99, 195, 230, 118, 9, 94, 127, 87, 155, 187, 237, + 189, 96, 236, 138, 127, 144, 234, 248, 73, 197, 15, 20, 120, 194, 152, 140, 107, 37, + 27, 34, 134, 179, 94, 76, 245, 110, 61, 147, 57, 96, 34, 213, 76, 50, 120, 16, + 204, 184, 132, 121, 145, 68, 139, 51, 195, 199, 252, 81, 173, 209, 163, 211, 232, 148, + 41, 179, 188, 248, 68, 241, 45, 228, 163, 124, 81, 232, 178, 242, 151, 91, 82, 96, + 139, 71, 202, 213, 229, 131, 34, 189, 237, 105, 196, 213, 225, 104, 88, 224, 169, 22, + 252, 239, 31, 158, 68, 220, 216, 107, 1, 0, 0, 10, 6, 97, 115, 115, 101, 116, + 115, 0, 0, 0, 9, 97, 118, 108, 95, 113, 117, 101, 117, 101, 0, 0, 0, 6, + 102, 97, 117, 99, 101, 116, 0, 0, 0, 7, 116, 97, 98, 108, 105, 115, 116, 0, + 0, 0, 16, 114, 101, 115, 111, 117, 114, 99, 101, 95, 97, 99, 99, 111, 117, 110, + 116, 0, 0, 0, 10, 105, 110, 99, 101, 110, 116, 105, 118, 101, 115, 0, 0, 0, + 8, 114, 101, 103, 105, 115, 116, 114, 121, 0, 0, 0, 4, 117, 115, 101, 114, 0, + 0, 0, 6, 109, 97, 114, 107, 101, 116, 0, 0, 0, 19, 116, 120, 110, 95, 103, + 101, 110, 101, 114, 97, 116, 111, 114, 95, 117, 116, 105, 108, 115, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 14, 65, 112, 116, + 111, 115, 70, 114, 97, 109, 101, 119, 111, 114, 107, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 11, 65, 112, 116, 111, 115, 83, 116, 100, 108, 105, + 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 77, 111, + 118, 101, 83, 116, 100, 108, 105, 98, 0, + ] +}); + +#[rustfmt::skip] +pub static MODULE_ECONIA_ASSETS: Lazy> = Lazy::new(|| { + vec![ + 161, 28, 235, 11, 7, 0, 0, 10, 14, 1, 0, 10, 2, 10, 169, 4, 3, 179, + 4, 69, 4, 248, 4, 147, 2, 5, 139, 7, 249, 3, 7, 132, 11, 201, 5, 8, + 205, 16, 64, 6, 141, 17, 239, 17, 16, 252, 34, 173, 1, 10, 169, 36, 147, 5, + 11, 188, 41, 137, 2, 12, 197, 43, 197, 23, 13, 138, 67, 4, 14, 142, 67, 4, + 0, 0, 1, 85, 1, 140, 1, 1, 143, 1, 0, 1, 0, 0, 0, 3, 0, 0, + 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 8, + 0, 0, 0, 9, 0, 0, 0, 10, 0, 0, 0, 11, 0, 0, 0, 12, 0, 0, + 0, 13, 0, 0, 0, 14, 0, 0, 0, 15, 0, 0, 0, 16, 0, 0, 0, 17, + 0, 0, 0, 18, 0, 0, 0, 19, 0, 0, 0, 20, 0, 0, 0, 21, 0, 0, + 0, 22, 0, 0, 0, 23, 0, 0, 0, 24, 0, 0, 0, 25, 0, 0, 0, 26, + 0, 0, 0, 27, 0, 0, 0, 28, 0, 0, 0, 29, 0, 0, 0, 30, 0, 0, + 0, 31, 0, 0, 0, 32, 0, 0, 0, 33, 0, 0, 0, 34, 0, 0, 0, 35, + 0, 0, 0, 36, 0, 0, 0, 37, 0, 0, 0, 38, 0, 0, 0, 39, 0, 0, + 0, 40, 0, 0, 0, 41, 0, 0, 0, 42, 0, 0, 0, 43, 0, 0, 0, 44, + 0, 0, 0, 45, 0, 0, 0, 46, 0, 0, 0, 47, 0, 0, 0, 48, 0, 0, + 0, 49, 0, 0, 0, 50, 0, 0, 0, 51, 0, 0, 0, 52, 0, 0, 0, 53, + 0, 0, 0, 54, 0, 0, 0, 55, 0, 0, 0, 56, 0, 0, 0, 57, 0, 0, + 0, 58, 0, 0, 0, 59, 0, 0, 0, 60, 0, 0, 0, 61, 0, 0, 0, 62, + 0, 0, 0, 63, 0, 0, 0, 64, 0, 0, 0, 65, 0, 0, 0, 66, 0, 0, + 0, 67, 0, 0, 0, 68, 0, 0, 0, 69, 0, 0, 0, 70, 0, 0, 0, 71, + 0, 0, 0, 72, 0, 0, 0, 73, 0, 0, 0, 74, 0, 0, 0, 75, 0, 0, + 0, 76, 0, 0, 0, 77, 0, 0, 0, 78, 0, 0, 0, 79, 0, 0, 0, 80, + 0, 0, 0, 81, 0, 0, 0, 82, 8, 1, 0, 1, 1, 84, 5, 1, 0, 1, + 1, 87, 5, 1, 0, 1, 1, 89, 5, 1, 0, 1, 0, 90, 0, 0, 0, 91, + 0, 0, 0, 92, 0, 0, 0, 93, 0, 0, 0, 94, 0, 0, 0, 95, 0, 0, + 0, 96, 0, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, 99, 0, 0, 0, 100, + 0, 0, 0, 101, 0, 0, 0, 102, 0, 0, 0, 103, 0, 0, 0, 104, 0, 0, + 0, 105, 0, 0, 0, 106, 0, 0, 0, 107, 0, 0, 0, 108, 0, 0, 0, 109, + 0, 0, 0, 110, 0, 0, 0, 111, 0, 0, 0, 112, 0, 0, 0, 113, 0, 0, + 0, 114, 0, 0, 0, 115, 0, 0, 0, 116, 0, 0, 0, 117, 0, 0, 0, 118, + 0, 0, 0, 119, 0, 0, 0, 120, 0, 0, 0, 121, 0, 0, 0, 122, 0, 0, + 0, 123, 0, 0, 0, 124, 0, 0, 0, 125, 0, 0, 0, 126, 0, 0, 0, 127, + 0, 0, 0, 128, 1, 0, 0, 0, 129, 1, 0, 0, 0, 130, 1, 0, 0, 0, + 131, 1, 0, 0, 0, 132, 1, 0, 0, 0, 133, 1, 0, 0, 0, 134, 1, 0, + 0, 0, 135, 1, 0, 0, 0, 136, 1, 0, 0, 1, 138, 1, 4, 1, 0, 1, + 3, 145, 1, 7, 0, 0, 137, 1, 0, 1, 1, 0, 1, 1, 137, 1, 3, 1, + 1, 0, 1, 0, 139, 1, 5, 0, 1, 0, 1, 2, 141, 1, 6, 7, 0, 1, + 1, 139, 1, 8, 0, 1, 0, 1, 0, 142, 1, 10, 1, 1, 0, 1, 3, 144, + 1, 11, 12, 0, 1, 1, 146, 1, 13, 14, 1, 0, 1, 0, 147, 1, 6, 1, + 0, 1, 1, 2, 4, 2, 7, 2, 5, 16, 5, 17, 5, 18, 5, 19, 5, 20, + 5, 21, 5, 22, 5, 23, 5, 24, 5, 25, 5, 26, 5, 27, 5, 28, 5, 29, + 5, 30, 5, 31, 5, 32, 5, 33, 5, 34, 5, 35, 5, 36, 5, 37, 5, 38, + 5, 39, 5, 40, 5, 41, 5, 42, 5, 43, 5, 44, 5, 45, 5, 46, 5, 47, + 5, 48, 5, 49, 5, 50, 5, 51, 5, 52, 5, 53, 5, 54, 5, 55, 5, 56, + 5, 57, 5, 58, 5, 59, 5, 60, 5, 61, 5, 62, 5, 63, 5, 64, 5, 65, + 5, 66, 5, 67, 5, 68, 5, 69, 5, 70, 5, 71, 5, 72, 5, 73, 5, 74, + 5, 75, 5, 76, 5, 77, 5, 78, 5, 79, 5, 80, 5, 81, 5, 82, 5, 83, + 5, 84, 5, 85, 5, 86, 5, 87, 5, 88, 5, 89, 5, 90, 5, 91, 5, 92, + 5, 93, 5, 94, 5, 95, 5, 96, 5, 97, 5, 98, 5, 99, 5, 100, 5, 101, + 5, 102, 5, 103, 5, 104, 5, 105, 5, 106, 5, 107, 5, 108, 5, 109, 5, 110, + 5, 111, 5, 112, 5, 113, 5, 114, 5, 115, 5, 116, 5, 117, 5, 118, 5, 119, + 5, 120, 5, 121, 5, 122, 5, 123, 5, 124, 5, 125, 5, 126, 5, 127, 5, 128, + 1, 5, 129, 1, 5, 130, 1, 5, 131, 1, 5, 132, 1, 5, 133, 1, 5, 134, + 1, 5, 135, 1, 5, 136, 1, 5, 137, 1, 5, 138, 1, 5, 139, 1, 5, 140, + 1, 5, 141, 1, 5, 142, 1, 1, 11, 131, 1, 1, 9, 0, 0, 1, 9, 0, + 2, 11, 131, 1, 1, 9, 0, 6, 11, 81, 1, 9, 0, 1, 6, 11, 81, 1, + 9, 0, 2, 6, 12, 3, 1, 6, 12, 1, 5, 2, 3, 6, 11, 83, 1, 9, + 0, 3, 5, 5, 6, 11, 83, 1, 9, 0, 4, 6, 12, 10, 2, 10, 2, 2, + 1, 10, 2, 1, 8, 132, 1, 5, 6, 12, 8, 132, 1, 8, 132, 1, 2, 1, + 3, 11, 81, 1, 9, 0, 11, 82, 1, 9, 0, 11, 83, 1, 9, 0, 6, 6, + 12, 2, 1, 8, 132, 1, 8, 132, 1, 11, 80, 1, 9, 0, 1, 8, 29, 1, + 8, 123, 1, 8, 127, 1, 8, 2, 1, 8, 86, 1, 8, 112, 1, 8, 111, 1, + 8, 113, 1, 8, 114, 1, 8, 115, 1, 8, 116, 1, 8, 117, 1, 8, 118, 1, + 8, 119, 1, 8, 120, 1, 8, 121, 1, 8, 122, 1, 8, 124, 1, 8, 125, 1, + 8, 126, 1, 8, 128, 1, 1, 8, 129, 1, 1, 8, 130, 1, 1, 8, 0, 1, + 8, 1, 1, 8, 3, 1, 8, 4, 1, 8, 5, 1, 8, 6, 1, 8, 7, 1, + 8, 8, 1, 8, 9, 1, 8, 10, 1, 8, 11, 1, 8, 12, 1, 8, 13, 1, + 8, 14, 1, 8, 15, 1, 8, 16, 1, 8, 17, 1, 8, 18, 1, 8, 19, 1, + 8, 20, 1, 8, 21, 1, 8, 22, 1, 8, 23, 1, 8, 24, 1, 8, 25, 1, + 8, 26, 1, 8, 27, 1, 8, 28, 1, 8, 30, 1, 8, 31, 1, 8, 32, 1, + 8, 33, 1, 8, 34, 1, 8, 35, 1, 8, 36, 1, 8, 37, 1, 8, 38, 1, + 8, 39, 1, 8, 40, 1, 8, 41, 1, 8, 42, 1, 8, 43, 1, 8, 44, 1, + 8, 45, 1, 8, 46, 1, 8, 47, 1, 8, 48, 1, 8, 49, 1, 8, 50, 1, + 8, 51, 1, 8, 52, 1, 8, 53, 1, 8, 54, 1, 8, 55, 1, 8, 56, 1, + 8, 57, 1, 8, 58, 1, 8, 59, 1, 8, 60, 1, 8, 61, 1, 8, 62, 1, + 8, 63, 1, 8, 64, 1, 8, 65, 1, 8, 66, 1, 8, 67, 1, 8, 68, 1, + 8, 69, 1, 8, 70, 1, 8, 71, 1, 8, 72, 1, 8, 73, 1, 8, 74, 1, + 8, 75, 1, 8, 76, 1, 8, 77, 1, 8, 78, 1, 8, 79, 1, 8, 84, 1, + 8, 85, 1, 8, 87, 1, 8, 88, 1, 8, 89, 1, 8, 90, 1, 8, 91, 1, + 8, 92, 1, 8, 93, 1, 8, 94, 1, 8, 95, 1, 8, 96, 1, 8, 97, 1, + 8, 98, 1, 8, 99, 1, 8, 100, 1, 8, 101, 1, 8, 102, 1, 8, 103, 1, + 8, 104, 1, 8, 105, 1, 8, 106, 1, 8, 107, 1, 8, 108, 1, 8, 109, 1, + 8, 110, 3, 2, 10, 2, 10, 2, 6, 97, 115, 115, 101, 116, 115, 3, 65, 65, + 67, 11, 100, 117, 109, 109, 121, 95, 102, 105, 101, 108, 100, 3, 65, 66, 67, 2, + 65, 67, 3, 65, 67, 67, 3, 65, 68, 67, 3, 65, 69, 67, 3, 65, 70, 67, + 3, 65, 71, 67, 3, 65, 72, 67, 3, 65, 73, 67, 3, 65, 74, 67, 3, 65, + 75, 67, 3, 65, 76, 67, 3, 65, 77, 67, 3, 65, 78, 67, 3, 65, 79, 67, + 3, 65, 80, 67, 3, 65, 81, 67, 3, 65, 82, 67, 3, 65, 83, 67, 3, 65, + 84, 67, 3, 65, 85, 67, 3, 65, 86, 67, 3, 65, 87, 67, 3, 65, 88, 67, + 3, 65, 89, 67, 3, 65, 90, 67, 3, 66, 65, 67, 3, 66, 66, 67, 2, 66, + 67, 3, 66, 67, 67, 3, 66, 68, 67, 3, 66, 69, 67, 3, 66, 70, 67, 3, + 66, 71, 67, 3, 66, 72, 67, 3, 66, 73, 67, 3, 66, 74, 67, 3, 66, 75, + 67, 3, 66, 76, 67, 3, 66, 77, 67, 3, 66, 78, 67, 3, 66, 79, 67, 3, + 66, 80, 67, 3, 66, 81, 67, 3, 66, 82, 67, 3, 66, 83, 67, 3, 66, 84, + 67, 3, 66, 85, 67, 3, 66, 86, 67, 3, 66, 87, 67, 3, 66, 88, 67, 3, + 66, 89, 67, 3, 66, 90, 67, 3, 67, 65, 67, 3, 67, 66, 67, 3, 67, 67, + 67, 3, 67, 68, 67, 3, 67, 69, 67, 3, 67, 70, 67, 3, 67, 71, 67, 3, + 67, 72, 67, 3, 67, 73, 67, 3, 67, 74, 67, 3, 67, 75, 67, 3, 67, 76, + 67, 3, 67, 77, 67, 3, 67, 78, 67, 3, 67, 79, 67, 3, 67, 80, 67, 3, + 67, 81, 67, 3, 67, 82, 67, 3, 67, 83, 67, 3, 67, 84, 67, 3, 67, 85, + 67, 3, 67, 86, 67, 3, 67, 87, 67, 3, 67, 88, 67, 3, 67, 89, 67, 3, + 67, 90, 67, 16, 67, 111, 105, 110, 67, 97, 112, 97, 98, 105, 108, 105, 116, 105, + 101, 115, 15, 98, 117, 114, 110, 95, 99, 97, 112, 97, 98, 105, 108, 105, 116, 121, + 14, 66, 117, 114, 110, 67, 97, 112, 97, 98, 105, 108, 105, 116, 121, 4, 99, 111, + 105, 110, 17, 102, 114, 101, 101, 122, 101, 95, 99, 97, 112, 97, 98, 105, 108, 105, + 116, 121, 16, 70, 114, 101, 101, 122, 101, 67, 97, 112, 97, 98, 105, 108, 105, 116, + 121, 15, 109, 105, 110, 116, 95, 99, 97, 112, 97, 98, 105, 108, 105, 116, 121, 14, + 77, 105, 110, 116, 67, 97, 112, 97, 98, 105, 108, 105, 116, 121, 3, 68, 65, 67, + 3, 68, 66, 67, 2, 68, 67, 3, 68, 67, 67, 3, 68, 68, 67, 3, 68, 69, + 67, 3, 68, 70, 67, 3, 68, 71, 67, 3, 68, 72, 67, 3, 68, 73, 67, 3, + 68, 74, 67, 3, 68, 75, 67, 3, 68, 76, 67, 3, 68, 77, 67, 3, 68, 78, + 67, 3, 68, 79, 67, 3, 68, 80, 67, 3, 68, 81, 67, 3, 68, 82, 67, 3, + 68, 83, 67, 3, 68, 84, 67, 3, 68, 85, 67, 3, 68, 86, 67, 3, 68, 87, + 67, 3, 68, 88, 67, 3, 68, 89, 67, 3, 68, 90, 67, 2, 69, 67, 2, 70, + 67, 2, 71, 67, 2, 72, 67, 2, 73, 67, 2, 74, 67, 2, 75, 67, 2, 76, + 67, 2, 77, 67, 2, 78, 67, 2, 79, 67, 2, 80, 67, 2, 81, 67, 2, 82, + 67, 2, 83, 67, 2, 84, 67, 2, 85, 67, 2, 86, 67, 2, 87, 67, 2, 88, + 67, 4, 98, 117, 114, 110, 4, 67, 111, 105, 110, 4, 109, 105, 110, 116, 6, 115, + 105, 103, 110, 101, 114, 10, 97, 100, 100, 114, 101, 115, 115, 95, 111, 102, 14, 105, + 110, 105, 116, 95, 99, 111, 105, 110, 95, 116, 121, 112, 101, 6, 115, 116, 114, 105, + 110, 103, 4, 117, 116, 102, 56, 6, 83, 116, 114, 105, 110, 103, 10, 105, 110, 105, + 116, 105, 97, 108, 105, 122, 101, 11, 105, 110, 105, 116, 95, 109, 111, 100, 117, 108, + 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 35, 69, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 35, 69, 10, 2, 10, 9, 66, 97, 115, 101, 32, + 99, 111, 105, 110, 10, 2, 3, 2, 66, 67, 10, 2, 11, 10, 81, 117, 111, 116, + 101, 32, 99, 111, 105, 110, 10, 2, 3, 2, 81, 67, 10, 2, 13, 12, 85, 116, + 105, 108, 105, 116, 121, 32, 99, 111, 105, 110, 10, 2, 3, 2, 85, 67, 10, 2, + 7, 6, 65, 32, 67, 111, 105, 110, 10, 2, 3, 2, 65, 67, 10, 2, 7, 6, + 68, 32, 67, 111, 105, 110, 10, 2, 3, 2, 68, 67, 10, 2, 7, 6, 70, 32, + 67, 111, 105, 110, 10, 2, 3, 2, 70, 67, 10, 2, 7, 6, 69, 32, 67, 111, + 105, 110, 10, 2, 3, 2, 69, 67, 10, 2, 7, 6, 71, 32, 67, 111, 105, 110, + 10, 2, 3, 2, 71, 67, 10, 2, 7, 6, 72, 32, 67, 111, 105, 110, 10, 2, + 3, 2, 72, 67, 10, 2, 7, 6, 73, 32, 67, 111, 105, 110, 10, 2, 3, 2, + 73, 67, 10, 2, 7, 6, 74, 32, 67, 111, 105, 110, 10, 2, 3, 2, 74, 67, + 10, 2, 7, 6, 75, 32, 67, 111, 105, 110, 10, 2, 3, 2, 75, 67, 10, 2, + 7, 6, 76, 32, 67, 111, 105, 110, 10, 2, 3, 2, 76, 67, 10, 2, 7, 6, + 77, 32, 67, 111, 105, 110, 10, 2, 3, 2, 77, 67, 10, 2, 7, 6, 78, 32, + 67, 111, 105, 110, 10, 2, 3, 2, 78, 67, 10, 2, 7, 6, 79, 32, 67, 111, + 105, 110, 10, 2, 3, 2, 79, 67, 10, 2, 7, 6, 80, 32, 67, 111, 105, 110, + 10, 2, 3, 2, 80, 67, 10, 2, 7, 6, 82, 32, 67, 111, 105, 110, 10, 2, + 3, 2, 82, 67, 10, 2, 7, 6, 83, 32, 67, 111, 105, 110, 10, 2, 3, 2, + 83, 67, 10, 2, 7, 6, 84, 32, 67, 111, 105, 110, 10, 2, 3, 2, 84, 67, + 10, 2, 7, 6, 86, 32, 67, 111, 105, 110, 10, 2, 3, 2, 86, 67, 10, 2, + 7, 6, 87, 32, 67, 111, 105, 110, 10, 2, 3, 2, 87, 67, 10, 2, 7, 6, + 88, 32, 67, 111, 105, 110, 10, 2, 3, 2, 88, 67, 10, 2, 8, 7, 65, 65, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 65, 67, 10, 2, 8, 7, 65, 66, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 66, 67, 10, 2, 8, 7, 65, 67, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 67, 67, 10, 2, 8, 7, 65, 68, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 68, 67, 10, 2, 8, 7, 65, 69, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 69, 67, 10, 2, 8, 7, 65, 70, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 70, 67, 10, 2, 8, 7, 65, 71, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 71, 67, 10, 2, 8, 7, 65, 72, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 72, 67, 10, 2, 8, 7, 65, 73, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 73, 67, 10, 2, 8, 7, 65, 74, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 74, 67, 10, 2, 8, 7, 65, 75, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 75, 67, 10, 2, 8, 7, 65, 76, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 76, 67, 10, 2, 8, 7, 65, 77, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 77, 67, 10, 2, 8, 7, 65, 78, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 78, 67, 10, 2, 8, 7, 65, 79, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 79, 67, 10, 2, 8, 7, 65, 80, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 80, 67, 10, 2, 8, 7, 65, 81, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 81, 67, 10, 2, 8, 7, 65, 82, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 82, 67, 10, 2, 8, 7, 65, 83, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 83, 67, 10, 2, 8, 7, 65, 84, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 84, 67, 10, 2, 8, 7, 65, 85, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 85, 67, 10, 2, 8, 7, 65, 86, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 86, 67, 10, 2, 8, 7, 65, 87, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 87, 67, 10, 2, 8, 7, 65, 88, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 88, 67, 10, 2, 8, 7, 65, 89, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 89, 67, 10, 2, 8, 7, 65, 90, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 65, 90, 67, 10, 2, 8, 7, 66, 65, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 65, 67, 10, 2, 8, 7, 66, 66, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 66, 67, 10, 2, 8, 7, 66, 67, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 67, 67, 10, 2, 8, 7, 66, 68, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 68, 67, 10, 2, 8, 7, 66, 69, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 69, 67, 10, 2, 8, 7, 66, 70, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 70, 67, 10, 2, 8, 7, 66, 71, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 71, 67, 10, 2, 8, 7, 66, 72, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 72, 67, 10, 2, 8, 7, 66, 73, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 73, 67, 10, 2, 8, 7, 66, 74, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 74, 67, 10, 2, 8, 7, 66, 75, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 75, 67, 10, 2, 8, 7, 66, 76, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 76, 67, 10, 2, 8, 7, 66, 77, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 77, 67, 10, 2, 8, 7, 66, 78, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 78, 67, 10, 2, 8, 7, 66, 79, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 79, 67, 10, 2, 8, 7, 66, 80, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 80, 67, 10, 2, 8, 7, 66, 81, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 81, 67, 10, 2, 8, 7, 66, 82, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 82, 67, 10, 2, 8, 7, 66, 83, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 83, 67, 10, 2, 8, 7, 66, 84, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 84, 67, 10, 2, 8, 7, 66, 85, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 85, 67, 10, 2, 8, 7, 66, 86, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 86, 67, 10, 2, 8, 7, 66, 87, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 87, 67, 10, 2, 8, 7, 66, 88, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 88, 67, 10, 2, 8, 7, 66, 89, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 89, 67, 10, 2, 8, 7, 66, 90, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 66, 90, 67, 10, 2, 8, 7, 67, 65, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 65, 67, 10, 2, 8, 7, 67, 66, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 66, 67, 10, 2, 8, 7, 67, 67, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 67, 67, 10, 2, 8, 7, 67, 68, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 68, 67, 10, 2, 8, 7, 67, 69, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 69, 67, 10, 2, 8, 7, 67, 70, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 70, 67, 10, 2, 8, 7, 67, 71, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 71, 67, 10, 2, 8, 7, 67, 72, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 72, 67, 10, 2, 8, 7, 67, 73, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 73, 67, 10, 2, 8, 7, 67, 74, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 74, 67, 10, 2, 8, 7, 67, 75, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 75, 67, 10, 2, 8, 7, 67, 76, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 76, 67, 10, 2, 8, 7, 67, 77, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 77, 67, 10, 2, 8, 7, 67, 78, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 78, 67, 10, 2, 8, 7, 67, 79, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 79, 67, 10, 2, 8, 7, 67, 80, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 80, 67, 10, 2, 8, 7, 67, 81, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 81, 67, 10, 2, 8, 7, 67, 82, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 82, 67, 10, 2, 8, 7, 67, 83, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 83, 67, 10, 2, 8, 7, 67, 84, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 84, 67, 10, 2, 8, 7, 67, 85, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 85, 67, 10, 2, 8, 7, 67, 86, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 86, 67, 10, 2, 8, 7, 67, 87, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 87, 67, 10, 2, 8, 7, 67, 88, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 88, 67, 10, 2, 8, 7, 67, 89, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 89, 67, 10, 2, 8, 7, 67, 90, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 67, 90, 67, 10, 2, 8, 7, 68, 65, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 65, 67, 10, 2, 8, 7, 68, 66, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 66, 67, 10, 2, 8, 7, 68, 67, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 67, 67, 10, 2, 8, 7, 68, 68, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 68, 67, 10, 2, 8, 7, 68, 69, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 69, 67, 10, 2, 8, 7, 68, 70, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 70, 67, 10, 2, 8, 7, 68, 71, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 71, 67, 10, 2, 8, 7, 68, 72, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 72, 67, 10, 2, 8, 7, 68, 73, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 73, 67, 10, 2, 8, 7, 68, 74, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 74, 67, 10, 2, 8, 7, 68, 75, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 75, 67, 10, 2, 8, 7, 68, 76, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 76, 67, 10, 2, 8, 7, 68, 77, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 77, 67, 10, 2, 8, 7, 68, 78, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 78, 67, 10, 2, 8, 7, 68, 79, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 79, 67, 10, 2, 8, 7, 68, 80, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 80, 67, 10, 2, 8, 7, 68, 81, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 81, 67, 10, 2, 8, 7, 68, 82, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 82, 67, 10, 2, 8, 7, 68, 83, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 83, 67, 10, 2, 8, 7, 68, 84, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 84, 67, 10, 2, 8, 7, 68, 85, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 85, 67, 10, 2, 8, 7, 68, 86, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 86, 67, 10, 2, 8, 7, 68, 87, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 87, 67, 10, 2, 8, 7, 68, 88, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 88, 67, 10, 2, 8, 7, 68, 89, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 89, 67, 10, 2, 8, 7, 68, 90, + 32, 67, 111, 105, 110, 10, 2, 4, 3, 68, 90, 67, 20, 99, 111, 109, 112, 105, + 108, 97, 116, 105, 111, 110, 95, 109, 101, 116, 97, 100, 97, 116, 97, 9, 0, 3, + 50, 46, 48, 3, 50, 46, 49, 18, 97, 112, 116, 111, 115, 58, 58, 109, 101, 116, + 97, 100, 97, 116, 97, 95, 118, 49, 122, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 12, 69, 95, 78, 79, 84, 95, 69, 67, 79, 78, 73, 65, 21, 67, 97, 108, 108, + 101, 114, 32, 105, 115, 32, 110, 111, 116, 32, 69, 99, 111, 110, 105, 97, 46, 1, + 0, 0, 0, 0, 0, 0, 0, 18, 69, 95, 72, 65, 83, 95, 67, 65, 80, 65, + 66, 73, 76, 73, 84, 73, 69, 83, 48, 67, 111, 105, 110, 32, 99, 97, 112, 97, + 98, 105, 108, 105, 116, 105, 101, 115, 32, 104, 97, 118, 101, 32, 97, 108, 114, 101, + 97, 100, 121, 32, 98, 101, 101, 110, 32, 105, 110, 105, 116, 105, 97, 108, 105, 122, + 101, 100, 46, 0, 0, 0, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 2, 1, + 2, 1, 3, 2, 1, 2, 1, 4, 2, 1, 2, 1, 5, 2, 1, 2, 1, 6, + 2, 1, 2, 1, 7, 2, 1, 2, 1, 8, 2, 1, 2, 1, 9, 2, 1, 2, + 1, 10, 2, 1, 2, 1, 11, 2, 1, 2, 1, 12, 2, 1, 2, 1, 13, 2, + 1, 2, 1, 14, 2, 1, 2, 1, 15, 2, 1, 2, 1, 16, 2, 1, 2, 1, + 17, 2, 1, 2, 1, 18, 2, 1, 2, 1, 19, 2, 1, 2, 1, 20, 2, 1, + 2, 1, 21, 2, 1, 2, 1, 22, 2, 1, 2, 1, 23, 2, 1, 2, 1, 24, + 2, 1, 2, 1, 25, 2, 1, 2, 1, 26, 2, 1, 2, 1, 27, 2, 1, 2, + 1, 28, 2, 1, 2, 1, 29, 2, 1, 2, 1, 30, 2, 1, 2, 1, 31, 2, + 1, 2, 1, 32, 2, 1, 2, 1, 33, 2, 1, 2, 1, 34, 2, 1, 2, 1, + 35, 2, 1, 2, 1, 36, 2, 1, 2, 1, 37, 2, 1, 2, 1, 38, 2, 1, + 2, 1, 39, 2, 1, 2, 1, 40, 2, 1, 2, 1, 41, 2, 1, 2, 1, 42, + 2, 1, 2, 1, 43, 2, 1, 2, 1, 44, 2, 1, 2, 1, 45, 2, 1, 2, + 1, 46, 2, 1, 2, 1, 47, 2, 1, 2, 1, 48, 2, 1, 2, 1, 49, 2, + 1, 2, 1, 50, 2, 1, 2, 1, 51, 2, 1, 2, 1, 52, 2, 1, 2, 1, + 53, 2, 1, 2, 1, 54, 2, 1, 2, 1, 55, 2, 1, 2, 1, 56, 2, 1, + 2, 1, 57, 2, 1, 2, 1, 58, 2, 1, 2, 1, 59, 2, 1, 2, 1, 60, + 2, 1, 2, 1, 61, 2, 1, 2, 1, 62, 2, 1, 2, 1, 63, 2, 1, 2, + 1, 64, 2, 1, 2, 1, 65, 2, 1, 2, 1, 66, 2, 1, 2, 1, 67, 2, + 1, 2, 1, 68, 2, 1, 2, 1, 69, 2, 1, 2, 1, 70, 2, 1, 2, 1, + 71, 2, 1, 2, 1, 72, 2, 1, 2, 1, 73, 2, 1, 2, 1, 74, 2, 1, + 2, 1, 75, 2, 1, 2, 1, 76, 2, 1, 2, 1, 77, 2, 1, 2, 1, 78, + 2, 1, 2, 1, 79, 2, 1, 2, 1, 80, 2, 3, 83, 11, 81, 1, 9, 0, + 86, 11, 82, 1, 9, 0, 88, 11, 83, 1, 9, 0, 84, 2, 1, 2, 1, 85, + 2, 1, 2, 1, 86, 2, 1, 2, 1, 87, 2, 1, 2, 1, 88, 2, 1, 2, + 1, 89, 2, 1, 2, 1, 90, 2, 1, 2, 1, 91, 2, 1, 2, 1, 92, 2, + 1, 2, 1, 93, 2, 1, 2, 1, 94, 2, 1, 2, 1, 95, 2, 1, 2, 1, + 96, 2, 1, 2, 1, 97, 2, 1, 2, 1, 98, 2, 1, 2, 1, 99, 2, 1, + 2, 1, 100, 2, 1, 2, 1, 101, 2, 1, 2, 1, 102, 2, 1, 2, 1, 103, + 2, 1, 2, 1, 104, 2, 1, 2, 1, 105, 2, 1, 2, 1, 106, 2, 1, 2, + 1, 107, 2, 1, 2, 1, 108, 2, 1, 2, 1, 109, 2, 1, 2, 1, 110, 2, + 1, 2, 1, 111, 2, 1, 2, 1, 112, 2, 1, 2, 1, 113, 2, 1, 2, 1, + 114, 2, 1, 2, 1, 115, 2, 1, 2, 1, 116, 2, 1, 2, 1, 117, 2, 1, + 2, 1, 118, 2, 1, 2, 1, 119, 2, 1, 2, 1, 120, 2, 1, 2, 1, 121, + 2, 1, 2, 1, 122, 2, 1, 2, 1, 123, 2, 1, 2, 1, 124, 2, 1, 2, + 1, 125, 2, 1, 2, 1, 126, 2, 1, 2, 1, 127, 2, 1, 2, 1, 128, 1, + 2, 1, 2, 1, 129, 1, 2, 1, 2, 1, 130, 1, 2, 1, 2, 1, 80, 2, + 80, 19, 80, 20, 80, 21, 80, 22, 80, 23, 80, 24, 80, 25, 80, 26, 80, 27, + 80, 28, 80, 29, 80, 30, 80, 31, 80, 32, 80, 33, 80, 34, 80, 35, 80, 36, + 80, 37, 80, 38, 80, 39, 80, 40, 80, 41, 80, 42, 80, 43, 80, 44, 80, 45, + 80, 46, 80, 47, 80, 48, 80, 49, 80, 50, 80, 51, 80, 52, 80, 53, 80, 54, + 80, 55, 80, 56, 80, 57, 80, 58, 80, 59, 80, 60, 80, 61, 80, 62, 80, 63, + 80, 64, 80, 65, 80, 66, 80, 67, 80, 68, 80, 69, 80, 70, 80, 71, 80, 72, + 80, 73, 80, 74, 80, 75, 80, 76, 80, 77, 80, 78, 80, 79, 80, 80, 80, 81, + 80, 82, 80, 83, 80, 84, 80, 85, 80, 86, 80, 87, 80, 88, 80, 89, 80, 90, + 80, 91, 80, 92, 80, 93, 80, 94, 80, 95, 80, 96, 80, 97, 80, 98, 80, 99, + 80, 100, 80, 101, 80, 102, 80, 103, 80, 104, 80, 105, 80, 106, 80, 107, 80, 108, + 80, 109, 80, 110, 80, 111, 80, 112, 80, 113, 80, 114, 80, 115, 80, 116, 80, 117, + 80, 118, 80, 119, 80, 120, 80, 121, 80, 122, 80, 123, 80, 124, 80, 125, 80, 126, + 80, 127, 80, 128, 1, 80, 129, 1, 80, 130, 1, 80, 131, 1, 80, 132, 1, 80, + 133, 1, 80, 134, 1, 80, 135, 1, 80, 136, 1, 80, 137, 1, 80, 138, 1, 80, + 139, 1, 80, 140, 1, 80, 141, 1, 80, 142, 1, 0, 1, 0, 1, 80, 4, 8, + 7, 0, 61, 0, 55, 0, 12, 1, 11, 0, 11, 1, 56, 0, 2, 2, 1, 0, + 1, 80, 9, 17, 11, 0, 17, 3, 12, 2, 10, 2, 7, 0, 33, 4, 15, 11, + 2, 61, 0, 55, 1, 12, 4, 11, 1, 11, 4, 56, 1, 2, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 39, 5, 0, 0, 0, 15, 30, 10, 0, 17, 3, 59, 0, + 3, 26, 10, 0, 12, 4, 11, 1, 17, 6, 11, 2, 17, 6, 11, 3, 12, 5, + 12, 7, 12, 8, 11, 4, 11, 8, 11, 7, 11, 5, 9, 56, 2, 57, 0, 12, + 9, 11, 0, 11, 9, 63, 0, 2, 11, 0, 1, 6, 1, 0, 0, 0, 0, 0, + 0, 0, 39, 8, 0, 0, 0, 143, 1, 234, 9, 10, 0, 7, 1, 7, 2, 49, + 4, 56, 3, 10, 0, 7, 3, 7, 4, 49, 12, 56, 4, 10, 0, 7, 5, 7, + 6, 49, 10, 56, 5, 10, 0, 17, 3, 59, 1, 4, 20, 5, 25, 10, 0, 7, + 7, 7, 8, 49, 10, 56, 6, 10, 0, 17, 3, 59, 2, 4, 30, 5, 35, 10, + 0, 7, 9, 7, 10, 49, 10, 56, 7, 10, 0, 17, 3, 59, 3, 4, 40, 5, + 45, 10, 0, 7, 11, 7, 12, 49, 10, 56, 8, 10, 0, 17, 3, 59, 4, 4, + 50, 5, 55, 10, 0, 7, 13, 7, 14, 49, 10, 56, 9, 10, 0, 17, 3, 59, + 5, 4, 60, 5, 65, 10, 0, 7, 15, 7, 16, 49, 10, 56, 10, 10, 0, 17, + 3, 59, 6, 4, 70, 5, 75, 10, 0, 7, 17, 7, 18, 49, 10, 56, 11, 10, + 0, 17, 3, 59, 7, 4, 80, 5, 85, 10, 0, 7, 19, 7, 20, 49, 10, 56, + 12, 10, 0, 17, 3, 59, 8, 4, 90, 5, 95, 10, 0, 7, 21, 7, 22, 49, + 10, 56, 13, 10, 0, 17, 3, 59, 9, 4, 100, 5, 105, 10, 0, 7, 23, 7, + 24, 49, 10, 56, 14, 10, 0, 17, 3, 59, 10, 4, 110, 5, 115, 10, 0, 7, + 25, 7, 26, 49, 10, 56, 15, 10, 0, 17, 3, 59, 11, 4, 120, 5, 125, 10, + 0, 7, 27, 7, 28, 49, 10, 56, 16, 10, 0, 17, 3, 59, 12, 4, 130, 1, + 5, 135, 1, 10, 0, 7, 29, 7, 30, 49, 10, 56, 17, 10, 0, 17, 3, 59, + 13, 4, 140, 1, 5, 145, 1, 10, 0, 7, 31, 7, 32, 49, 10, 56, 18, 10, + 0, 17, 3, 59, 14, 4, 150, 1, 5, 155, 1, 10, 0, 7, 33, 7, 34, 49, + 10, 56, 19, 10, 0, 17, 3, 59, 15, 4, 160, 1, 5, 165, 1, 10, 0, 7, + 35, 7, 36, 49, 10, 56, 20, 10, 0, 17, 3, 59, 16, 4, 170, 1, 5, 175, + 1, 10, 0, 7, 37, 7, 38, 49, 10, 56, 21, 10, 0, 17, 3, 59, 17, 4, + 180, 1, 5, 185, 1, 10, 0, 7, 39, 7, 40, 49, 10, 56, 22, 10, 0, 17, + 3, 59, 18, 4, 190, 1, 5, 195, 1, 10, 0, 7, 41, 7, 42, 49, 10, 56, + 23, 10, 0, 17, 3, 59, 19, 4, 200, 1, 5, 205, 1, 10, 0, 7, 43, 7, + 44, 49, 10, 56, 24, 10, 0, 17, 3, 59, 20, 4, 210, 1, 5, 215, 1, 10, + 0, 7, 45, 7, 46, 49, 10, 56, 25, 10, 0, 17, 3, 59, 21, 4, 220, 1, + 5, 225, 1, 10, 0, 7, 47, 7, 48, 49, 10, 56, 26, 10, 0, 17, 3, 59, + 22, 4, 230, 1, 5, 235, 1, 10, 0, 7, 49, 7, 50, 49, 10, 56, 27, 10, + 0, 17, 3, 59, 23, 4, 240, 1, 5, 245, 1, 10, 0, 7, 51, 7, 52, 49, + 10, 56, 28, 10, 0, 17, 3, 59, 24, 4, 250, 1, 5, 255, 1, 10, 0, 7, + 53, 7, 54, 49, 10, 56, 29, 10, 0, 17, 3, 59, 25, 4, 132, 2, 5, 137, + 2, 10, 0, 7, 55, 7, 56, 49, 10, 56, 30, 10, 0, 17, 3, 59, 26, 4, + 142, 2, 5, 147, 2, 10, 0, 7, 57, 7, 58, 49, 10, 56, 31, 10, 0, 17, + 3, 59, 27, 4, 152, 2, 5, 157, 2, 10, 0, 7, 59, 7, 60, 49, 10, 56, + 32, 10, 0, 17, 3, 59, 28, 4, 162, 2, 5, 167, 2, 10, 0, 7, 61, 7, + 62, 49, 10, 56, 33, 10, 0, 17, 3, 59, 29, 4, 172, 2, 5, 177, 2, 10, + 0, 7, 63, 7, 64, 49, 10, 56, 34, 10, 0, 17, 3, 59, 30, 4, 182, 2, + 5, 187, 2, 10, 0, 7, 65, 7, 66, 49, 10, 56, 35, 10, 0, 17, 3, 59, + 31, 4, 192, 2, 5, 197, 2, 10, 0, 7, 67, 7, 68, 49, 10, 56, 36, 10, + 0, 17, 3, 59, 32, 4, 202, 2, 5, 207, 2, 10, 0, 7, 69, 7, 70, 49, + 10, 56, 37, 10, 0, 17, 3, 59, 33, 4, 212, 2, 5, 217, 2, 10, 0, 7, + 71, 7, 72, 49, 10, 56, 38, 10, 0, 17, 3, 59, 34, 4, 222, 2, 5, 227, + 2, 10, 0, 7, 73, 7, 74, 49, 10, 56, 39, 10, 0, 17, 3, 59, 35, 4, + 232, 2, 5, 237, 2, 10, 0, 7, 75, 7, 76, 49, 10, 56, 40, 10, 0, 17, + 3, 59, 36, 4, 242, 2, 5, 247, 2, 10, 0, 7, 77, 7, 78, 49, 10, 56, + 41, 10, 0, 17, 3, 59, 37, 4, 252, 2, 5, 129, 3, 10, 0, 7, 79, 7, + 80, 49, 10, 56, 42, 10, 0, 17, 3, 59, 38, 4, 134, 3, 5, 139, 3, 10, + 0, 7, 81, 7, 82, 49, 10, 56, 43, 10, 0, 17, 3, 59, 39, 4, 144, 3, + 5, 149, 3, 10, 0, 7, 83, 7, 84, 49, 10, 56, 44, 10, 0, 17, 3, 59, + 40, 4, 154, 3, 5, 159, 3, 10, 0, 7, 85, 7, 86, 49, 10, 56, 45, 10, + 0, 17, 3, 59, 41, 4, 164, 3, 5, 169, 3, 10, 0, 7, 87, 7, 88, 49, + 10, 56, 46, 10, 0, 17, 3, 59, 42, 4, 174, 3, 5, 179, 3, 10, 0, 7, + 89, 7, 90, 49, 10, 56, 47, 10, 0, 17, 3, 59, 43, 4, 184, 3, 5, 189, + 3, 10, 0, 7, 91, 7, 92, 49, 10, 56, 48, 10, 0, 17, 3, 59, 44, 4, + 194, 3, 5, 199, 3, 10, 0, 7, 93, 7, 94, 49, 10, 56, 49, 10, 0, 17, + 3, 59, 45, 4, 204, 3, 5, 209, 3, 10, 0, 7, 95, 7, 96, 49, 10, 56, + 50, 10, 0, 17, 3, 59, 46, 4, 214, 3, 5, 219, 3, 10, 0, 7, 97, 7, + 98, 49, 10, 56, 51, 10, 0, 17, 3, 59, 47, 4, 224, 3, 5, 229, 3, 10, + 0, 7, 99, 7, 100, 49, 10, 56, 52, 10, 0, 17, 3, 59, 48, 4, 234, 3, + 5, 239, 3, 10, 0, 7, 101, 7, 102, 49, 10, 56, 53, 10, 0, 17, 3, 59, + 49, 4, 244, 3, 5, 249, 3, 10, 0, 7, 103, 7, 104, 49, 10, 56, 54, 10, + 0, 17, 3, 59, 50, 4, 254, 3, 5, 131, 4, 10, 0, 7, 105, 7, 106, 49, + 10, 56, 55, 10, 0, 17, 3, 59, 51, 4, 136, 4, 5, 141, 4, 10, 0, 7, + 107, 7, 108, 49, 10, 56, 56, 10, 0, 17, 3, 59, 52, 4, 146, 4, 5, 151, + 4, 10, 0, 7, 109, 7, 110, 49, 10, 56, 57, 10, 0, 17, 3, 59, 53, 4, + 156, 4, 5, 161, 4, 10, 0, 7, 111, 7, 112, 49, 10, 56, 58, 10, 0, 17, + 3, 59, 54, 4, 166, 4, 5, 171, 4, 10, 0, 7, 113, 7, 114, 49, 10, 56, + 59, 10, 0, 17, 3, 59, 55, 4, 176, 4, 5, 181, 4, 10, 0, 7, 115, 7, + 116, 49, 10, 56, 60, 10, 0, 17, 3, 59, 56, 4, 186, 4, 5, 191, 4, 10, + 0, 7, 117, 7, 118, 49, 10, 56, 61, 10, 0, 17, 3, 59, 57, 4, 196, 4, + 5, 201, 4, 10, 0, 7, 119, 7, 120, 49, 10, 56, 62, 10, 0, 17, 3, 59, + 58, 4, 206, 4, 5, 211, 4, 10, 0, 7, 121, 7, 122, 49, 10, 56, 63, 10, + 0, 17, 3, 59, 59, 4, 216, 4, 5, 221, 4, 10, 0, 7, 123, 7, 124, 49, + 10, 56, 64, 10, 0, 17, 3, 59, 60, 4, 226, 4, 5, 231, 4, 10, 0, 7, + 125, 7, 126, 49, 10, 56, 65, 10, 0, 17, 3, 59, 61, 4, 236, 4, 5, 241, + 4, 10, 0, 7, 127, 7, 128, 1, 49, 10, 56, 66, 10, 0, 17, 3, 59, 62, + 4, 246, 4, 5, 251, 4, 10, 0, 7, 129, 1, 7, 130, 1, 49, 10, 56, 67, + 10, 0, 17, 3, 59, 63, 4, 128, 5, 5, 133, 5, 10, 0, 7, 131, 1, 7, + 132, 1, 49, 10, 56, 68, 10, 0, 17, 3, 59, 64, 4, 138, 5, 5, 143, 5, + 10, 0, 7, 133, 1, 7, 134, 1, 49, 10, 56, 69, 10, 0, 17, 3, 59, 65, + 4, 148, 5, 5, 153, 5, 10, 0, 7, 135, 1, 7, 136, 1, 49, 10, 56, 70, + 10, 0, 17, 3, 59, 66, 4, 158, 5, 5, 163, 5, 10, 0, 7, 137, 1, 7, + 138, 1, 49, 10, 56, 71, 10, 0, 17, 3, 59, 67, 4, 168, 5, 5, 173, 5, + 10, 0, 7, 139, 1, 7, 140, 1, 49, 10, 56, 72, 10, 0, 17, 3, 59, 68, + 4, 178, 5, 5, 183, 5, 10, 0, 7, 141, 1, 7, 142, 1, 49, 10, 56, 73, + 10, 0, 17, 3, 59, 69, 4, 188, 5, 5, 193, 5, 10, 0, 7, 143, 1, 7, + 144, 1, 49, 10, 56, 74, 10, 0, 17, 3, 59, 70, 4, 198, 5, 5, 203, 5, + 10, 0, 7, 145, 1, 7, 146, 1, 49, 10, 56, 75, 10, 0, 17, 3, 59, 71, + 4, 208, 5, 5, 213, 5, 10, 0, 7, 147, 1, 7, 148, 1, 49, 10, 56, 76, + 10, 0, 17, 3, 59, 72, 4, 218, 5, 5, 223, 5, 10, 0, 7, 149, 1, 7, + 150, 1, 49, 10, 56, 77, 10, 0, 17, 3, 59, 73, 4, 228, 5, 5, 233, 5, + 10, 0, 7, 151, 1, 7, 152, 1, 49, 10, 56, 78, 10, 0, 17, 3, 59, 74, + 4, 238, 5, 5, 243, 5, 10, 0, 7, 153, 1, 7, 154, 1, 49, 10, 56, 79, + 10, 0, 17, 3, 59, 75, 4, 248, 5, 5, 253, 5, 10, 0, 7, 155, 1, 7, + 156, 1, 49, 10, 56, 80, 10, 0, 17, 3, 59, 76, 4, 130, 6, 5, 135, 6, + 10, 0, 7, 157, 1, 7, 158, 1, 49, 10, 56, 81, 10, 0, 17, 3, 59, 77, + 4, 140, 6, 5, 145, 6, 10, 0, 7, 159, 1, 7, 160, 1, 49, 10, 56, 82, + 10, 0, 17, 3, 59, 78, 4, 150, 6, 5, 155, 6, 10, 0, 7, 161, 1, 7, + 162, 1, 49, 10, 56, 83, 10, 0, 17, 3, 59, 79, 4, 160, 6, 5, 165, 6, + 10, 0, 7, 163, 1, 7, 164, 1, 49, 10, 56, 84, 10, 0, 17, 3, 59, 80, + 4, 170, 6, 5, 175, 6, 10, 0, 7, 165, 1, 7, 166, 1, 49, 10, 56, 85, + 10, 0, 17, 3, 59, 81, 4, 180, 6, 5, 185, 6, 10, 0, 7, 167, 1, 7, + 168, 1, 49, 10, 56, 86, 10, 0, 17, 3, 59, 82, 4, 190, 6, 5, 195, 6, + 10, 0, 7, 169, 1, 7, 170, 1, 49, 10, 56, 87, 10, 0, 17, 3, 59, 83, + 4, 200, 6, 5, 205, 6, 10, 0, 7, 171, 1, 7, 172, 1, 49, 10, 56, 88, + 10, 0, 17, 3, 59, 84, 4, 210, 6, 5, 215, 6, 10, 0, 7, 173, 1, 7, + 174, 1, 49, 10, 56, 89, 10, 0, 17, 3, 59, 85, 4, 220, 6, 5, 225, 6, + 10, 0, 7, 175, 1, 7, 176, 1, 49, 10, 56, 90, 10, 0, 17, 3, 59, 86, + 4, 230, 6, 5, 235, 6, 10, 0, 7, 177, 1, 7, 178, 1, 49, 10, 56, 91, + 10, 0, 17, 3, 59, 87, 4, 240, 6, 5, 245, 6, 10, 0, 7, 179, 1, 7, + 180, 1, 49, 10, 56, 92, 10, 0, 17, 3, 59, 88, 4, 250, 6, 5, 255, 6, + 10, 0, 7, 181, 1, 7, 182, 1, 49, 10, 56, 93, 10, 0, 17, 3, 59, 89, + 4, 132, 7, 5, 137, 7, 10, 0, 7, 183, 1, 7, 184, 1, 49, 10, 56, 94, + 10, 0, 17, 3, 59, 90, 4, 142, 7, 5, 147, 7, 10, 0, 7, 185, 1, 7, + 186, 1, 49, 10, 56, 95, 10, 0, 17, 3, 59, 91, 4, 152, 7, 5, 157, 7, + 10, 0, 7, 187, 1, 7, 188, 1, 49, 10, 56, 96, 10, 0, 17, 3, 59, 92, + 4, 162, 7, 5, 167, 7, 10, 0, 7, 189, 1, 7, 190, 1, 49, 10, 56, 97, + 10, 0, 17, 3, 59, 93, 4, 172, 7, 5, 177, 7, 10, 0, 7, 191, 1, 7, + 192, 1, 49, 10, 56, 98, 10, 0, 17, 3, 59, 94, 4, 182, 7, 5, 187, 7, + 10, 0, 7, 193, 1, 7, 194, 1, 49, 10, 56, 99, 10, 0, 17, 3, 59, 95, + 4, 192, 7, 5, 197, 7, 10, 0, 7, 195, 1, 7, 196, 1, 49, 10, 56, 100, + 10, 0, 17, 3, 59, 96, 4, 202, 7, 5, 207, 7, 10, 0, 7, 197, 1, 7, + 198, 1, 49, 10, 56, 101, 10, 0, 17, 3, 59, 97, 4, 212, 7, 5, 217, 7, + 10, 0, 7, 199, 1, 7, 200, 1, 49, 10, 56, 102, 10, 0, 17, 3, 59, 98, + 4, 222, 7, 5, 227, 7, 10, 0, 7, 201, 1, 7, 202, 1, 49, 10, 56, 103, + 10, 0, 17, 3, 59, 99, 4, 232, 7, 5, 237, 7, 10, 0, 7, 203, 1, 7, + 204, 1, 49, 10, 56, 104, 10, 0, 17, 3, 59, 100, 4, 242, 7, 5, 247, 7, + 10, 0, 7, 205, 1, 7, 206, 1, 49, 10, 56, 105, 10, 0, 17, 3, 59, 101, + 4, 252, 7, 5, 129, 8, 10, 0, 7, 207, 1, 7, 208, 1, 49, 10, 56, 106, + 10, 0, 17, 3, 59, 102, 4, 134, 8, 5, 139, 8, 10, 0, 7, 209, 1, 7, + 210, 1, 49, 10, 56, 107, 10, 0, 17, 3, 59, 103, 4, 144, 8, 5, 149, 8, + 10, 0, 7, 211, 1, 7, 212, 1, 49, 10, 56, 108, 10, 0, 17, 3, 59, 104, + 4, 154, 8, 5, 159, 8, 10, 0, 7, 213, 1, 7, 214, 1, 49, 10, 56, 109, + 10, 0, 17, 3, 59, 105, 4, 164, 8, 5, 169, 8, 10, 0, 7, 215, 1, 7, + 216, 1, 49, 10, 56, 110, 10, 0, 17, 3, 59, 106, 4, 174, 8, 5, 179, 8, + 10, 0, 7, 217, 1, 7, 218, 1, 49, 10, 56, 111, 10, 0, 17, 3, 59, 107, + 4, 184, 8, 5, 189, 8, 10, 0, 7, 219, 1, 7, 220, 1, 49, 10, 56, 112, + 10, 0, 17, 3, 59, 108, 4, 194, 8, 5, 199, 8, 10, 0, 7, 221, 1, 7, + 222, 1, 49, 10, 56, 113, 10, 0, 17, 3, 59, 109, 4, 204, 8, 5, 209, 8, + 10, 0, 7, 223, 1, 7, 224, 1, 49, 10, 56, 114, 10, 0, 17, 3, 59, 110, + 4, 214, 8, 5, 219, 8, 10, 0, 7, 225, 1, 7, 226, 1, 49, 10, 56, 115, + 10, 0, 17, 3, 59, 111, 4, 224, 8, 5, 229, 8, 10, 0, 7, 227, 1, 7, + 228, 1, 49, 10, 56, 116, 10, 0, 17, 3, 59, 112, 4, 234, 8, 5, 239, 8, + 10, 0, 7, 229, 1, 7, 230, 1, 49, 10, 56, 117, 10, 0, 17, 3, 59, 113, + 4, 244, 8, 5, 249, 8, 10, 0, 7, 231, 1, 7, 232, 1, 49, 10, 56, 118, + 10, 0, 17, 3, 59, 114, 4, 254, 8, 5, 131, 9, 10, 0, 7, 233, 1, 7, + 234, 1, 49, 10, 56, 119, 10, 0, 17, 3, 59, 115, 4, 136, 9, 5, 141, 9, + 10, 0, 7, 235, 1, 7, 236, 1, 49, 10, 56, 120, 10, 0, 17, 3, 59, 116, + 4, 146, 9, 5, 151, 9, 10, 0, 7, 237, 1, 7, 238, 1, 49, 10, 56, 121, + 10, 0, 17, 3, 59, 117, 4, 156, 9, 5, 161, 9, 10, 0, 7, 239, 1, 7, + 240, 1, 49, 10, 56, 122, 10, 0, 17, 3, 59, 118, 4, 166, 9, 5, 171, 9, + 10, 0, 7, 241, 1, 7, 242, 1, 49, 10, 56, 123, 10, 0, 17, 3, 59, 119, + 4, 176, 9, 5, 181, 9, 10, 0, 7, 243, 1, 7, 244, 1, 49, 10, 56, 124, + 10, 0, 17, 3, 59, 120, 4, 186, 9, 5, 191, 9, 10, 0, 7, 245, 1, 7, + 246, 1, 49, 10, 56, 125, 10, 0, 17, 3, 59, 121, 4, 196, 9, 5, 201, 9, + 10, 0, 7, 247, 1, 7, 248, 1, 49, 10, 56, 126, 10, 0, 17, 3, 59, 122, + 4, 206, 9, 5, 211, 9, 10, 0, 7, 249, 1, 7, 250, 1, 49, 10, 56, 127, + 10, 0, 17, 3, 59, 123, 4, 216, 9, 5, 221, 9, 10, 0, 7, 251, 1, 7, + 252, 1, 49, 10, 56, 128, 1, 10, 0, 17, 3, 59, 124, 3, 231, 9, 11, 0, + 7, 253, 1, 7, 254, 1, 49, 10, 56, 129, 1, 2, 11, 0, 1, 5, 230, 9, + 80, 0, 80, 2, 0, 2, 1, 2, 0, + ] +}); + +#[rustfmt::skip] +pub static MODULE_ECONIA_AVL_QUEUE: Lazy> = Lazy::new(|| { + vec![ + 161, 28, 235, 11, 7, 0, 0, 10, 13, 1, 0, 8, 2, 8, 36, 3, 44, 220, + 3, 4, 136, 4, 116, 5, 252, 4, 244, 7, 7, 240, 12, 131, 9, 8, 243, 21, + 64, 16, 179, 22, 138, 4, 10, 189, 26, 47, 11, 236, 26, 2, 12, 238, 26, 215, + 77, 13, 197, 104, 20, 14, 217, 104, 10, 0, 0, 1, 6, 1, 12, 1, 14, 0, + 1, 4, 1, 0, 0, 1, 5, 4, 2, 3, 1, 0, 1, 0, 7, 4, 0, 0, + 9, 4, 0, 2, 11, 4, 2, 3, 1, 0, 1, 3, 13, 7, 1, 0, 0, 0, + 19, 0, 1, 1, 0, 1, 2, 19, 4, 5, 2, 3, 0, 1, 3, 19, 6, 1, + 1, 0, 1, 0, 20, 8, 9, 1, 0, 1, 2, 20, 10, 11, 2, 3, 0, 1, + 3, 20, 12, 9, 1, 0, 1, 0, 21, 13, 14, 1, 0, 1, 0, 22, 0, 15, + 1, 0, 1, 0, 23, 13, 14, 1, 0, 1, 0, 24, 16, 14, 1, 0, 1, 0, + 25, 18, 19, 1, 0, 1, 0, 26, 20, 19, 1, 0, 1, 3, 27, 6, 17, 1, + 0, 1, 0, 28, 22, 17, 1, 0, 1, 0, 29, 8, 2, 1, 0, 1, 0, 30, + 8, 23, 1, 0, 1, 0, 31, 24, 19, 1, 0, 1, 0, 32, 24, 19, 1, 0, + 1, 0, 33, 8, 19, 1, 0, 1, 0, 34, 26, 27, 1, 4, 1, 1, 34, 19, + 29, 2, 3, 4, 1, 2, 34, 19, 31, 2, 3, 4, 1, 1, 35, 32, 19, 2, + 3, 0, 1, 3, 36, 19, 33, 1, 0, 1, 2, 35, 34, 19, 2, 3, 0, 1, + 0, 37, 22, 1, 1, 0, 1, 0, 38, 36, 9, 1, 0, 1, 0, 39, 22, 1, + 1, 0, 1, 0, 40, 36, 9, 1, 0, 1, 0, 41, 0, 17, 1, 0, 1, 2, + 42, 4, 17, 2, 3, 0, 1, 0, 43, 14, 14, 0, 1, 0, 44, 22, 38, 1, + 0, 1, 3, 45, 2, 33, 1, 0, 1, 0, 46, 22, 40, 1, 0, 1, 1, 19, + 41, 5, 2, 3, 0, 1, 0, 47, 22, 38, 1, 0, 1, 0, 48, 0, 17, 1, + 0, 1, 3, 49, 6, 17, 1, 0, 1, 0, 50, 44, 45, 1, 0, 1, 1, 51, + 46, 14, 2, 3, 0, 1, 0, 52, 13, 49, 1, 0, 1, 0, 53, 0, 51, 1, + 0, 1, 0, 54, 52, 14, 1, 0, 1, 1, 20, 53, 11, 2, 3, 0, 1, 3, + 55, 55, 19, 1, 0, 1, 0, 56, 58, 19, 1, 0, 1, 0, 57, 22, 17, 1, + 0, 1, 0, 58, 14, 17, 0, 1, 0, 59, 0, 17, 1, 0, 1, 0, 60, 0, + 14, 1, 0, 1, 0, 61, 62, 63, 1, 0, 1, 0, 62, 36, 2, 1, 0, 1, + 0, 63, 36, 2, 1, 0, 1, 0, 64, 66, 67, 1, 0, 1, 3, 65, 12, 2, + 1, 0, 1, 0, 66, 70, 71, 1, 0, 1, 0, 67, 72, 19, 1, 0, 1, 0, + 68, 77, 78, 0, 1, 0, 69, 79, 80, 1, 0, 1, 0, 70, 81, 77, 1, 0, + 1, 0, 71, 84, 80, 1, 0, 1, 0, 72, 84, 80, 1, 0, 1, 0, 73, 84, + 80, 1, 0, 1, 0, 74, 84, 80, 1, 0, 1, 0, 75, 0, 17, 1, 0, 1, + 0, 76, 0, 17, 1, 0, 1, 1, 3, 2, 2, 4, 3, 5, 2, 7, 2, 8, + 2, 9, 2, 2, 17, 10, 2, 11, 2, 12, 17, 15, 2, 12, 14, 2, 14, 16, + 2, 17, 2, 18, 2, 20, 28, 20, 30, 21, 3, 22, 28, 22, 30, 23, 2, 24, + 3, 30, 3, 12, 2, 23, 14, 33, 14, 23, 7, 35, 28, 33, 7, 38, 17, 6, + 2, 40, 30, 33, 2, 35, 30, 14, 2, 42, 2, 43, 2, 44, 30, 44, 28, 45, + 2, 40, 28, 46, 2, 47, 2, 51, 2, 54, 2, 55, 2, 56, 2, 57, 2, 59, + 2, 60, 2, 61, 2, 62, 2, 63, 2, 64, 2, 23, 17, 33, 17, 2, 6, 11, + 0, 1, 9, 0, 3, 1, 6, 9, 0, 1, 9, 0, 2, 3, 11, 5, 1, 9, + 0, 2, 6, 11, 4, 2, 9, 0, 9, 1, 9, 0, 1, 6, 9, 1, 1, 6, + 11, 5, 1, 9, 0, 1, 2, 2, 7, 11, 0, 1, 9, 0, 3, 1, 7, 9, + 0, 2, 7, 11, 4, 2, 9, 0, 9, 1, 9, 0, 1, 7, 9, 1, 1, 7, + 11, 5, 1, 9, 0, 3, 7, 11, 0, 1, 9, 0, 3, 9, 0, 1, 3, 2, + 3, 11, 5, 1, 1, 5, 7, 11, 0, 1, 9, 0, 3, 3, 3, 11, 5, 1, + 1, 1, 1, 4, 7, 11, 0, 1, 9, 0, 3, 1, 1, 0, 3, 7, 11, 0, + 1, 9, 0, 3, 3, 12, 3, 11, 5, 1, 1, 3, 1, 3, 3, 3, 1, 7, + 11, 0, 1, 9, 0, 1, 1, 2, 1, 6, 11, 0, 1, 9, 0, 3, 9, 0, + 11, 5, 1, 3, 11, 5, 1, 3, 4, 7, 11, 0, 1, 9, 0, 3, 1, 3, + 17, 2, 3, 11, 5, 1, 3, 11, 5, 1, 3, 9, 0, 1, 1, 1, 4, 3, + 3, 1, 2, 1, 7, 11, 0, 1, 9, 0, 1, 9, 0, 3, 1, 3, 3, 1, + 11, 0, 1, 9, 0, 2, 3, 8, 2, 1, 11, 1, 2, 9, 0, 9, 1, 2, + 3, 8, 3, 1, 11, 4, 2, 9, 0, 9, 1, 3, 7, 11, 1, 2, 9, 0, + 9, 1, 9, 0, 9, 1, 1, 11, 5, 1, 9, 0, 3, 7, 11, 4, 2, 9, + 0, 9, 1, 9, 0, 9, 1, 13, 3, 1, 4, 4, 11, 4, 2, 3, 11, 5, + 1, 9, 0, 11, 1, 2, 3, 8, 3, 11, 1, 2, 3, 8, 2, 2, 11, 0, + 1, 9, 0, 3, 2, 3, 11, 0, 1, 9, 0, 1, 7, 11, 0, 1, 9, 0, + 2, 2, 1, 1, 11, 5, 1, 3, 4, 4, 2, 3, 11, 5, 1, 3, 1, 11, + 5, 1, 2, 2, 6, 11, 1, 2, 9, 0, 9, 1, 9, 0, 6, 3, 3, 4, + 2, 2, 2, 4, 3, 11, 5, 1, 1, 1, 1, 4, 7, 11, 0, 1, 9, 0, + 3, 9, 0, 2, 3, 3, 3, 11, 5, 1, 9, 0, 1, 6, 11, 1, 2, 9, + 0, 9, 1, 23, 2, 4, 3, 3, 3, 4, 3, 2, 2, 1, 1, 1, 2, 2, + 1, 1, 1, 6, 8, 3, 3, 3, 11, 5, 1, 9, 0, 3, 11, 5, 1, 9, + 0, 13, 4, 2, 2, 3, 3, 3, 3, 1, 1, 3, 1, 1, 1, 3, 3, 3, + 9, 0, 12, 4, 2, 2, 3, 3, 3, 1, 1, 1, 6, 8, 3, 3, 9, 0, + 2, 3, 3, 4, 7, 11, 0, 1, 9, 0, 3, 3, 9, 0, 2, 7, 11, 1, + 2, 9, 0, 9, 1, 9, 0, 6, 3, 3, 3, 7, 8, 3, 2, 7, 8, 2, + 2, 7, 11, 5, 1, 9, 0, 9, 0, 15, 7, 11, 1, 2, 3, 8, 3, 7, + 11, 4, 2, 3, 11, 5, 1, 9, 0, 2, 3, 2, 2, 2, 8, 3, 7, 11, + 4, 2, 3, 11, 5, 1, 9, 0, 11, 5, 1, 9, 0, 7, 8, 3, 2, 4, + 4, 7, 2, 4, 6, 11, 1, 2, 3, 8, 2, 3, 3, 3, 4, 7, 11, 0, + 1, 9, 0, 3, 3, 11, 5, 1, 1, 8, 4, 7, 11, 1, 2, 3, 8, 2, + 8, 2, 7, 8, 2, 2, 4, 4, 7, 4, 3, 3, 7, 8, 2, 2, 2, 2, + 6, 8, 3, 3, 6, 11, 0, 1, 9, 0, 3, 1, 3, 3, 3, 3, 5, 2, + 6, 8, 3, 3, 1, 3, 4, 3, 6, 8, 3, 2, 3, 7, 7, 11, 0, 1, + 9, 0, 3, 3, 1, 1, 3, 3, 2, 11, 5, 1, 3, 11, 5, 1, 3, 13, + 7, 8, 3, 3, 3, 2, 3, 3, 3, 3, 1, 1, 11, 5, 1, 3, 11, 5, + 1, 3, 9, 0, 11, 1, 11, 5, 1, 3, 11, 5, 1, 3, 7, 11, 1, 2, + 3, 8, 2, 7, 11, 1, 2, 3, 8, 3, 7, 8, 2, 11, 5, 1, 3, 11, + 5, 1, 3, 7, 8, 3, 2, 3, 6, 7, 11, 0, 1, 9, 0, 2, 2, 3, + 3, 3, 3, 3, 3, 1, 6, 7, 11, 0, 1, 9, 0, 3, 3, 3, 3, 1, + 17, 4, 2, 2, 2, 3, 3, 3, 3, 1, 1, 3, 1, 1, 1, 3, 7, 8, + 2, 1, 4, 3, 1, 7, 8, 2, 2, 17, 7, 11, 1, 2, 3, 8, 2, 7, + 8, 2, 3, 3, 3, 1, 7, 8, 2, 3, 3, 1, 4, 2, 3, 4, 7, 8, + 2, 7, 8, 2, 3, 4, 3, 4, 3, 4, 4, 7, 8, 2, 1, 1, 2, 4, + 2, 2, 2, 2, 4, 7, 11, 0, 1, 9, 0, 3, 3, 1, 2, 3, 2, 6, + 7, 11, 0, 1, 9, 0, 3, 3, 3, 2, 2, 13, 2, 7, 8, 2, 3, 2, + 2, 2, 2, 3, 1, 2, 2, 3, 2, 9, 7, 8, 2, 1, 1, 2, 2, 7, + 8, 2, 1, 1, 2, 5, 7, 11, 0, 1, 9, 0, 3, 3, 3, 2, 7, 4, + 2, 2, 2, 3, 3, 3, 9, 7, 11, 1, 2, 3, 8, 2, 3, 7, 8, 2, + 2, 2, 2, 2, 2, 2, 9, 7, 11, 1, 2, 3, 8, 2, 4, 2, 2, 2, + 3, 3, 3, 7, 8, 2, 14, 4, 2, 2, 2, 1, 2, 2, 2, 4, 2, 2, + 2, 2, 2, 9, 3, 3, 11, 5, 1, 1, 6, 11, 1, 2, 3, 8, 2, 6, + 8, 2, 3, 2, 1, 3, 12, 6, 11, 1, 2, 3, 8, 2, 6, 8, 2, 1, + 2, 2, 4, 3, 3, 2, 3, 4, 3, 9, 3, 4, 2, 2, 3, 1, 3, 1, + 1, 9, 97, 118, 108, 95, 113, 117, 101, 117, 101, 8, 65, 86, 76, 113, 117, 101, + 117, 101, 4, 98, 105, 116, 115, 9, 114, 111, 111, 116, 95, 108, 115, 98, 115, 10, + 116, 114, 101, 101, 95, 110, 111, 100, 101, 115, 15, 84, 97, 98, 108, 101, 87, 105, + 116, 104, 76, 101, 110, 103, 116, 104, 17, 116, 97, 98, 108, 101, 95, 119, 105, 116, + 104, 95, 108, 101, 110, 103, 116, 104, 8, 84, 114, 101, 101, 78, 111, 100, 101, 10, + 108, 105, 115, 116, 95, 110, 111, 100, 101, 115, 8, 76, 105, 115, 116, 78, 111, 100, + 101, 6, 118, 97, 108, 117, 101, 115, 5, 84, 97, 98, 108, 101, 5, 116, 97, 98, + 108, 101, 6, 79, 112, 116, 105, 111, 110, 6, 111, 112, 116, 105, 111, 110, 9, 108, + 97, 115, 116, 95, 109, 115, 98, 115, 9, 108, 97, 115, 116, 95, 108, 115, 98, 115, + 9, 110, 101, 120, 116, 95, 109, 115, 98, 115, 9, 110, 101, 120, 116, 95, 108, 115, + 98, 115, 6, 98, 111, 114, 114, 111, 119, 10, 98, 111, 114, 114, 111, 119, 95, 109, + 117, 116, 6, 105, 110, 115, 101, 114, 116, 6, 115, 101, 97, 114, 99, 104, 16, 105, + 110, 115, 101, 114, 116, 95, 108, 105, 115, 116, 95, 110, 111, 100, 101, 16, 105, 110, + 115, 101, 114, 116, 95, 116, 114, 101, 101, 95, 110, 111, 100, 101, 7, 114, 101, 116, + 114, 97, 99, 101, 22, 105, 110, 115, 101, 114, 116, 95, 99, 104, 101, 99, 107, 95, + 104, 101, 97, 100, 95, 116, 97, 105, 108, 7, 105, 115, 95, 115, 111, 109, 101, 8, + 105, 115, 95, 101, 109, 112, 116, 121, 6, 114, 101, 109, 111, 118, 101, 16, 114, 101, + 109, 111, 118, 101, 95, 108, 105, 115, 116, 95, 110, 111, 100, 101, 18, 114, 101, 109, + 111, 118, 101, 95, 117, 112, 100, 97, 116, 101, 95, 104, 101, 97, 100, 18, 114, 101, + 109, 111, 118, 101, 95, 117, 112, 100, 97, 116, 101, 95, 116, 97, 105, 108, 16, 114, + 101, 109, 111, 118, 101, 95, 116, 114, 101, 101, 95, 110, 111, 100, 101, 3, 110, 101, + 119, 3, 97, 100, 100, 4, 110, 111, 110, 101, 11, 98, 111, 114, 114, 111, 119, 95, + 104, 101, 97, 100, 15, 98, 111, 114, 114, 111, 119, 95, 104, 101, 97, 100, 95, 109, + 117, 116, 11, 98, 111, 114, 114, 111, 119, 95, 116, 97, 105, 108, 15, 98, 111, 114, + 114, 111, 119, 95, 116, 97, 105, 108, 95, 109, 117, 116, 28, 99, 111, 110, 116, 97, + 105, 110, 115, 95, 97, 99, 116, 105, 118, 101, 95, 108, 105, 115, 116, 95, 110, 111, + 100, 101, 95, 105, 100, 8, 99, 111, 110, 116, 97, 105, 110, 115, 28, 103, 101, 116, + 95, 97, 99, 99, 101, 115, 115, 95, 107, 101, 121, 95, 105, 110, 115, 101, 114, 116, + 105, 111, 110, 95, 107, 101, 121, 12, 103, 101, 116, 95, 104, 101, 97, 100, 95, 107, + 101, 121, 4, 115, 111, 109, 101, 10, 103, 101, 116, 95, 104, 101, 105, 103, 104, 116, + 12, 103, 101, 116, 95, 116, 97, 105, 108, 95, 107, 101, 121, 7, 104, 97, 115, 95, + 107, 101, 121, 7, 105, 115, 95, 110, 111, 110, 101, 21, 105, 110, 115, 101, 114, 116, + 95, 99, 104, 101, 99, 107, 95, 101, 118, 105, 99, 116, 105, 111, 110, 6, 108, 101, + 110, 103, 116, 104, 17, 105, 110, 115, 101, 114, 116, 95, 101, 118, 105, 99, 116, 95, + 116, 97, 105, 108, 30, 105, 110, 115, 101, 114, 116, 95, 108, 105, 115, 116, 95, 110, + 111, 100, 101, 95, 103, 101, 116, 95, 108, 97, 115, 116, 95, 110, 101, 120, 116, 30, + 105, 110, 115, 101, 114, 116, 95, 108, 105, 115, 116, 95, 110, 111, 100, 101, 95, 97, + 115, 115, 105, 103, 110, 95, 102, 105, 101, 108, 100, 115, 4, 102, 105, 108, 108, 35, + 105, 110, 115, 101, 114, 116, 95, 116, 114, 101, 101, 95, 110, 111, 100, 101, 95, 117, + 112, 100, 97, 116, 101, 95, 112, 97, 114, 101, 110, 116, 95, 101, 100, 103, 101, 12, + 105, 115, 95, 97, 115, 99, 101, 110, 100, 105, 110, 103, 23, 105, 115, 95, 97, 115, + 99, 101, 110, 100, 105, 110, 103, 95, 97, 99, 99, 101, 115, 115, 95, 107, 101, 121, + 13, 105, 115, 95, 108, 111, 99, 97, 108, 95, 116, 97, 105, 108, 31, 110, 101, 120, + 116, 95, 108, 105, 115, 116, 95, 110, 111, 100, 101, 95, 105, 100, 95, 105, 110, 95, + 97, 99, 99, 101, 115, 115, 95, 107, 101, 121, 8, 116, 114, 97, 118, 101, 114, 115, + 101, 8, 112, 111, 112, 95, 104, 101, 97, 100, 8, 112, 111, 112, 95, 116, 97, 105, + 108, 29, 114, 101, 109, 111, 118, 101, 95, 108, 105, 115, 116, 95, 110, 111, 100, 101, + 95, 117, 112, 100, 97, 116, 101, 95, 101, 100, 103, 101, 115, 7, 101, 120, 116, 114, + 97, 99, 116, 30, 114, 101, 109, 111, 118, 101, 95, 116, 114, 101, 101, 95, 110, 111, + 100, 101, 95, 119, 105, 116, 104, 95, 99, 104, 105, 108, 100, 114, 101, 110, 26, 114, + 101, 109, 111, 118, 101, 95, 116, 114, 101, 101, 95, 110, 111, 100, 101, 95, 102, 111, + 108, 108, 111, 119, 95, 117, 112, 22, 114, 101, 116, 114, 97, 99, 101, 95, 117, 112, + 100, 97, 116, 101, 95, 104, 101, 105, 103, 104, 116, 115, 17, 114, 101, 116, 114, 97, + 99, 101, 95, 114, 101, 98, 97, 108, 97, 110, 99, 101, 20, 114, 101, 116, 114, 97, + 99, 101, 95, 112, 114, 101, 112, 95, 105, 116, 101, 114, 97, 116, 101, 35, 114, 101, + 116, 114, 97, 99, 101, 95, 114, 101, 98, 97, 108, 97, 110, 99, 101, 95, 114, 111, + 116, 97, 116, 101, 95, 108, 101, 102, 116, 95, 114, 105, 103, 104, 116, 30, 114, 101, + 116, 114, 97, 99, 101, 95, 114, 101, 98, 97, 108, 97, 110, 99, 101, 95, 114, 111, + 116, 97, 116, 101, 95, 114, 105, 103, 104, 116, 35, 114, 101, 116, 114, 97, 99, 101, + 95, 114, 101, 98, 97, 108, 97, 110, 99, 101, 95, 114, 111, 116, 97, 116, 101, 95, + 114, 105, 103, 104, 116, 95, 108, 101, 102, 116, 29, 114, 101, 116, 114, 97, 99, 101, + 95, 114, 101, 98, 97, 108, 97, 110, 99, 101, 95, 114, 111, 116, 97, 116, 101, 95, + 108, 101, 102, 116, 17, 119, 111, 117, 108, 100, 95, 117, 112, 100, 97, 116, 101, 95, + 104, 101, 97, 100, 17, 119, 111, 117, 108, 100, 95, 117, 112, 100, 97, 116, 101, 95, + 116, 97, 105, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 35, 69, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 20, 99, 111, 109, + 112, 105, 108, 97, 116, 105, 111, 110, 95, 109, 101, 116, 97, 100, 97, 116, 97, 9, + 0, 3, 50, 46, 48, 3, 50, 46, 49, 18, 97, 112, 116, 111, 115, 58, 58, 109, + 101, 116, 97, 100, 97, 116, 97, 95, 118, 49, 214, 3, 6, 0, 0, 0, 0, 0, + 0, 0, 0, 21, 69, 95, 84, 79, 79, 95, 77, 65, 78, 89, 95, 84, 82, 69, + 69, 95, 78, 79, 68, 69, 83, 43, 78, 117, 109, 98, 101, 114, 32, 111, 102, 32, + 97, 108, 108, 111, 99, 97, 116, 101, 100, 32, 116, 114, 101, 101, 32, 110, 111, 100, + 101, 115, 32, 105, 115, 32, 116, 111, 111, 32, 104, 105, 103, 104, 46, 1, 0, 0, + 0, 0, 0, 0, 0, 21, 69, 95, 84, 79, 79, 95, 77, 65, 78, 89, 95, 76, + 73, 83, 84, 95, 78, 79, 68, 69, 83, 43, 78, 117, 109, 98, 101, 114, 32, 111, + 102, 32, 97, 108, 108, 111, 99, 97, 116, 101, 100, 32, 108, 105, 115, 116, 32, 110, + 111, 100, 101, 115, 32, 105, 115, 32, 116, 111, 111, 32, 104, 105, 103, 104, 46, 2, + 0, 0, 0, 0, 0, 0, 0, 25, 69, 95, 73, 78, 83, 69, 82, 84, 73, 79, + 78, 95, 75, 69, 89, 95, 84, 79, 79, 95, 76, 65, 82, 71, 69, 27, 73, 110, + 115, 101, 114, 116, 105, 111, 110, 32, 107, 101, 121, 32, 105, 115, 32, 116, 111, 111, + 32, 108, 97, 114, 103, 101, 46, 3, 0, 0, 0, 0, 0, 0, 0, 13, 69, 95, + 69, 86, 73, 67, 84, 95, 69, 77, 80, 84, 89, 55, 65, 116, 116, 101, 109, 112, + 116, 101, 100, 32, 105, 110, 115, 101, 114, 116, 105, 111, 110, 32, 119, 105, 116, 104, + 32, 101, 118, 105, 99, 116, 105, 111, 110, 32, 102, 114, 111, 109, 32, 101, 109, 112, + 116, 121, 32, 65, 86, 76, 32, 113, 117, 101, 117, 101, 46, 4, 0, 0, 0, 0, + 0, 0, 0, 16, 69, 95, 69, 86, 73, 67, 84, 95, 78, 69, 87, 95, 84, 65, + 73, 76, 91, 65, 116, 116, 101, 109, 112, 116, 101, 100, 32, 105, 110, 115, 101, 114, + 116, 105, 111, 110, 32, 119, 105, 116, 104, 32, 101, 118, 105, 99, 116, 105, 111, 110, + 32, 102, 111, 114, 32, 107, 101, 121, 45, 118, 97, 108, 117, 101, 32, 105, 110, 115, + 101, 114, 116, 105, 111, 110, 32, 112, 97, 105, 114, 10, 32, 116, 104, 97, 116, 32, + 119, 111, 117, 108, 100, 32, 98, 101, 99, 111, 109, 101, 32, 110, 101, 119, 32, 116, + 97, 105, 108, 46, 5, 0, 0, 0, 0, 0, 0, 0, 16, 69, 95, 73, 78, 86, + 65, 76, 73, 68, 95, 72, 69, 73, 71, 72, 84, 36, 83, 112, 101, 99, 105, 102, + 105, 101, 100, 32, 104, 101, 105, 103, 104, 116, 32, 101, 120, 99, 101, 101, 100, 115, + 32, 109, 97, 120, 32, 104, 101, 105, 103, 104, 116, 46, 0, 0, 0, 2, 5, 2, + 4, 3, 2, 4, 11, 1, 2, 3, 8, 2, 8, 11, 1, 2, 3, 8, 3, 10, + 11, 4, 2, 3, 11, 5, 1, 9, 0, 3, 2, 4, 15, 2, 16, 2, 17, 2, + 18, 2, 2, 2, 1, 2, 4, 0, 2, 0, 1, 0, 0, 7, 12, 11, 1, 49, + 33, 48, 6, 255, 63, 0, 0, 0, 0, 0, 0, 28, 12, 1, 11, 0, 55, 0, + 11, 1, 56, 0, 56, 1, 2, 3, 1, 0, 0, 7, 12, 11, 1, 49, 33, 48, + 6, 255, 63, 0, 0, 0, 0, 0, 0, 28, 12, 1, 11, 0, 54, 0, 11, 1, + 56, 2, 56, 3, 2, 6, 1, 0, 0, 21, 102, 10, 1, 6, 255, 255, 255, 255, + 0, 0, 0, 0, 37, 4, 98, 10, 0, 46, 10, 1, 56, 4, 12, 4, 12, 3, + 10, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 94, 8, 12, 6, 10, + 6, 4, 91, 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, 5, 10, 0, 11, 5, + 11, 2, 56, 5, 12, 7, 10, 6, 4, 88, 10, 0, 10, 1, 10, 3, 10, 7, + 10, 4, 56, 6, 12, 8, 11, 6, 4, 85, 10, 3, 6, 0, 0, 0, 0, 0, + 0, 0, 0, 34, 12, 10, 11, 10, 3, 43, 5, 58, 10, 0, 12, 11, 11, 3, + 12, 9, 8, 14, 4, 56, 7, 20, 12, 12, 12, 13, 11, 11, 11, 9, 11, 13, + 11, 12, 56, 8, 10, 0, 10, 1, 10, 7, 56, 9, 11, 0, 55, 1, 20, 49, + 126, 48, 50, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 28, 52, 49, 32, 47, 12, 9, 11, 1, 11, 9, 27, 11, 7, 49, 33, 47, + 27, 11, 8, 49, 47, 47, 27, 2, 9, 12, 10, 5, 40, 10, 3, 12, 8, 5, + 34, 10, 3, 12, 5, 5, 20, 14, 4, 56, 10, 12, 6, 5, 16, 11, 0, 1, + 6, 2, 0, 0, 0, 0, 0, 0, 0, 39, 13, 1, 0, 0, 19, 10, 11, 0, + 55, 1, 20, 49, 84, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 28, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 33, 2, 14, 1, 0, 0, 25, 116, 10, 1, 49, 33, 48, + 6, 255, 63, 0, 0, 0, 0, 0, 0, 28, 12, 3, 10, 0, 10, 3, 56, 11, + 12, 4, 12, 5, 12, 6, 14, 5, 56, 12, 12, 7, 14, 4, 56, 12, 12, 8, + 10, 7, 4, 113, 8, 12, 9, 11, 9, 4, 110, 10, 0, 55, 1, 20, 12, 10, + 10, 10, 49, 84, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 28, 52, 10, 10, 49, 38, 48, 50, 255, 63, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 11, 10, 49, 126, 48, 50, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 51, + 12, 2, 12, 11, 10, 3, 33, 11, 11, 11, 3, 33, 12, 13, 11, 2, 49, 1, + 33, 12, 15, 11, 1, 49, 47, 48, 6, 255, 63, 0, 0, 0, 0, 0, 0, 28, + 12, 12, 3, 66, 5, 79, 10, 0, 12, 16, 14, 5, 56, 13, 20, 10, 15, 12, + 17, 12, 11, 11, 16, 11, 11, 11, 17, 10, 12, 56, 14, 11, 13, 3, 82, 5, + 93, 10, 0, 12, 16, 14, 4, 56, 13, 20, 12, 11, 11, 16, 11, 11, 11, 15, + 10, 12, 56, 15, 11, 7, 4, 107, 11, 8, 12, 17, 11, 17, 4, 104, 11, 0, + 11, 12, 56, 16, 11, 6, 2, 11, 0, 1, 5, 102, 9, 12, 17, 5, 97, 11, + 0, 1, 5, 102, 10, 8, 12, 9, 5, 22, 19, 1, 0, 0, 35, 119, 10, 1, + 6, 255, 63, 0, 0, 0, 0, 0, 0, 37, 4, 117, 10, 2, 6, 255, 63, 0, + 0, 0, 0, 0, 0, 37, 4, 115, 11, 0, 9, 33, 4, 112, 50, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 5, 10, 1, 53, + 49, 112, 47, 12, 6, 11, 5, 11, 6, 27, 10, 2, 53, 49, 98, 47, 27, 12, + 6, 49, 0, 56, 17, 56, 18, 56, 19, 12, 7, 12, 8, 12, 9, 12, 10, 11, + 6, 11, 10, 11, 9, 11, 8, 11, 7, 57, 0, 12, 11, 10, 1, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 36, 3, 48, 5, 69, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 12, 3, 10, 3, 10, 1, 35, 3, 55, 5, 69, 13, 11, 54, 2, 10, + 3, 6, 1, 0, 0, 0, 0, 0, 0, 0, 22, 10, 3, 53, 18, 2, 56, 20, + 11, 3, 6, 1, 0, 0, 0, 0, 0, 0, 0, 22, 12, 3, 5, 50, 10, 2, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 36, 3, 74, 5, 110, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 12, 3, 10, 3, 10, 2, 35, 3, 81, 5, 110, 13, 11, + 54, 3, 10, 3, 6, 1, 0, 0, 0, 0, 0, 0, 0, 22, 49, 0, 49, 0, + 10, 3, 49, 8, 48, 51, 10, 3, 6, 255, 0, 0, 0, 0, 0, 0, 0, 28, + 51, 18, 1, 56, 21, 13, 11, 54, 0, 10, 3, 6, 1, 0, 0, 0, 0, 0, + 0, 0, 22, 56, 22, 56, 23, 11, 3, 6, 1, 0, 0, 0, 0, 0, 0, 0, + 22, 12, 3, 5, 76, 11, 11, 2, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 64, 12, 5, 5, 14, 6, 1, 0, 0, 0, 0, 0, + 0, 0, 39, 6, 0, 0, 0, 0, 0, 0, 0, 0, 39, 25, 1, 0, 0, 14, + 15, 10, 0, 55, 1, 20, 49, 84, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 1, 11, 0, 55, 0, 11, 1, + 56, 0, 56, 1, 2, 26, 1, 0, 0, 14, 15, 10, 0, 55, 1, 20, 49, 84, + 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 28, 52, 12, 1, 11, 0, 54, 0, 11, 1, 56, 2, 56, 3, 2, 27, 1, 0, + 0, 14, 15, 10, 0, 55, 1, 20, 49, 38, 48, 50, 255, 63, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 1, 11, 0, 55, 0, + 11, 1, 56, 0, 56, 1, 2, 28, 1, 0, 0, 14, 15, 10, 0, 55, 1, 20, + 49, 38, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 28, 52, 12, 1, 11, 0, 54, 0, 11, 1, 56, 2, 56, 3, 2, 29, + 1, 0, 0, 37, 24, 11, 1, 49, 33, 48, 6, 255, 63, 0, 0, 0, 0, 0, + 0, 28, 12, 1, 10, 0, 55, 0, 10, 1, 56, 24, 3, 17, 11, 0, 1, 9, + 12, 3, 11, 3, 2, 11, 0, 55, 0, 11, 1, 56, 0, 56, 25, 12, 3, 5, + 15, 31, 1, 0, 0, 14, 4, 11, 0, 6, 255, 255, 255, 255, 0, 0, 0, 0, + 28, 2, 32, 1, 0, 0, 39, 28, 11, 0, 55, 1, 20, 12, 1, 10, 1, 49, + 84, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 28, 52, 11, 1, 49, 52, 48, 50, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 3, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 33, 4, 24, 56, 26, 12, 4, 11, 4, 2, 11, 3, 56, 27, 12, 4, + 5, 22, 34, 1, 0, 0, 42, 55, 10, 0, 55, 1, 20, 50, 63, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 49, 8, 47, 52, 10, + 0, 55, 4, 20, 52, 27, 12, 1, 10, 1, 6, 0, 0, 0, 0, 0, 0, 0, + 0, 33, 4, 22, 11, 0, 1, 56, 28, 2, 11, 0, 55, 2, 11, 1, 56, 29, + 16, 5, 20, 12, 3, 10, 3, 49, 89, 48, 50, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 51, 12, 4, 11, 3, 49, 84, 48, + 50, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, + 51, 12, 5, 10, 4, 10, 5, 38, 4, 52, 11, 4, 12, 6, 11, 6, 56, 30, + 2, 11, 5, 12, 6, 5, 49, 36, 1, 0, 0, 39, 28, 11, 0, 55, 1, 20, + 12, 1, 10, 1, 49, 38, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 28, 52, 11, 1, 49, 6, 48, 50, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 3, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 4, 24, 56, 26, 12, 4, 11, 4, 2, 11, + 3, 56, 27, 12, 4, 5, 22, 37, 1, 0, 0, 43, 30, 10, 1, 6, 255, 255, + 255, 255, 0, 0, 0, 0, 37, 4, 26, 11, 0, 11, 1, 56, 4, 12, 3, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 34, 4, 23, 14, 3, 56, 31, 12, 4, 11, + 4, 4, 20, 8, 12, 5, 11, 5, 2, 9, 12, 5, 5, 18, 9, 12, 4, 5, + 14, 11, 0, 1, 6, 2, 0, 0, 0, 0, 0, 0, 0, 39, 39, 1, 0, 0, + 47, 214, 1, 10, 3, 49, 18, 37, 4, 210, 1, 10, 0, 55, 1, 20, 12, 5, + 10, 5, 49, 38, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 28, 52, 12, 6, 10, 6, 6, 0, 0, 0, 0, 0, 0, 0, + 0, 33, 4, 26, 11, 0, 11, 1, 11, 2, 56, 32, 6, 0, 0, 0, 0, 0, + 0, 0, 0, 56, 22, 2, 10, 5, 49, 98, 48, 50, 255, 63, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 8, 10, 5, 50, 63, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 49, 8, + 47, 52, 10, 0, 55, 4, 20, 52, 27, 12, 10, 10, 0, 55, 2, 11, 10, 56, + 29, 16, 5, 20, 12, 9, 10, 9, 49, 89, 48, 50, 31, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 51, 12, 4, 11, 9, 49, 84, + 48, 50, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 28, 51, 12, 11, 10, 4, 10, 11, 38, 4, 207, 1, 11, 4, 12, 12, 11, 12, + 11, 3, 36, 12, 13, 10, 0, 55, 3, 56, 33, 6, 255, 63, 0, 0, 0, 0, + 0, 0, 33, 4, 204, 1, 11, 8, 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, + 12, 14, 11, 13, 4, 201, 1, 8, 12, 15, 11, 15, 4, 196, 1, 10, 5, 49, + 126, 48, 50, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 28, 51, 12, 16, 10, 16, 49, 1, 33, 12, 18, 11, 5, 49, 6, 48, 50, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, + 12, 10, 10, 18, 4, 193, 1, 10, 1, 10, 10, 38, 12, 19, 11, 19, 4, 183, + 1, 8, 12, 20, 11, 20, 4, 129, 1, 11, 0, 1, 6, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 11, 2, 56, 34, 2, 10, + 0, 55, 3, 10, 6, 56, 35, 12, 21, 10, 21, 16, 6, 20, 52, 49, 8, 47, + 11, 21, 16, 7, 20, 52, 27, 6, 255, 63, 0, 0, 0, 0, 0, 0, 28, 11, + 16, 52, 49, 32, 47, 12, 22, 11, 10, 11, 22, 27, 11, 6, 49, 33, 47, 27, + 12, 22, 49, 47, 47, 12, 23, 11, 22, 11, 23, 27, 12, 23, 10, 0, 10, 23, + 56, 36, 56, 34, 12, 24, 11, 0, 11, 1, 11, 2, 56, 32, 11, 23, 12, 25, + 11, 24, 12, 26, 11, 25, 11, 26, 2, 11, 18, 3, 190, 1, 10, 1, 10, 10, + 37, 12, 20, 5, 120, 9, 12, 20, 5, 120, 9, 12, 19, 5, 116, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 12, 23, 56, 22, 12, 24, 5, 172, 1, 11, 14, 12, + 15, 5, 90, 9, 12, 14, 5, 86, 11, 11, 12, 12, 5, 72, 11, 0, 1, 6, + 5, 0, 0, 0, 0, 0, 0, 0, 39, 11, 0, 0, 0, 48, 160, 1, 10, 0, + 55, 1, 20, 12, 3, 10, 3, 49, 126, 48, 50, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 51, 10, 3, 49, 84, 48, 50, 255, + 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 10, + 3, 49, 52, 48, 50, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 28, 52, 12, 6, 10, 3, 49, 38, 48, 50, 255, 63, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 7, 11, 3, 49, + 6, 48, 50, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 28, 52, 12, 8, 12, 9, 49, 1, 33, 12, 10, 9, 12, 11, 11, 9, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 131, 1, 8, 12, 11, 11, 11, 3, + 52, 5, 70, 10, 0, 55, 1, 20, 50, 255, 255, 255, 255, 255, 255, 15, 0, 0, + 0, 0, 0, 252, 255, 255, 255, 28, 10, 2, 53, 49, 84, 47, 27, 10, 1, 53, + 49, 52, 47, 27, 10, 0, 54, 1, 21, 9, 12, 13, 11, 7, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 33, 4, 102, 8, 12, 13, 11, 13, 4, 99, 10, 0, 55, + 1, 20, 50, 63, 0, 0, 0, 0, 0, 240, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 28, 11, 2, 53, 49, 38, 47, 27, 11, 1, 53, 49, 6, 47, 27, 11, 0, + 54, 1, 21, 2, 11, 0, 1, 5, 98, 10, 10, 4, 128, 1, 10, 1, 10, 8, + 38, 12, 14, 11, 14, 4, 118, 8, 12, 15, 11, 15, 3, 115, 5, 78, 8, 12, + 13, 5, 78, 11, 10, 3, 125, 10, 1, 11, 8, 37, 12, 15, 5, 112, 9, 12, + 15, 5, 112, 9, 12, 14, 5, 108, 10, 10, 4, 157, 1, 10, 1, 10, 6, 35, + 12, 13, 11, 13, 4, 147, 1, 8, 12, 14, 11, 14, 3, 144, 1, 5, 49, 8, + 12, 11, 5, 49, 10, 10, 3, 154, 1, 10, 1, 11, 6, 36, 12, 14, 5, 141, + 1, 9, 12, 14, 5, 141, 1, 9, 12, 13, 5, 137, 1, 41, 1, 0, 0, 50, + 115, 10, 0, 55, 1, 20, 12, 3, 10, 3, 49, 126, 48, 50, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 51, 12, 4, 10, 3, + 49, 38, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 28, 52, 12, 6, 11, 3, 49, 6, 48, 50, 255, 255, 255, 255, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 7, 10, 6, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 34, 4, 111, 10, 4, 49, 1, 33, 12, 9, 10, + 9, 4, 108, 10, 1, 10, 7, 35, 12, 10, 11, 10, 4, 98, 8, 12, 11, 11, + 11, 4, 94, 10, 0, 55, 3, 10, 6, 56, 35, 12, 12, 10, 12, 16, 6, 20, + 52, 49, 8, 47, 11, 12, 16, 7, 20, 52, 27, 6, 255, 63, 0, 0, 0, 0, + 0, 0, 28, 11, 4, 52, 49, 32, 47, 12, 13, 11, 7, 11, 13, 27, 11, 6, + 49, 33, 47, 27, 12, 13, 49, 47, 47, 12, 8, 11, 13, 11, 8, 27, 12, 8, + 10, 0, 11, 1, 11, 2, 56, 32, 11, 0, 10, 8, 56, 36, 12, 14, 11, 8, + 11, 14, 2, 11, 0, 1, 6, 4, 0, 0, 0, 0, 0, 0, 0, 39, 11, 9, + 3, 105, 10, 1, 10, 7, 36, 12, 11, 5, 43, 9, 12, 11, 5, 43, 9, 12, + 10, 5, 39, 11, 0, 1, 6, 3, 0, 0, 0, 0, 0, 0, 0, 39, 8, 0, + 0, 0, 54, 58, 10, 0, 46, 10, 1, 56, 37, 12, 3, 12, 4, 10, 0, 10, + 4, 11, 3, 11, 2, 56, 38, 12, 3, 10, 1, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 34, 4, 55, 10, 0, 54, 2, 11, 0, 54, 3, 11, 4, 56, 39, 12, + 6, 10, 3, 49, 8, 48, 51, 10, 6, 15, 6, 21, 10, 3, 6, 255, 0, 0, + 0, 0, 0, 0, 0, 28, 51, 11, 6, 15, 7, 21, 11, 1, 56, 40, 12, 8, + 10, 8, 16, 5, 20, 50, 255, 63, 0, 240, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 28, 10, 3, 53, 49, 14, 47, 27, 11, 8, 15, 5, 21, 11, + 3, 2, 11, 0, 1, 5, 53, 43, 0, 0, 0, 56, 140, 1, 10, 0, 54, 3, + 12, 4, 10, 0, 54, 0, 12, 5, 10, 1, 49, 8, 48, 51, 12, 6, 11, 1, + 6, 255, 0, 0, 0, 0, 0, 0, 0, 28, 51, 12, 8, 10, 2, 49, 8, 48, + 51, 12, 9, 11, 2, 6, 255, 0, 0, 0, 0, 0, 0, 0, 28, 51, 12, 10, + 10, 0, 55, 1, 20, 49, 98, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 1, 10, 1, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 33, 4, 80, 11, 0, 1, 10, 4, 46, 56, 33, 6, 1, 0, + 0, 0, 0, 0, 0, 0, 22, 12, 1, 10, 1, 6, 255, 63, 0, 0, 0, 0, + 0, 0, 37, 4, 74, 11, 6, 11, 8, 11, 9, 11, 10, 18, 1, 12, 11, 11, + 4, 10, 1, 11, 11, 56, 21, 11, 5, 12, 12, 10, 1, 12, 2, 11, 3, 56, + 34, 12, 13, 11, 12, 11, 2, 11, 13, 56, 23, 11, 1, 2, 11, 4, 1, 11, + 5, 1, 6, 1, 0, 0, 0, 0, 0, 0, 0, 39, 11, 4, 10, 1, 56, 39, + 12, 14, 10, 14, 16, 6, 20, 53, 49, 8, 47, 10, 14, 16, 7, 20, 53, 27, + 10, 0, 55, 1, 20, 50, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 3, 0, 255, 255, 28, 12, 16, 49, 98, 47, 12, 17, 11, 16, 11, 17, 27, 11, + 0, 54, 1, 21, 10, 14, 15, 8, 12, 18, 11, 6, 11, 18, 21, 10, 14, 15, + 9, 12, 18, 11, 8, 11, 18, 21, 10, 14, 15, 6, 12, 18, 11, 9, 11, 18, + 21, 11, 14, 15, 7, 12, 18, 11, 10, 11, 18, 21, 11, 5, 10, 1, 56, 2, + 11, 3, 56, 41, 5, 72, 42, 0, 0, 0, 57, 55, 10, 0, 55, 2, 12, 2, + 10, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 41, 11, 0, 55, 1, + 20, 49, 112, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 28, 52, 12, 1, 10, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 33, 4, 38, 11, 2, 56, 42, 6, 1, 0, 0, 0, 0, 0, 0, 0, 22, 12, + 1, 10, 1, 6, 0, 64, 0, 0, 0, 0, 0, 0, 27, 12, 3, 11, 3, 12, + 4, 11, 1, 6, 0, 64, 0, 0, 0, 0, 0, 0, 27, 12, 5, 11, 4, 11, + 5, 2, 11, 2, 1, 5, 25, 11, 0, 1, 11, 2, 10, 1, 56, 29, 16, 5, + 20, 49, 14, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 28, 52, 12, 3, 5, 29, 9, 0, 0, 0, 59, 87, 11, 1, 53, + 49, 94, 47, 10, 2, 53, 49, 70, 47, 27, 10, 3, 53, 49, 28, 47, 27, 11, + 3, 53, 49, 14, 47, 27, 12, 5, 10, 0, 55, 1, 20, 49, 112, 48, 50, 255, + 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, + 1, 10, 0, 54, 2, 12, 6, 10, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 33, 4, 56, 10, 6, 46, 56, 42, 6, 1, 0, 0, 0, 0, 0, 0, 0, 22, + 12, 1, 11, 5, 18, 2, 12, 7, 11, 6, 10, 1, 11, 7, 56, 20, 11, 0, + 10, 1, 11, 2, 11, 4, 56, 43, 11, 1, 2, 11, 6, 10, 1, 56, 40, 12, + 8, 10, 8, 16, 5, 20, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 28, 10, 0, 55, 1, 20, 50, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 192, 28, 12, 10, 49, 112, 47, 12, 11, + 11, 10, 11, 11, 27, 10, 0, 54, 1, 21, 11, 8, 15, 5, 12, 12, 11, 5, + 11, 12, 21, 5, 49, 46, 0, 0, 0, 60, 58, 14, 3, 56, 31, 4, 24, 10, + 0, 55, 1, 20, 50, 192, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 28, 10, 1, 53, 49, 8, 48, 27, 10, 0, 54, 1, 21, 11, 1, + 6, 255, 0, 0, 0, 0, 0, 0, 0, 28, 51, 11, 0, 54, 4, 21, 2, 11, + 0, 54, 2, 11, 2, 56, 40, 12, 5, 14, 3, 56, 7, 20, 8, 33, 4, 55, + 49, 56, 12, 6, 10, 5, 16, 5, 20, 50, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 10, 6, 47, 29, 28, 11, 1, 53, 11, 6, 47, + 27, 11, 5, 15, 5, 21, 5, 23, 49, 42, 12, 6, 5, 37, 47, 1, 0, 0, + 19, 10, 11, 0, 55, 1, 20, 49, 126, 48, 50, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 50, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 2, 48, 1, 0, 0, 7, 9, 11, + 0, 49, 32, 48, 6, 1, 0, 0, 0, 0, 0, 0, 0, 28, 51, 49, 1, 33, + 2, 49, 1, 0, 0, 61, 30, 11, 1, 49, 33, 48, 6, 255, 63, 0, 0, 0, + 0, 0, 0, 28, 12, 1, 11, 0, 55, 3, 11, 1, 56, 35, 12, 3, 10, 3, + 16, 6, 20, 52, 49, 8, 47, 11, 3, 16, 7, 20, 52, 27, 49, 14, 48, 6, + 1, 0, 0, 0, 0, 0, 0, 0, 28, 51, 49, 1, 33, 2, 50, 1, 0, 0, + 64, 59, 11, 1, 49, 33, 48, 6, 255, 63, 0, 0, 0, 0, 0, 0, 28, 12, + 1, 10, 0, 55, 3, 11, 1, 56, 35, 12, 3, 10, 3, 16, 6, 20, 52, 49, + 8, 47, 11, 3, 16, 7, 20, 52, 27, 12, 1, 10, 1, 49, 14, 48, 6, 1, + 0, 0, 0, 0, 0, 0, 0, 28, 6, 1, 0, 0, 0, 0, 0, 0, 0, 33, + 11, 1, 6, 255, 63, 0, 0, 0, 0, 0, 0, 28, 12, 1, 4, 54, 10, 0, + 56, 44, 4, 51, 9, 12, 5, 11, 0, 11, 1, 11, 5, 56, 45, 1, 12, 6, + 1, 11, 6, 49, 33, 47, 2, 8, 12, 5, 5, 40, 11, 0, 1, 11, 1, 12, + 6, 5, 47, 52, 1, 0, 0, 65, 43, 10, 0, 55, 1, 20, 49, 84, 48, 50, + 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, + 12, 1, 10, 0, 54, 3, 46, 10, 1, 56, 35, 12, 2, 10, 2, 16, 8, 20, + 52, 49, 8, 47, 11, 2, 16, 9, 20, 52, 27, 6, 255, 63, 0, 0, 0, 0, + 0, 0, 28, 11, 1, 49, 33, 47, 12, 1, 49, 47, 47, 12, 4, 11, 1, 11, + 4, 27, 12, 1, 11, 0, 11, 1, 56, 36, 2, 53, 1, 0, 0, 65, 43, 10, + 0, 55, 1, 20, 49, 38, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 28, 52, 12, 1, 10, 0, 54, 3, 46, 10, 1, 56, + 35, 12, 2, 10, 2, 16, 6, 20, 52, 49, 8, 47, 11, 2, 16, 7, 20, 52, + 27, 6, 255, 63, 0, 0, 0, 0, 0, 0, 28, 11, 1, 49, 33, 47, 12, 1, + 49, 47, 47, 12, 4, 11, 1, 11, 4, 27, 12, 1, 11, 0, 11, 1, 56, 36, + 2, 15, 0, 0, 0, 68, 115, 10, 0, 54, 3, 10, 1, 56, 39, 12, 2, 10, + 2, 16, 8, 20, 52, 49, 8, 47, 10, 2, 16, 9, 20, 52, 27, 12, 3, 10, + 2, 16, 6, 20, 52, 49, 8, 47, 10, 2, 16, 7, 20, 52, 27, 12, 4, 10, + 3, 49, 14, 48, 6, 1, 0, 0, 0, 0, 0, 0, 0, 28, 6, 1, 0, 0, + 0, 0, 0, 0, 0, 33, 10, 4, 49, 14, 48, 6, 1, 0, 0, 0, 0, 0, + 0, 0, 28, 6, 1, 0, 0, 0, 0, 0, 0, 0, 33, 10, 3, 6, 255, 63, + 0, 0, 0, 0, 0, 0, 28, 10, 4, 6, 255, 63, 0, 0, 0, 0, 0, 0, + 28, 10, 0, 55, 1, 20, 49, 98, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 8, 49, 0, 10, 2, 15, 8, + 21, 49, 0, 10, 2, 15, 9, 21, 10, 8, 49, 8, 48, 51, 10, 2, 15, 6, + 21, 11, 8, 6, 255, 0, 0, 0, 0, 0, 0, 0, 28, 51, 11, 2, 15, 7, + 21, 10, 0, 55, 1, 20, 50, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 3, 0, 255, 255, 28, 10, 1, 53, 49, 98, 47, 27, 10, 0, 54, 1, 21, + 12, 7, 12, 6, 12, 10, 12, 11, 10, 0, 11, 3, 11, 4, 11, 11, 11, 10, + 11, 6, 11, 7, 56, 46, 12, 12, 12, 13, 11, 0, 54, 0, 11, 1, 56, 2, + 56, 47, 11, 13, 11, 12, 2, 54, 0, 0, 0, 69, 119, 10, 3, 4, 116, 10, + 4, 12, 7, 11, 7, 4, 13, 11, 0, 1, 6, 0, 0, 0, 0, 0, 0, 0, + 0, 56, 27, 6, 0, 0, 0, 0, 0, 0, 0, 0, 56, 27, 2, 56, 26, 12, + 8, 56, 26, 12, 9, 10, 0, 54, 2, 12, 10, 11, 0, 54, 3, 12, 11, 11, + 3, 4, 97, 10, 10, 10, 5, 56, 40, 12, 12, 10, 12, 16, 5, 20, 50, 255, + 255, 255, 15, 0, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 28, 10, 6, + 53, 49, 28, 47, 27, 11, 12, 15, 5, 21, 10, 6, 56, 27, 12, 8, 11, 4, + 4, 76, 11, 11, 1, 11, 10, 11, 6, 56, 40, 12, 12, 10, 12, 16, 5, 20, + 50, 255, 63, 0, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 28, + 10, 5, 53, 49, 14, 47, 27, 11, 12, 15, 5, 21, 11, 5, 56, 27, 12, 9, + 11, 8, 12, 13, 11, 9, 12, 14, 11, 13, 11, 14, 2, 11, 10, 1, 11, 11, + 11, 6, 56, 39, 12, 15, 10, 1, 49, 8, 48, 51, 10, 15, 15, 8, 21, 11, + 1, 6, 255, 0, 0, 0, 0, 0, 0, 0, 28, 51, 11, 15, 15, 9, 21, 5, + 69, 10, 11, 10, 5, 56, 39, 12, 15, 10, 2, 49, 8, 48, 51, 10, 15, 15, + 6, 21, 11, 2, 6, 255, 0, 0, 0, 0, 0, 0, 0, 28, 51, 11, 15, 15, + 7, 21, 5, 45, 9, 12, 7, 5, 4, 18, 0, 0, 0, 73, 135, 1, 10, 0, + 54, 2, 46, 10, 1, 56, 29, 16, 5, 20, 12, 2, 10, 2, 49, 89, 48, 50, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 51, + 12, 3, 10, 2, 49, 84, 48, 50, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 28, 51, 12, 4, 10, 2, 49, 70, 48, 50, 255, 63, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 6, + 10, 2, 49, 56, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 28, 52, 12, 7, 11, 2, 49, 42, 48, 50, 255, 63, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 8, 10, 7, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 34, 12, 10, 10, 8, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 34, 12, 11, 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, + 9, 10, 6, 12, 12, 9, 12, 13, 10, 10, 4, 132, 1, 10, 11, 32, 12, 14, + 11, 14, 4, 124, 8, 12, 15, 11, 15, 3, 69, 5, 93, 10, 10, 4, 121, 10, + 7, 12, 16, 11, 16, 12, 9, 10, 0, 54, 2, 10, 9, 56, 40, 12, 17, 10, + 17, 16, 5, 20, 50, 255, 255, 255, 255, 255, 255, 255, 255, 63, 0, 240, 255, 255, + 255, 255, 255, 28, 10, 6, 53, 49, 70, 47, 27, 11, 17, 15, 5, 21, 11, 10, + 4, 118, 11, 11, 12, 18, 11, 18, 3, 100, 5, 110, 10, 0, 11, 3, 11, 4, + 10, 6, 11, 7, 11, 8, 56, 48, 12, 13, 12, 12, 12, 9, 11, 0, 11, 1, + 11, 6, 11, 9, 11, 12, 11, 13, 56, 49, 2, 9, 12, 18, 5, 97, 10, 8, + 12, 16, 5, 73, 10, 10, 3, 129, 1, 10, 11, 12, 15, 5, 66, 9, 12, 15, + 5, 66, 9, 12, 14, 5, 62, 57, 0, 0, 0, 74, 122, 10, 2, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 33, 4, 65, 10, 0, 55, 1, 20, 50, 192, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 28, 10, 3, 53, 49, + 8, 48, 27, 10, 0, 54, 1, 21, 11, 3, 6, 255, 0, 0, 0, 0, 0, 0, + 0, 28, 51, 10, 0, 54, 4, 21, 10, 4, 6, 0, 0, 0, 0, 0, 0, 0, + 0, 34, 3, 29, 5, 34, 10, 0, 11, 4, 9, 11, 5, 56, 8, 10, 0, 55, + 1, 20, 49, 112, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 28, 52, 10, 0, 54, 2, 10, 1, 56, 40, 12, 8, 53, 11, + 8, 15, 5, 21, 10, 0, 55, 1, 20, 50, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 192, 28, 11, 1, 53, 49, 112, 47, 27, 11, 0, + 54, 1, 21, 2, 10, 0, 54, 2, 10, 2, 56, 40, 12, 8, 10, 8, 16, 5, + 20, 49, 56, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 28, 52, 12, 6, 10, 6, 10, 1, 33, 4, 119, 49, 56, 12, 9, + 10, 8, 16, 5, 20, 50, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 9, 47, 29, 28, 11, 3, 53, 11, 9, 47, 27, 11, 8, 15, + 5, 21, 10, 4, 11, 2, 33, 3, 107, 5, 24, 11, 6, 10, 1, 33, 4, 116, + 8, 12, 7, 11, 7, 12, 5, 5, 24, 9, 12, 7, 5, 113, 49, 42, 12, 9, + 5, 85, 56, 0, 0, 0, 75, 228, 1, 11, 0, 54, 2, 12, 6, 10, 6, 10, + 4, 56, 40, 12, 7, 10, 7, 16, 5, 20, 49, 42, 48, 50, 255, 63, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 8, 10, 8, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 81, 10, 7, 16, 5, 20, 50, + 255, 255, 255, 255, 255, 3, 0, 255, 63, 0, 0, 192, 255, 255, 255, 255, 28, 11, + 1, 53, 49, 89, 47, 27, 11, 2, 53, 49, 84, 47, 27, 11, 3, 53, 49, 70, + 47, 27, 10, 5, 53, 49, 42, 47, 27, 11, 7, 15, 5, 21, 10, 4, 12, 9, + 11, 4, 12, 10, 8, 12, 11, 11, 6, 11, 5, 56, 40, 12, 12, 10, 12, 16, + 5, 20, 50, 255, 255, 255, 255, 255, 255, 255, 255, 63, 0, 240, 255, 255, 255, 255, + 255, 28, 10, 9, 53, 49, 70, 47, 27, 11, 12, 15, 5, 21, 11, 9, 12, 13, + 11, 10, 12, 14, 11, 11, 12, 15, 11, 13, 11, 14, 11, 15, 2, 11, 7, 1, + 10, 6, 10, 8, 56, 40, 12, 12, 10, 12, 16, 5, 20, 49, 42, 48, 50, 255, + 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, + 13, 10, 13, 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 223, 1, 10, 12, + 16, 5, 20, 12, 16, 10, 16, 49, 70, 48, 50, 255, 63, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 14, 10, 16, 49, 56, 48, + 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, + 52, 12, 18, 11, 16, 50, 255, 255, 255, 255, 255, 3, 0, 0, 0, 0, 0, 192, + 255, 255, 255, 255, 28, 11, 1, 53, 49, 89, 47, 27, 11, 2, 53, 49, 84, 47, + 27, 11, 3, 53, 49, 70, 47, 27, 10, 4, 53, 49, 56, 47, 27, 10, 5, 53, + 49, 42, 47, 27, 11, 12, 15, 5, 21, 10, 6, 10, 14, 56, 40, 12, 20, 10, + 20, 16, 5, 20, 50, 255, 255, 255, 255, 255, 3, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 28, 10, 18, 53, 49, 42, 47, 27, 10, 20, 15, 5, 21, 10, 14, + 10, 4, 33, 4, 216, 1, 11, 20, 12, 21, 11, 21, 12, 7, 10, 7, 16, 5, + 20, 50, 255, 255, 255, 255, 255, 255, 255, 255, 63, 0, 240, 255, 255, 255, 255, 255, + 28, 10, 8, 53, 49, 70, 47, 27, 11, 7, 15, 5, 21, 10, 18, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 34, 3, 192, 1, 5, 209, 1, 10, 6, 11, 18, 56, + 40, 12, 7, 10, 7, 16, 5, 20, 50, 255, 255, 255, 255, 255, 255, 255, 255, 63, + 0, 240, 255, 255, 255, 255, 255, 28, 10, 14, 53, 49, 70, 47, 27, 11, 7, 15, + 5, 21, 11, 8, 12, 9, 11, 14, 12, 10, 9, 12, 11, 5, 54, 11, 20, 1, + 10, 6, 11, 4, 56, 40, 12, 21, 5, 172, 1, 11, 12, 1, 11, 13, 12, 8, + 5, 83, 16, 0, 0, 0, 76, 51, 10, 1, 6, 0, 0, 0, 0, 0, 0, 0, + 0, 34, 4, 20, 11, 1, 12, 4, 10, 0, 55, 1, 20, 50, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 15, 0, 252, 255, 255, 255, 28, 11, 4, 53, 49, 84, + 47, 27, 11, 0, 54, 1, 21, 2, 11, 2, 4, 48, 9, 12, 2, 10, 0, 46, + 11, 3, 11, 2, 56, 45, 1, 12, 4, 10, 0, 55, 1, 20, 50, 255, 255, 255, + 255, 255, 255, 15, 0, 0, 0, 240, 255, 255, 255, 255, 255, 28, 12, 5, 53, 49, + 52, 47, 12, 7, 11, 5, 11, 7, 27, 10, 0, 54, 1, 21, 5, 6, 8, 12, + 2, 5, 24, 17, 0, 0, 0, 76, 51, 10, 1, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 34, 4, 20, 11, 1, 12, 4, 10, 0, 55, 1, 20, 50, 255, 255, 255, + 255, 63, 0, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 28, 11, 4, 53, 49, + 38, 47, 27, 11, 0, 54, 1, 21, 2, 11, 2, 4, 48, 8, 12, 2, 10, 0, + 46, 11, 3, 11, 2, 56, 45, 12, 4, 1, 10, 0, 55, 1, 20, 50, 63, 0, + 0, 0, 192, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 28, 12, 5, 53, + 49, 6, 47, 12, 7, 11, 5, 11, 7, 27, 10, 0, 54, 1, 21, 5, 6, 9, + 12, 2, 5, 24, 10, 0, 0, 0, 82, 136, 1, 49, 1, 12, 4, 10, 0, 54, + 2, 10, 1, 56, 40, 12, 5, 10, 5, 16, 5, 20, 49, 70, 48, 50, 255, 63, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 6, + 10, 5, 11, 3, 11, 2, 11, 4, 17, 58, 12, 7, 12, 8, 12, 9, 12, 10, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, 11, 10, 10, 10, 9, 34, 4, 133, + 1, 10, 10, 10, 9, 36, 4, 126, 8, 12, 12, 11, 10, 11, 9, 23, 12, 13, + 11, 13, 49, 1, 36, 4, 123, 10, 12, 4, 120, 49, 56, 12, 14, 11, 5, 16, + 5, 20, 11, 14, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 28, 52, 12, 15, 10, 0, 10, 1, 11, 15, 11, 12, 56, 50, + 12, 8, 12, 11, 10, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 97, + 10, 11, 6, 0, 0, 0, 0, 0, 0, 0, 0, 34, 4, 94, 10, 0, 55, 1, + 20, 50, 192, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 28, 10, 11, 53, 49, 8, 48, 27, 10, 0, 54, 1, 21, 11, 11, 6, 255, 0, + 0, 0, 0, 0, 0, 0, 28, 51, 11, 0, 54, 4, 21, 2, 11, 0, 1, 5, + 93, 10, 0, 10, 6, 11, 1, 11, 11, 11, 8, 11, 7, 56, 51, 12, 4, 12, + 3, 12, 2, 12, 5, 10, 4, 49, 0, 33, 4, 117, 11, 0, 1, 11, 5, 1, + 2, 11, 6, 12, 1, 5, 7, 49, 42, 12, 14, 5, 49, 11, 5, 1, 5, 65, + 9, 12, 12, 11, 9, 11, 10, 23, 12, 13, 5, 41, 11, 5, 1, 5, 65, 60, + 0, 0, 0, 83, 82, 11, 0, 54, 2, 11, 1, 56, 40, 12, 6, 10, 6, 16, + 5, 20, 49, 56, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 28, 52, 11, 2, 33, 4, 79, 8, 12, 7, 10, 3, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 34, 3, 23, 5, 46, 10, 7, 8, 33, 4, 76, + 49, 56, 12, 9, 10, 6, 16, 5, 20, 50, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 10, 9, 47, 29, 28, 11, 3, 53, 11, 9, 47, + 27, 10, 6, 15, 5, 21, 10, 4, 10, 5, 36, 4, 69, 8, 12, 8, 11, 4, + 11, 5, 23, 12, 10, 11, 6, 12, 11, 11, 8, 12, 12, 11, 7, 12, 13, 11, + 10, 12, 14, 11, 11, 11, 12, 11, 13, 11, 14, 2, 9, 12, 8, 11, 5, 11, + 4, 23, 12, 10, 5, 56, 49, 42, 12, 9, 5, 29, 9, 12, 7, 5, 18, 59, + 0, 0, 0, 85, 83, 10, 0, 55, 2, 10, 2, 56, 29, 16, 5, 20, 12, 4, + 10, 4, 49, 89, 48, 50, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 28, 51, 12, 5, 10, 4, 49, 84, 48, 50, 31, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 51, 12, 6, 10, 4, + 49, 56, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 28, 52, 12, 8, 11, 4, 49, 42, 48, 50, 255, 63, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 9, 11, 3, 4, 61, + 10, 6, 10, 5, 36, 4, 52, 11, 0, 11, 1, 11, 2, 11, 9, 11, 5, 56, + 52, 12, 7, 12, 10, 11, 10, 11, 7, 2, 11, 0, 11, 1, 11, 2, 11, 9, + 11, 6, 56, 53, 12, 7, 12, 10, 5, 49, 10, 5, 10, 6, 36, 4, 74, 11, + 0, 11, 1, 11, 2, 11, 8, 11, 6, 56, 54, 12, 7, 12, 10, 5, 49, 11, + 0, 11, 1, 11, 2, 11, 8, 11, 5, 56, 55, 12, 7, 12, 10, 5, 49, 64, + 0, 0, 0, 86, 135, 1, 11, 0, 54, 2, 12, 5, 10, 3, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 34, 3, 8, 5, 25, 10, 5, 10, 3, 56, 40, 12, 7, + 10, 7, 16, 5, 20, 50, 255, 255, 255, 255, 255, 255, 255, 255, 63, 0, 240, 255, + 255, 255, 255, 255, 28, 10, 1, 53, 49, 70, 47, 27, 11, 7, 15, 5, 21, 10, + 5, 10, 1, 56, 40, 12, 7, 10, 7, 16, 5, 20, 49, 89, 48, 50, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 51, 12, 8, + 10, 7, 16, 5, 20, 49, 70, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 6, 10, 7, 16, 5, 20, 50, 255, + 255, 255, 255, 255, 3, 0, 255, 63, 0, 0, 254, 255, 255, 255, 255, 28, 11, 3, + 53, 49, 42, 47, 27, 10, 4, 53, 49, 84, 47, 27, 10, 2, 53, 49, 70, 47, + 27, 11, 7, 15, 5, 21, 10, 8, 10, 4, 38, 4, 132, 1, 11, 8, 12, 9, + 11, 9, 49, 1, 22, 12, 10, 11, 5, 10, 2, 56, 40, 12, 7, 10, 7, 16, + 5, 20, 50, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 240, 193, 255, 255, 255, + 255, 28, 11, 1, 53, 49, 56, 47, 27, 10, 10, 53, 49, 89, 47, 27, 11, 6, + 53, 49, 70, 47, 27, 10, 7, 15, 5, 21, 11, 7, 16, 5, 20, 49, 84, 48, + 50, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, + 51, 12, 11, 10, 11, 10, 10, 38, 4, 129, 1, 11, 11, 12, 12, 11, 2, 12, + 3, 11, 12, 12, 13, 11, 3, 11, 13, 2, 11, 10, 12, 12, 5, 122, 11, 4, + 12, 9, 5, 76, 61, 0, 0, 0, 87, 198, 1, 11, 0, 54, 2, 12, 5, 10, + 5, 46, 10, 3, 56, 29, 16, 5, 20, 12, 6, 10, 6, 49, 89, 48, 50, 31, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 51, 12, + 7, 10, 6, 49, 84, 48, 50, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 28, 51, 12, 8, 10, 6, 49, 56, 48, 50, 255, 63, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 10, 11, + 6, 49, 42, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 28, 52, 12, 11, 10, 10, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 34, 3, 43, 5, 60, 10, 5, 10, 10, 56, 40, 12, 13, 10, 13, 16, 5, 20, + 50, 255, 255, 255, 255, 255, 255, 255, 255, 63, 0, 240, 255, 255, 255, 255, 255, 28, + 10, 2, 53, 49, 70, 47, 27, 11, 13, 15, 5, 21, 10, 11, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 34, 3, 65, 5, 82, 10, 5, 10, 11, 56, 40, 12, 13, + 10, 13, 16, 5, 20, 50, 255, 255, 255, 255, 255, 255, 255, 255, 63, 0, 240, 255, + 255, 255, 255, 255, 28, 10, 1, 53, 49, 70, 47, 27, 11, 13, 15, 5, 21, 10, + 5, 10, 1, 56, 40, 12, 13, 10, 13, 16, 5, 20, 49, 70, 48, 50, 255, 63, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 12, + 10, 13, 16, 5, 20, 50, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 240, 193, + 255, 255, 255, 255, 28, 11, 11, 53, 49, 56, 47, 27, 11, 8, 53, 49, 89, 47, + 27, 10, 3, 53, 49, 70, 47, 27, 11, 13, 15, 5, 21, 10, 5, 10, 2, 56, + 40, 12, 13, 10, 13, 16, 5, 20, 50, 255, 255, 255, 255, 255, 3, 0, 255, 63, + 0, 0, 254, 255, 255, 255, 255, 28, 11, 10, 53, 49, 42, 47, 27, 10, 7, 53, + 49, 84, 47, 27, 10, 3, 53, 49, 70, 47, 27, 11, 13, 15, 5, 21, 10, 7, + 10, 4, 38, 4, 195, 1, 11, 7, 12, 8, 11, 8, 49, 1, 22, 12, 9, 11, + 5, 10, 3, 56, 40, 12, 13, 10, 13, 16, 5, 20, 50, 255, 255, 255, 255, 255, + 3, 0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 28, 11, 2, 53, 49, 56, 47, + 27, 11, 1, 53, 49, 42, 47, 27, 10, 9, 53, 49, 89, 47, 27, 10, 9, 53, + 49, 84, 47, 27, 11, 12, 53, 49, 70, 47, 27, 11, 13, 15, 5, 21, 11, 3, + 11, 9, 2, 11, 4, 12, 8, 5, 151, 1, 62, 0, 0, 0, 86, 135, 1, 11, + 0, 54, 2, 12, 5, 10, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 34, 3, + 8, 5, 25, 10, 5, 10, 3, 56, 40, 12, 7, 10, 7, 16, 5, 20, 50, 255, + 255, 255, 255, 255, 255, 255, 255, 63, 0, 240, 255, 255, 255, 255, 255, 28, 10, 1, + 53, 49, 70, 47, 27, 11, 7, 15, 5, 21, 10, 5, 10, 1, 56, 40, 12, 7, + 10, 7, 16, 5, 20, 49, 84, 48, 50, 31, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 28, 51, 12, 8, 10, 7, 16, 5, 20, 49, 70, + 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 28, 52, 12, 6, 10, 7, 16, 5, 20, 50, 255, 255, 255, 255, 255, 255, 255, 0, + 0, 0, 240, 193, 255, 255, 255, 255, 28, 11, 3, 53, 49, 56, 47, 27, 10, 4, + 53, 49, 89, 47, 27, 10, 2, 53, 49, 70, 47, 27, 11, 7, 15, 5, 21, 10, + 8, 10, 4, 38, 4, 132, 1, 11, 8, 12, 9, 11, 9, 49, 1, 22, 12, 10, + 11, 5, 10, 2, 56, 40, 12, 7, 10, 7, 16, 5, 20, 50, 255, 255, 255, 255, + 255, 3, 0, 255, 63, 0, 0, 254, 255, 255, 255, 255, 28, 11, 1, 53, 49, 42, + 47, 27, 10, 10, 53, 49, 84, 47, 27, 11, 6, 53, 49, 70, 47, 27, 10, 7, + 15, 5, 21, 11, 7, 16, 5, 20, 49, 89, 48, 50, 31, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 51, 12, 11, 10, 11, 10, 10, + 38, 4, 129, 1, 11, 11, 12, 12, 11, 2, 12, 3, 11, 12, 12, 13, 11, 3, + 11, 13, 2, 11, 10, 12, 12, 5, 122, 11, 4, 12, 9, 5, 76, 63, 0, 0, + 0, 87, 198, 1, 11, 0, 54, 2, 12, 5, 10, 5, 46, 10, 3, 56, 29, 16, + 5, 20, 12, 6, 10, 6, 49, 89, 48, 50, 31, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 28, 51, 12, 7, 10, 6, 49, 84, 48, 50, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 51, + 12, 8, 10, 6, 49, 56, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 28, 52, 12, 10, 11, 6, 49, 42, 48, 50, 255, 63, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 11, + 10, 10, 6, 0, 0, 0, 0, 0, 0, 0, 0, 34, 3, 43, 5, 60, 10, 5, + 10, 10, 56, 40, 12, 13, 10, 13, 16, 5, 20, 50, 255, 255, 255, 255, 255, 255, + 255, 255, 63, 0, 240, 255, 255, 255, 255, 255, 28, 10, 1, 53, 49, 70, 47, 27, + 11, 13, 15, 5, 21, 10, 11, 6, 0, 0, 0, 0, 0, 0, 0, 0, 34, 3, + 65, 5, 82, 10, 5, 10, 11, 56, 40, 12, 13, 10, 13, 16, 5, 20, 50, 255, + 255, 255, 255, 255, 255, 255, 255, 63, 0, 240, 255, 255, 255, 255, 255, 28, 10, 2, + 53, 49, 70, 47, 27, 11, 13, 15, 5, 21, 10, 5, 10, 1, 56, 40, 12, 13, + 10, 13, 16, 5, 20, 49, 70, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 12, 10, 13, 16, 5, 20, 50, 255, + 255, 255, 255, 255, 3, 0, 255, 63, 0, 0, 254, 255, 255, 255, 255, 28, 11, 10, + 53, 49, 42, 47, 27, 11, 7, 53, 49, 84, 47, 27, 10, 3, 53, 49, 70, 47, + 27, 11, 13, 15, 5, 21, 10, 5, 10, 2, 56, 40, 12, 13, 10, 13, 16, 5, + 20, 50, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 240, 193, 255, 255, 255, 255, + 28, 11, 11, 53, 49, 56, 47, 27, 10, 8, 53, 49, 89, 47, 27, 10, 3, 53, + 49, 70, 47, 27, 11, 13, 15, 5, 21, 10, 8, 10, 4, 38, 4, 195, 1, 11, + 8, 12, 7, 11, 7, 49, 1, 22, 12, 9, 11, 5, 10, 3, 56, 40, 12, 13, + 10, 13, 16, 5, 20, 50, 255, 255, 255, 255, 255, 3, 0, 0, 0, 0, 0, 192, + 255, 255, 255, 255, 28, 11, 1, 53, 49, 56, 47, 27, 11, 2, 53, 49, 42, 47, + 27, 10, 9, 53, 49, 89, 47, 27, 10, 9, 53, 49, 84, 47, 27, 11, 12, 53, + 49, 70, 47, 27, 11, 13, 15, 5, 21, 11, 3, 11, 9, 2, 11, 4, 12, 7, + 5, 151, 1, 58, 0, 0, 0, 88, 101, 10, 0, 16, 5, 20, 12, 4, 10, 4, + 49, 89, 48, 50, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 28, 51, 12, 5, 10, 4, 49, 84, 48, 50, 31, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 51, 12, 6, 10, 5, 10, 6, + 38, 4, 98, 10, 5, 12, 7, 10, 1, 8, 33, 4, 93, 10, 5, 12, 9, 49, + 89, 12, 10, 11, 2, 8, 33, 4, 88, 11, 9, 11, 3, 22, 12, 11, 50, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 50, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 47, 29, + 12, 12, 11, 4, 11, 12, 28, 10, 11, 53, 11, 10, 47, 27, 11, 0, 15, 5, + 21, 11, 1, 8, 33, 4, 85, 11, 11, 12, 5, 10, 5, 10, 6, 38, 4, 82, + 10, 5, 12, 13, 11, 5, 12, 14, 11, 6, 12, 15, 11, 13, 12, 16, 11, 7, + 12, 17, 11, 14, 11, 15, 11, 16, 11, 17, 2, 10, 6, 12, 13, 5, 69, 11, + 11, 12, 6, 5, 63, 11, 9, 11, 3, 23, 12, 11, 5, 40, 10, 6, 12, 9, + 49, 84, 12, 10, 5, 32, 10, 6, 12, 7, 5, 24, 7, 0, 0, 0, 89, 97, + 10, 0, 55, 1, 20, 50, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 28, 52, 49, 8, 47, 10, 0, 55, 4, 20, 52, 27, 12, 2, + 10, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 27, 11, 0, 1, 11, + 2, 12, 3, 56, 56, 12, 4, 11, 3, 11, 4, 2, 11, 0, 55, 2, 12, 5, + 10, 5, 10, 2, 56, 29, 12, 6, 10, 6, 16, 5, 20, 49, 94, 48, 50, 255, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, + 7, 10, 1, 10, 7, 33, 4, 58, 11, 5, 1, 11, 6, 1, 11, 2, 12, 3, + 56, 56, 12, 4, 11, 3, 11, 4, 2, 10, 1, 11, 7, 35, 4, 92, 49, 56, + 12, 8, 8, 12, 9, 11, 6, 16, 5, 20, 11, 8, 48, 50, 255, 63, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 7, 10, 7, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 89, 11, 5, 1, 11, 2, 12, + 3, 11, 9, 56, 57, 12, 4, 11, 3, 11, 4, 2, 11, 7, 12, 2, 5, 30, + 49, 42, 12, 8, 9, 12, 9, 5, 66, 51, 0, 0, 0, 90, 125, 11, 0, 55, + 2, 12, 3, 10, 3, 10, 1, 56, 29, 12, 4, 11, 2, 8, 33, 4, 120, 49, + 56, 12, 6, 49, 42, 12, 7, 11, 4, 16, 5, 20, 12, 8, 10, 8, 11, 6, + 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 28, 52, 12, 9, 10, 9, 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 3, 31, + 5, 97, 11, 1, 12, 9, 11, 8, 49, 70, 48, 50, 255, 63, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 10, 10, 10, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 4, 50, 11, 3, 1, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 10, 3, 10, 10, 56, 29, 12, 4, 10, 4, 16, 5, 20, + 12, 8, 10, 8, 10, 7, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 28, 52, 11, 9, 33, 4, 92, 11, 3, 1, 11, 4, + 16, 5, 20, 12, 13, 10, 13, 49, 94, 48, 50, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 10, 13, 49, 28, 48, 50, 255, + 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 11, + 13, 49, 14, 48, 50, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 28, 52, 2, 11, 4, 1, 11, 10, 12, 9, 5, 33, 10, 3, 11, + 9, 56, 29, 12, 4, 10, 4, 16, 5, 20, 10, 7, 48, 50, 255, 63, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 9, 10, 9, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 117, 11, 3, 1, 5, 69, 11, + 4, 1, 5, 97, 49, 42, 12, 6, 49, 56, 12, 7, 5, 15, 65, 1, 0, 0, + 91, 66, 10, 1, 6, 255, 255, 255, 255, 0, 0, 0, 0, 37, 4, 62, 11, 0, + 55, 1, 20, 12, 3, 10, 3, 49, 126, 48, 50, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 51, 10, 3, 49, 84, 48, 50, 255, + 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 11, + 3, 49, 52, 48, 50, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 28, 52, 12, 6, 12, 2, 49, 1, 33, 12, 7, 11, 2, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 4, 37, 8, 2, 10, 7, 4, 59, 10, 1, + 10, 6, 35, 12, 9, 11, 9, 4, 49, 8, 12, 10, 11, 10, 2, 11, 7, 3, + 56, 11, 1, 11, 6, 36, 12, 10, 5, 47, 9, 12, 10, 5, 47, 9, 12, 9, + 5, 43, 11, 0, 1, 6, 2, 0, 0, 0, 0, 0, 0, 0, 39, 66, 1, 0, + 0, 91, 66, 10, 1, 6, 255, 255, 255, 255, 0, 0, 0, 0, 37, 4, 62, 11, + 0, 55, 1, 20, 12, 3, 10, 3, 49, 126, 48, 50, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 51, 10, 3, 49, 38, 48, 50, + 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, + 11, 3, 49, 6, 48, 50, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 28, 52, 12, 6, 12, 2, 49, 1, 33, 12, 7, 11, 2, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 37, 8, 2, 10, 7, 4, 59, 10, + 1, 10, 6, 38, 12, 9, 11, 9, 4, 49, 8, 12, 10, 11, 10, 2, 11, 7, + 3, 56, 11, 1, 11, 6, 37, 12, 10, 5, 47, 9, 12, 10, 5, 47, 9, 12, + 9, 5, 43, 11, 0, 1, 6, 2, 0, 0, 0, 0, 0, 0, 0, 39, 0, 4, + 0, 0, 0, 2, 0, 3, 0, 1, 2, 0, 1, 2, 1, 3, 1, 0, 1, 1, + 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 0, + ] +}); + +#[rustfmt::skip] +pub static MODULE_ECONIA_FAUCET: Lazy> = Lazy::new(|| { + vec![ + 161, 28, 235, 11, 7, 0, 0, 10, 13, 1, 0, 10, 2, 10, 38, 3, 48, 68, + 4, 116, 12, 5, 128, 1, 91, 7, 219, 1, 239, 1, 8, 202, 3, 64, 16, 138, + 4, 131, 1, 10, 141, 5, 21, 11, 162, 5, 2, 12, 164, 5, 110, 13, 146, 6, + 2, 14, 148, 6, 2, 0, 0, 1, 4, 1, 11, 1, 13, 1, 17, 0, 1, 8, + 1, 0, 1, 1, 3, 5, 1, 0, 1, 1, 6, 5, 1, 0, 1, 1, 8, 5, + 1, 0, 1, 2, 10, 7, 0, 4, 19, 7, 0, 1, 21, 4, 1, 0, 1, 0, + 9, 0, 1, 1, 0, 1, 1, 9, 0, 3, 1, 0, 1, 0, 12, 5, 1, 1, + 0, 1, 3, 14, 6, 7, 0, 1, 1, 15, 7, 8, 1, 0, 1, 1, 16, 6, + 1, 1, 0, 1, 4, 18, 1, 9, 1, 0, 1, 4, 20, 10, 7, 0, 1, 1, + 12, 11, 12, 1, 0, 1, 1, 22, 13, 1, 1, 0, 1, 1, 2, 4, 2, 5, + 2, 6, 2, 8, 2, 9, 2, 5, 6, 12, 8, 4, 8, 4, 2, 1, 0, 1, + 9, 0, 3, 11, 1, 1, 9, 0, 11, 2, 1, 9, 0, 11, 3, 1, 9, 0, + 1, 11, 0, 1, 9, 0, 2, 6, 12, 3, 1, 6, 12, 1, 5, 1, 1, 1, + 8, 5, 1, 6, 8, 5, 2, 3, 6, 11, 3, 1, 9, 0, 1, 11, 6, 1, + 9, 0, 2, 5, 11, 6, 1, 9, 0, 6, 5, 8, 5, 5, 5, 6, 11, 3, + 1, 9, 0, 11, 6, 1, 9, 0, 6, 102, 97, 117, 99, 101, 116, 15, 67, 97, + 112, 97, 98, 105, 108, 105, 116, 121, 83, 116, 111, 114, 101, 8, 98, 117, 114, 110, + 95, 99, 97, 112, 14, 66, 117, 114, 110, 67, 97, 112, 97, 98, 105, 108, 105, 116, + 121, 4, 99, 111, 105, 110, 10, 102, 114, 101, 101, 122, 101, 95, 99, 97, 112, 16, + 70, 114, 101, 101, 122, 101, 67, 97, 112, 97, 98, 105, 108, 105, 116, 121, 8, 109, + 105, 110, 116, 95, 99, 97, 112, 14, 77, 105, 110, 116, 67, 97, 112, 97, 98, 105, + 108, 105, 116, 121, 10, 105, 110, 105, 116, 105, 97, 108, 105, 122, 101, 6, 83, 116, + 114, 105, 110, 103, 6, 115, 116, 114, 105, 110, 103, 4, 109, 105, 110, 116, 6, 115, + 105, 103, 110, 101, 114, 10, 97, 100, 100, 114, 101, 115, 115, 95, 111, 102, 21, 105, + 115, 95, 97, 99, 99, 111, 117, 110, 116, 95, 114, 101, 103, 105, 115, 116, 101, 114, + 101, 100, 8, 114, 101, 103, 105, 115, 116, 101, 114, 9, 116, 121, 112, 101, 95, 105, + 110, 102, 111, 7, 116, 121, 112, 101, 95, 111, 102, 8, 84, 121, 112, 101, 73, 110, + 102, 111, 15, 97, 99, 99, 111, 117, 110, 116, 95, 97, 100, 100, 114, 101, 115, 115, + 4, 67, 111, 105, 110, 7, 100, 101, 112, 111, 115, 105, 116, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 35, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 20, 99, 111, 109, 112, 105, 108, 97, 116, 105, 111, 110, 95, + 109, 101, 116, 97, 100, 97, 116, 97, 9, 0, 3, 50, 46, 48, 3, 50, 46, 49, + 18, 97, 112, 116, 111, 115, 58, 58, 109, 101, 116, 97, 100, 97, 116, 97, 95, 118, + 49, 80, 1, 0, 0, 0, 0, 0, 0, 0, 0, 14, 69, 95, 78, 79, 95, 67, + 65, 80, 95, 83, 84, 79, 82, 69, 53, 78, 111, 32, 99, 97, 112, 97, 98, 105, + 108, 105, 116, 121, 32, 115, 116, 111, 114, 101, 32, 97, 116, 32, 99, 111, 105, 110, + 32, 116, 121, 112, 101, 32, 112, 117, 98, 108, 105, 115, 104, 101, 114, 39, 115, 32, + 97, 99, 99, 111, 117, 110, 116, 46, 0, 0, 0, 2, 3, 2, 11, 1, 1, 9, + 0, 5, 11, 2, 1, 9, 0, 7, 11, 3, 1, 9, 0, 0, 2, 0, 1, 4, + 0, 4, 12, 10, 0, 11, 1, 11, 2, 11, 3, 11, 4, 56, 0, 57, 0, 12, + 5, 11, 0, 11, 5, 63, 0, 2, 2, 1, 4, 1, 0, 14, 35, 10, 0, 17, + 3, 12, 2, 10, 2, 56, 1, 3, 32, 11, 0, 56, 2, 56, 3, 12, 3, 14, + 3, 17, 7, 12, 4, 10, 4, 59, 0, 4, 30, 11, 4, 61, 0, 55, 0, 11, + 2, 12, 5, 12, 6, 11, 1, 11, 6, 56, 4, 12, 7, 11, 5, 11, 7, 56, + 5, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, 39, 11, 0, 1, 5, 8, 0, + 2, 0, 2, 0, + ] +}); + +#[rustfmt::skip] +pub static MODULE_ECONIA_TABLIST: Lazy> = Lazy::new(|| { + vec![ + 161, 28, 235, 11, 7, 0, 0, 10, 13, 1, 0, 6, 2, 6, 30, 3, 36, 220, + 1, 4, 128, 2, 34, 5, 162, 2, 174, 2, 7, 208, 4, 161, 2, 8, 241, 6, + 64, 16, 177, 7, 133, 1, 10, 182, 8, 46, 11, 228, 8, 4, 12, 232, 8, 208, + 3, 13, 184, 12, 12, 14, 196, 12, 12, 0, 0, 1, 5, 1, 10, 0, 1, 4, + 2, 7, 0, 4, 0, 1, 4, 7, 1, 0, 0, 0, 7, 4, 2, 7, 0, 4, + 0, 2, 9, 4, 2, 3, 1, 0, 1, 0, 13, 0, 1, 2, 7, 4, 1, 2, + 13, 4, 1, 2, 3, 0, 1, 0, 14, 0, 6, 2, 7, 4, 1, 2, 14, 4, + 6, 2, 3, 0, 1, 0, 15, 7, 8, 2, 7, 4, 1, 2, 15, 9, 8, 2, + 3, 0, 1, 0, 16, 10, 5, 2, 7, 4, 1, 0, 17, 11, 1, 2, 7, 4, + 1, 2, 16, 12, 5, 2, 3, 0, 1, 2, 18, 13, 1, 2, 3, 0, 1, 0, + 19, 11, 14, 2, 7, 4, 1, 2, 19, 13, 14, 2, 3, 0, 1, 0, 20, 7, + 15, 2, 7, 4, 1, 0, 21, 7, 16, 2, 7, 4, 1, 0, 22, 2, 10, 2, + 7, 4, 1, 0, 23, 5, 10, 2, 7, 4, 1, 0, 24, 17, 5, 2, 7, 4, + 1, 1, 25, 5, 20, 1, 0, 1, 2, 24, 21, 5, 2, 3, 0, 1, 1, 26, + 22, 1, 1, 0, 1, 1, 27, 19, 20, 1, 0, 1, 1, 14, 22, 23, 1, 0, + 1, 2, 23, 5, 12, 2, 3, 4, 1, 0, 28, 11, 20, 2, 7, 4, 1, 0, + 29, 11, 20, 2, 7, 4, 1, 0, 30, 0, 25, 2, 7, 4, 1, 0, 31, 7, + 27, 2, 7, 4, 1, 2, 20, 9, 15, 2, 3, 0, 1, 1, 3, 3, 3, 5, + 3, 7, 2, 8, 3, 9, 3, 11, 3, 13, 2, 15, 2, 16, 2, 17, 19, 18, + 3, 19, 19, 20, 19, 21, 19, 22, 3, 27, 3, 2, 6, 11, 2, 2, 9, 0, + 9, 1, 9, 0, 1, 1, 2, 9, 0, 9, 1, 2, 9, 0, 11, 0, 2, 9, + 0, 9, 1, 2, 6, 11, 3, 2, 9, 0, 9, 1, 9, 0, 0, 1, 6, 9, + 1, 2, 7, 11, 2, 2, 9, 0, 9, 1, 9, 0, 1, 7, 9, 1, 2, 7, + 11, 3, 2, 9, 0, 9, 1, 9, 0, 1, 11, 2, 2, 9, 0, 9, 1, 1, + 6, 11, 2, 2, 9, 0, 9, 1, 1, 11, 3, 2, 9, 0, 9, 1, 1, 6, + 11, 3, 2, 9, 0, 9, 1, 1, 3, 1, 9, 1, 3, 9, 1, 11, 1, 1, + 9, 0, 11, 1, 1, 9, 0, 3, 7, 11, 2, 2, 9, 0, 9, 1, 9, 0, + 9, 1, 2, 11, 2, 2, 9, 0, 9, 1, 11, 2, 2, 9, 0, 9, 1, 1, + 9, 0, 1, 11, 1, 1, 9, 0, 3, 7, 11, 3, 2, 9, 0, 9, 1, 9, + 0, 9, 1, 1, 6, 11, 1, 1, 9, 0, 1, 6, 9, 0, 7, 11, 1, 1, + 9, 0, 11, 1, 1, 9, 0, 11, 0, 2, 9, 0, 9, 1, 7, 11, 3, 2, + 9, 0, 11, 0, 2, 9, 0, 9, 1, 6, 9, 0, 9, 0, 7, 11, 1, 1, + 9, 0, 3, 6, 9, 1, 11, 1, 1, 9, 0, 11, 1, 1, 9, 0, 1, 6, + 11, 0, 2, 9, 0, 9, 1, 3, 7, 9, 1, 11, 1, 1, 9, 0, 11, 1, + 1, 9, 0, 1, 7, 11, 0, 2, 9, 0, 9, 1, 7, 11, 1, 1, 9, 0, + 11, 1, 1, 9, 0, 9, 1, 7, 11, 1, 1, 9, 0, 9, 1, 11, 1, 1, + 9, 0, 11, 1, 1, 9, 0, 7, 116, 97, 98, 108, 105, 115, 116, 4, 78, 111, + 100, 101, 5, 118, 97, 108, 117, 101, 8, 112, 114, 101, 118, 105, 111, 117, 115, 6, + 79, 112, 116, 105, 111, 110, 6, 111, 112, 116, 105, 111, 110, 4, 110, 101, 120, 116, + 7, 84, 97, 98, 108, 105, 115, 116, 5, 116, 97, 98, 108, 101, 15, 84, 97, 98, + 108, 101, 87, 105, 116, 104, 76, 101, 110, 103, 116, 104, 17, 116, 97, 98, 108, 101, + 95, 119, 105, 116, 104, 95, 108, 101, 110, 103, 116, 104, 4, 104, 101, 97, 100, 4, + 116, 97, 105, 108, 8, 99, 111, 110, 116, 97, 105, 110, 115, 6, 98, 111, 114, 114, + 111, 119, 10, 98, 111, 114, 114, 111, 119, 95, 109, 117, 116, 13, 100, 101, 115, 116, + 114, 111, 121, 95, 101, 109, 112, 116, 121, 8, 105, 115, 95, 101, 109, 112, 116, 121, + 5, 101, 109, 112, 116, 121, 6, 108, 101, 110, 103, 116, 104, 6, 114, 101, 109, 111, + 118, 101, 15, 114, 101, 109, 111, 118, 101, 95, 105, 116, 101, 114, 97, 98, 108, 101, + 9, 115, 105, 110, 103, 108, 101, 116, 111, 110, 3, 110, 101, 119, 3, 97, 100, 100, + 4, 110, 111, 110, 101, 7, 105, 115, 95, 110, 111, 110, 101, 4, 115, 111, 109, 101, + 12, 103, 101, 116, 95, 104, 101, 97, 100, 95, 107, 101, 121, 12, 103, 101, 116, 95, + 116, 97, 105, 108, 95, 107, 101, 121, 15, 98, 111, 114, 114, 111, 119, 95, 105, 116, + 101, 114, 97, 98, 108, 101, 19, 98, 111, 114, 114, 111, 119, 95, 105, 116, 101, 114, + 97, 98, 108, 101, 95, 109, 117, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 35, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 20, 99, 111, 109, 112, 105, 108, 97, 116, 105, 111, 110, 95, 109, 101, 116, 97, 100, + 97, 116, 97, 9, 0, 3, 50, 46, 48, 3, 50, 46, 49, 18, 97, 112, 116, 111, + 115, 58, 58, 109, 101, 116, 97, 100, 97, 116, 97, 95, 118, 49, 82, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 19, 69, 95, 68, 69, 83, 84, 82, 79, 89, 95, 78, + 79, 84, 95, 69, 77, 80, 84, 89, 50, 65, 116, 116, 101, 109, 112, 116, 105, 110, + 103, 32, 116, 111, 32, 100, 101, 115, 116, 114, 111, 121, 32, 97, 32, 116, 97, 98, + 108, 105, 115, 116, 32, 116, 104, 97, 116, 32, 105, 115, 32, 110, 111, 116, 32, 101, + 109, 112, 116, 121, 46, 0, 0, 0, 2, 3, 2, 9, 1, 3, 11, 1, 1, 9, + 0, 6, 11, 1, 1, 9, 0, 2, 2, 3, 8, 11, 3, 2, 9, 0, 11, 0, + 2, 9, 0, 9, 1, 11, 11, 1, 1, 9, 0, 12, 11, 1, 1, 9, 0, 1, + 2, 0, 2, 0, 1, 0, 0, 5, 5, 11, 0, 55, 0, 11, 1, 56, 0, 2, + 2, 1, 0, 0, 5, 6, 11, 0, 55, 0, 11, 1, 56, 1, 55, 1, 2, 4, + 1, 0, 0, 5, 6, 11, 0, 54, 0, 11, 1, 56, 2, 54, 1, 2, 6, 1, + 0, 0, 5, 11, 14, 0, 56, 3, 4, 9, 11, 0, 58, 0, 1, 1, 56, 4, + 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, 39, 7, 1, 0, 0, 5, 4, 11, + 0, 55, 0, 56, 5, 2, 10, 1, 0, 0, 5, 4, 11, 0, 55, 0, 56, 6, + 2, 12, 1, 0, 0, 5, 6, 11, 0, 11, 1, 56, 7, 1, 1, 2, 14, 1, + 0, 0, 18, 8, 56, 8, 12, 2, 13, 2, 11, 0, 11, 1, 56, 9, 11, 2, + 2, 16, 1, 0, 0, 24, 51, 10, 0, 55, 2, 20, 56, 10, 12, 3, 12, 4, + 11, 2, 11, 4, 11, 3, 57, 1, 12, 5, 10, 0, 54, 0, 10, 1, 11, 5, + 56, 11, 10, 0, 55, 3, 56, 12, 4, 31, 10, 1, 56, 13, 10, 0, 54, 3, + 21, 11, 1, 56, 13, 11, 0, 54, 2, 21, 2, 10, 0, 55, 2, 56, 14, 10, + 1, 56, 13, 10, 0, 54, 0, 12, 6, 12, 4, 20, 12, 8, 11, 6, 11, 8, + 56, 2, 54, 4, 12, 9, 11, 4, 11, 9, 21, 5, 25, 15, 1, 0, 0, 5, + 5, 56, 15, 56, 10, 56, 10, 57, 0, 2, 23, 1, 0, 0, 5, 4, 11, 0, + 55, 3, 20, 2, 24, 1, 0, 0, 5, 4, 11, 0, 55, 2, 20, 2, 25, 1, + 0, 0, 26, 14, 11, 0, 55, 0, 11, 1, 56, 1, 12, 2, 10, 2, 55, 1, + 10, 2, 55, 5, 20, 11, 2, 55, 4, 20, 2, 26, 1, 0, 0, 28, 14, 11, + 0, 54, 0, 11, 1, 56, 2, 12, 2, 10, 2, 54, 1, 10, 2, 55, 5, 20, + 11, 2, 55, 4, 20, 2, 13, 1, 0, 0, 29, 60, 10, 0, 54, 0, 11, 1, + 56, 16, 58, 1, 12, 2, 12, 3, 12, 4, 14, 3, 56, 12, 4, 48, 10, 0, + 54, 3, 12, 5, 10, 2, 11, 5, 21, 14, 2, 56, 12, 4, 36, 11, 0, 54, + 2, 12, 5, 10, 3, 11, 5, 21, 11, 4, 12, 6, 11, 3, 12, 7, 11, 2, + 12, 8, 11, 6, 11, 7, 11, 8, 2, 11, 0, 54, 0, 14, 2, 56, 14, 20, + 56, 2, 54, 5, 12, 5, 10, 3, 11, 5, 21, 5, 26, 10, 0, 54, 0, 14, + 3, 56, 14, 20, 56, 2, 54, 4, 12, 5, 10, 2, 11, 5, 21, 5, 17, 1, + 0, 0, 0, 1, 2, 1, 1, 0, 2, 0, 1, 0, 2, 1, 2, 2, 2, 3, + 2, 4, 2, 5, 2, 0, + ] +}); + +#[rustfmt::skip] +pub static MODULE_ECONIA_RESOURCE_ACCOUNT: Lazy> = Lazy::new(|| { + vec![ + 161, 28, 235, 11, 7, 0, 0, 10, 13, 1, 0, 8, 2, 8, 8, 3, 16, 49, + 4, 65, 2, 5, 67, 36, 7, 103, 131, 2, 8, 234, 2, 64, 6, 170, 3, 34, + 16, 204, 3, 69, 10, 145, 4, 6, 12, 151, 4, 66, 13, 217, 4, 2, 15, 219, + 4, 4, 0, 0, 1, 4, 1, 8, 1, 10, 0, 1, 8, 0, 1, 3, 6, 0, + 0, 5, 0, 1, 0, 1, 1, 6, 2, 1, 0, 1, 0, 7, 3, 0, 0, 1, + 2, 9, 0, 4, 0, 1, 3, 11, 5, 6, 1, 0, 1, 1, 12, 7, 8, 0, + 1, 0, 13, 0, 10, 0, 1, 1, 14, 2, 10, 0, 1, 4, 4, 0, 1, 12, + 1, 6, 8, 1, 1, 6, 12, 1, 3, 1, 6, 9, 0, 1, 10, 2, 2, 6, + 12, 10, 2, 2, 12, 8, 1, 3, 3, 10, 2, 8, 0, 1, 5, 16, 114, 101, + 115, 111, 117, 114, 99, 101, 95, 97, 99, 99, 111, 117, 110, 116, 21, 83, 105, 103, + 110, 101, 114, 67, 97, 112, 97, 98, 105, 108, 105, 116, 121, 83, 116, 111, 114, 101, + 17, 115, 105, 103, 110, 101, 114, 95, 99, 97, 112, 97, 98, 105, 108, 105, 116, 121, + 16, 83, 105, 103, 110, 101, 114, 67, 97, 112, 97, 98, 105, 108, 105, 116, 121, 7, + 97, 99, 99, 111, 117, 110, 116, 10, 103, 101, 116, 95, 115, 105, 103, 110, 101, 114, + 29, 99, 114, 101, 97, 116, 101, 95, 115, 105, 103, 110, 101, 114, 95, 119, 105, 116, + 104, 95, 99, 97, 112, 97, 98, 105, 108, 105, 116, 121, 11, 105, 110, 105, 116, 95, + 109, 111, 100, 117, 108, 101, 9, 116, 105, 109, 101, 115, 116, 97, 109, 112, 16, 110, + 111, 119, 95, 109, 105, 99, 114, 111, 115, 101, 99, 111, 110, 100, 115, 3, 98, 99, + 115, 8, 116, 111, 95, 98, 121, 116, 101, 115, 23, 99, 114, 101, 97, 116, 101, 95, + 114, 101, 115, 111, 117, 114, 99, 101, 95, 97, 99, 99, 111, 117, 110, 116, 11, 103, + 101, 116, 95, 97, 100, 100, 114, 101, 115, 115, 29, 103, 101, 116, 95, 115, 105, 103, + 110, 101, 114, 95, 99, 97, 112, 97, 98, 105, 108, 105, 116, 121, 95, 97, 100, 100, + 114, 101, 115, 115, 10, 105, 110, 99, 101, 110, 116, 105, 118, 101, 115, 6, 109, 97, + 114, 107, 101, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 35, 69, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 32, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 35, 69, 20, 99, 111, 109, 112, 105, + 108, 97, 116, 105, 111, 110, 95, 109, 101, 116, 97, 100, 97, 116, 97, 9, 0, 3, + 50, 46, 48, 3, 50, 46, 49, 18, 97, 112, 116, 111, 115, 58, 58, 109, 101, 116, + 97, 100, 97, 116, 97, 95, 118, 49, 18, 0, 0, 1, 11, 103, 101, 116, 95, 97, + 100, 100, 114, 101, 115, 115, 1, 1, 0, 0, 2, 1, 2, 8, 1, 0, 3, 0, + 1, 0, 0, 5, 7, 0, 43, 0, 16, 0, 17, 1, 2, 2, 0, 0, 0, 9, + 15, 17, 3, 12, 1, 14, 1, 56, 0, 12, 2, 10, 0, 11, 2, 17, 5, 18, + 0, 12, 3, 11, 0, 11, 3, 45, 0, 1, 2, 6, 3, 0, 1, 0, 0, 5, + 7, 0, 43, 0, 16, 0, 17, 7, 2, 0, 0, 0, 15, 0, 16, 0, + ] +}); + +#[rustfmt::skip] +pub static MODULE_ECONIA_INCENTIVES: Lazy> = Lazy::new(|| { + vec![ + 161, 28, 235, 11, 7, 0, 0, 10, 15, 1, 0, 16, 2, 16, 54, 3, 70, 203, + 3, 4, 145, 4, 78, 5, 223, 4, 205, 3, 7, 172, 8, 148, 16, 8, 192, 24, + 64, 6, 128, 25, 215, 1, 16, 215, 26, 195, 19, 10, 154, 46, 73, 11, 227, 46, + 8, 12, 235, 46, 174, 19, 13, 153, 66, 28, 14, 181, 66, 10, 15, 191, 66, 4, + 0, 0, 0, 4, 1, 6, 1, 10, 0, 27, 0, 36, 1, 50, 1, 72, 0, 1, + 8, 1, 0, 1, 1, 3, 4, 2, 7, 0, 4, 0, 2, 5, 4, 1, 0, 1, + 0, 7, 10, 0, 3, 9, 7, 0, 0, 16, 6, 0, 0, 17, 4, 1, 0, 1, + 0, 23, 8, 1, 0, 1, 0, 24, 8, 1, 0, 1, 4, 26, 0, 0, 0, 25, + 0, 1, 0, 1, 0, 28, 3, 1, 1, 0, 1, 0, 29, 5, 6, 1, 0, 1, + 1, 30, 9, 10, 2, 7, 4, 1, 1, 31, 11, 12, 2, 7, 4, 1, 0, 32, + 13, 14, 0, 1, 0, 33, 15, 1, 1, 0, 1, 2, 34, 16, 17, 1, 0, 1, + 2, 35, 18, 1, 1, 0, 1, 5, 37, 1, 19, 0, 1, 0, 38, 22, 14, 0, + 1, 0, 39, 17, 1, 1, 0, 1, 0, 40, 1, 14, 0, 1, 0, 41, 6, 1, + 1, 0, 1, 0, 42, 17, 1, 1, 0, 1, 0, 43, 1, 14, 0, 1, 0, 44, + 17, 1, 1, 0, 1, 0, 45, 1, 14, 0, 1, 0, 46, 17, 1, 1, 0, 1, + 2, 47, 24, 14, 1, 0, 1, 0, 48, 1, 1, 1, 0, 1, 0, 49, 26, 14, + 2, 0, 0, 1, 6, 51, 0, 19, 0, 1, 0, 52, 28, 14, 2, 0, 0, 1, + 1, 53, 9, 29, 2, 7, 4, 1, 0, 54, 13, 14, 0, 1, 0, 55, 33, 14, + 1, 0, 1, 0, 56, 34, 14, 1, 0, 1, 0, 57, 13, 14, 0, 1, 0, 58, + 1, 14, 0, 1, 0, 59, 1, 14, 0, 1, 0, 60, 0, 1, 1, 0, 1, 2, + 61, 1, 10, 1, 0, 1, 2, 62, 1, 17, 1, 0, 1, 0, 63, 1, 10, 1, + 0, 1, 3, 64, 1, 36, 1, 0, 1, 0, 65, 14, 1, 1, 0, 1, 5, 66, + 1, 38, 0, 1, 1, 67, 1, 39, 2, 7, 4, 1, 1, 68, 40, 1, 2, 7, + 4, 1, 0, 69, 42, 1, 2, 0, 0, 1, 0, 70, 45, 1, 0, 1, 0, 71, + 47, 1, 0, 1, 7, 73, 50, 10, 1, 0, 1, 0, 74, 51, 1, 1, 0, 1, + 0, 75, 42, 1, 2, 0, 0, 1, 0, 76, 26, 1, 2, 0, 0, 1, 2, 77, + 33, 17, 1, 0, 1, 0, 78, 55, 17, 1, 0, 1, 0, 79, 56, 17, 1, 0, + 1, 0, 80, 33, 17, 1, 0, 1, 0, 81, 33, 1, 1, 0, 1, 0, 82, 56, + 1, 1, 0, 1, 2, 83, 25, 17, 1, 0, 1, 0, 84, 55, 1, 1, 0, 1, + 2, 85, 19, 10, 1, 0, 1, 2, 86, 0, 1, 1, 0, 1, 2, 87, 58, 1, + 1, 0, 1, 0, 88, 54, 17, 2, 0, 0, 1, 0, 89, 33, 1, 2, 0, 0, + 1, 0, 90, 33, 17, 1, 0, 1, 0, 91, 61, 17, 1, 0, 1, 0, 92, 0, + 17, 1, 0, 1, 0, 93, 0, 1, 1, 0, 1, 0, 94, 61, 1, 1, 0, 1, + 0, 95, 33, 1, 1, 0, 1, 1, 2, 3, 8, 4, 8, 6, 7, 7, 7, 8, + 7, 4, 20, 13, 7, 19, 7, 20, 7, 18, 7, 23, 27, 24, 8, 27, 7, 32, + 7, 33, 7, 35, 7, 38, 20, 39, 20, 13, 43, 38, 8, 39, 8, 31, 7, 43, + 46, 1, 7, 21, 27, 47, 43, 45, 27, 34, 7, 49, 7, 52, 7, 53, 7, 55, + 7, 56, 7, 57, 7, 26, 7, 58, 27, 61, 7, 64, 7, 1, 6, 12, 0, 1, + 8, 9, 7, 6, 12, 3, 3, 3, 3, 6, 10, 10, 3, 1, 7, 10, 10, 3, + 1, 6, 10, 10, 3, 3, 3, 3, 3, 5, 3, 5, 3, 3, 11, 2, 1, 9, + 0, 2, 11, 2, 1, 9, 0, 3, 1, 9, 0, 2, 3, 11, 6, 1, 9, 0, + 2, 6, 11, 1, 2, 9, 0, 9, 1, 9, 0, 1, 1, 2, 7, 11, 1, 2, + 9, 0, 9, 1, 9, 0, 1, 7, 9, 1, 1, 2, 1, 3, 3, 3, 6, 11, + 2, 1, 9, 0, 3, 2, 7, 11, 2, 1, 9, 0, 3, 1, 11, 2, 1, 9, + 0, 2, 7, 11, 2, 1, 9, 0, 11, 2, 1, 9, 0, 1, 5, 2, 3, 11, + 2, 1, 9, 0, 8, 3, 7, 11, 1, 2, 3, 11, 6, 1, 9, 0, 7, 11, + 6, 1, 9, 0, 3, 6, 11, 2, 1, 9, 0, 11, 2, 1, 9, 0, 7, 11, + 2, 1, 9, 0, 3, 3, 1, 3, 3, 5, 4, 1, 4, 4, 4, 1, 6, 11, + 2, 1, 9, 0, 1, 7, 11, 2, 1, 9, 0, 3, 6, 12, 3, 2, 2, 9, + 0, 9, 1, 3, 5, 3, 2, 1, 6, 9, 1, 2, 2, 3, 1, 8, 5, 2, + 6, 10, 8, 5, 3, 2, 6, 12, 3, 2, 5, 3, 2, 6, 12, 11, 8, 1, + 9, 0, 1, 8, 4, 3, 4, 3, 4, 1, 12, 1, 11, 1, 2, 9, 0, 9, + 1, 3, 7, 11, 1, 2, 9, 0, 9, 1, 9, 0, 9, 1, 4, 12, 5, 11, + 2, 1, 9, 0, 7, 11, 1, 2, 3, 11, 2, 1, 9, 0, 4, 6, 12, 3, + 2, 11, 2, 1, 9, 1, 1, 9, 1, 7, 3, 5, 6, 12, 11, 7, 1, 9, + 0, 2, 11, 2, 1, 9, 0, 11, 6, 1, 9, 0, 6, 6, 12, 3, 3, 3, + 3, 6, 10, 10, 3, 1, 10, 3, 3, 3, 6, 10, 10, 3, 7, 10, 8, 5, + 7, 12, 3, 10, 8, 5, 7, 8, 3, 7, 10, 8, 5, 8, 4, 8, 3, 11, + 3, 3, 3, 3, 3, 6, 10, 3, 3, 6, 3, 6, 3, 6, 3, 8, 5, 1, + 6, 10, 9, 0, 6, 6, 12, 3, 3, 3, 3, 10, 10, 3, 2, 1, 6, 10, + 10, 3, 2, 3, 7, 2, 3, 6, 12, 3, 11, 2, 1, 9, 1, 3, 6, 12, + 3, 3, 4, 6, 12, 3, 1, 3, 2, 3, 1, 2, 5, 11, 2, 1, 9, 0, + 1, 7, 11, 6, 1, 9, 0, 3, 3, 11, 2, 1, 9, 1, 11, 2, 1, 9, + 0, 3, 6, 12, 1, 3, 10, 105, 110, 99, 101, 110, 116, 105, 118, 101, 115, 14, + 69, 99, 111, 110, 105, 97, 70, 101, 101, 83, 116, 111, 114, 101, 3, 109, 97, 112, + 7, 84, 97, 98, 108, 105, 115, 116, 7, 116, 97, 98, 108, 105, 115, 116, 4, 67, + 111, 105, 110, 4, 99, 111, 105, 110, 19, 73, 110, 99, 101, 110, 116, 105, 118, 101, + 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 22, 117, 116, 105, 108, 105, 116, 121, + 95, 99, 111, 105, 110, 95, 116, 121, 112, 101, 95, 105, 110, 102, 111, 8, 84, 121, + 112, 101, 73, 110, 102, 111, 9, 116, 121, 112, 101, 95, 105, 110, 102, 111, 23, 109, + 97, 114, 107, 101, 116, 95, 114, 101, 103, 105, 115, 116, 114, 97, 116, 105, 111, 110, + 95, 102, 101, 101, 28, 117, 110, 100, 101, 114, 119, 114, 105, 116, 101, 114, 95, 114, + 101, 103, 105, 115, 116, 114, 97, 116, 105, 111, 110, 95, 102, 101, 101, 26, 99, 117, + 115, 116, 111, 100, 105, 97, 110, 95, 114, 101, 103, 105, 115, 116, 114, 97, 116, 105, + 111, 110, 95, 102, 101, 101, 17, 116, 97, 107, 101, 114, 95, 102, 101, 101, 95, 100, + 105, 118, 105, 115, 111, 114, 26, 105, 110, 116, 101, 103, 114, 97, 116, 111, 114, 95, + 102, 101, 101, 95, 115, 116, 111, 114, 101, 95, 116, 105, 101, 114, 115, 32, 73, 110, + 116, 101, 103, 114, 97, 116, 111, 114, 70, 101, 101, 83, 116, 111, 114, 101, 84, 105, + 101, 114, 80, 97, 114, 97, 109, 101, 116, 101, 114, 115, 18, 73, 110, 116, 101, 103, + 114, 97, 116, 111, 114, 70, 101, 101, 83, 116, 111, 114, 101, 4, 116, 105, 101, 114, + 5, 99, 111, 105, 110, 115, 17, 102, 101, 101, 95, 115, 104, 97, 114, 101, 95, 100, + 105, 118, 105, 115, 111, 114, 19, 116, 105, 101, 114, 95, 97, 99, 116, 105, 118, 97, + 116, 105, 111, 110, 95, 102, 101, 101, 14, 119, 105, 116, 104, 100, 114, 97, 119, 97, + 108, 95, 102, 101, 101, 19, 73, 110, 116, 101, 103, 114, 97, 116, 111, 114, 70, 101, + 101, 83, 116, 111, 114, 101, 115, 16, 85, 116, 105, 108, 105, 116, 121, 67, 111, 105, + 110, 83, 116, 111, 114, 101, 11, 105, 110, 105, 116, 95, 109, 111, 100, 117, 108, 101, + 2, 85, 67, 6, 97, 115, 115, 101, 116, 115, 24, 115, 101, 116, 95, 105, 110, 99, + 101, 110, 116, 105, 118, 101, 95, 112, 97, 114, 97, 109, 101, 116, 101, 114, 115, 17, + 97, 115, 115, 101, 115, 115, 95, 116, 97, 107, 101, 114, 95, 102, 101, 101, 115, 8, + 99, 111, 110, 116, 97, 105, 110, 115, 10, 98, 111, 114, 114, 111, 119, 95, 109, 117, + 116, 21, 103, 101, 116, 95, 102, 101, 101, 95, 115, 104, 97, 114, 101, 95, 100, 105, + 118, 105, 115, 111, 114, 22, 114, 97, 110, 103, 101, 95, 99, 104, 101, 99, 107, 95, + 99, 111, 105, 110, 95, 109, 101, 114, 103, 101, 7, 101, 120, 116, 114, 97, 99, 116, + 5, 109, 101, 114, 103, 101, 16, 114, 101, 115, 111, 117, 114, 99, 101, 95, 97, 99, + 99, 111, 117, 110, 116, 11, 103, 101, 116, 95, 97, 100, 100, 114, 101, 115, 115, 25, + 99, 97, 108, 99, 117, 108, 97, 116, 101, 95, 109, 97, 120, 95, 113, 117, 111, 116, + 101, 95, 109, 97, 116, 99, 104, 44, 100, 101, 112, 111, 115, 105, 116, 95, 99, 117, + 115, 116, 111, 100, 105, 97, 110, 95, 114, 101, 103, 105, 115, 116, 114, 97, 116, 105, + 111, 110, 95, 117, 116, 105, 108, 105, 116, 121, 95, 99, 111, 105, 110, 115, 30, 103, + 101, 116, 95, 99, 117, 115, 116, 111, 100, 105, 97, 110, 95, 114, 101, 103, 105, 115, + 116, 114, 97, 116, 105, 111, 110, 95, 102, 101, 101, 30, 100, 101, 112, 111, 115, 105, + 116, 95, 117, 116, 105, 108, 105, 116, 121, 95, 99, 111, 105, 110, 115, 95, 118, 101, + 114, 105, 102, 105, 101, 100, 41, 100, 101, 112, 111, 115, 105, 116, 95, 109, 97, 114, + 107, 101, 116, 95, 114, 101, 103, 105, 115, 116, 114, 97, 116, 105, 111, 110, 95, 117, + 116, 105, 108, 105, 116, 121, 95, 99, 111, 105, 110, 115, 27, 103, 101, 116, 95, 109, + 97, 114, 107, 101, 116, 95, 114, 101, 103, 105, 115, 116, 114, 97, 116, 105, 111, 110, + 95, 102, 101, 101, 46, 100, 101, 112, 111, 115, 105, 116, 95, 117, 110, 100, 101, 114, + 119, 114, 105, 116, 101, 114, 95, 114, 101, 103, 105, 115, 116, 114, 97, 116, 105, 111, + 110, 95, 117, 116, 105, 108, 105, 116, 121, 95, 99, 111, 105, 110, 115, 32, 103, 101, + 116, 95, 117, 110, 100, 101, 114, 119, 114, 105, 116, 101, 114, 95, 114, 101, 103, 105, + 115, 116, 114, 97, 116, 105, 111, 110, 95, 102, 101, 101, 21, 100, 101, 112, 111, 115, + 105, 116, 95, 117, 116, 105, 108, 105, 116, 121, 95, 99, 111, 105, 110, 115, 5, 118, + 97, 108, 117, 101, 24, 118, 101, 114, 105, 102, 121, 95, 117, 116, 105, 108, 105, 116, + 121, 95, 99, 111, 105, 110, 95, 116, 121, 112, 101, 40, 103, 101, 116, 95, 99, 111, + 115, 116, 95, 116, 111, 95, 117, 112, 103, 114, 97, 100, 101, 95, 105, 110, 116, 101, + 103, 114, 97, 116, 111, 114, 95, 102, 101, 101, 95, 115, 116, 111, 114, 101, 6, 115, + 105, 103, 110, 101, 114, 10, 97, 100, 100, 114, 101, 115, 115, 95, 111, 102, 45, 103, + 101, 116, 95, 99, 111, 115, 116, 95, 116, 111, 95, 117, 112, 103, 114, 97, 100, 101, + 95, 105, 110, 116, 101, 103, 114, 97, 116, 111, 114, 95, 102, 101, 101, 95, 115, 116, + 111, 114, 101, 95, 118, 105, 101, 119, 6, 98, 111, 114, 114, 111, 119, 23, 103, 101, + 116, 95, 116, 105, 101, 114, 95, 97, 99, 116, 105, 118, 97, 116, 105, 111, 110, 95, + 102, 101, 101, 29, 103, 101, 116, 95, 105, 110, 116, 101, 103, 114, 97, 116, 111, 114, + 95, 119, 105, 116, 104, 100, 114, 97, 119, 97, 108, 95, 102, 101, 101, 34, 103, 101, + 116, 95, 105, 110, 116, 101, 103, 114, 97, 116, 111, 114, 95, 119, 105, 116, 104, 100, + 114, 97, 119, 97, 108, 95, 102, 101, 101, 95, 118, 105, 101, 119, 23, 103, 101, 116, + 95, 116, 105, 101, 114, 95, 119, 105, 116, 104, 100, 114, 97, 119, 97, 108, 95, 102, + 101, 101, 21, 103, 101, 116, 95, 110, 95, 102, 101, 101, 95, 115, 116, 111, 114, 101, + 95, 116, 105, 101, 114, 115, 21, 103, 101, 116, 95, 116, 97, 107, 101, 114, 95, 102, + 101, 101, 95, 100, 105, 118, 105, 115, 111, 114, 23, 105, 110, 105, 116, 95, 117, 116, + 105, 108, 105, 116, 121, 95, 99, 111, 105, 110, 95, 115, 116, 111, 114, 101, 19, 105, + 115, 95, 99, 111, 105, 110, 95, 105, 110, 105, 116, 105, 97, 108, 105, 122, 101, 100, + 4, 122, 101, 114, 111, 20, 105, 115, 95, 117, 116, 105, 108, 105, 116, 121, 95, 99, + 111, 105, 110, 95, 116, 121, 112, 101, 7, 116, 121, 112, 101, 95, 111, 102, 31, 114, + 101, 103, 105, 115, 116, 101, 114, 95, 101, 99, 111, 110, 105, 97, 95, 102, 101, 101, + 95, 115, 116, 111, 114, 101, 95, 101, 110, 116, 114, 121, 10, 103, 101, 116, 95, 115, + 105, 103, 110, 101, 114, 3, 110, 101, 119, 3, 97, 100, 100, 29, 114, 101, 103, 105, + 115, 116, 101, 114, 95, 105, 110, 116, 101, 103, 114, 97, 116, 111, 114, 95, 102, 101, + 101, 95, 115, 116, 111, 114, 101, 43, 115, 101, 116, 95, 105, 110, 99, 101, 110, 116, + 105, 118, 101, 95, 112, 97, 114, 97, 109, 101, 116, 101, 114, 115, 95, 114, 97, 110, + 103, 101, 95, 99, 104, 101, 99, 107, 95, 105, 110, 112, 117, 116, 115, 43, 115, 101, + 116, 95, 105, 110, 99, 101, 110, 116, 105, 118, 101, 95, 112, 97, 114, 97, 109, 101, + 116, 101, 114, 115, 95, 112, 97, 114, 115, 101, 95, 116, 105, 101, 114, 115, 95, 118, + 101, 99, 116, 111, 114, 6, 118, 101, 99, 116, 111, 114, 8, 105, 115, 95, 101, 109, + 112, 116, 121, 17, 117, 112, 100, 97, 116, 101, 95, 105, 110, 99, 101, 110, 116, 105, + 118, 101, 115, 28, 117, 112, 103, 114, 97, 100, 101, 95, 105, 110, 116, 101, 103, 114, + 97, 116, 111, 114, 95, 102, 101, 101, 95, 115, 116, 111, 114, 101, 42, 117, 112, 103, + 114, 97, 100, 101, 95, 105, 110, 116, 101, 103, 114, 97, 116, 111, 114, 95, 102, 101, + 101, 95, 115, 116, 111, 114, 101, 95, 118, 105, 97, 95, 99, 111, 105, 110, 115, 116, + 111, 114, 101, 8, 119, 105, 116, 104, 100, 114, 97, 119, 20, 119, 105, 116, 104, 100, + 114, 97, 119, 95, 101, 99, 111, 110, 105, 97, 95, 102, 101, 101, 115, 29, 119, 105, + 116, 104, 100, 114, 97, 119, 95, 101, 99, 111, 110, 105, 97, 95, 102, 101, 101, 115, + 95, 105, 110, 116, 101, 114, 110, 97, 108, 24, 119, 105, 116, 104, 100, 114, 97, 119, + 95, 101, 99, 111, 110, 105, 97, 95, 102, 101, 101, 115, 95, 97, 108, 108, 38, 119, + 105, 116, 104, 100, 114, 97, 119, 95, 101, 99, 111, 110, 105, 97, 95, 102, 101, 101, + 115, 95, 97, 108, 108, 95, 116, 111, 95, 99, 111, 105, 110, 95, 115, 116, 111, 114, + 101, 43, 119, 105, 116, 104, 100, 114, 97, 119, 95, 101, 99, 111, 110, 105, 97, 95, + 102, 101, 101, 115, 95, 116, 111, 95, 99, 111, 105, 110, 95, 115, 116, 111, 114, 101, + 95, 105, 110, 116, 101, 114, 110, 97, 108, 11, 101, 120, 116, 114, 97, 99, 116, 95, + 97, 108, 108, 34, 119, 105, 116, 104, 100, 114, 97, 119, 95, 101, 99, 111, 110, 105, + 97, 95, 102, 101, 101, 115, 95, 116, 111, 95, 99, 111, 105, 110, 95, 115, 116, 111, + 114, 101, 21, 105, 115, 95, 97, 99, 99, 111, 117, 110, 116, 95, 114, 101, 103, 105, + 115, 116, 101, 114, 101, 100, 8, 114, 101, 103, 105, 115, 116, 101, 114, 7, 100, 101, + 112, 111, 115, 105, 116, 24, 119, 105, 116, 104, 100, 114, 97, 119, 95, 105, 110, 116, + 101, 103, 114, 97, 116, 111, 114, 95, 102, 101, 101, 115, 39, 119, 105, 116, 104, 100, + 114, 97, 119, 95, 105, 110, 116, 101, 103, 114, 97, 116, 111, 114, 95, 102, 101, 101, + 115, 95, 118, 105, 97, 95, 99, 111, 105, 110, 115, 116, 111, 114, 101, 115, 22, 119, + 105, 116, 104, 100, 114, 97, 119, 95, 117, 116, 105, 108, 105, 116, 121, 95, 99, 111, + 105, 110, 115, 31, 119, 105, 116, 104, 100, 114, 97, 119, 95, 117, 116, 105, 108, 105, + 116, 121, 95, 99, 111, 105, 110, 115, 95, 105, 110, 116, 101, 114, 110, 97, 108, 26, + 119, 105, 116, 104, 100, 114, 97, 119, 95, 117, 116, 105, 108, 105, 116, 121, 95, 99, + 111, 105, 110, 115, 95, 97, 108, 108, 40, 119, 105, 116, 104, 100, 114, 97, 119, 95, + 117, 116, 105, 108, 105, 116, 121, 95, 99, 111, 105, 110, 115, 95, 97, 108, 108, 95, + 116, 111, 95, 99, 111, 105, 110, 95, 115, 116, 111, 114, 101, 45, 119, 105, 116, 104, + 100, 114, 97, 119, 95, 117, 116, 105, 108, 105, 116, 121, 95, 99, 111, 105, 110, 115, + 95, 116, 111, 95, 99, 111, 105, 110, 95, 115, 116, 111, 114, 101, 95, 105, 110, 116, + 101, 114, 110, 97, 108, 36, 119, 105, 116, 104, 100, 114, 97, 119, 95, 117, 116, 105, + 108, 105, 116, 121, 95, 99, 111, 105, 110, 115, 95, 116, 111, 95, 99, 111, 105, 110, + 95, 115, 116, 111, 114, 101, 8, 114, 101, 103, 105, 115, 116, 114, 121, 6, 109, 97, + 114, 107, 101, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 35, 69, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 10, 3, 176, + 1, 7, 3, 16, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 176, 3, 25, 0, 0, 0, 0, 0, 3, 141, 32, 0, 0, 0, 0, 0, 0, + 176, 3, 25, 0, 0, 0, 0, 0, 129, 195, 23, 0, 0, 0, 0, 0, 3, 12, + 30, 0, 0, 0, 0, 0, 0, 83, 55, 119, 1, 0, 0, 0, 0, 81, 131, 22, + 0, 0, 0, 0, 0, 3, 231, 27, 0, 0, 0, 0, 0, 0, 180, 225, 138, 19, + 0, 0, 0, 0, 34, 67, 21, 0, 0, 0, 0, 0, 3, 11, 26, 0, 0, 0, + 0, 0, 0, 79, 5, 72, 244, 0, 0, 0, 0, 243, 2, 20, 0, 0, 0, 0, + 0, 3, 106, 24, 0, 0, 0, 0, 0, 0, 188, 63, 96, 115, 11, 0, 0, 0, + 196, 194, 18, 0, 0, 0, 0, 0, 3, 250, 22, 0, 0, 0, 0, 0, 0, 155, + 231, 98, 151, 133, 0, 0, 0, 148, 130, 17, 0, 0, 0, 0, 0, 5, 32, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 35, 69, 20, 99, 111, 109, 112, + 105, 108, 97, 116, 105, 111, 110, 95, 109, 101, 116, 97, 100, 97, 116, 97, 9, 0, + 3, 50, 46, 48, 3, 50, 46, 49, 18, 97, 112, 116, 111, 115, 58, 58, 109, 101, + 116, 97, 100, 97, 116, 97, 95, 118, 49, 143, 19, 24, 0, 0, 0, 0, 0, 0, + 0, 0, 12, 69, 95, 78, 79, 84, 95, 69, 67, 79, 78, 73, 65, 36, 67, 97, + 108, 108, 101, 114, 32, 105, 115, 32, 110, 111, 116, 32, 69, 99, 111, 110, 105, 97, + 44, 32, 98, 117, 116, 32, 115, 104, 111, 117, 108, 100, 32, 98, 101, 46, 1, 0, + 0, 0, 0, 0, 0, 0, 10, 69, 95, 78, 79, 84, 95, 67, 79, 73, 78, 48, + 84, 121, 112, 101, 32, 100, 111, 101, 115, 32, 110, 111, 116, 32, 99, 111, 114, 114, + 101, 115, 112, 111, 110, 100, 32, 116, 111, 32, 97, 110, 32, 105, 110, 105, 116, 105, + 97, 108, 105, 122, 101, 100, 32, 99, 111, 105, 110, 46, 2, 0, 0, 0, 0, 0, + 0, 0, 23, 69, 95, 69, 77, 80, 84, 89, 95, 70, 69, 69, 95, 83, 84, 79, + 82, 69, 95, 84, 73, 69, 82, 83, 39, 80, 97, 115, 115, 101, 100, 32, 102, 101, + 101, 32, 115, 116, 111, 114, 101, 32, 116, 105, 101, 114, 115, 32, 118, 101, 99, 116, + 111, 114, 32, 105, 115, 32, 101, 109, 112, 116, 121, 46, 3, 0, 0, 0, 0, 0, + 0, 0, 27, 69, 95, 70, 69, 69, 95, 83, 72, 65, 82, 69, 95, 68, 73, 86, + 73, 83, 79, 82, 95, 84, 79, 79, 95, 66, 73, 71, 54, 73, 110, 100, 105, 99, + 97, 116, 101, 100, 32, 102, 101, 101, 32, 115, 104, 97, 114, 101, 32, 100, 105, 118, + 105, 115, 111, 114, 32, 102, 111, 114, 32, 103, 105, 118, 101, 110, 32, 116, 105, 101, + 114, 32, 105, 115, 32, 116, 111, 111, 32, 98, 105, 103, 46, 4, 0, 0, 0, 0, + 0, 0, 0, 29, 69, 95, 70, 69, 69, 95, 83, 72, 65, 82, 69, 95, 68, 73, + 86, 73, 83, 79, 82, 95, 84, 79, 79, 95, 83, 77, 65, 76, 76, 95, 84, 104, + 101, 32, 105, 110, 100, 105, 99, 97, 116, 101, 100, 32, 102, 101, 101, 32, 115, 104, + 97, 114, 101, 32, 100, 105, 118, 105, 115, 111, 114, 32, 102, 111, 114, 32, 97, 32, + 103, 105, 118, 101, 110, 32, 116, 105, 101, 114, 32, 105, 115, 32, 108, 101, 115, 115, + 32, 116, 104, 97, 110, 10, 32, 116, 104, 101, 32, 105, 110, 100, 105, 99, 97, 116, + 101, 100, 32, 116, 97, 107, 101, 114, 32, 102, 101, 101, 32, 100, 105, 118, 105, 115, + 111, 114, 46, 5, 0, 0, 0, 0, 0, 0, 0, 39, 69, 95, 77, 65, 82, 75, + 69, 84, 95, 82, 69, 71, 73, 83, 84, 82, 65, 84, 73, 79, 78, 95, 70, 69, + 69, 95, 76, 69, 83, 83, 95, 84, 72, 65, 78, 95, 77, 73, 78, 49, 77, 97, + 114, 107, 101, 116, 32, 114, 101, 103, 105, 115, 116, 114, 97, 116, 105, 111, 110, 32, + 102, 101, 101, 32, 105, 115, 32, 108, 101, 115, 115, 32, 116, 104, 97, 110, 32, 116, + 104, 101, 32, 109, 105, 110, 105, 109, 117, 109, 46, 6, 0, 0, 0, 0, 0, 0, + 0, 42, 69, 95, 67, 85, 83, 84, 79, 68, 73, 65, 78, 95, 82, 69, 71, 73, + 83, 84, 82, 65, 84, 73, 79, 78, 95, 70, 69, 69, 95, 76, 69, 83, 83, 95, + 84, 72, 65, 78, 95, 77, 73, 78, 52, 67, 117, 115, 116, 111, 100, 105, 97, 110, + 32, 114, 101, 103, 105, 115, 116, 114, 97, 116, 105, 111, 110, 32, 102, 101, 101, 32, + 105, 115, 32, 108, 101, 115, 115, 32, 116, 104, 97, 110, 32, 116, 104, 101, 32, 109, + 105, 110, 105, 109, 117, 109, 46, 7, 0, 0, 0, 0, 0, 0, 0, 29, 69, 95, + 84, 65, 75, 69, 82, 95, 68, 73, 86, 73, 83, 79, 82, 95, 76, 69, 83, 83, + 95, 84, 72, 65, 78, 95, 77, 73, 78, 43, 84, 97, 107, 101, 114, 32, 102, 101, + 101, 32, 100, 105, 118, 105, 115, 111, 114, 32, 105, 115, 32, 108, 101, 115, 115, 32, + 116, 104, 97, 110, 32, 116, 104, 101, 32, 109, 105, 110, 105, 109, 117, 109, 46, 8, + 0, 0, 0, 0, 0, 0, 0, 26, 69, 95, 84, 73, 69, 82, 95, 70, 73, 69, + 76, 68, 83, 95, 87, 82, 79, 78, 71, 95, 76, 69, 78, 71, 84, 72, 55, 84, + 104, 101, 32, 119, 114, 111, 110, 103, 32, 110, 117, 109, 98, 101, 114, 32, 111, 102, + 32, 102, 105, 101, 108, 100, 115, 32, 97, 114, 101, 32, 112, 97, 115, 115, 101, 100, + 32, 102, 111, 114, 32, 97, 32, 103, 105, 118, 101, 110, 32, 116, 105, 101, 114, 46, + 9, 0, 0, 0, 0, 0, 0, 0, 26, 69, 95, 65, 67, 84, 73, 86, 65, 84, + 73, 79, 78, 95, 70, 69, 69, 95, 84, 79, 79, 95, 83, 77, 65, 76, 76, 47, + 84, 104, 101, 32, 105, 110, 100, 105, 99, 97, 116, 101, 100, 32, 116, 105, 101, 114, + 32, 97, 99, 116, 105, 118, 97, 116, 105, 111, 110, 32, 102, 101, 101, 32, 105, 115, + 32, 116, 111, 111, 32, 115, 109, 97, 108, 108, 46, 10, 0, 0, 0, 0, 0, 0, + 0, 24, 69, 95, 87, 73, 84, 72, 68, 82, 65, 87, 65, 76, 95, 70, 69, 69, + 95, 84, 79, 79, 95, 66, 73, 71, 40, 84, 104, 101, 32, 105, 110, 100, 105, 99, + 97, 116, 101, 100, 32, 119, 105, 116, 104, 100, 114, 97, 119, 97, 108, 32, 102, 101, + 101, 32, 105, 115, 32, 116, 111, 111, 32, 98, 105, 103, 46, 11, 0, 0, 0, 0, + 0, 0, 0, 26, 69, 95, 87, 73, 84, 72, 68, 82, 65, 87, 65, 76, 95, 70, + 69, 69, 95, 84, 79, 79, 95, 83, 77, 65, 76, 76, 42, 84, 104, 101, 32, 105, + 110, 100, 105, 99, 97, 116, 101, 100, 32, 119, 105, 116, 104, 100, 114, 97, 119, 97, + 108, 32, 102, 101, 101, 32, 105, 115, 32, 116, 111, 111, 32, 115, 109, 97, 108, 108, + 46, 12, 0, 0, 0, 0, 0, 0, 0, 27, 69, 95, 73, 78, 86, 65, 76, 73, + 68, 95, 85, 84, 73, 76, 73, 84, 89, 95, 67, 79, 73, 78, 95, 84, 89, 80, + 69, 34, 84, 121, 112, 101, 32, 105, 115, 32, 110, 111, 116, 32, 116, 104, 101, 32, + 117, 116, 105, 108, 105, 116, 121, 32, 99, 111, 105, 110, 32, 116, 121, 112, 101, 46, + 13, 0, 0, 0, 0, 0, 0, 0, 26, 69, 95, 78, 79, 84, 95, 69, 78, 79, + 85, 71, 72, 95, 85, 84, 73, 76, 73, 84, 89, 95, 67, 79, 73, 78, 83, 34, + 78, 111, 116, 32, 101, 110, 111, 117, 103, 104, 32, 117, 116, 105, 108, 105, 116, 121, + 32, 99, 111, 105, 110, 115, 32, 112, 114, 111, 118, 105, 100, 101, 100, 46, 14, 0, + 0, 0, 0, 0, 0, 0, 16, 69, 95, 84, 79, 79, 95, 77, 65, 78, 89, 95, + 84, 73, 69, 82, 83, 46, 84, 111, 111, 32, 109, 97, 110, 121, 32, 105, 110, 116, + 101, 103, 114, 97, 116, 111, 114, 32, 102, 101, 101, 32, 115, 116, 111, 114, 101, 32, + 116, 105, 101, 114, 115, 32, 105, 110, 100, 105, 99, 97, 116, 101, 100, 46, 15, 0, + 0, 0, 0, 0, 0, 0, 16, 69, 95, 78, 79, 84, 95, 65, 78, 95, 85, 80, + 71, 82, 65, 68, 69, 48, 73, 110, 100, 105, 99, 97, 116, 101, 100, 32, 116, 105, + 101, 114, 32, 105, 115, 32, 110, 111, 116, 32, 104, 105, 103, 104, 101, 114, 32, 116, + 104, 97, 110, 32, 101, 120, 105, 115, 116, 105, 110, 103, 32, 116, 105, 101, 114, 46, + 16, 0, 0, 0, 0, 0, 0, 0, 13, 69, 95, 70, 69, 87, 69, 82, 95, 84, + 73, 69, 82, 83, 84, 65, 110, 32, 117, 112, 100, 97, 116, 101, 32, 116, 111, 32, + 116, 104, 101, 32, 105, 110, 99, 101, 110, 116, 105, 118, 101, 32, 112, 97, 114, 97, + 109, 101, 116, 101, 114, 115, 32, 115, 101, 116, 32, 105, 110, 100, 105, 99, 97, 116, + 101, 115, 32, 97, 32, 114, 101, 100, 117, 99, 116, 105, 111, 110, 10, 32, 105, 110, + 32, 102, 101, 101, 32, 115, 116, 111, 114, 101, 32, 116, 105, 101, 114, 115, 46, 17, + 0, 0, 0, 0, 0, 0, 0, 35, 69, 95, 70, 73, 82, 83, 84, 95, 84, 73, + 69, 82, 95, 65, 67, 84, 73, 86, 65, 84, 73, 79, 78, 95, 70, 69, 69, 95, + 78, 79, 78, 90, 69, 82, 79, 42, 84, 104, 101, 32, 99, 111, 115, 116, 32, 116, + 111, 32, 97, 99, 116, 105, 118, 97, 116, 101, 32, 116, 111, 32, 116, 105, 101, 114, + 32, 48, 32, 105, 115, 32, 110, 111, 110, 122, 101, 114, 111, 46, 18, 0, 0, 0, + 0, 0, 0, 0, 44, 69, 95, 85, 78, 68, 69, 82, 87, 82, 73, 84, 69, 82, + 95, 82, 69, 71, 73, 83, 84, 82, 65, 84, 73, 79, 78, 95, 70, 69, 69, 95, + 76, 69, 83, 83, 95, 84, 72, 65, 78, 95, 77, 73, 78, 52, 67, 117, 115, 116, + 111, 100, 105, 97, 110, 32, 114, 101, 103, 105, 115, 116, 114, 97, 116, 105, 111, 110, + 32, 102, 101, 101, 32, 105, 115, 32, 108, 101, 115, 115, 32, 116, 104, 97, 110, 32, + 116, 104, 101, 32, 109, 105, 110, 105, 109, 117, 109, 46, 19, 0, 0, 0, 0, 0, + 0, 0, 31, 69, 95, 73, 78, 84, 69, 71, 82, 65, 84, 79, 82, 95, 70, 69, + 69, 95, 83, 84, 79, 82, 69, 95, 79, 86, 69, 82, 70, 76, 79, 87, 67, 68, + 101, 112, 111, 115, 105, 116, 105, 110, 103, 32, 116, 111, 32, 97, 110, 32, 105, 110, + 116, 101, 103, 114, 97, 116, 111, 114, 32, 102, 101, 101, 32, 115, 116, 111, 114, 101, + 32, 119, 111, 117, 108, 100, 32, 114, 101, 115, 117, 108, 116, 32, 105, 110, 32, 97, + 110, 10, 32, 111, 118, 101, 114, 102, 108, 111, 119, 46, 20, 0, 0, 0, 0, 0, + 0, 0, 27, 69, 95, 69, 67, 79, 78, 73, 65, 95, 70, 69, 69, 95, 83, 84, + 79, 82, 69, 95, 79, 86, 69, 82, 70, 76, 79, 87, 62, 68, 101, 112, 111, 115, + 105, 116, 105, 110, 103, 32, 116, 111, 32, 97, 110, 32, 69, 99, 111, 110, 105, 97, + 32, 102, 101, 101, 32, 115, 116, 111, 114, 101, 32, 119, 111, 117, 108, 100, 32, 114, + 101, 115, 117, 108, 116, 32, 105, 110, 32, 97, 110, 32, 111, 118, 101, 114, 102, 108, + 111, 119, 46, 21, 0, 0, 0, 0, 0, 0, 0, 29, 69, 95, 85, 84, 73, 76, + 73, 84, 89, 95, 67, 79, 73, 78, 95, 83, 84, 79, 82, 69, 95, 79, 86, 69, + 82, 70, 76, 79, 87, 63, 68, 101, 112, 111, 115, 105, 116, 105, 110, 103, 32, 116, + 111, 32, 97, 32, 117, 116, 105, 108, 105, 116, 121, 32, 99, 111, 105, 110, 32, 115, + 116, 111, 114, 101, 32, 119, 111, 117, 108, 100, 32, 114, 101, 115, 117, 108, 116, 32, + 105, 110, 32, 97, 110, 32, 111, 118, 101, 114, 102, 108, 111, 119, 46, 22, 0, 0, + 0, 0, 0, 0, 0, 14, 69, 95, 73, 78, 86, 65, 76, 73, 68, 95, 84, 73, + 69, 82, 35, 84, 104, 101, 114, 101, 32, 105, 115, 32, 110, 111, 32, 116, 105, 101, + 114, 32, 119, 105, 116, 104, 32, 103, 105, 118, 101, 110, 32, 110, 117, 109, 98, 101, + 114, 46, 23, 0, 0, 0, 0, 0, 0, 0, 24, 69, 95, 84, 73, 69, 82, 95, + 67, 79, 83, 84, 95, 78, 79, 84, 95, 73, 78, 67, 82, 69, 65, 83, 69, 81, + 67, 117, 109, 117, 108, 97, 116, 105, 118, 101, 32, 97, 99, 116, 105, 118, 97, 116, + 105, 111, 110, 32, 102, 101, 101, 32, 102, 111, 114, 32, 110, 101, 119, 32, 116, 105, + 101, 114, 32, 105, 115, 32, 110, 111, 116, 32, 103, 114, 101, 97, 116, 101, 114, 32, + 116, 104, 97, 110, 32, 116, 104, 97, 116, 10, 32, 111, 102, 32, 99, 117, 114, 114, + 101, 110, 116, 32, 116, 105, 101, 114, 46, 0, 11, 20, 105, 115, 95, 117, 116, 105, + 108, 105, 116, 121, 95, 99, 111, 105, 110, 95, 116, 121, 112, 101, 1, 1, 0, 21, + 103, 101, 116, 95, 102, 101, 101, 95, 115, 104, 97, 114, 101, 95, 100, 105, 118, 105, + 115, 111, 114, 1, 1, 0, 21, 103, 101, 116, 95, 110, 95, 102, 101, 101, 95, 115, + 116, 111, 114, 101, 95, 116, 105, 101, 114, 115, 1, 1, 0, 21, 103, 101, 116, 95, + 116, 97, 107, 101, 114, 95, 102, 101, 101, 95, 100, 105, 118, 105, 115, 111, 114, 1, + 1, 0, 23, 103, 101, 116, 95, 116, 105, 101, 114, 95, 97, 99, 116, 105, 118, 97, + 116, 105, 111, 110, 95, 102, 101, 101, 1, 1, 0, 23, 103, 101, 116, 95, 116, 105, + 101, 114, 95, 119, 105, 116, 104, 100, 114, 97, 119, 97, 108, 95, 102, 101, 101, 1, + 1, 0, 27, 103, 101, 116, 95, 109, 97, 114, 107, 101, 116, 95, 114, 101, 103, 105, + 115, 116, 114, 97, 116, 105, 111, 110, 95, 102, 101, 101, 1, 1, 0, 30, 103, 101, + 116, 95, 99, 117, 115, 116, 111, 100, 105, 97, 110, 95, 114, 101, 103, 105, 115, 116, + 114, 97, 116, 105, 111, 110, 95, 102, 101, 101, 1, 1, 0, 32, 103, 101, 116, 95, + 117, 110, 100, 101, 114, 119, 114, 105, 116, 101, 114, 95, 114, 101, 103, 105, 115, 116, + 114, 97, 116, 105, 111, 110, 95, 102, 101, 101, 1, 1, 0, 34, 103, 101, 116, 95, + 105, 110, 116, 101, 103, 114, 97, 116, 111, 114, 95, 119, 105, 116, 104, 100, 114, 97, + 119, 97, 108, 95, 102, 101, 101, 95, 118, 105, 101, 119, 1, 1, 0, 45, 103, 101, + 116, 95, 99, 111, 115, 116, 95, 116, 111, 95, 117, 112, 103, 114, 97, 100, 101, 95, + 105, 110, 116, 101, 103, 114, 97, 116, 111, 114, 95, 102, 101, 101, 95, 115, 116, 111, + 114, 101, 95, 118, 105, 101, 119, 1, 1, 0, 0, 2, 1, 2, 11, 1, 2, 3, + 11, 2, 1, 9, 0, 3, 2, 6, 8, 8, 4, 11, 3, 12, 3, 13, 3, 14, + 3, 15, 10, 8, 5, 6, 2, 2, 18, 2, 19, 11, 2, 1, 9, 0, 5, 2, + 3, 20, 3, 21, 3, 22, 3, 7, 2, 1, 2, 11, 1, 2, 3, 11, 6, 1, + 9, 0, 8, 2, 1, 19, 11, 2, 1, 9, 0, 4, 7, 0, 7, 5, 7, 2, + 7, 0, 0, 0, 1, 1, 4, 21, 6, 16, 205, 54, 12, 0, 0, 0, 0, 6, + 47, 64, 1, 0, 0, 0, 0, 0, 6, 47, 64, 1, 0, 0, 0, 0, 0, 6, + 208, 7, 0, 0, 0, 0, 0, 0, 7, 0, 12, 1, 14, 1, 12, 3, 12, 4, + 12, 5, 12, 6, 12, 7, 11, 0, 11, 7, 11, 6, 11, 5, 11, 4, 11, 3, + 9, 56, 0, 2, 2, 3, 0, 3, 0, 1, 4, 21, 81, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 12, 5, 10, 3, 11, 2, 26, 12, 2, 10, 1, 59, 0, 3, + 10, 5, 47, 11, 1, 60, 0, 54, 0, 12, 6, 10, 6, 46, 10, 0, 56, 1, + 4, 78, 11, 6, 10, 0, 56, 2, 12, 7, 10, 7, 55, 1, 20, 17, 5, 12, + 8, 11, 3, 11, 8, 26, 12, 5, 10, 7, 55, 2, 12, 9, 10, 5, 11, 9, + 6, 19, 0, 0, 0, 0, 0, 0, 0, 56, 3, 13, 4, 10, 5, 56, 4, 12, + 10, 11, 7, 54, 2, 11, 10, 56, 5, 10, 2, 11, 5, 23, 12, 8, 13, 4, + 10, 8, 56, 4, 12, 10, 17, 9, 60, 1, 54, 3, 11, 0, 56, 6, 12, 11, + 10, 11, 46, 12, 9, 11, 8, 11, 9, 6, 20, 0, 0, 0, 0, 0, 0, 0, + 56, 3, 11, 11, 11, 10, 56, 5, 11, 4, 12, 10, 11, 2, 12, 8, 11, 10, + 11, 8, 2, 11, 6, 1, 5, 47, 10, 3, 0, 0, 23, 37, 10, 1, 53, 11, + 2, 53, 24, 12, 3, 11, 0, 9, 33, 4, 31, 11, 1, 6, 1, 0, 0, 0, + 0, 0, 0, 0, 22, 53, 12, 5, 11, 3, 11, 5, 26, 12, 6, 10, 6, 50, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 36, 4, + 27, 6, 255, 255, 255, 255, 255, 255, 255, 255, 12, 2, 11, 2, 2, 11, 6, 52, + 12, 2, 5, 25, 11, 1, 6, 1, 0, 0, 0, 0, 0, 0, 0, 23, 53, 12, + 5, 5, 15, 11, 3, 0, 2, 1, 5, 14, 6, 17, 12, 12, 1, 11, 0, 11, + 1, 56, 7, 2, 14, 3, 0, 2, 1, 5, 14, 6, 17, 15, 12, 1, 11, 0, + 11, 1, 56, 7, 2, 16, 3, 0, 2, 1, 5, 14, 6, 17, 17, 12, 1, 11, + 0, 11, 1, 56, 7, 2, 18, 0, 0, 1, 5, 25, 14, 17, 9, 60, 2, 54, + 4, 12, 1, 14, 0, 56, 8, 10, 1, 46, 6, 21, 0, 0, 0, 0, 0, 0, + 0, 56, 3, 11, 1, 11, 0, 56, 5, 2, 13, 0, 0, 2, 1, 5, 1, 11, + 56, 9, 14, 0, 56, 8, 11, 1, 38, 4, 9, 11, 0, 56, 10, 2, 6, 13, + 0, 0, 0, 0, 0, 0, 0, 39, 21, 1, 0, 2, 1, 4, 1, 6, 11, 0, + 17, 22, 11, 1, 11, 2, 56, 11, 2, 23, 0, 0, 2, 1, 4, 30, 30, 11, + 0, 61, 0, 55, 0, 11, 1, 56, 12, 55, 1, 20, 12, 3, 10, 2, 10, 3, + 36, 4, 28, 11, 3, 17, 25, 12, 1, 11, 2, 17, 25, 12, 4, 10, 4, 10, + 1, 36, 4, 26, 11, 4, 11, 1, 23, 2, 6, 23, 0, 0, 0, 0, 0, 0, + 0, 39, 6, 15, 0, 0, 0, 0, 0, 0, 0, 39, 12, 1, 0, 1, 1, 1, + 5, 7, 1, 43, 1, 16, 5, 20, 2, 5, 1, 0, 1, 1, 32, 23, 7, 1, + 43, 1, 16, 6, 12, 1, 10, 0, 52, 10, 1, 65, 31, 35, 4, 19, 11, 0, + 52, 12, 2, 11, 1, 11, 2, 66, 31, 16, 7, 20, 2, 11, 1, 1, 6, 22, + 0, 0, 0, 0, 0, 0, 0, 39, 26, 1, 0, 2, 1, 4, 1, 5, 11, 0, + 17, 22, 11, 1, 56, 13, 2, 27, 0, 0, 2, 1, 4, 1, 9, 11, 0, 61, + 0, 55, 0, 11, 1, 56, 12, 55, 1, 20, 17, 28, 2, 15, 1, 0, 1, 1, + 1, 5, 7, 1, 43, 1, 16, 8, 20, 2, 29, 1, 0, 1, 1, 1, 5, 7, + 1, 43, 1, 16, 6, 65, 31, 2, 30, 1, 0, 1, 1, 1, 5, 7, 1, 43, + 1, 16, 9, 20, 2, 25, 1, 0, 1, 1, 32, 23, 7, 1, 43, 1, 16, 6, + 12, 1, 10, 0, 52, 10, 1, 65, 31, 35, 4, 19, 11, 0, 52, 12, 2, 11, + 1, 11, 2, 66, 31, 16, 10, 20, 2, 11, 1, 1, 6, 22, 0, 0, 0, 0, + 0, 0, 0, 39, 28, 1, 0, 1, 1, 32, 23, 7, 1, 43, 1, 16, 6, 12, + 1, 10, 0, 52, 10, 1, 65, 31, 35, 4, 19, 11, 0, 52, 12, 2, 11, 1, + 11, 2, 66, 31, 16, 11, 20, 2, 11, 1, 1, 6, 22, 0, 0, 0, 0, 0, + 0, 0, 39, 17, 1, 0, 1, 1, 1, 5, 7, 1, 43, 1, 16, 12, 20, 2, + 31, 0, 0, 0, 35, 22, 56, 14, 4, 18, 10, 0, 17, 22, 59, 2, 3, 15, + 11, 0, 12, 1, 56, 15, 57, 2, 12, 2, 11, 1, 11, 2, 63, 2, 2, 11, + 0, 1, 5, 14, 11, 0, 1, 6, 1, 0, 0, 0, 0, 0, 0, 0, 39, 34, + 1, 0, 1, 1, 1, 7, 56, 16, 7, 1, 43, 1, 16, 13, 20, 33, 2, 6, + 0, 0, 0, 37, 16, 11, 1, 56, 8, 11, 0, 53, 12, 3, 53, 12, 5, 11, + 3, 11, 5, 22, 50, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 37, 4, 14, 2, 11, 2, 39, 36, 3, 0, 1, 0, 41, 22, 17, + 37, 12, 1, 14, 1, 17, 22, 12, 2, 10, 2, 59, 1, 4, 9, 5, 13, 14, + 1, 56, 17, 57, 1, 63, 1, 11, 2, 60, 1, 54, 3, 56, 15, 12, 3, 11, + 0, 11, 3, 56, 18, 2, 40, 3, 0, 3, 1, 4, 5, 44, 38, 10, 2, 17, + 25, 12, 4, 11, 3, 11, 4, 56, 19, 10, 0, 17, 22, 12, 5, 10, 5, 59, + 0, 3, 35, 11, 0, 12, 6, 56, 20, 57, 0, 12, 7, 11, 6, 11, 7, 63, + 0, 11, 2, 12, 8, 56, 15, 12, 9, 11, 8, 11, 9, 57, 3, 12, 10, 11, + 5, 60, 0, 54, 0, 11, 1, 11, 10, 56, 21, 2, 11, 0, 1, 5, 20, 1, + 0, 0, 1, 1, 48, 63, 10, 0, 10, 1, 10, 2, 10, 3, 10, 4, 10, 5, + 17, 41, 17, 37, 12, 7, 14, 7, 56, 22, 11, 6, 3, 14, 5, 35, 17, 29, + 12, 8, 10, 5, 65, 46, 11, 8, 38, 4, 57, 10, 0, 17, 22, 42, 1, 64, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 12, 9, 15, 6, 12, 11, 11, 9, 11, + 11, 21, 10, 0, 17, 22, 44, 1, 1, 56, 16, 64, 31, 0, 0, 0, 0, 0, + 0, 0, 0, 12, 9, 11, 1, 11, 2, 11, 3, 10, 4, 11, 9, 18, 1, 12, + 13, 11, 0, 11, 13, 45, 1, 7, 1, 42, 1, 15, 6, 12, 11, 11, 4, 11, + 5, 11, 11, 17, 42, 2, 11, 0, 1, 11, 5, 1, 6, 16, 0, 0, 0, 0, + 0, 0, 0, 39, 42, 0, 0, 0, 49, 178, 1, 6, 255, 255, 255, 255, 255, 255, + 255, 255, 12, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, 4, 6, 255, 255, + 255, 255, 255, 255, 255, 255, 12, 5, 10, 1, 65, 46, 12, 6, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 12, 7, 10, 7, 10, 6, 35, 4, 173, 1, 10, 1, 10, + 7, 66, 46, 12, 8, 10, 8, 65, 14, 6, 3, 0, 0, 0, 0, 0, 0, 0, + 33, 4, 165, 1, 10, 8, 6, 0, 0, 0, 0, 0, 0, 0, 0, 66, 14, 12, + 10, 10, 10, 20, 11, 3, 35, 4, 155, 1, 10, 10, 20, 10, 0, 38, 4, 145, + 1, 10, 8, 6, 1, 0, 0, 0, 0, 0, 0, 0, 66, 14, 12, 11, 10, 7, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 127, 10, 11, 20, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 33, 4, 115, 5, 52, 11, 8, 6, 2, 0, 0, 0, + 0, 0, 0, 0, 66, 14, 12, 12, 10, 12, 20, 11, 5, 35, 4, 103, 10, 12, + 20, 6, 1, 0, 0, 0, 0, 0, 0, 0, 38, 4, 91, 10, 10, 20, 10, 11, + 20, 10, 12, 20, 18, 3, 12, 13, 10, 2, 11, 13, 68, 31, 11, 10, 20, 12, + 3, 11, 11, 20, 12, 4, 11, 12, 20, 12, 5, 11, 7, 6, 1, 0, 0, 0, + 0, 0, 0, 0, 22, 12, 7, 5, 11, 11, 1, 1, 11, 2, 1, 11, 10, 1, + 11, 11, 1, 11, 12, 1, 6, 11, 0, 0, 0, 0, 0, 0, 0, 39, 11, 1, + 1, 11, 2, 1, 11, 10, 1, 11, 11, 1, 11, 12, 1, 6, 10, 0, 0, 0, + 0, 0, 0, 0, 39, 11, 1, 1, 11, 2, 1, 11, 8, 1, 11, 10, 1, 11, + 11, 1, 6, 17, 0, 0, 0, 0, 0, 0, 0, 39, 10, 11, 20, 11, 4, 36, + 4, 133, 1, 5, 52, 11, 1, 1, 11, 2, 1, 11, 8, 1, 11, 10, 1, 11, + 11, 1, 6, 9, 0, 0, 0, 0, 0, 0, 0, 39, 11, 1, 1, 11, 2, 1, + 11, 8, 1, 11, 10, 1, 6, 4, 0, 0, 0, 0, 0, 0, 0, 39, 11, 1, + 1, 11, 2, 1, 11, 8, 1, 11, 10, 1, 6, 3, 0, 0, 0, 0, 0, 0, + 0, 39, 11, 1, 1, 11, 2, 1, 11, 8, 1, 6, 8, 0, 0, 0, 0, 0, + 0, 0, 39, 11, 1, 1, 11, 2, 1, 2, 41, 0, 0, 0, 14, 56, 11, 0, + 17, 22, 7, 1, 33, 4, 52, 11, 1, 6, 1, 0, 0, 0, 0, 0, 0, 0, + 38, 4, 48, 11, 2, 6, 1, 0, 0, 0, 0, 0, 0, 0, 38, 4, 44, 11, + 3, 6, 1, 0, 0, 0, 0, 0, 0, 0, 38, 4, 40, 11, 4, 6, 2, 0, + 0, 0, 0, 0, 0, 0, 38, 4, 36, 10, 5, 56, 23, 3, 32, 11, 5, 65, + 46, 6, 255, 0, 0, 0, 0, 0, 0, 0, 37, 4, 30, 2, 6, 14, 0, 0, + 0, 0, 0, 0, 0, 39, 11, 5, 1, 6, 2, 0, 0, 0, 0, 0, 0, 0, + 39, 11, 5, 1, 6, 7, 0, 0, 0, 0, 0, 0, 0, 39, 11, 5, 1, 6, + 6, 0, 0, 0, 0, 0, 0, 0, 39, 11, 5, 1, 6, 18, 0, 0, 0, 0, + 0, 0, 0, 39, 11, 5, 1, 6, 5, 0, 0, 0, 0, 0, 0, 0, 39, 11, + 5, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 39, 44, 1, 4, 1, 1, 52, + 11, 14, 5, 12, 7, 11, 0, 11, 1, 11, 2, 11, 3, 11, 4, 11, 7, 8, + 56, 24, 2, 45, 1, 0, 3, 1, 4, 5, 53, 20, 10, 0, 10, 1, 10, 2, + 56, 25, 12, 4, 11, 3, 11, 4, 56, 19, 11, 0, 17, 22, 60, 0, 54, 0, + 11, 1, 56, 2, 54, 1, 12, 5, 11, 2, 11, 5, 21, 2, 46, 1, 4, 3, + 1, 4, 5, 54, 17, 10, 0, 10, 1, 10, 2, 56, 25, 10, 0, 12, 3, 12, + 4, 11, 0, 11, 4, 56, 26, 12, 5, 11, 3, 11, 1, 11, 2, 11, 5, 56, + 27, 2, 20, 1, 0, 1, 1, 1, 5, 56, 28, 4, 3, 2, 6, 12, 0, 0, + 0, 0, 0, 0, 0, 39, 48, 1, 0, 1, 0, 10, 6, 11, 0, 11, 1, 9, + 11, 2, 56, 29, 2, 50, 1, 0, 1, 0, 57, 6, 11, 0, 11, 1, 8, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 56, 29, 2, 51, 1, 4, 1, 0, 57, 6, + 11, 0, 11, 1, 8, 6, 0, 0, 0, 0, 0, 0, 0, 0, 56, 30, 2, 49, + 0, 0, 1, 0, 18, 25, 11, 0, 17, 22, 7, 1, 33, 4, 23, 17, 9, 60, + 1, 54, 3, 11, 1, 56, 6, 12, 4, 11, 2, 4, 18, 11, 4, 56, 31, 12, + 5, 11, 5, 2, 11, 4, 11, 3, 56, 4, 12, 5, 5, 16, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 39, 54, 1, 4, 1, 0, 10, 6, 11, 0, 11, 1, 9, + 11, 2, 56, 30, 2, 52, 0, 0, 1, 0, 17, 18, 10, 0, 11, 1, 11, 2, + 11, 3, 56, 29, 12, 4, 7, 1, 56, 32, 3, 15, 11, 0, 56, 33, 7, 1, + 11, 4, 56, 34, 2, 11, 0, 1, 5, 11, 58, 1, 0, 3, 1, 4, 5, 59, + 19, 11, 0, 17, 22, 60, 0, 54, 0, 11, 1, 56, 2, 12, 3, 10, 3, 55, + 1, 20, 17, 28, 12, 1, 11, 2, 11, 1, 56, 19, 11, 3, 54, 2, 56, 31, + 2, 59, 1, 4, 3, 1, 4, 5, 60, 25, 10, 0, 10, 1, 56, 35, 12, 2, + 10, 0, 11, 2, 56, 26, 12, 3, 10, 0, 11, 1, 11, 3, 56, 36, 12, 4, + 10, 0, 17, 22, 56, 32, 4, 18, 5, 20, 10, 0, 56, 33, 11, 0, 17, 22, + 11, 4, 56, 34, 2, 60, 1, 0, 1, 5, 10, 5, 11, 0, 9, 11, 1, 56, + 37, 2, 62, 1, 0, 1, 5, 57, 5, 11, 0, 8, 6, 0, 0, 0, 0, 0, + 0, 0, 0, 56, 37, 2, 63, 1, 4, 1, 5, 57, 5, 11, 0, 8, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 56, 38, 2, 61, 0, 0, 1, 5, 18, 23, 11, + 0, 17, 22, 7, 1, 33, 4, 21, 17, 9, 60, 2, 54, 4, 12, 3, 11, 1, + 4, 16, 11, 3, 56, 31, 12, 4, 11, 4, 2, 11, 3, 11, 2, 56, 4, 12, + 4, 5, 14, 6, 0, 0, 0, 0, 0, 0, 0, 0, 39, 65, 1, 4, 1, 5, + 10, 5, 11, 0, 9, 11, 1, 56, 38, 2, 64, 0, 0, 1, 5, 17, 17, 10, + 0, 11, 1, 11, 2, 56, 37, 12, 3, 7, 1, 56, 32, 3, 14, 11, 0, 56, + 33, 7, 1, 11, 3, 56, 34, 2, 11, 0, 1, 5, 10, 4, 0, 2, 0, 2, + 1, 0, 0, 5, 0, 1, 3, 1, 5, 3, 0, 1, 1, 1, 4, 3, 1, 3, + 2, 1, 2, 1, 0, 0, 7, 1, 7, 2, 7, 3, 7, 4, 7, 0, 96, 0, + 97, 0, + ] +}); + +#[rustfmt::skip] +pub static MODULE_ECONIA_REGISTRY: Lazy> = Lazy::new(|| { + vec![ + 161, 28, 235, 11, 7, 0, 0, 10, 13, 1, 0, 22, 2, 22, 94, 3, 116, 220, + 3, 4, 208, 4, 78, 5, 158, 5, 160, 4, 7, 190, 9, 134, 17, 8, 196, 26, + 64, 6, 132, 27, 38, 16, 170, 27, 157, 11, 10, 199, 38, 180, 1, 12, 251, 39, + 246, 15, 13, 241, 55, 42, 15, 155, 56, 4, 0, 0, 1, 5, 1, 17, 1, 33, + 0, 38, 1, 41, 1, 46, 1, 53, 1, 57, 0, 61, 1, 109, 0, 1, 3, 0, + 1, 4, 7, 0, 0, 7, 4, 0, 0, 9, 8, 0, 0, 11, 3, 0, 0, 14, + 7, 0, 2, 16, 7, 0, 0, 24, 3, 0, 0, 27, 6, 0, 0, 28, 6, 0, + 0, 30, 7, 0, 3, 32, 7, 1, 0, 0, 0, 34, 7, 0, 0, 35, 8, 0, + 4, 37, 4, 2, 7, 0, 4, 0, 5, 40, 4, 1, 6, 1, 0, 42, 8, 0, + 6, 45, 4, 2, 3, 1, 0, 1, 0, 50, 4, 0, 8, 56, 4, 1, 0, 1, + 0, 51, 0, 1, 0, 1, 4, 52, 1, 3, 2, 7, 4, 1, 6, 52, 1, 5, + 2, 3, 4, 1, 7, 54, 0, 7, 1, 6, 1, 0, 55, 11, 1, 2, 0, 0, + 1, 4, 58, 12, 13, 2, 7, 4, 1, 4, 59, 12, 14, 2, 7, 4, 1, 2, + 60, 1, 16, 1, 0, 1, 9, 55, 11, 1, 2, 0, 0, 1, 0, 62, 1, 19, + 0, 1, 0, 63, 1, 19, 0, 1, 0, 64, 1, 19, 0, 1, 0, 65, 1, 19, + 0, 1, 0, 66, 20, 19, 0, 1, 0, 67, 1, 21, 0, 1, 4, 68, 22, 19, + 2, 7, 4, 1, 0, 69, 23, 24, 0, 1, 6, 58, 25, 13, 2, 3, 0, 1, + 6, 59, 25, 14, 2, 3, 0, 1, 3, 70, 15, 26, 1, 0, 1, 3, 71, 1, + 26, 1, 0, 1, 0, 72, 28, 24, 2, 0, 0, 1, 1, 73, 29, 30, 0, 1, + 0, 74, 33, 24, 1, 0, 1, 0, 75, 19, 36, 0, 1, 0, 76, 37, 13, 0, + 1, 0, 77, 37, 38, 0, 1, 0, 78, 39, 40, 0, 1, 0, 79, 42, 33, 0, + 1, 0, 80, 1, 19, 2, 0, 0, 1, 0, 81, 1, 38, 2, 0, 0, 1, 0, + 82, 30, 19, 1, 0, 1, 0, 83, 30, 38, 1, 0, 1, 0, 84, 35, 38, 0, + 1, 0, 85, 45, 38, 0, 1, 0, 86, 46, 19, 0, 1, 0, 87, 35, 13, 0, + 1, 0, 88, 1, 13, 2, 0, 0, 1, 0, 89, 45, 13, 0, 1, 0, 90, 30, + 13, 1, 0, 1, 0, 91, 19, 13, 0, 1, 0, 92, 48, 49, 1, 0, 1, 9, + 93, 48, 1, 1, 0, 1, 0, 94, 51, 1, 2, 0, 0, 1, 8, 95, 1, 48, + 1, 0, 1, 0, 96, 53, 1, 2, 0, 0, 1, 9, 97, 54, 19, 0, 1, 8, + 98, 51, 48, 1, 0, 1, 0, 99, 56, 19, 3, 0, 0, 0, 1, 8, 100, 1, + 13, 1, 0, 1, 0, 101, 58, 19, 2, 0, 0, 1, 0, 102, 60, 19, 2, 0, + 0, 1, 1, 68, 61, 19, 0, 1, 6, 103, 63, 1, 2, 3, 0, 1, 4, 103, + 64, 1, 2, 7, 4, 1, 5, 104, 65, 1, 1, 6, 1, 9, 105, 48, 1, 1, + 0, 1, 0, 106, 48, 67, 1, 0, 1, 9, 107, 48, 1, 1, 0, 1, 0, 108, + 51, 1, 0, 1, 10, 110, 0, 68, 0, 1, 4, 111, 69, 31, 2, 7, 4, 1, + 0, 112, 72, 1, 0, 1, 0, 113, 51, 1, 0, 1, 4, 114, 69, 73, 2, 7, + 4, 1, 0, 115, 72, 1, 0, 1, 2, 116, 39, 68, 0, 1, 2, 3, 39, 29, + 0, 1, 2, 117, 39, 29, 0, 1, 1, 2, 2, 4, 3, 6, 1, 8, 3, 9, + 5, 2, 6, 2, 7, 15, 8, 17, 15, 2, 15, 8, 17, 4, 18, 4, 19, 19, + 20, 19, 7, 31, 7, 34, 30, 17, 32, 15, 5, 8, 6, 8, 42, 15, 44, 31, + 4, 17, 47, 31, 49, 15, 50, 57, 50, 17, 53, 4, 54, 2, 55, 6, 56, 31, + 58, 15, 61, 8, 20, 70, 55, 9, 54, 8, 19, 70, 64, 8, 1, 6, 12, 0, + 2, 3, 8, 5, 1, 11, 14, 2, 9, 0, 9, 1, 2, 8, 5, 3, 1, 11, + 17, 2, 9, 0, 9, 1, 1, 8, 8, 1, 11, 15, 1, 9, 0, 2, 8, 10, + 8, 12, 1, 8, 9, 3, 6, 12, 8, 16, 8, 13, 4, 6, 12, 3, 2, 11, + 19, 1, 9, 1, 2, 6, 11, 14, 2, 9, 0, 9, 1, 9, 0, 1, 1, 1, + 6, 9, 1, 1, 9, 0, 1, 8, 6, 2, 9, 0, 9, 1, 1, 6, 11, 14, + 2, 3, 8, 5, 1, 3, 1, 6, 8, 2, 1, 8, 4, 1, 6, 11, 14, 2, + 9, 0, 9, 1, 1, 8, 5, 1, 11, 11, 1, 3, 2, 6, 11, 17, 2, 9, + 0, 9, 1, 9, 0, 1, 11, 11, 1, 9, 0, 2, 6, 11, 17, 2, 8, 5, + 3, 11, 11, 1, 3, 3, 3, 3, 3, 1, 10, 2, 1, 8, 1, 1, 9, 1, + 4, 3, 8, 6, 8, 1, 8, 6, 5, 8, 1, 3, 3, 3, 3, 1, 8, 3, + 2, 8, 6, 8, 6, 1, 8, 7, 1, 8, 10, 5, 3, 3, 3, 3, 3, 1, + 6, 8, 6, 1, 8, 0, 16, 6, 11, 14, 2, 3, 8, 5, 6, 8, 5, 8, + 6, 8, 1, 8, 6, 8, 10, 3, 1, 1, 8, 1, 3, 3, 3, 3, 8, 0, + 8, 0, 3, 3, 8, 6, 8, 6, 3, 6, 11, 14, 2, 3, 8, 5, 6, 8, + 5, 8, 6, 2, 6, 11, 14, 2, 8, 10, 8, 12, 8, 12, 2, 8, 1, 8, + 6, 1, 6, 8, 18, 2, 3, 1, 1, 11, 19, 1, 9, 0, 1, 8, 2, 3, + 7, 8, 16, 3, 7, 3, 2, 6, 12, 3, 2, 11, 19, 1, 9, 1, 2, 3, + 6, 12, 3, 2, 1, 2, 4, 6, 12, 2, 3, 11, 19, 1, 9, 1, 4, 3, + 3, 3, 11, 19, 1, 9, 2, 2, 9, 1, 9, 2, 7, 8, 6, 8, 1, 3, + 3, 3, 3, 11, 19, 1, 9, 1, 6, 3, 3, 3, 3, 8, 1, 8, 6, 6, + 8, 1, 3, 3, 3, 6, 8, 18, 11, 19, 1, 9, 1, 1, 6, 8, 1, 6, + 3, 3, 8, 1, 3, 3, 3, 3, 7, 11, 17, 2, 9, 0, 9, 1, 9, 0, + 9, 1, 3, 7, 11, 14, 2, 9, 0, 9, 1, 9, 0, 9, 1, 2, 7, 11, + 15, 1, 9, 0, 9, 0, 6, 3, 8, 6, 8, 5, 7, 8, 16, 7, 11, 17, + 2, 8, 5, 3, 7, 11, 14, 2, 3, 8, 5, 1, 8, 18, 1, 5, 2, 7, + 11, 14, 2, 9, 0, 9, 1, 9, 0, 1, 8, 12, 6, 6, 8, 5, 8, 10, + 7, 8, 13, 7, 11, 14, 2, 8, 10, 8, 12, 8, 10, 11, 11, 1, 8, 12, + 2, 6, 12, 10, 3, 1, 7, 9, 1, 16, 6, 8, 5, 3, 3, 3, 3, 8, + 6, 8, 1, 8, 6, 8, 10, 8, 12, 7, 8, 13, 7, 11, 14, 2, 8, 10, + 8, 12, 7, 11, 15, 1, 8, 9, 11, 11, 1, 8, 12, 8, 9, 7, 8, 12, + 8, 114, 101, 103, 105, 115, 116, 114, 121, 13, 65, 115, 115, 101, 116, 84, 121, 112, + 101, 86, 105, 101, 119, 15, 112, 97, 99, 107, 97, 103, 101, 95, 97, 100, 100, 114, + 101, 115, 115, 11, 109, 111, 100, 117, 108, 101, 95, 110, 97, 109, 101, 6, 83, 116, + 114, 105, 110, 103, 6, 115, 116, 114, 105, 110, 103, 9, 116, 121, 112, 101, 95, 110, + 97, 109, 101, 19, 67, 117, 115, 116, 111, 100, 105, 97, 110, 67, 97, 112, 97, 98, + 105, 108, 105, 116, 121, 12, 99, 117, 115, 116, 111, 100, 105, 97, 110, 95, 105, 100, + 12, 71, 101, 110, 101, 114, 105, 99, 65, 115, 115, 101, 116, 11, 100, 117, 109, 109, + 121, 95, 102, 105, 101, 108, 100, 12, 77, 97, 114, 107, 101, 116, 67, 111, 117, 110, + 116, 115, 9, 110, 95, 109, 97, 114, 107, 101, 116, 115, 20, 110, 95, 114, 101, 99, + 111, 103, 110, 105, 122, 101, 100, 95, 109, 97, 114, 107, 101, 116, 115, 10, 77, 97, + 114, 107, 101, 116, 73, 110, 102, 111, 9, 98, 97, 115, 101, 95, 116, 121, 112, 101, + 8, 84, 121, 112, 101, 73, 110, 102, 111, 9, 116, 121, 112, 101, 95, 105, 110, 102, + 111, 17, 98, 97, 115, 101, 95, 110, 97, 109, 101, 95, 103, 101, 110, 101, 114, 105, + 99, 10, 113, 117, 111, 116, 101, 95, 116, 121, 112, 101, 8, 108, 111, 116, 95, 115, + 105, 122, 101, 9, 116, 105, 99, 107, 95, 115, 105, 122, 101, 8, 109, 105, 110, 95, + 115, 105, 122, 101, 14, 117, 110, 100, 101, 114, 119, 114, 105, 116, 101, 114, 95, 105, + 100, 14, 77, 97, 114, 107, 101, 116, 73, 110, 102, 111, 86, 105, 101, 119, 9, 109, + 97, 114, 107, 101, 116, 95, 105, 100, 13, 105, 115, 95, 114, 101, 99, 111, 103, 110, + 105, 122, 101, 100, 23, 77, 97, 114, 107, 101, 116, 82, 101, 103, 105, 115, 116, 114, + 97, 116, 105, 111, 110, 69, 118, 101, 110, 116, 21, 82, 101, 99, 111, 103, 110, 105, + 122, 101, 100, 77, 97, 114, 107, 101, 116, 69, 118, 101, 110, 116, 12, 116, 114, 97, + 100, 105, 110, 103, 95, 112, 97, 105, 114, 11, 84, 114, 97, 100, 105, 110, 103, 80, + 97, 105, 114, 22, 114, 101, 99, 111, 103, 110, 105, 122, 101, 100, 95, 109, 97, 114, + 107, 101, 116, 95, 105, 110, 102, 111, 6, 79, 112, 116, 105, 111, 110, 6, 111, 112, + 116, 105, 111, 110, 20, 82, 101, 99, 111, 103, 110, 105, 122, 101, 100, 77, 97, 114, + 107, 101, 116, 73, 110, 102, 111, 17, 82, 101, 99, 111, 103, 110, 105, 122, 101, 100, + 77, 97, 114, 107, 101, 116, 115, 3, 109, 97, 112, 7, 84, 97, 98, 108, 105, 115, + 116, 7, 116, 97, 98, 108, 105, 115, 116, 24, 114, 101, 99, 111, 103, 110, 105, 122, + 101, 100, 95, 109, 97, 114, 107, 101, 116, 95, 101, 118, 101, 110, 116, 115, 11, 69, + 118, 101, 110, 116, 72, 97, 110, 100, 108, 101, 5, 101, 118, 101, 110, 116, 8, 82, + 101, 103, 105, 115, 116, 114, 121, 17, 109, 97, 114, 107, 101, 116, 95, 105, 100, 95, + 116, 111, 95, 105, 110, 102, 111, 17, 109, 97, 114, 107, 101, 116, 95, 105, 110, 102, + 111, 95, 116, 111, 95, 105, 100, 5, 84, 97, 98, 108, 101, 5, 116, 97, 98, 108, + 101, 12, 110, 95, 99, 117, 115, 116, 111, 100, 105, 97, 110, 115, 14, 110, 95, 117, + 110, 100, 101, 114, 119, 114, 105, 116, 101, 114, 115, 26, 109, 97, 114, 107, 101, 116, + 95, 114, 101, 103, 105, 115, 116, 114, 97, 116, 105, 111, 110, 95, 101, 118, 101, 110, + 116, 115, 21, 85, 110, 100, 101, 114, 119, 114, 105, 116, 101, 114, 67, 97, 112, 97, + 98, 105, 108, 105, 116, 121, 11, 105, 110, 105, 116, 95, 109, 111, 100, 117, 108, 101, + 3, 110, 101, 119, 7, 97, 99, 99, 111, 117, 110, 116, 16, 110, 101, 119, 95, 101, + 118, 101, 110, 116, 95, 104, 97, 110, 100, 108, 101, 29, 114, 101, 103, 105, 115, 116, + 101, 114, 95, 105, 110, 116, 101, 103, 114, 97, 116, 111, 114, 95, 102, 101, 101, 95, + 115, 116, 111, 114, 101, 4, 67, 111, 105, 110, 4, 99, 111, 105, 110, 8, 99, 111, + 110, 116, 97, 105, 110, 115, 6, 98, 111, 114, 114, 111, 119, 7, 116, 121, 112, 101, + 95, 111, 102, 10, 105, 110, 99, 101, 110, 116, 105, 118, 101, 115, 26, 103, 101, 116, + 95, 77, 65, 88, 95, 67, 72, 65, 82, 65, 67, 84, 69, 82, 83, 95, 71, 69, + 78, 69, 82, 73, 67, 26, 103, 101, 116, 95, 77, 73, 78, 95, 67, 72, 65, 82, + 65, 67, 84, 69, 82, 83, 95, 71, 69, 78, 69, 82, 73, 67, 16, 103, 101, 116, + 95, 78, 79, 95, 67, 85, 83, 84, 79, 68, 73, 65, 78, 18, 103, 101, 116, 95, + 78, 79, 95, 85, 78, 68, 69, 82, 87, 82, 73, 84, 69, 82, 16, 103, 101, 116, + 95, 99, 117, 115, 116, 111, 100, 105, 97, 110, 95, 105, 100, 17, 103, 101, 116, 95, + 109, 97, 114, 107, 101, 116, 95, 99, 111, 117, 110, 116, 115, 6, 108, 101, 110, 103, + 116, 104, 13, 103, 101, 116, 95, 109, 97, 114, 107, 101, 116, 95, 105, 100, 4, 115, + 111, 109, 101, 4, 110, 111, 110, 101, 23, 103, 101, 116, 95, 109, 97, 114, 107, 101, + 116, 95, 105, 100, 95, 98, 97, 115, 101, 95, 99, 111, 105, 110, 4, 117, 116, 102, + 56, 26, 103, 101, 116, 95, 109, 97, 114, 107, 101, 116, 95, 105, 100, 95, 98, 97, + 115, 101, 95, 103, 101, 110, 101, 114, 105, 99, 15, 103, 101, 116, 95, 109, 97, 114, + 107, 101, 116, 95, 105, 110, 102, 111, 21, 104, 97, 115, 95, 114, 101, 99, 111, 103, + 110, 105, 122, 101, 100, 95, 109, 97, 114, 107, 101, 116, 26, 103, 101, 116, 95, 114, + 101, 99, 111, 103, 110, 105, 122, 101, 100, 95, 109, 97, 114, 107, 101, 116, 95, 105, + 110, 102, 111, 18, 116, 111, 95, 97, 115, 115, 101, 116, 95, 116, 121, 112, 101, 95, + 118, 105, 101, 119, 34, 103, 101, 116, 95, 109, 97, 114, 107, 101, 116, 95, 105, 110, + 102, 111, 95, 102, 111, 114, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, 111, + 117, 110, 116, 34, 103, 101, 116, 95, 114, 101, 99, 111, 103, 110, 105, 122, 101, 100, + 95, 109, 97, 114, 107, 101, 116, 95, 105, 100, 95, 98, 97, 115, 101, 95, 99, 111, + 105, 110, 44, 103, 101, 116, 95, 114, 101, 99, 111, 103, 110, 105, 122, 101, 100, 95, + 109, 97, 114, 107, 101, 116, 95, 105, 110, 102, 111, 95, 98, 97, 115, 101, 95, 99, + 111, 105, 110, 95, 98, 121, 95, 116, 121, 112, 101, 37, 103, 101, 116, 95, 114, 101, + 99, 111, 103, 110, 105, 122, 101, 100, 95, 109, 97, 114, 107, 101, 116, 95, 105, 100, + 95, 98, 97, 115, 101, 95, 103, 101, 110, 101, 114, 105, 99, 47, 103, 101, 116, 95, + 114, 101, 99, 111, 103, 110, 105, 122, 101, 100, 95, 109, 97, 114, 107, 101, 116, 95, + 105, 110, 102, 111, 95, 98, 97, 115, 101, 95, 103, 101, 110, 101, 114, 105, 99, 95, + 98, 121, 95, 116, 121, 112, 101, 36, 103, 101, 116, 95, 114, 101, 99, 111, 103, 110, + 105, 122, 101, 100, 95, 109, 97, 114, 107, 101, 116, 95, 105, 110, 102, 111, 95, 98, + 97, 115, 101, 95, 99, 111, 105, 110, 39, 103, 101, 116, 95, 114, 101, 99, 111, 103, + 110, 105, 122, 101, 100, 95, 109, 97, 114, 107, 101, 116, 95, 105, 110, 102, 111, 95, + 98, 97, 115, 101, 95, 103, 101, 110, 101, 114, 105, 99, 18, 103, 101, 116, 95, 117, + 110, 100, 101, 114, 119, 114, 105, 116, 101, 114, 95, 105, 100, 31, 104, 97, 115, 95, + 114, 101, 99, 111, 103, 110, 105, 122, 101, 100, 95, 109, 97, 114, 107, 101, 116, 95, + 98, 97, 115, 101, 95, 99, 111, 105, 110, 39, 104, 97, 115, 95, 114, 101, 99, 111, + 103, 110, 105, 122, 101, 100, 95, 109, 97, 114, 107, 101, 116, 95, 98, 97, 115, 101, + 95, 99, 111, 105, 110, 95, 98, 121, 95, 116, 121, 112, 101, 34, 104, 97, 115, 95, + 114, 101, 99, 111, 103, 110, 105, 122, 101, 100, 95, 109, 97, 114, 107, 101, 116, 95, + 98, 97, 115, 101, 95, 103, 101, 110, 101, 114, 105, 99, 42, 104, 97, 115, 95, 114, + 101, 99, 111, 103, 110, 105, 122, 101, 100, 95, 109, 97, 114, 107, 101, 116, 95, 98, + 97, 115, 101, 95, 103, 101, 110, 101, 114, 105, 99, 95, 98, 121, 95, 116, 121, 112, + 101, 26, 105, 115, 95, 114, 101, 103, 105, 115, 116, 101, 114, 101, 100, 95, 99, 117, + 115, 116, 111, 100, 105, 97, 110, 95, 105, 100, 29, 114, 101, 103, 105, 115, 116, 101, + 114, 95, 99, 117, 115, 116, 111, 100, 105, 97, 110, 95, 99, 97, 112, 97, 98, 105, + 108, 105, 116, 121, 44, 100, 101, 112, 111, 115, 105, 116, 95, 99, 117, 115, 116, 111, + 100, 105, 97, 110, 95, 114, 101, 103, 105, 115, 116, 114, 97, 116, 105, 111, 110, 95, + 117, 116, 105, 108, 105, 116, 121, 95, 99, 111, 105, 110, 115, 39, 114, 101, 103, 105, + 115, 116, 101, 114, 95, 105, 110, 116, 101, 103, 114, 97, 116, 111, 114, 95, 102, 101, + 101, 95, 115, 116, 111, 114, 101, 95, 98, 97, 115, 101, 95, 116, 105, 101, 114, 4, + 122, 101, 114, 111, 44, 114, 101, 103, 105, 115, 116, 101, 114, 95, 105, 110, 116, 101, + 103, 114, 97, 116, 111, 114, 95, 102, 101, 101, 95, 115, 116, 111, 114, 101, 95, 102, + 114, 111, 109, 95, 99, 111, 105, 110, 115, 116, 111, 114, 101, 23, 103, 101, 116, 95, + 116, 105, 101, 114, 95, 97, 99, 116, 105, 118, 97, 116, 105, 111, 110, 95, 102, 101, + 101, 8, 119, 105, 116, 104, 100, 114, 97, 119, 34, 114, 101, 103, 105, 115, 116, 101, + 114, 95, 109, 97, 114, 107, 101, 116, 95, 98, 97, 115, 101, 95, 99, 111, 105, 110, + 95, 105, 110, 116, 101, 114, 110, 97, 108, 19, 105, 115, 95, 99, 111, 105, 110, 95, + 105, 110, 105, 116, 105, 97, 108, 105, 122, 101, 100, 24, 114, 101, 103, 105, 115, 116, + 101, 114, 95, 109, 97, 114, 107, 101, 116, 95, 105, 110, 116, 101, 114, 110, 97, 108, + 37, 114, 101, 103, 105, 115, 116, 101, 114, 95, 109, 97, 114, 107, 101, 116, 95, 98, + 97, 115, 101, 95, 103, 101, 110, 101, 114, 105, 99, 95, 105, 110, 116, 101, 114, 110, + 97, 108, 3, 97, 100, 100, 10, 101, 109, 105, 116, 95, 101, 118, 101, 110, 116, 41, + 100, 101, 112, 111, 115, 105, 116, 95, 109, 97, 114, 107, 101, 116, 95, 114, 101, 103, + 105, 115, 116, 114, 97, 116, 105, 111, 110, 95, 117, 116, 105, 108, 105, 116, 121, 95, + 99, 111, 105, 110, 115, 31, 114, 101, 103, 105, 115, 116, 101, 114, 95, 117, 110, 100, + 101, 114, 119, 114, 105, 116, 101, 114, 95, 99, 97, 112, 97, 98, 105, 108, 105, 116, + 121, 46, 100, 101, 112, 111, 115, 105, 116, 95, 117, 110, 100, 101, 114, 119, 114, 105, + 116, 101, 114, 95, 114, 101, 103, 105, 115, 116, 114, 97, 116, 105, 111, 110, 95, 117, + 116, 105, 108, 105, 116, 121, 95, 99, 111, 105, 110, 115, 24, 114, 101, 109, 111, 118, + 101, 95, 114, 101, 99, 111, 103, 110, 105, 122, 101, 100, 95, 109, 97, 114, 107, 101, + 116, 6, 115, 105, 103, 110, 101, 114, 10, 97, 100, 100, 114, 101, 115, 115, 95, 111, + 102, 6, 114, 101, 109, 111, 118, 101, 25, 114, 101, 109, 111, 118, 101, 95, 114, 101, + 99, 111, 103, 110, 105, 122, 101, 100, 95, 109, 97, 114, 107, 101, 116, 115, 21, 115, + 101, 116, 95, 114, 101, 99, 111, 103, 110, 105, 122, 101, 100, 95, 109, 97, 114, 107, + 101, 116, 10, 98, 111, 114, 114, 111, 119, 95, 109, 117, 116, 22, 115, 101, 116, 95, + 114, 101, 99, 111, 103, 110, 105, 122, 101, 100, 95, 109, 97, 114, 107, 101, 116, 115, + 15, 97, 99, 99, 111, 117, 110, 116, 95, 97, 100, 100, 114, 101, 115, 115, 11, 115, + 116, 114, 117, 99, 116, 95, 110, 97, 109, 101, 4, 117, 115, 101, 114, 6, 109, 97, + 114, 107, 101, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 35, 69, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 32, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 35, 69, 10, 2, 1, 0, 20, 99, + 111, 109, 112, 105, 108, 97, 116, 105, 111, 110, 95, 109, 101, 116, 97, 100, 97, 116, + 97, 9, 0, 3, 50, 46, 48, 3, 50, 46, 49, 18, 97, 112, 116, 111, 115, 58, + 58, 109, 101, 116, 97, 100, 97, 116, 97, 95, 118, 49, 233, 10, 15, 0, 0, 0, + 0, 0, 0, 0, 0, 12, 69, 95, 76, 79, 84, 95, 83, 73, 90, 69, 95, 48, + 24, 76, 111, 116, 32, 115, 105, 122, 101, 32, 115, 112, 101, 99, 105, 102, 105, 101, + 100, 32, 97, 115, 32, 48, 46, 1, 0, 0, 0, 0, 0, 0, 0, 13, 69, 95, + 84, 73, 67, 75, 95, 83, 73, 90, 69, 95, 48, 25, 84, 105, 99, 107, 32, 115, + 105, 122, 101, 32, 115, 112, 101, 99, 105, 102, 105, 101, 100, 32, 97, 115, 32, 48, + 46, 2, 0, 0, 0, 0, 0, 0, 0, 12, 69, 95, 77, 73, 78, 95, 83, 73, + 90, 69, 95, 48, 34, 77, 105, 110, 105, 109, 117, 109, 32, 111, 114, 100, 101, 114, + 32, 115, 105, 122, 101, 32, 115, 112, 101, 99, 105, 102, 105, 101, 100, 32, 97, 115, + 32, 48, 46, 3, 0, 0, 0, 0, 0, 0, 0, 16, 69, 95, 81, 85, 79, 84, + 69, 95, 78, 79, 84, 95, 67, 79, 73, 78, 52, 81, 117, 111, 116, 101, 32, 97, + 115, 115, 101, 116, 32, 116, 121, 112, 101, 32, 104, 97, 115, 32, 110, 111, 116, 32, + 98, 101, 101, 110, 32, 105, 110, 105, 116, 105, 97, 108, 105, 122, 101, 100, 32, 97, + 115, 32, 97, 32, 99, 111, 105, 110, 46, 4, 0, 0, 0, 0, 0, 0, 0, 17, + 69, 95, 66, 65, 83, 69, 95, 81, 85, 79, 84, 69, 95, 83, 65, 77, 69, 47, + 66, 97, 115, 101, 32, 97, 110, 100, 32, 113, 117, 111, 116, 101, 32, 97, 115, 115, + 101, 116, 32, 100, 101, 115, 99, 114, 105, 112, 116, 111, 114, 115, 32, 97, 114, 101, + 32, 105, 100, 101, 110, 116, 105, 99, 97, 108, 46, 5, 0, 0, 0, 0, 0, 0, + 0, 19, 69, 95, 77, 65, 82, 75, 69, 84, 95, 82, 69, 71, 73, 83, 84, 69, + 82, 69, 68, 29, 77, 97, 114, 107, 101, 116, 32, 105, 115, 32, 97, 108, 114, 101, + 97, 100, 121, 32, 114, 101, 103, 105, 115, 116, 101, 114, 101, 100, 46, 6, 0, 0, + 0, 0, 0, 0, 0, 15, 69, 95, 66, 65, 83, 69, 95, 78, 79, 84, 95, 67, + 79, 73, 78, 63, 66, 97, 115, 101, 32, 99, 111, 105, 110, 32, 116, 121, 112, 101, + 32, 104, 97, 115, 32, 110, 111, 116, 32, 98, 101, 101, 110, 32, 105, 110, 105, 116, + 105, 97, 108, 105, 122, 101, 100, 32, 102, 111, 114, 32, 97, 32, 112, 117, 114, 101, + 32, 99, 111, 105, 110, 32, 109, 97, 114, 107, 101, 116, 46, 7, 0, 0, 0, 0, + 0, 0, 0, 28, 69, 95, 71, 69, 78, 69, 82, 73, 67, 95, 84, 79, 79, 95, + 70, 69, 87, 95, 67, 72, 65, 82, 65, 67, 84, 69, 82, 83, 53, 71, 101, 110, + 101, 114, 105, 99, 32, 98, 97, 115, 101, 32, 97, 115, 115, 101, 116, 32, 100, 101, + 115, 99, 114, 105, 112, 116, 111, 114, 32, 104, 97, 115, 32, 116, 111, 111, 32, 102, + 101, 119, 32, 99, 104, 97, 114, 97, 99, 116, 101, 114, 115, 46, 8, 0, 0, 0, + 0, 0, 0, 0, 29, 69, 95, 71, 69, 78, 69, 82, 73, 67, 95, 84, 79, 79, + 95, 77, 65, 78, 89, 95, 67, 72, 65, 82, 65, 67, 84, 69, 82, 83, 54, 71, + 101, 110, 101, 114, 105, 99, 32, 98, 97, 115, 101, 32, 97, 115, 115, 101, 116, 32, + 100, 101, 115, 99, 114, 105, 112, 116, 111, 114, 32, 104, 97, 115, 32, 116, 111, 111, + 32, 109, 97, 110, 121, 32, 99, 104, 97, 114, 97, 99, 116, 101, 114, 115, 46, 9, + 0, 0, 0, 0, 0, 0, 0, 12, 69, 95, 78, 79, 84, 95, 69, 67, 79, 78, + 73, 65, 36, 67, 97, 108, 108, 101, 114, 32, 105, 115, 32, 110, 111, 116, 32, 69, + 99, 111, 110, 105, 97, 44, 32, 98, 117, 116, 32, 115, 104, 111, 117, 108, 100, 32, + 98, 101, 46, 10, 0, 0, 0, 0, 0, 0, 0, 22, 69, 95, 78, 79, 95, 82, + 69, 67, 79, 71, 78, 73, 90, 69, 68, 95, 77, 65, 82, 75, 69, 84, 45, 84, + 114, 97, 100, 105, 110, 103, 32, 112, 97, 105, 114, 32, 100, 111, 101, 115, 32, 110, + 111, 116, 32, 104, 97, 118, 101, 32, 114, 101, 99, 111, 103, 110, 105, 122, 101, 100, + 32, 109, 97, 114, 107, 101, 116, 46, 11, 0, 0, 0, 0, 0, 0, 0, 25, 69, + 95, 87, 82, 79, 78, 71, 95, 82, 69, 67, 79, 71, 78, 73, 90, 69, 68, 95, + 77, 65, 82, 75, 69, 84, 59, 77, 97, 114, 107, 101, 116, 32, 73, 68, 32, 105, + 115, 32, 110, 111, 116, 32, 114, 101, 99, 111, 103, 110, 105, 122, 101, 100, 32, 102, + 111, 114, 32, 99, 111, 114, 114, 101, 115, 112, 111, 110, 100, 105, 110, 103, 32, 116, + 114, 97, 100, 105, 110, 103, 32, 112, 97, 105, 114, 46, 12, 0, 0, 0, 0, 0, + 0, 0, 19, 69, 95, 73, 78, 86, 65, 76, 73, 68, 95, 77, 65, 82, 75, 69, + 84, 95, 73, 68, 21, 77, 97, 114, 107, 101, 116, 32, 73, 68, 32, 105, 115, 32, + 105, 110, 118, 97, 108, 105, 100, 46, 13, 0, 0, 0, 0, 0, 0, 0, 14, 69, + 95, 73, 78, 86, 65, 76, 73, 68, 95, 66, 65, 83, 69, 27, 66, 97, 115, 101, + 32, 97, 115, 115, 101, 116, 32, 116, 121, 112, 101, 32, 105, 115, 32, 105, 110, 118, + 97, 108, 105, 100, 46, 14, 0, 0, 0, 0, 0, 0, 0, 15, 69, 95, 73, 78, + 86, 65, 76, 73, 68, 95, 81, 85, 79, 84, 69, 28, 81, 117, 111, 116, 101, 32, + 97, 115, 115, 101, 116, 32, 116, 121, 112, 101, 32, 105, 115, 32, 105, 110, 118, 97, + 108, 105, 100, 46, 0, 12, 15, 103, 101, 116, 95, 109, 97, 114, 107, 101, 116, 95, + 105, 110, 102, 111, 1, 1, 0, 16, 103, 101, 116, 95, 78, 79, 95, 67, 85, 83, + 84, 79, 68, 73, 65, 78, 1, 1, 0, 17, 103, 101, 116, 95, 109, 97, 114, 107, + 101, 116, 95, 99, 111, 117, 110, 116, 115, 1, 1, 0, 18, 103, 101, 116, 95, 78, + 79, 95, 85, 78, 68, 69, 82, 87, 82, 73, 84, 69, 82, 1, 1, 0, 23, 103, + 101, 116, 95, 109, 97, 114, 107, 101, 116, 95, 105, 100, 95, 98, 97, 115, 101, 95, + 99, 111, 105, 110, 1, 1, 0, 26, 103, 101, 116, 95, 77, 65, 88, 95, 67, 72, + 65, 82, 65, 67, 84, 69, 82, 83, 95, 71, 69, 78, 69, 82, 73, 67, 1, 1, + 0, 26, 103, 101, 116, 95, 77, 73, 78, 95, 67, 72, 65, 82, 65, 67, 84, 69, + 82, 83, 95, 71, 69, 78, 69, 82, 73, 67, 1, 1, 0, 26, 103, 101, 116, 95, + 109, 97, 114, 107, 101, 116, 95, 105, 100, 95, 98, 97, 115, 101, 95, 103, 101, 110, + 101, 114, 105, 99, 1, 1, 0, 34, 103, 101, 116, 95, 114, 101, 99, 111, 103, 110, + 105, 122, 101, 100, 95, 109, 97, 114, 107, 101, 116, 95, 105, 100, 95, 98, 97, 115, + 101, 95, 99, 111, 105, 110, 1, 1, 0, 37, 103, 101, 116, 95, 114, 101, 99, 111, + 103, 110, 105, 122, 101, 100, 95, 109, 97, 114, 107, 101, 116, 95, 105, 100, 95, 98, + 97, 115, 101, 95, 103, 101, 110, 101, 114, 105, 99, 1, 1, 0, 39, 104, 97, 115, + 95, 114, 101, 99, 111, 103, 110, 105, 122, 101, 100, 95, 109, 97, 114, 107, 101, 116, + 95, 98, 97, 115, 101, 95, 99, 111, 105, 110, 95, 98, 121, 95, 116, 121, 112, 101, + 1, 1, 0, 42, 104, 97, 115, 95, 114, 101, 99, 111, 103, 110, 105, 122, 101, 100, + 95, 109, 97, 114, 107, 101, 116, 95, 98, 97, 115, 101, 95, 103, 101, 110, 101, 114, + 105, 99, 95, 98, 121, 95, 116, 121, 112, 101, 1, 1, 0, 0, 2, 3, 2, 5, + 3, 8, 1, 6, 8, 1, 2, 2, 1, 8, 3, 3, 2, 1, 10, 1, 4, 2, + 2, 12, 3, 13, 3, 5, 2, 7, 15, 8, 6, 18, 8, 1, 19, 8, 6, 20, + 3, 21, 3, 22, 3, 23, 3, 7, 2, 9, 25, 3, 26, 1, 15, 8, 0, 18, + 8, 1, 19, 8, 0, 20, 3, 21, 3, 22, 3, 23, 3, 8, 2, 8, 25, 3, + 15, 8, 6, 18, 8, 1, 19, 8, 6, 20, 3, 21, 3, 22, 3, 23, 3, 9, + 2, 2, 29, 8, 10, 31, 11, 11, 1, 8, 12, 12, 2, 5, 25, 3, 20, 3, + 21, 3, 22, 3, 23, 3, 13, 2, 2, 36, 11, 14, 2, 8, 10, 8, 12, 39, + 11, 15, 1, 8, 9, 16, 2, 5, 43, 11, 14, 2, 3, 8, 5, 44, 11, 17, + 2, 8, 5, 3, 47, 3, 48, 3, 49, 11, 15, 1, 8, 8, 10, 2, 3, 15, + 8, 6, 18, 8, 1, 19, 8, 6, 18, 2, 1, 23, 3, 0, 0, 0, 0, 10, + 24, 10, 0, 12, 1, 56, 0, 56, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 56, 2, 18, 10, 12, 2, 11, + 1, 11, 2, 45, 10, 10, 0, 12, 1, 56, 3, 11, 0, 56, 4, 18, 9, 12, + 3, 11, 1, 11, 3, 45, 9, 2, 4, 1, 0, 1, 10, 18, 32, 7, 0, 43, + 10, 16, 0, 12, 4, 10, 4, 10, 1, 56, 5, 4, 26, 11, 4, 10, 1, 56, + 6, 16, 1, 20, 56, 7, 33, 4, 22, 11, 0, 11, 1, 11, 2, 11, 3, 56, + 8, 2, 11, 0, 1, 6, 14, 0, 0, 0, 0, 0, 0, 0, 39, 11, 0, 1, + 11, 4, 1, 6, 12, 0, 0, 0, 0, 0, 0, 0, 39, 9, 1, 0, 0, 1, + 2, 6, 72, 0, 0, 0, 0, 0, 0, 0, 2, 10, 1, 0, 0, 1, 2, 6, + 4, 0, 0, 0, 0, 0, 0, 0, 2, 11, 1, 0, 0, 1, 2, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 12, 1, 0, 0, 1, 2, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 13, 1, 0, 0, 1, 4, 11, 0, 16, 2, 20, 2, 14, + 0, 0, 2, 9, 10, 1, 10, 7, 0, 43, 10, 16, 0, 56, 9, 7, 0, 43, + 9, 16, 3, 56, 10, 18, 3, 2, 16, 0, 0, 1, 10, 27, 21, 7, 0, 43, + 10, 16, 4, 12, 1, 10, 1, 10, 0, 56, 11, 4, 16, 11, 1, 11, 0, 56, + 12, 20, 56, 13, 12, 2, 11, 2, 2, 11, 1, 1, 56, 14, 12, 2, 5, 14, + 21, 0, 0, 1, 10, 32, 11, 56, 7, 7, 1, 17, 22, 56, 15, 11, 0, 11, + 1, 11, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, 18, 4, 17, 16, 2, 23, + 0, 0, 1, 10, 35, 12, 56, 16, 56, 7, 12, 5, 11, 0, 11, 5, 11, 1, + 11, 2, 11, 3, 11, 4, 18, 4, 17, 16, 2, 24, 0, 0, 2, 9, 10, 41, + 89, 7, 0, 43, 10, 16, 0, 12, 1, 10, 1, 10, 0, 56, 5, 4, 85, 11, + 1, 10, 0, 56, 6, 12, 2, 10, 2, 16, 5, 20, 12, 3, 10, 2, 16, 6, + 20, 12, 4, 10, 2, 16, 1, 20, 12, 5, 10, 3, 10, 4, 10, 5, 18, 11, + 12, 6, 10, 6, 17, 25, 4, 82, 11, 6, 17, 26, 1, 1, 1, 1, 12, 7, + 10, 0, 11, 7, 33, 12, 8, 11, 0, 12, 7, 11, 8, 12, 9, 14, 3, 17, + 27, 11, 4, 12, 10, 14, 5, 17, 27, 10, 2, 16, 7, 20, 10, 2, 16, 8, + 20, 10, 2, 16, 9, 20, 11, 2, 16, 10, 20, 12, 11, 12, 12, 12, 13, 12, + 14, 12, 15, 12, 16, 11, 7, 11, 9, 11, 16, 11, 10, 11, 15, 11, 14, 11, + 13, 11, 12, 11, 11, 18, 5, 2, 9, 12, 8, 5, 43, 11, 1, 1, 6, 12, + 0, 0, 0, 0, 0, 0, 0, 39, 28, 3, 0, 1, 10, 43, 56, 7, 0, 43, + 10, 16, 0, 12, 3, 10, 3, 10, 0, 56, 5, 4, 52, 11, 3, 11, 0, 56, + 6, 12, 4, 10, 4, 16, 5, 20, 12, 5, 11, 1, 11, 5, 33, 4, 48, 10, + 4, 16, 1, 20, 12, 5, 11, 2, 11, 5, 33, 4, 44, 10, 4, 16, 6, 20, + 10, 4, 16, 7, 20, 10, 4, 16, 8, 20, 10, 4, 16, 9, 20, 11, 4, 16, + 10, 20, 2, 11, 4, 1, 6, 14, 0, 0, 0, 0, 0, 0, 0, 39, 11, 4, + 1, 6, 13, 0, 0, 0, 0, 0, 0, 0, 39, 11, 3, 1, 6, 12, 0, 0, + 0, 0, 0, 0, 0, 39, 29, 1, 0, 1, 9, 1, 6, 56, 17, 1, 1, 1, + 1, 2, 31, 1, 0, 1, 9, 1, 7, 11, 0, 56, 18, 1, 1, 1, 1, 2, + 26, 0, 0, 1, 9, 44, 33, 7, 0, 43, 9, 16, 3, 12, 1, 10, 1, 10, + 0, 56, 19, 4, 29, 11, 1, 11, 0, 56, 20, 20, 12, 2, 14, 2, 16, 11, + 20, 14, 2, 16, 12, 20, 14, 2, 16, 13, 20, 14, 2, 16, 14, 20, 14, 2, + 16, 15, 20, 2, 11, 1, 1, 6, 10, 0, 0, 0, 0, 0, 0, 0, 39, 33, + 1, 0, 1, 9, 30, 9, 7, 1, 17, 22, 12, 2, 11, 0, 11, 2, 11, 1, + 18, 11, 17, 26, 2, 30, 1, 0, 1, 9, 1, 4, 56, 7, 56, 15, 17, 33, + 2, 34, 1, 0, 1, 9, 1, 6, 56, 16, 11, 0, 11, 1, 18, 11, 17, 26, + 2, 32, 1, 0, 1, 9, 16, 6, 56, 7, 12, 1, 11, 0, 11, 1, 17, 34, + 2, 35, 1, 0, 0, 1, 4, 11, 0, 16, 16, 20, 2, 25, 0, 0, 1, 9, + 1, 6, 7, 0, 43, 9, 16, 3, 11, 0, 56, 19, 2, 36, 1, 0, 1, 9, + 30, 9, 7, 1, 17, 22, 12, 2, 11, 0, 11, 2, 11, 1, 18, 11, 17, 25, + 2, 37, 1, 0, 1, 9, 1, 4, 56, 7, 56, 15, 17, 36, 2, 38, 1, 0, + 1, 9, 1, 6, 56, 16, 11, 0, 11, 1, 18, 11, 17, 25, 2, 39, 1, 0, + 1, 9, 16, 6, 56, 7, 12, 1, 11, 0, 11, 1, 17, 38, 2, 40, 3, 0, + 1, 10, 47, 18, 7, 0, 43, 10, 16, 17, 20, 12, 1, 10, 0, 11, 1, 37, + 4, 15, 11, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 34, 12, 2, 11, 2, + 2, 9, 12, 2, 5, 13, 41, 1, 0, 1, 10, 50, 20, 7, 0, 42, 10, 12, + 1, 10, 1, 16, 17, 20, 6, 1, 0, 0, 0, 0, 0, 0, 0, 22, 12, 2, + 11, 1, 15, 17, 12, 3, 10, 2, 11, 3, 21, 11, 0, 56, 21, 11, 2, 18, + 1, 2, 43, 1, 4, 1, 10, 52, 10, 49, 0, 56, 22, 12, 2, 12, 3, 11, + 0, 11, 1, 11, 3, 11, 2, 56, 23, 2, 45, 1, 4, 1, 10, 55, 17, 10, + 0, 12, 3, 10, 2, 12, 4, 11, 2, 17, 46, 12, 5, 11, 0, 11, 5, 56, + 24, 12, 6, 11, 3, 11, 1, 11, 4, 11, 6, 56, 23, 2, 48, 3, 0, 1, + 10, 59, 20, 56, 25, 4, 18, 56, 7, 7, 1, 17, 22, 11, 0, 12, 4, 11, + 1, 12, 5, 11, 2, 12, 6, 11, 4, 11, 5, 11, 6, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 11, 3, 56, 26, 2, 6, 6, 0, 0, 0, 0, 0, 0, 0, + 39, 51, 3, 0, 1, 10, 62, 40, 14, 0, 17, 52, 12, 6, 10, 6, 6, 4, + 0, 0, 0, 0, 0, 0, 0, 38, 4, 36, 11, 6, 6, 72, 0, 0, 0, 0, + 0, 0, 0, 37, 4, 32, 11, 4, 16, 16, 20, 12, 7, 56, 16, 11, 0, 12, + 8, 11, 1, 12, 9, 11, 2, 12, 10, 11, 3, 12, 11, 11, 8, 11, 9, 11, + 10, 11, 11, 11, 7, 11, 5, 56, 27, 2, 11, 4, 1, 6, 8, 0, 0, 0, + 0, 0, 0, 0, 39, 11, 4, 1, 6, 7, 0, 0, 0, 0, 0, 0, 0, 39, + 50, 0, 0, 1, 10, 66, 89, 10, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 36, 4, 87, 10, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 36, 4, 85, 10, + 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 36, 4, 83, 56, 25, 4, 81, 56, + 7, 12, 8, 10, 0, 10, 8, 34, 4, 79, 10, 0, 10, 1, 10, 8, 10, 2, + 10, 3, 10, 4, 10, 5, 18, 4, 12, 9, 7, 0, 42, 10, 12, 10, 10, 10, + 15, 4, 12, 11, 10, 11, 46, 10, 9, 56, 11, 3, 73, 10, 10, 15, 0, 12, + 12, 10, 12, 46, 56, 9, 6, 1, 0, 0, 0, 0, 0, 0, 0, 22, 12, 7, + 11, 11, 10, 9, 10, 7, 56, 28, 11, 12, 10, 7, 11, 9, 56, 29, 11, 10, + 15, 18, 10, 7, 11, 0, 11, 1, 11, 8, 11, 2, 11, 3, 11, 4, 11, 5, + 18, 6, 56, 30, 11, 6, 56, 31, 11, 7, 2, 11, 10, 1, 11, 11, 1, 6, + 5, 0, 0, 0, 0, 0, 0, 0, 39, 6, 4, 0, 0, 0, 0, 0, 0, 0, + 39, 6, 3, 0, 0, 0, 0, 0, 0, 0, 39, 6, 2, 0, 0, 0, 0, 0, + 0, 0, 39, 6, 1, 0, 0, 0, 0, 0, 0, 0, 39, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 39, 57, 1, 0, 1, 10, 50, 20, 7, 0, 42, 10, 12, 1, + 10, 1, 16, 19, 20, 6, 1, 0, 0, 0, 0, 0, 0, 0, 22, 12, 2, 11, + 1, 15, 19, 12, 3, 10, 2, 11, 3, 21, 11, 0, 56, 32, 11, 2, 18, 12, + 2, 59, 1, 4, 2, 9, 10, 71, 71, 11, 0, 17, 60, 7, 0, 33, 4, 69, + 7, 0, 43, 10, 16, 0, 10, 1, 56, 6, 12, 2, 10, 2, 16, 5, 20, 10, + 2, 16, 6, 20, 11, 2, 16, 1, 20, 18, 11, 12, 3, 7, 0, 42, 9, 12, + 4, 10, 4, 15, 3, 12, 5, 10, 5, 46, 10, 3, 56, 19, 4, 63, 10, 5, + 46, 10, 3, 56, 20, 16, 11, 20, 11, 1, 33, 4, 57, 11, 5, 10, 3, 56, + 33, 1, 11, 4, 15, 20, 11, 3, 12, 6, 56, 34, 12, 7, 11, 6, 11, 7, + 18, 7, 56, 35, 2, 11, 4, 1, 11, 5, 1, 6, 11, 0, 0, 0, 0, 0, + 0, 0, 39, 11, 4, 1, 11, 5, 1, 6, 10, 0, 0, 0, 0, 0, 0, 0, + 39, 6, 9, 0, 0, 0, 0, 0, 0, 0, 39, 62, 1, 4, 2, 9, 10, 28, + 25, 14, 1, 65, 19, 12, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, 3, + 10, 3, 10, 2, 35, 4, 22, 14, 1, 10, 3, 66, 19, 20, 12, 4, 10, 0, + 11, 4, 17, 59, 11, 3, 6, 1, 0, 0, 0, 0, 0, 0, 0, 22, 12, 3, + 5, 5, 11, 0, 1, 2, 63, 1, 4, 2, 9, 10, 74, 84, 11, 0, 17, 60, + 7, 0, 33, 4, 82, 7, 0, 43, 10, 16, 0, 10, 1, 56, 6, 12, 2, 10, + 2, 16, 5, 20, 10, 2, 16, 6, 20, 10, 2, 16, 1, 20, 10, 2, 16, 7, + 20, 10, 2, 16, 8, 20, 10, 2, 16, 9, 20, 11, 2, 16, 10, 20, 12, 3, + 12, 4, 12, 5, 12, 6, 18, 11, 12, 10, 11, 1, 11, 6, 11, 5, 11, 4, + 11, 3, 18, 8, 12, 11, 7, 0, 42, 9, 12, 12, 10, 12, 15, 3, 12, 13, + 10, 13, 46, 10, 10, 56, 19, 3, 74, 11, 13, 10, 10, 10, 11, 56, 36, 11, + 11, 56, 37, 11, 12, 15, 20, 12, 14, 12, 15, 11, 10, 11, 15, 18, 7, 12, + 16, 11, 14, 11, 16, 56, 35, 2, 11, 13, 10, 10, 56, 38, 12, 17, 10, 11, + 11, 17, 21, 5, 60, 6, 9, 0, 0, 0, 0, 0, 0, 0, 39, 65, 1, 4, + 2, 9, 10, 28, 25, 14, 1, 65, 19, 12, 2, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 12, 3, 10, 3, 10, 2, 35, 4, 22, 14, 1, 10, 3, 66, 19, 20, + 12, 4, 10, 0, 11, 4, 17, 63, 11, 3, 6, 1, 0, 0, 0, 0, 0, 0, + 0, 22, 12, 3, 5, 5, 11, 0, 1, 2, 27, 0, 0, 0, 1, 10, 10, 0, + 17, 66, 10, 0, 17, 67, 17, 22, 11, 0, 17, 68, 17, 22, 18, 0, 2, 10, + 0, 4, 2, 1, 0, 9, 0, 10, 1, 4, 0, 4, 1, 4, 3, 4, 4, 4, + 5, 4, 6, 8, 0, 8, 1, 8, 2, 8, 3, 8, 4, 12, 0, 10, 2, 10, + 4, 10, 3, 9, 1, 0, 118, 0, 119, 0, + ] +}); + +#[rustfmt::skip] +pub static MODULE_ECONIA_USER: Lazy> = Lazy::new(|| { + vec![ + 161, 28, 235, 11, 7, 0, 0, 10, 14, 1, 0, 29, 2, 29, 114, 3, 143, 1, + 239, 5, 4, 254, 6, 180, 1, 5, 178, 8, 226, 10, 7, 148, 19, 162, 26, 8, + 182, 45, 64, 16, 246, 45, 234, 16, 10, 224, 62, 141, 2, 11, 237, 64, 4, 12, + 241, 64, 212, 44, 13, 197, 109, 64, 14, 133, 110, 4, 15, 137, 110, 3, 0, 0, + 0, 12, 1, 14, 1, 30, 1, 33, 1, 53, 1, 65, 0, 81, 1, 83, 1, 104, + 1, 146, 1, 1, 150, 1, 1, 159, 1, 0, 1, 7, 0, 0, 6, 7, 0, 0, + 9, 8, 1, 0, 1, 1, 11, 4, 2, 7, 0, 4, 0, 2, 13, 4, 1, 0, + 1, 0, 15, 7, 0, 0, 27, 4, 0, 3, 29, 7, 0, 4, 32, 7, 0, 0, + 40, 4, 0, 0, 50, 4, 0, 0, 51, 8, 0, 5, 52, 4, 2, 3, 1, 0, + 1, 0, 55, 3, 0, 0, 61, 8, 0, 0, 62, 4, 0, 6, 64, 4, 1, 6, + 1, 0, 69, 7, 0, 0, 71, 7, 0, 7, 80, 8, 0, 8, 85, 7, 1, 0, + 0, 7, 108, 4, 0, 7, 135, 1, 4, 0, 11, 151, 1, 6, 0, 0, 78, 0, + 1, 1, 0, 1, 3, 79, 1, 3, 1, 0, 1, 2, 82, 5, 6, 1, 0, 1, + 8, 84, 2, 8, 1, 0, 1, 0, 86, 9, 1, 1, 0, 1, 0, 87, 1, 6, + 0, 1, 0, 88, 10, 6, 0, 1, 0, 89, 10, 6, 0, 1, 0, 90, 12, 10, + 0, 1, 5, 91, 14, 15, 2, 3, 0, 1, 1, 91, 17, 15, 2, 7, 4, 1, + 5, 92, 19, 20, 2, 3, 0, 1, 6, 93, 22, 1, 1, 6, 1, 0, 94, 24, + 1, 0, 1, 1, 95, 25, 26, 2, 7, 4, 1, 0, 96, 27, 1, 0, 1, 0, + 97, 30, 21, 0, 1, 0, 98, 31, 32, 0, 1, 8, 99, 8, 1, 1, 0, 1, + 8, 100, 8, 2, 1, 0, 1, 2, 101, 34, 1, 1, 0, 1, 0, 102, 36, 1, + 2, 0, 0, 1, 0, 103, 38, 1, 1, 0, 1, 9, 105, 39, 40, 0, 1, 2, + 106, 41, 7, 1, 0, 1, 0, 107, 43, 1, 0, 1, 8, 109, 1, 8, 1, 0, + 1, 7, 110, 45, 6, 0, 1, 0, 111, 47, 1, 0, 1, 8, 112, 49, 20, 1, + 0, 1, 8, 95, 49, 50, 1, 0, 1, 0, 113, 52, 1, 0, 1, 0, 114, 55, + 1, 0, 1, 0, 115, 57, 58, 2, 0, 0, 1, 8, 91, 59, 60, 1, 0, 1, + 2, 116, 61, 7, 1, 0, 1, 0, 117, 1, 20, 0, 1, 0, 118, 1, 20, 0, + 1, 0, 119, 1, 11, 0, 1, 0, 120, 1, 11, 0, 1, 0, 121, 1, 11, 0, + 1, 0, 122, 1, 11, 0, 1, 0, 123, 1, 11, 0, 1, 0, 124, 1, 11, 0, + 1, 0, 125, 1, 11, 0, 1, 0, 126, 1, 11, 0, 1, 0, 127, 1, 11, 0, + 1, 0, 128, 1, 64, 65, 0, 1, 5, 95, 19, 26, 2, 3, 0, 1, 1, 129, + 1, 66, 6, 2, 7, 4, 1, 0, 130, 1, 68, 65, 0, 1, 1, 92, 25, 20, + 2, 7, 4, 1, 0, 131, 1, 40, 65, 0, 1, 1, 132, 1, 66, 8, 2, 7, + 4, 1, 1, 133, 1, 25, 71, 2, 7, 4, 1, 0, 134, 1, 73, 74, 0, 1, + 7, 88, 75, 6, 0, 1, 0, 136, 1, 76, 74, 0, 1, 0, 137, 1, 41, 74, + 0, 1, 0, 138, 1, 76, 79, 0, 1, 0, 139, 1, 80, 10, 0, 1, 0, 140, + 1, 81, 20, 0, 1, 0, 141, 1, 82, 83, 0, 1, 0, 142, 1, 76, 85, 0, + 1, 0, 143, 1, 73, 85, 0, 1, 0, 144, 1, 41, 85, 0, 1, 0, 145, 1, + 40, 86, 0, 1, 10, 147, 1, 87, 20, 1, 0, 1, 10, 148, 1, 88, 1, 1, + 0, 1, 0, 149, 1, 76, 90, 0, 1, 6, 150, 1, 92, 93, 1, 6, 1, 11, + 152, 1, 93, 6, 0, 1, 0, 153, 1, 64, 6, 0, 1, 0, 154, 1, 96, 97, + 0, 1, 0, 155, 1, 76, 20, 0, 1, 0, 156, 1, 68, 20, 0, 1, 0, 157, + 1, 99, 1, 0, 1, 5, 158, 1, 1, 100, 2, 3, 4, 1, 12, 160, 1, 39, + 101, 1, 6, 1, 5, 161, 1, 102, 1, 2, 3, 0, 1, 1, 161, 1, 104, 1, + 2, 7, 4, 1, 0, 162, 1, 99, 1, 2, 0, 0, 1, 7, 163, 1, 6, 20, + 0, 1, 0, 164, 1, 107, 1, 2, 0, 0, 1, 2, 165, 1, 1, 20, 1, 0, + 1, 0, 166, 1, 108, 1, 1, 0, 1, 7, 167, 1, 110, 111, 0, 1, 1, 158, + 1, 1, 112, 2, 7, 4, 1, 10, 168, 1, 2, 113, 1, 0, 1, 2, 169, 1, + 1, 7, 1, 0, 1, 0, 170, 1, 99, 1, 1, 0, 1, 1, 171, 1, 17, 118, + 2, 7, 4, 1, 0, 172, 1, 120, 121, 1, 0, 1, 0, 173, 1, 123, 124, 2, + 0, 0, 1, 0, 174, 1, 125, 7, 1, 0, 1, 0, 175, 1, 126, 7, 1, 0, + 1, 0, 176, 1, 99, 7, 1, 0, 1, 0, 177, 1, 43, 1, 0, 1, 0, 178, + 1, 127, 1, 0, 1, 0, 179, 1, 128, 1, 1, 0, 1, 0, 180, 1, 99, 1, + 1, 0, 1, 2, 181, 1, 40, 20, 1, 0, 1, 2, 182, 1, 39, 1, 1, 0, + 1, 2, 183, 1, 129, 1, 1, 1, 0, 1, 1, 2, 1, 4, 2, 2, 3, 7, + 4, 2, 9, 13, 10, 16, 11, 18, 9, 18, 12, 21, 14, 16, 12, 28, 11, 13, + 18, 7, 19, 7, 10, 33, 20, 2, 0, 37, 24, 2, 0, 2, 26, 44, 4, 4, + 12, 48, 12, 32, 29, 11, 30, 11, 12, 53, 29, 7, 34, 7, 35, 2, 10, 62, + 35, 37, 20, 37, 48, 13, 49, 16, 51, 69, 14, 69, 53, 69, 29, 6, 30, 6, + 54, 69, 67, 10, 68, 10, 26, 91, 48, 18, 70, 21, 70, 28, 70, 32, 70, 48, + 70, 53, 3, 91, 26, 10, 51, 16, 3, 10, 77, 18, 78, 21, 78, 28, 78, 32, + 78, 48, 78, 53, 79, 18, 80, 16, 83, 106, 84, 2, 85, 2, 85, 37, 1, 37, + 77, 13, 87, 69, 87, 16, 79, 13, 88, 6, 80, 69, 10, 69, 87, 33, 89, 2, + 80, 33, 81, 116, 53, 16, 91, 16, 26, 7, 92, 2, 94, 37, 94, 2, 92, 4, + 18, 44, 101, 2, 102, 2, 96, 2, 103, 2, 4, 5, 3, 3, 11, 4, 1, 9, + 0, 0, 1, 9, 0, 1, 8, 7, 1, 8, 19, 1, 6, 11, 4, 1, 9, 0, + 1, 3, 1, 11, 4, 1, 9, 0, 1, 11, 20, 1, 9, 0, 6, 5, 3, 3, + 3, 11, 20, 1, 11, 4, 1, 9, 0, 3, 1, 4, 1, 2, 9, 5, 3, 3, + 1, 3, 3, 3, 4, 2, 2, 4, 8, 6, 2, 7, 11, 12, 2, 9, 0, 9, + 1, 9, 0, 1, 7, 9, 1, 2, 3, 8, 9, 2, 7, 11, 3, 2, 9, 0, + 9, 1, 9, 0, 2, 4, 8, 15, 2, 6, 11, 12, 2, 9, 0, 9, 1, 9, + 0, 1, 1, 1, 8, 0, 2, 7, 11, 16, 1, 9, 0, 9, 0, 15, 4, 7, + 8, 6, 1, 7, 11, 3, 2, 3, 8, 9, 7, 3, 7, 3, 7, 3, 3, 3, + 7, 8, 9, 3, 4, 3, 2, 7, 11, 12, 2, 4, 8, 15, 9, 5, 3, 3, + 1, 3, 3, 3, 3, 4, 2, 6, 11, 3, 2, 9, 0, 9, 1, 9, 0, 1, + 6, 9, 1, 8, 5, 3, 3, 1, 3, 3, 4, 3, 1, 8, 1, 6, 4, 7, + 8, 6, 1, 6, 11, 3, 2, 3, 8, 9, 2, 7, 11, 12, 2, 4, 8, 15, + 5, 3, 4, 5, 3, 2, 12, 3, 3, 3, 1, 5, 3, 4, 5, 3, 4, 3, + 3, 1, 8, 5, 2, 4, 11, 4, 1, 9, 0, 2, 7, 11, 4, 1, 9, 0, + 11, 4, 1, 9, 0, 12, 7, 11, 12, 2, 4, 8, 6, 4, 7, 8, 6, 8, + 7, 8, 7, 7, 3, 7, 3, 7, 3, 8, 7, 3, 11, 4, 1, 9, 0, 3, + 7, 5, 3, 3, 3, 11, 20, 1, 11, 4, 1, 9, 0, 11, 4, 1, 9, 1, + 3, 1, 9, 1, 4, 6, 12, 3, 3, 3, 1, 6, 12, 1, 5, 2, 6, 12, + 3, 2, 11, 4, 1, 9, 0, 5, 5, 5, 3, 3, 3, 6, 8, 21, 1, 11, + 4, 1, 8, 19, 1, 6, 8, 21, 2, 3, 11, 20, 1, 11, 4, 1, 8, 19, + 13, 3, 5, 3, 5, 1, 3, 3, 2, 2, 3, 4, 6, 10, 8, 5, 6, 11, + 20, 1, 2, 1, 8, 17, 1, 6, 11, 20, 1, 9, 0, 1, 6, 9, 0, 17, + 7, 11, 12, 2, 4, 8, 15, 4, 7, 8, 15, 6, 10, 8, 5, 3, 3, 6, + 8, 5, 8, 5, 7, 4, 3, 4, 5, 2, 8, 0, 8, 5, 7, 11, 12, 2, + 4, 8, 15, 3, 10, 3, 5, 3, 5, 1, 3, 2, 4, 6, 10, 8, 5, 6, + 11, 20, 1, 2, 1, 8, 18, 14, 7, 11, 12, 2, 4, 8, 15, 4, 7, 8, + 15, 6, 10, 8, 5, 3, 3, 6, 8, 5, 3, 4, 5, 2, 8, 0, 7, 11, + 12, 2, 4, 8, 15, 3, 1, 6, 10, 8, 5, 8, 3, 3, 6, 8, 5, 5, + 7, 11, 12, 2, 4, 8, 15, 3, 3, 4, 12, 5, 3, 3, 1, 3, 3, 3, + 1, 11, 20, 1, 11, 4, 1, 9, 0, 11, 4, 1, 9, 1, 3, 3, 3, 11, + 20, 1, 11, 4, 1, 9, 0, 11, 4, 1, 9, 1, 4, 1, 7, 11, 20, 1, + 9, 0, 1, 7, 9, 0, 2, 7, 11, 4, 1, 9, 0, 3, 2, 4, 11, 4, + 1, 9, 1, 20, 4, 7, 8, 6, 1, 7, 11, 3, 2, 3, 8, 9, 7, 3, + 7, 3, 7, 3, 7, 3, 7, 3, 7, 8, 9, 4, 7, 11, 4, 1, 9, 0, + 7, 11, 4, 1, 9, 0, 7, 11, 4, 1, 9, 0, 11, 4, 1, 9, 0, 7, + 11, 4, 1, 9, 1, 7, 11, 4, 1, 9, 1, 11, 4, 1, 9, 1, 11, 20, + 1, 11, 4, 1, 9, 0, 4, 4, 5, 3, 3, 1, 1, 10, 4, 1, 6, 11, + 3, 2, 9, 0, 9, 1, 11, 6, 11, 12, 2, 4, 8, 6, 4, 6, 8, 6, + 1, 6, 11, 3, 2, 3, 8, 9, 10, 4, 3, 3, 6, 8, 9, 3, 10, 4, + 2, 5, 3, 2, 3, 10, 3, 9, 10, 4, 10, 4, 6, 11, 3, 2, 3, 10, + 3, 6, 10, 3, 3, 3, 4, 3, 4, 3, 6, 9, 1, 11, 20, 1, 9, 0, + 11, 20, 1, 9, 0, 12, 10, 4, 10, 4, 6, 11, 3, 2, 3, 10, 3, 11, + 20, 1, 3, 3, 11, 20, 1, 3, 6, 10, 3, 3, 3, 4, 3, 4, 3, 5, + 3, 6, 8, 22, 6, 3, 3, 3, 3, 3, 3, 1, 6, 8, 22, 3, 5, 3, + 3, 3, 6, 11, 12, 2, 4, 8, 6, 4, 6, 8, 6, 2, 3, 5, 1, 8, + 10, 2, 3, 3, 2, 5, 4, 1, 7, 11, 3, 2, 3, 8, 9, 1, 10, 8, + 9, 12, 4, 7, 8, 6, 3, 3, 3, 3, 3, 3, 3, 3, 10, 8, 9, 10, + 8, 9, 7, 8, 7, 8, 8, 8, 7, 3, 3, 3, 3, 1, 10, 8, 10, 1, + 6, 10, 9, 0, 1, 7, 10, 9, 0, 11, 10, 4, 10, 8, 10, 10, 8, 10, + 10, 4, 10, 4, 3, 3, 4, 7, 10, 8, 10, 3, 8, 10, 1, 11, 20, 1, + 8, 13, 1, 8, 13, 1, 6, 11, 16, 1, 9, 0, 1, 6, 8, 23, 3, 6, + 11, 12, 2, 4, 8, 15, 4, 6, 8, 15, 7, 6, 11, 12, 2, 4, 8, 6, + 4, 6, 8, 6, 1, 6, 11, 3, 2, 3, 8, 9, 6, 3, 3, 5, 5, 3, + 3, 1, 3, 1, 11, 20, 1, 4, 4, 4, 6, 8, 6, 1, 6, 11, 3, 2, + 3, 8, 9, 3, 6, 12, 3, 3, 1, 11, 12, 2, 9, 0, 9, 1, 1, 11, + 16, 1, 9, 0, 3, 7, 11, 12, 2, 9, 0, 9, 1, 9, 0, 9, 1, 6, + 5, 6, 12, 8, 14, 7, 11, 12, 2, 4, 8, 15, 4, 8, 15, 3, 7, 11, + 3, 2, 9, 0, 9, 1, 9, 0, 9, 1, 18, 3, 7, 8, 6, 4, 4, 4, + 1, 7, 11, 3, 2, 3, 8, 9, 7, 3, 7, 3, 7, 3, 4, 4, 4, 8, + 9, 3, 7, 8, 9, 7, 4, 7, 3, 2, 9, 0, 9, 1, 4, 6, 12, 4, + 3, 3, 2, 6, 12, 4, 2, 3, 4, 3, 3, 8, 7, 8, 7, 5, 8, 8, + 3, 3, 3, 3, 1, 11, 3, 2, 9, 0, 9, 1, 1, 10, 9, 0, 34, 5, + 8, 7, 8, 7, 3, 3, 3, 3, 8, 8, 6, 12, 8, 11, 7, 11, 12, 2, + 4, 8, 6, 7, 11, 12, 2, 4, 8, 6, 4, 8, 7, 8, 8, 8, 7, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 11, 3, 2, 3, 8, 9, 11, + 3, 2, 3, 8, 9, 8, 6, 7, 11, 3, 2, 3, 10, 3, 7, 11, 3, 2, + 3, 10, 3, 10, 3, 6, 5, 6, 12, 11, 2, 1, 9, 0, 4, 11, 4, 1, + 9, 0, 7, 11, 3, 2, 4, 11, 4, 1, 9, 0, 2, 8, 19, 9, 0, 1, + 8, 9, 3, 9, 1, 11, 20, 1, 9, 0, 11, 20, 1, 9, 0, 7, 10, 8, + 9, 11, 20, 1, 3, 7, 11, 3, 2, 3, 8, 9, 3, 11, 20, 1, 3, 8, + 9, 10, 8, 9, 5, 5, 3, 3, 3, 3, 1, 11, 20, 1, 11, 4, 1, 9, + 0, 11, 7, 11, 12, 2, 4, 8, 6, 4, 7, 8, 6, 8, 7, 8, 7, 7, + 3, 7, 3, 7, 3, 3, 8, 7, 11, 20, 1, 11, 4, 1, 9, 0, 6, 5, + 3, 3, 3, 3, 3, 2, 11, 20, 1, 11, 4, 1, 9, 0, 11, 4, 1, 9, + 1, 4, 5, 3, 3, 3, 4, 5, 3, 3, 6, 8, 22, 5, 5, 3, 3, 6, + 8, 22, 6, 8, 21, 4, 6, 12, 3, 3, 6, 8, 21, 2, 5, 11, 4, 1, + 9, 0, 4, 117, 115, 101, 114, 16, 67, 97, 110, 99, 101, 108, 79, 114, 100, 101, + 114, 69, 118, 101, 110, 116, 9, 109, 97, 114, 107, 101, 116, 95, 105, 100, 8, 111, + 114, 100, 101, 114, 95, 105, 100, 12, 99, 117, 115, 116, 111, 100, 105, 97, 110, 95, + 105, 100, 6, 114, 101, 97, 115, 111, 110, 20, 67, 104, 97, 110, 103, 101, 79, 114, + 100, 101, 114, 83, 105, 122, 101, 69, 118, 101, 110, 116, 4, 115, 105, 100, 101, 8, + 110, 101, 119, 95, 115, 105, 122, 101, 10, 67, 111, 108, 108, 97, 116, 101, 114, 97, + 108, 3, 109, 97, 112, 7, 84, 97, 98, 108, 105, 115, 116, 7, 116, 97, 98, 108, + 105, 115, 116, 4, 67, 111, 105, 110, 4, 99, 111, 105, 110, 9, 70, 105, 108, 108, + 69, 118, 101, 110, 116, 4, 115, 105, 122, 101, 5, 112, 114, 105, 99, 101, 10, 109, + 97, 107, 101, 114, 95, 115, 105, 100, 101, 5, 109, 97, 107, 101, 114, 18, 109, 97, + 107, 101, 114, 95, 99, 117, 115, 116, 111, 100, 105, 97, 110, 95, 105, 100, 14, 109, + 97, 107, 101, 114, 95, 111, 114, 100, 101, 114, 95, 105, 100, 5, 116, 97, 107, 101, + 114, 18, 116, 97, 107, 101, 114, 95, 99, 117, 115, 116, 111, 100, 105, 97, 110, 95, + 105, 100, 14, 116, 97, 107, 101, 114, 95, 111, 114, 100, 101, 114, 95, 105, 100, 21, + 116, 97, 107, 101, 114, 95, 113, 117, 111, 116, 101, 95, 102, 101, 101, 115, 95, 112, + 97, 105, 100, 25, 115, 101, 113, 117, 101, 110, 99, 101, 95, 110, 117, 109, 98, 101, + 114, 95, 102, 111, 114, 95, 116, 114, 97, 100, 101, 13, 77, 97, 114, 107, 101, 116, + 65, 99, 99, 111, 117, 110, 116, 9, 98, 97, 115, 101, 95, 116, 121, 112, 101, 8, + 84, 121, 112, 101, 73, 110, 102, 111, 9, 116, 121, 112, 101, 95, 105, 110, 102, 111, + 17, 98, 97, 115, 101, 95, 110, 97, 109, 101, 95, 103, 101, 110, 101, 114, 105, 99, + 6, 83, 116, 114, 105, 110, 103, 6, 115, 116, 114, 105, 110, 103, 10, 113, 117, 111, + 116, 101, 95, 116, 121, 112, 101, 8, 108, 111, 116, 95, 115, 105, 122, 101, 9, 116, + 105, 99, 107, 95, 115, 105, 122, 101, 8, 109, 105, 110, 95, 115, 105, 122, 101, 14, + 117, 110, 100, 101, 114, 119, 114, 105, 116, 101, 114, 95, 105, 100, 4, 97, 115, 107, + 115, 5, 79, 114, 100, 101, 114, 4, 98, 105, 100, 115, 14, 97, 115, 107, 115, 95, + 115, 116, 97, 99, 107, 95, 116, 111, 112, 14, 98, 105, 100, 115, 95, 115, 116, 97, + 99, 107, 95, 116, 111, 112, 10, 98, 97, 115, 101, 95, 116, 111, 116, 97, 108, 14, + 98, 97, 115, 101, 95, 97, 118, 97, 105, 108, 97, 98, 108, 101, 12, 98, 97, 115, + 101, 95, 99, 101, 105, 108, 105, 110, 103, 11, 113, 117, 111, 116, 101, 95, 116, 111, + 116, 97, 108, 15, 113, 117, 111, 116, 101, 95, 97, 118, 97, 105, 108, 97, 98, 108, + 101, 13, 113, 117, 111, 116, 101, 95, 99, 101, 105, 108, 105, 110, 103, 17, 77, 97, + 114, 107, 101, 116, 65, 99, 99, 111, 117, 110, 116, 86, 105, 101, 119, 14, 77, 97, + 114, 107, 101, 116, 65, 99, 99, 111, 117, 110, 116, 115, 5, 84, 97, 98, 108, 101, + 5, 116, 97, 98, 108, 101, 10, 99, 117, 115, 116, 111, 100, 105, 97, 110, 115, 32, + 77, 97, 114, 107, 101, 116, 69, 118, 101, 110, 116, 72, 97, 110, 100, 108, 101, 67, + 114, 101, 97, 116, 105, 111, 110, 78, 117, 109, 98, 101, 114, 115, 39, 99, 97, 110, + 99, 101, 108, 95, 111, 114, 100, 101, 114, 95, 101, 118, 101, 110, 116, 115, 95, 104, + 97, 110, 100, 108, 101, 95, 99, 114, 101, 97, 116, 105, 111, 110, 95, 110, 117, 109, + 44, 99, 104, 97, 110, 103, 101, 95, 111, 114, 100, 101, 114, 95, 115, 105, 122, 101, + 95, 101, 118, 101, 110, 116, 115, 95, 104, 97, 110, 100, 108, 101, 95, 99, 114, 101, + 97, 116, 105, 111, 110, 95, 110, 117, 109, 31, 102, 105, 108, 108, 95, 101, 118, 101, + 110, 116, 115, 95, 104, 97, 110, 100, 108, 101, 95, 99, 114, 101, 97, 116, 105, 111, + 110, 95, 110, 117, 109, 44, 112, 108, 97, 99, 101, 95, 108, 105, 109, 105, 116, 95, + 111, 114, 100, 101, 114, 95, 101, 118, 101, 110, 116, 115, 95, 104, 97, 110, 100, 108, + 101, 95, 99, 114, 101, 97, 116, 105, 111, 110, 95, 110, 117, 109, 45, 112, 108, 97, + 99, 101, 95, 109, 97, 114, 107, 101, 116, 95, 111, 114, 100, 101, 114, 95, 101, 118, + 101, 110, 116, 115, 95, 104, 97, 110, 100, 108, 101, 95, 99, 114, 101, 97, 116, 105, + 111, 110, 95, 110, 117, 109, 18, 77, 97, 114, 107, 101, 116, 69, 118, 101, 110, 116, + 72, 97, 110, 100, 108, 101, 115, 34, 77, 97, 114, 107, 101, 116, 69, 118, 101, 110, + 116, 72, 97, 110, 100, 108, 101, 115, 70, 111, 114, 77, 97, 114, 107, 101, 116, 65, + 99, 99, 111, 117, 110, 116, 19, 99, 97, 110, 99, 101, 108, 95, 111, 114, 100, 101, + 114, 95, 101, 118, 101, 110, 116, 115, 11, 69, 118, 101, 110, 116, 72, 97, 110, 100, + 108, 101, 5, 101, 118, 101, 110, 116, 24, 99, 104, 97, 110, 103, 101, 95, 111, 114, + 100, 101, 114, 95, 115, 105, 122, 101, 95, 101, 118, 101, 110, 116, 115, 11, 102, 105, + 108, 108, 95, 101, 118, 101, 110, 116, 115, 24, 112, 108, 97, 99, 101, 95, 108, 105, + 109, 105, 116, 95, 111, 114, 100, 101, 114, 95, 101, 118, 101, 110, 116, 115, 20, 80, + 108, 97, 99, 101, 76, 105, 109, 105, 116, 79, 114, 100, 101, 114, 69, 118, 101, 110, + 116, 25, 112, 108, 97, 99, 101, 95, 109, 97, 114, 107, 101, 116, 95, 111, 114, 100, + 101, 114, 95, 101, 118, 101, 110, 116, 115, 21, 80, 108, 97, 99, 101, 77, 97, 114, + 107, 101, 116, 79, 114, 100, 101, 114, 69, 118, 101, 110, 116, 15, 109, 97, 114, 107, + 101, 116, 95, 111, 114, 100, 101, 114, 95, 105, 100, 10, 105, 110, 116, 101, 103, 114, + 97, 116, 111, 114, 11, 114, 101, 115, 116, 114, 105, 99, 116, 105, 111, 110, 19, 115, + 101, 108, 102, 95, 109, 97, 116, 99, 104, 95, 98, 101, 104, 97, 118, 105, 111, 114, + 14, 114, 101, 109, 97, 105, 110, 105, 110, 103, 95, 115, 105, 122, 101, 9, 100, 105, + 114, 101, 99, 116, 105, 111, 110, 13, 100, 101, 112, 111, 115, 105, 116, 95, 99, 111, + 105, 110, 115, 7, 116, 121, 112, 101, 95, 111, 102, 12, 71, 101, 110, 101, 114, 105, + 99, 65, 115, 115, 101, 116, 8, 114, 101, 103, 105, 115, 116, 114, 121, 5, 118, 97, + 108, 117, 101, 6, 111, 112, 116, 105, 111, 110, 4, 115, 111, 109, 101, 6, 79, 112, + 116, 105, 111, 110, 13, 100, 101, 112, 111, 115, 105, 116, 95, 97, 115, 115, 101, 116, + 16, 103, 101, 116, 95, 78, 79, 95, 67, 85, 83, 84, 79, 68, 73, 65, 78, 16, + 103, 101, 116, 95, 99, 117, 115, 116, 111, 100, 105, 97, 110, 95, 105, 100, 13, 103, + 101, 116, 95, 109, 97, 114, 107, 101, 116, 95, 105, 100, 21, 99, 97, 110, 99, 101, + 108, 95, 111, 114, 100, 101, 114, 95, 105, 110, 116, 101, 114, 110, 97, 108, 10, 98, + 111, 114, 114, 111, 119, 95, 109, 117, 116, 8, 99, 111, 110, 116, 97, 105, 110, 115, + 10, 101, 109, 105, 116, 95, 101, 118, 101, 110, 116, 26, 99, 104, 97, 110, 103, 101, + 95, 111, 114, 100, 101, 114, 95, 115, 105, 122, 101, 95, 105, 110, 116, 101, 114, 110, + 97, 108, 6, 98, 111, 114, 114, 111, 119, 20, 112, 108, 97, 99, 101, 95, 111, 114, + 100, 101, 114, 95, 105, 110, 116, 101, 114, 110, 97, 108, 34, 99, 114, 101, 97, 116, + 101, 95, 99, 97, 110, 99, 101, 108, 95, 111, 114, 100, 101, 114, 95, 101, 118, 101, + 110, 116, 95, 105, 110, 116, 101, 114, 110, 97, 108, 26, 99, 114, 101, 97, 116, 101, + 95, 102, 105, 108, 108, 95, 101, 118, 101, 110, 116, 95, 105, 110, 116, 101, 114, 110, + 97, 108, 12, 100, 101, 115, 116, 114, 111, 121, 95, 110, 111, 110, 101, 12, 100, 101, + 115, 116, 114, 111, 121, 95, 115, 111, 109, 101, 5, 109, 101, 114, 103, 101, 23, 100, + 101, 112, 111, 115, 105, 116, 95, 97, 115, 115, 101, 116, 115, 95, 105, 110, 116, 101, + 114, 110, 97, 108, 22, 100, 101, 112, 111, 115, 105, 116, 95, 102, 114, 111, 109, 95, + 99, 111, 105, 110, 115, 116, 111, 114, 101, 6, 115, 105, 103, 110, 101, 114, 10, 97, + 100, 100, 114, 101, 115, 115, 95, 111, 102, 8, 119, 105, 116, 104, 100, 114, 97, 119, + 21, 100, 101, 112, 111, 115, 105, 116, 95, 103, 101, 110, 101, 114, 105, 99, 95, 97, + 115, 115, 101, 116, 21, 85, 110, 100, 101, 114, 119, 114, 105, 116, 101, 114, 67, 97, + 112, 97, 98, 105, 108, 105, 116, 121, 4, 110, 111, 110, 101, 18, 103, 101, 116, 95, + 117, 110, 100, 101, 114, 119, 114, 105, 116, 101, 114, 95, 105, 100, 32, 101, 109, 105, + 116, 95, 108, 105, 109, 105, 116, 95, 111, 114, 100, 101, 114, 95, 101, 118, 101, 110, + 116, 115, 95, 105, 110, 116, 101, 114, 110, 97, 108, 7, 105, 115, 95, 115, 111, 109, + 101, 33, 101, 109, 105, 116, 95, 109, 97, 114, 107, 101, 116, 95, 111, 114, 100, 101, + 114, 95, 101, 118, 101, 110, 116, 115, 95, 105, 110, 116, 101, 114, 110, 97, 108, 36, + 101, 109, 105, 116, 95, 115, 119, 97, 112, 95, 109, 97, 107, 101, 114, 95, 102, 105, + 108, 108, 95, 101, 118, 101, 110, 116, 115, 95, 105, 110, 116, 101, 114, 110, 97, 108, + 19, 102, 105, 108, 108, 95, 111, 114, 100, 101, 114, 95, 105, 110, 116, 101, 114, 110, + 97, 108, 7, 101, 120, 116, 114, 97, 99, 116, 7, 103, 101, 116, 95, 65, 83, 75, + 7, 103, 101, 116, 95, 66, 73, 68, 26, 103, 101, 116, 95, 67, 65, 78, 67, 69, + 76, 95, 82, 69, 65, 83, 79, 78, 95, 69, 86, 73, 67, 84, 73, 79, 78, 37, + 103, 101, 116, 95, 67, 65, 78, 67, 69, 76, 95, 82, 69, 65, 83, 79, 78, 95, + 73, 77, 77, 69, 68, 73, 65, 84, 69, 95, 79, 82, 95, 67, 65, 78, 67, 69, + 76, 31, 103, 101, 116, 95, 67, 65, 78, 67, 69, 76, 95, 82, 69, 65, 83, 79, + 78, 95, 77, 65, 78, 85, 65, 76, 95, 67, 65, 78, 67, 69, 76, 34, 103, 101, + 116, 95, 67, 65, 78, 67, 69, 76, 95, 82, 69, 65, 83, 79, 78, 95, 77, 65, + 88, 95, 81, 85, 79, 84, 69, 95, 84, 82, 65, 68, 69, 68, 38, 103, 101, 116, + 95, 67, 65, 78, 67, 69, 76, 95, 82, 69, 65, 83, 79, 78, 95, 78, 79, 84, + 95, 69, 78, 79, 85, 71, 72, 95, 76, 73, 81, 85, 73, 68, 73, 84, 89, 34, + 103, 101, 116, 95, 67, 65, 78, 67, 69, 76, 95, 82, 69, 65, 83, 79, 78, 95, + 83, 69, 76, 70, 95, 77, 65, 84, 67, 72, 95, 77, 65, 75, 69, 82, 34, 103, + 101, 116, 95, 67, 65, 78, 67, 69, 76, 95, 82, 69, 65, 83, 79, 78, 95, 83, + 69, 76, 70, 95, 77, 65, 84, 67, 72, 95, 84, 65, 75, 69, 82, 39, 103, 101, + 116, 95, 67, 65, 78, 67, 69, 76, 95, 82, 69, 65, 83, 79, 78, 95, 84, 79, + 79, 95, 83, 77, 65, 76, 76, 95, 84, 79, 95, 70, 73, 76, 76, 95, 76, 79, + 84, 38, 103, 101, 116, 95, 67, 65, 78, 67, 69, 76, 95, 82, 69, 65, 83, 79, + 78, 95, 86, 73, 79, 76, 65, 84, 69, 68, 95, 76, 73, 77, 73, 84, 95, 80, + 82, 73, 67, 69, 36, 103, 101, 116, 95, 97, 99, 116, 105, 118, 101, 95, 109, 97, + 114, 107, 101, 116, 95, 111, 114, 100, 101, 114, 95, 105, 100, 115, 95, 105, 110, 116, + 101, 114, 110, 97, 108, 6, 108, 101, 110, 103, 116, 104, 40, 103, 101, 116, 95, 97, + 108, 108, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, 111, 117, 110, 116, 95, + 105, 100, 115, 95, 102, 111, 114, 95, 109, 97, 114, 107, 101, 116, 95, 105, 100, 35, + 103, 101, 116, 95, 97, 108, 108, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, + 111, 117, 110, 116, 95, 105, 100, 115, 95, 102, 111, 114, 95, 117, 115, 101, 114, 12, + 103, 101, 116, 95, 104, 101, 97, 100, 95, 107, 101, 121, 15, 98, 111, 114, 114, 111, + 119, 95, 105, 116, 101, 114, 97, 98, 108, 101, 26, 103, 101, 116, 95, 97, 115, 115, + 101, 116, 95, 99, 111, 117, 110, 116, 115, 95, 99, 117, 115, 116, 111, 100, 105, 97, + 110, 19, 67, 117, 115, 116, 111, 100, 105, 97, 110, 67, 97, 112, 97, 98, 105, 108, + 105, 116, 121, 25, 103, 101, 116, 95, 97, 115, 115, 101, 116, 95, 99, 111, 117, 110, + 116, 115, 95, 105, 110, 116, 101, 114, 110, 97, 108, 21, 103, 101, 116, 95, 97, 115, + 115, 101, 116, 95, 99, 111, 117, 110, 116, 115, 95, 117, 115, 101, 114, 18, 103, 101, + 116, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, 111, 117, 110, 116, 21, 103, + 101, 116, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, 111, 117, 110, 116, 95, + 105, 100, 39, 104, 97, 115, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, 111, + 117, 110, 116, 95, 98, 121, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, 111, + 117, 110, 116, 95, 105, 100, 21, 118, 101, 99, 116, 111, 114, 105, 122, 101, 95, 111, + 112, 101, 110, 95, 111, 114, 100, 101, 114, 115, 30, 103, 101, 116, 95, 109, 97, 114, + 107, 101, 116, 95, 97, 99, 99, 111, 117, 110, 116, 95, 109, 97, 114, 107, 101, 116, + 95, 105, 110, 102, 111, 40, 103, 101, 116, 95, 109, 97, 114, 107, 101, 116, 95, 97, + 99, 99, 111, 117, 110, 116, 95, 109, 97, 114, 107, 101, 116, 95, 105, 110, 102, 111, + 95, 99, 117, 115, 116, 111, 100, 105, 97, 110, 35, 103, 101, 116, 95, 109, 97, 114, + 107, 101, 116, 95, 97, 99, 99, 111, 117, 110, 116, 95, 109, 97, 114, 107, 101, 116, + 95, 105, 110, 102, 111, 95, 117, 115, 101, 114, 19, 103, 101, 116, 95, 109, 97, 114, + 107, 101, 116, 95, 97, 99, 99, 111, 117, 110, 116, 115, 6, 118, 101, 99, 116, 111, + 114, 8, 105, 115, 95, 101, 109, 112, 116, 121, 7, 114, 101, 118, 101, 114, 115, 101, + 40, 103, 101, 116, 95, 109, 97, 114, 107, 101, 116, 95, 101, 118, 101, 110, 116, 95, + 104, 97, 110, 100, 108, 101, 95, 99, 114, 101, 97, 116, 105, 111, 110, 95, 110, 117, + 109, 98, 101, 114, 115, 4, 103, 117, 105, 100, 4, 71, 85, 73, 68, 12, 99, 114, + 101, 97, 116, 105, 111, 110, 95, 110, 117, 109, 34, 103, 101, 116, 95, 110, 101, 120, + 116, 95, 111, 114, 100, 101, 114, 95, 97, 99, 99, 101, 115, 115, 95, 107, 101, 121, + 95, 105, 110, 116, 101, 114, 110, 97, 108, 26, 103, 101, 116, 95, 111, 112, 101, 110, + 95, 111, 114, 100, 101, 114, 95, 105, 100, 95, 105, 110, 116, 101, 114, 110, 97, 108, + 18, 104, 97, 115, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, 111, 117, 110, + 116, 31, 104, 97, 115, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, 111, 117, + 110, 116, 95, 98, 121, 95, 109, 97, 114, 107, 101, 116, 95, 105, 100, 36, 105, 110, + 105, 116, 95, 109, 97, 114, 107, 101, 116, 95, 101, 118, 101, 110, 116, 95, 104, 97, + 110, 100, 108, 101, 115, 95, 105, 102, 95, 109, 105, 115, 115, 105, 110, 103, 3, 110, + 101, 119, 7, 97, 99, 99, 111, 117, 110, 116, 16, 110, 101, 119, 95, 101, 118, 101, + 110, 116, 95, 104, 97, 110, 100, 108, 101, 3, 97, 100, 100, 23, 114, 101, 103, 105, + 115, 116, 101, 114, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, 111, 117, 110, + 116, 26, 105, 115, 95, 114, 101, 103, 105, 115, 116, 101, 114, 101, 100, 95, 99, 117, + 115, 116, 111, 100, 105, 97, 110, 95, 105, 100, 39, 114, 101, 103, 105, 115, 116, 101, + 114, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, 111, 117, 110, 116, 95, 97, + 99, 99, 111, 117, 110, 116, 95, 101, 110, 116, 114, 105, 101, 115, 19, 105, 115, 95, + 99, 111, 105, 110, 95, 105, 110, 105, 116, 105, 97, 108, 105, 122, 101, 100, 40, 114, + 101, 103, 105, 115, 116, 101, 114, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, + 111, 117, 110, 116, 95, 99, 111, 108, 108, 97, 116, 101, 114, 97, 108, 95, 101, 110, + 116, 114, 121, 34, 103, 101, 116, 95, 109, 97, 114, 107, 101, 116, 95, 105, 110, 102, + 111, 95, 102, 111, 114, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, 111, 117, + 110, 116, 9, 115, 105, 110, 103, 108, 101, 116, 111, 110, 4, 122, 101, 114, 111, 36, + 114, 101, 103, 105, 115, 116, 101, 114, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, + 99, 111, 117, 110, 116, 95, 103, 101, 110, 101, 114, 105, 99, 95, 98, 97, 115, 101, + 15, 114, 101, 109, 111, 118, 101, 95, 105, 116, 101, 114, 97, 98, 108, 101, 14, 119, + 105, 116, 104, 100, 114, 97, 119, 95, 97, 115, 115, 101, 116, 24, 119, 105, 116, 104, + 100, 114, 97, 119, 95, 97, 115, 115, 101, 116, 115, 95, 105, 110, 116, 101, 114, 110, + 97, 108, 14, 119, 105, 116, 104, 100, 114, 97, 119, 95, 99, 111, 105, 110, 115, 24, + 119, 105, 116, 104, 100, 114, 97, 119, 95, 99, 111, 105, 110, 115, 95, 99, 117, 115, + 116, 111, 100, 105, 97, 110, 19, 119, 105, 116, 104, 100, 114, 97, 119, 95, 99, 111, + 105, 110, 115, 95, 117, 115, 101, 114, 22, 119, 105, 116, 104, 100, 114, 97, 119, 95, + 103, 101, 110, 101, 114, 105, 99, 95, 97, 115, 115, 101, 116, 32, 119, 105, 116, 104, + 100, 114, 97, 119, 95, 103, 101, 110, 101, 114, 105, 99, 95, 97, 115, 115, 101, 116, + 95, 99, 117, 115, 116, 111, 100, 105, 97, 110, 27, 119, 105, 116, 104, 100, 114, 97, + 119, 95, 103, 101, 110, 101, 114, 105, 99, 95, 97, 115, 115, 101, 116, 95, 117, 115, + 101, 114, 21, 119, 105, 116, 104, 100, 114, 97, 119, 95, 116, 111, 95, 99, 111, 105, + 110, 115, 116, 111, 114, 101, 21, 105, 115, 95, 97, 99, 99, 111, 117, 110, 116, 95, + 114, 101, 103, 105, 115, 116, 101, 114, 101, 100, 8, 114, 101, 103, 105, 115, 116, 101, + 114, 7, 100, 101, 112, 111, 115, 105, 116, 6, 109, 97, 114, 107, 101, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 35, 69, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 20, 99, 111, 109, 112, 105, 108, 97, 116, 105, + 111, 110, 95, 109, 101, 116, 97, 100, 97, 116, 97, 9, 0, 3, 50, 46, 48, 3, + 50, 46, 49, 18, 97, 112, 116, 111, 115, 58, 58, 109, 101, 116, 97, 100, 97, 116, + 97, 95, 118, 49, 182, 16, 19, 0, 0, 0, 0, 0, 0, 0, 0, 23, 69, 95, + 69, 88, 73, 83, 84, 83, 95, 77, 65, 82, 75, 69, 84, 95, 65, 67, 67, 79, + 85, 78, 84, 30, 77, 97, 114, 107, 101, 116, 32, 97, 99, 99, 111, 117, 110, 116, + 32, 97, 108, 114, 101, 97, 100, 121, 32, 101, 120, 105, 115, 116, 115, 46, 1, 0, + 0, 0, 0, 0, 0, 0, 24, 69, 95, 85, 78, 82, 69, 71, 73, 83, 84, 69, + 82, 69, 68, 95, 67, 85, 83, 84, 79, 68, 73, 65, 78, 37, 67, 117, 115, 116, + 111, 100, 105, 97, 110, 32, 73, 68, 32, 104, 97, 115, 32, 110, 111, 116, 32, 98, + 101, 101, 110, 32, 114, 101, 103, 105, 115, 116, 101, 114, 101, 100, 46, 2, 0, 0, + 0, 0, 0, 0, 0, 20, 69, 95, 78, 79, 95, 77, 65, 82, 75, 69, 84, 95, + 65, 67, 67, 79, 85, 78, 84, 83, 34, 78, 111, 32, 109, 97, 114, 107, 101, 116, + 32, 97, 99, 99, 111, 117, 110, 116, 115, 32, 114, 101, 115, 111, 117, 114, 99, 101, + 32, 102, 111, 117, 110, 100, 46, 3, 0, 0, 0, 0, 0, 0, 0, 19, 69, 95, + 78, 79, 95, 77, 65, 82, 75, 69, 84, 95, 65, 67, 67, 79, 85, 78, 84, 33, + 78, 111, 32, 109, 97, 114, 107, 101, 116, 32, 97, 99, 99, 111, 117, 110, 116, 32, + 114, 101, 115, 111, 117, 114, 99, 101, 32, 102, 111, 117, 110, 100, 46, 4, 0, 0, + 0, 0, 0, 0, 0, 19, 69, 95, 65, 83, 83, 69, 84, 95, 78, 79, 84, 95, + 73, 78, 95, 80, 65, 73, 82, 45, 65, 115, 115, 101, 116, 32, 116, 121, 112, 101, + 32, 105, 115, 32, 110, 111, 116, 32, 105, 110, 32, 116, 114, 97, 100, 105, 110, 103, + 32, 112, 97, 105, 114, 32, 102, 111, 114, 32, 109, 97, 114, 107, 101, 116, 46, 5, + 0, 0, 0, 0, 0, 0, 0, 32, 69, 95, 68, 69, 80, 79, 83, 73, 84, 95, + 79, 86, 69, 82, 70, 76, 79, 87, 95, 65, 83, 83, 69, 84, 95, 67, 69, 73, + 76, 73, 78, 71, 37, 68, 101, 112, 111, 115, 105, 116, 32, 119, 111, 117, 108, 100, + 32, 111, 118, 101, 114, 102, 108, 111, 119, 32, 97, 115, 115, 101, 116, 32, 99, 101, + 105, 108, 105, 110, 103, 46, 6, 0, 0, 0, 0, 0, 0, 0, 21, 69, 95, 73, + 78, 86, 65, 76, 73, 68, 95, 85, 78, 68, 69, 82, 87, 82, 73, 84, 69, 82, + 46, 85, 110, 100, 101, 114, 119, 114, 105, 116, 101, 114, 32, 105, 115, 32, 110, 111, + 116, 32, 118, 97, 108, 105, 100, 32, 102, 111, 114, 32, 105, 110, 100, 105, 99, 97, + 116, 101, 100, 32, 109, 97, 114, 107, 101, 116, 46, 7, 0, 0, 0, 0, 0, 0, + 0, 31, 69, 95, 87, 73, 84, 72, 68, 82, 65, 87, 95, 84, 79, 79, 95, 76, + 73, 84, 84, 76, 69, 95, 65, 86, 65, 73, 76, 65, 66, 76, 69, 36, 84, 111, + 111, 32, 108, 105, 116, 116, 108, 101, 32, 97, 118, 97, 105, 108, 97, 98, 108, 101, + 32, 102, 111, 114, 32, 119, 105, 116, 104, 100, 114, 97, 119, 97, 108, 46, 8, 0, + 0, 0, 0, 0, 0, 0, 9, 69, 95, 80, 82, 73, 67, 69, 95, 48, 14, 80, + 114, 105, 99, 101, 32, 105, 115, 32, 122, 101, 114, 111, 46, 9, 0, 0, 0, 0, + 0, 0, 0, 16, 69, 95, 80, 82, 73, 67, 69, 95, 84, 79, 79, 95, 72, 73, + 71, 72, 37, 80, 114, 105, 99, 101, 32, 101, 120, 99, 101, 101, 100, 115, 32, 109, + 97, 120, 105, 109, 117, 109, 32, 112, 111, 115, 115, 105, 98, 108, 101, 32, 112, 114, + 105, 99, 101, 46, 11, 0, 0, 0, 0, 0, 0, 0, 16, 69, 95, 84, 73, 67, + 75, 83, 95, 79, 86, 69, 82, 70, 76, 79, 87, 41, 84, 105, 99, 107, 115, 32, + 116, 111, 32, 102, 105, 108, 108, 32, 97, 110, 32, 111, 114, 100, 101, 114, 32, 111, + 118, 101, 114, 102, 108, 111, 119, 115, 32, 97, 32, 96, 117, 54, 52, 96, 46, 12, + 0, 0, 0, 0, 0, 0, 0, 19, 69, 95, 79, 86, 69, 82, 70, 76, 79, 87, + 95, 65, 83, 83, 69, 84, 95, 73, 78, 55, 70, 105, 108, 108, 105, 110, 103, 32, + 111, 114, 100, 101, 114, 32, 119, 111, 117, 108, 100, 32, 111, 118, 101, 114, 102, 108, + 111, 119, 32, 97, 115, 115, 101, 116, 32, 114, 101, 99, 101, 105, 118, 101, 100, 32, + 102, 114, 111, 109, 32, 116, 114, 97, 100, 101, 46, 13, 0, 0, 0, 0, 0, 0, + 0, 22, 69, 95, 78, 79, 84, 95, 69, 78, 79, 85, 71, 72, 95, 65, 83, 83, + 69, 84, 95, 79, 85, 84, 31, 78, 111, 116, 32, 101, 110, 111, 117, 103, 104, 32, + 97, 115, 115, 101, 116, 32, 116, 111, 32, 116, 114, 97, 100, 101, 32, 97, 119, 97, + 121, 46, 14, 0, 0, 0, 0, 0, 0, 0, 24, 69, 95, 67, 72, 65, 78, 71, + 69, 95, 79, 82, 68, 69, 82, 95, 78, 79, 95, 67, 72, 65, 78, 71, 69, 24, + 78, 111, 32, 99, 104, 97, 110, 103, 101, 32, 105, 110, 32, 111, 114, 100, 101, 114, + 32, 115, 105, 122, 101, 46, 15, 0, 0, 0, 0, 0, 0, 0, 25, 69, 95, 73, + 78, 86, 65, 76, 73, 68, 95, 77, 65, 82, 75, 69, 84, 95, 79, 82, 68, 69, + 82, 95, 73, 68, 48, 77, 97, 114, 107, 101, 116, 32, 111, 114, 100, 101, 114, 32, + 73, 68, 32, 109, 105, 115, 109, 97, 116, 99, 104, 32, 119, 105, 116, 104, 32, 117, + 115, 101, 114, 39, 115, 32, 111, 112, 101, 110, 32, 111, 114, 100, 101, 114, 46, 16, + 0, 0, 0, 0, 0, 0, 0, 22, 69, 95, 67, 79, 73, 78, 95, 65, 77, 79, + 85, 78, 84, 95, 77, 73, 83, 77, 65, 84, 67, 72, 49, 77, 105, 115, 109, 97, + 116, 99, 104, 32, 98, 101, 116, 119, 101, 101, 110, 32, 99, 111, 105, 110, 32, 118, + 97, 108, 117, 101, 32, 97, 110, 100, 32, 105, 110, 100, 105, 99, 97, 116, 101, 100, + 32, 97, 109, 111, 117, 110, 116, 46, 17, 0, 0, 0, 0, 0, 0, 0, 21, 69, + 95, 65, 67, 67, 69, 83, 83, 95, 75, 69, 89, 95, 77, 73, 83, 77, 65, 84, + 67, 72, 68, 69, 120, 112, 101, 99, 116, 101, 100, 32, 111, 114, 100, 101, 114, 32, + 97, 99, 99, 101, 115, 115, 32, 107, 101, 121, 32, 100, 111, 101, 115, 32, 110, 111, + 116, 32, 109, 97, 116, 99, 104, 32, 97, 115, 115, 105, 103, 110, 101, 100, 32, 111, + 114, 100, 101, 114, 32, 97, 99, 99, 101, 115, 115, 10, 32, 107, 101, 121, 46, 18, + 0, 0, 0, 0, 0, 0, 0, 28, 69, 95, 67, 79, 73, 78, 95, 84, 89, 80, + 69, 95, 73, 83, 95, 71, 69, 78, 69, 82, 73, 67, 95, 65, 83, 83, 69, 84, + 27, 67, 111, 105, 110, 32, 116, 121, 112, 101, 32, 105, 115, 32, 103, 101, 110, 101, + 114, 105, 99, 32, 97, 115, 115, 101, 116, 46, 19, 0, 0, 0, 0, 0, 0, 0, + 21, 69, 95, 83, 84, 65, 82, 84, 95, 83, 73, 90, 69, 95, 77, 73, 83, 77, + 65, 84, 67, 72, 82, 77, 105, 115, 109, 97, 116, 99, 104, 32, 98, 101, 116, 119, + 101, 101, 110, 32, 101, 120, 112, 101, 99, 116, 101, 100, 32, 115, 105, 122, 101, 32, + 98, 101, 102, 111, 114, 101, 32, 111, 112, 101, 114, 97, 116, 105, 111, 110, 32, 97, + 110, 100, 32, 97, 99, 116, 117, 97, 108, 32, 115, 105, 122, 101, 10, 32, 98, 101, + 102, 111, 114, 101, 32, 111, 112, 101, 114, 97, 116, 105, 111, 110, 46, 0, 23, 7, + 103, 101, 116, 95, 65, 83, 75, 1, 1, 0, 7, 103, 101, 116, 95, 66, 73, 68, + 1, 1, 0, 13, 103, 101, 116, 95, 109, 97, 114, 107, 101, 116, 95, 105, 100, 1, + 1, 0, 16, 103, 101, 116, 95, 78, 79, 95, 67, 85, 83, 84, 79, 68, 73, 65, + 78, 1, 1, 0, 16, 103, 101, 116, 95, 99, 117, 115, 116, 111, 100, 105, 97, 110, + 95, 105, 100, 1, 1, 0, 18, 103, 101, 116, 95, 109, 97, 114, 107, 101, 116, 95, + 97, 99, 99, 111, 117, 110, 116, 1, 1, 0, 18, 104, 97, 115, 95, 109, 97, 114, + 107, 101, 116, 95, 97, 99, 99, 111, 117, 110, 116, 1, 1, 0, 19, 103, 101, 116, + 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, 111, 117, 110, 116, 115, 1, 1, + 0, 21, 103, 101, 116, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, 111, 117, + 110, 116, 95, 105, 100, 1, 1, 0, 26, 103, 101, 116, 95, 67, 65, 78, 67, 69, + 76, 95, 82, 69, 65, 83, 79, 78, 95, 69, 86, 73, 67, 84, 73, 79, 78, 1, + 1, 0, 31, 103, 101, 116, 95, 67, 65, 78, 67, 69, 76, 95, 82, 69, 65, 83, + 79, 78, 95, 77, 65, 78, 85, 65, 76, 95, 67, 65, 78, 67, 69, 76, 1, 1, + 0, 31, 104, 97, 115, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, 111, 117, + 110, 116, 95, 98, 121, 95, 109, 97, 114, 107, 101, 116, 95, 105, 100, 1, 1, 0, + 34, 103, 101, 116, 95, 67, 65, 78, 67, 69, 76, 95, 82, 69, 65, 83, 79, 78, + 95, 77, 65, 88, 95, 81, 85, 79, 84, 69, 95, 84, 82, 65, 68, 69, 68, 1, + 1, 0, 34, 103, 101, 116, 95, 67, 65, 78, 67, 69, 76, 95, 82, 69, 65, 83, + 79, 78, 95, 83, 69, 76, 70, 95, 77, 65, 84, 67, 72, 95, 77, 65, 75, 69, + 82, 1, 1, 0, 34, 103, 101, 116, 95, 67, 65, 78, 67, 69, 76, 95, 82, 69, + 65, 83, 79, 78, 95, 83, 69, 76, 70, 95, 77, 65, 84, 67, 72, 95, 84, 65, + 75, 69, 82, 1, 1, 0, 35, 103, 101, 116, 95, 97, 108, 108, 95, 109, 97, 114, + 107, 101, 116, 95, 97, 99, 99, 111, 117, 110, 116, 95, 105, 100, 115, 95, 102, 111, + 114, 95, 117, 115, 101, 114, 1, 1, 0, 37, 103, 101, 116, 95, 67, 65, 78, 67, + 69, 76, 95, 82, 69, 65, 83, 79, 78, 95, 73, 77, 77, 69, 68, 73, 65, 84, + 69, 95, 79, 82, 95, 67, 65, 78, 67, 69, 76, 1, 1, 0, 38, 103, 101, 116, + 95, 67, 65, 78, 67, 69, 76, 95, 82, 69, 65, 83, 79, 78, 95, 78, 79, 84, + 95, 69, 78, 79, 85, 71, 72, 95, 76, 73, 81, 85, 73, 68, 73, 84, 89, 1, + 1, 0, 38, 103, 101, 116, 95, 67, 65, 78, 67, 69, 76, 95, 82, 69, 65, 83, + 79, 78, 95, 86, 73, 79, 76, 65, 84, 69, 68, 95, 76, 73, 77, 73, 84, 95, + 80, 82, 73, 67, 69, 1, 1, 0, 39, 103, 101, 116, 95, 67, 65, 78, 67, 69, + 76, 95, 82, 69, 65, 83, 79, 78, 95, 84, 79, 79, 95, 83, 77, 65, 76, 76, + 95, 84, 79, 95, 70, 73, 76, 76, 95, 76, 79, 84, 1, 1, 0, 39, 104, 97, + 115, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, 111, 117, 110, 116, 95, 98, + 121, 95, 109, 97, 114, 107, 101, 116, 95, 97, 99, 99, 111, 117, 110, 116, 95, 105, + 100, 1, 1, 0, 40, 103, 101, 116, 95, 97, 108, 108, 95, 109, 97, 114, 107, 101, + 116, 95, 97, 99, 99, 111, 117, 110, 116, 95, 105, 100, 115, 95, 102, 111, 114, 95, + 109, 97, 114, 107, 101, 116, 95, 105, 100, 1, 1, 0, 40, 103, 101, 116, 95, 109, + 97, 114, 107, 101, 116, 95, 101, 118, 101, 110, 116, 95, 104, 97, 110, 100, 108, 101, + 95, 99, 114, 101, 97, 116, 105, 111, 110, 95, 110, 117, 109, 98, 101, 114, 115, 1, + 1, 0, 0, 2, 5, 2, 3, 3, 4, 0, 5, 4, 3, 5, 2, 1, 2, 6, + 2, 3, 3, 4, 0, 5, 4, 3, 7, 1, 8, 3, 2, 2, 1, 10, 11, 3, + 2, 4, 11, 4, 1, 9, 0, 5, 2, 12, 2, 3, 16, 3, 17, 3, 18, 1, + 19, 5, 20, 3, 21, 4, 22, 5, 23, 3, 24, 4, 25, 3, 26, 3, 6, 2, + 17, 28, 8, 7, 31, 8, 8, 34, 8, 7, 35, 3, 36, 3, 37, 3, 38, 3, + 39, 11, 3, 2, 3, 8, 9, 41, 11, 3, 2, 3, 8, 9, 42, 3, 43, 3, + 44, 3, 45, 3, 46, 3, 47, 3, 48, 3, 49, 3, 10, 2, 10, 2, 3, 4, + 3, 39, 10, 8, 9, 41, 10, 8, 9, 44, 3, 45, 3, 46, 3, 47, 3, 48, + 3, 49, 3, 11, 2, 2, 10, 11, 12, 2, 4, 8, 6, 54, 11, 3, 2, 3, + 10, 3, 13, 2, 5, 56, 3, 57, 3, 58, 3, 59, 3, 60, 3, 14, 2, 1, + 10, 11, 12, 2, 4, 8, 15, 15, 2, 5, 63, 11, 16, 1, 8, 0, 66, 11, + 16, 1, 8, 1, 67, 11, 16, 1, 8, 5, 68, 11, 16, 1, 8, 17, 70, 11, + 16, 1, 8, 18, 9, 2, 2, 72, 4, 16, 3, 17, 2, 11, 2, 3, 0, 5, + 4, 3, 73, 5, 7, 1, 16, 3, 17, 3, 74, 2, 75, 2, 76, 3, 3, 4, + 18, 2, 8, 2, 3, 0, 5, 4, 3, 73, 5, 77, 1, 16, 3, 75, 2, 3, + 4, 2, 2, 2, 37, 0, 1, 0, 2, 2, 6, 9, 26, 56, 0, 56, 1, 33, + 3, 24, 11, 0, 12, 4, 11, 1, 12, 5, 11, 2, 12, 6, 14, 3, 56, 2, + 11, 3, 56, 3, 12, 8, 12, 9, 11, 4, 11, 5, 11, 6, 11, 9, 11, 8, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 56, 4, 2, 6, 18, 0, 0, 0, 0, + 0, 0, 0, 39, 5, 1, 0, 0, 1, 2, 6, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 6, 1, 0, 0, 10, 5, 11, 0, 50, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 2, 7, 1, 0, 0, 11, 5, + 11, 0, 49, 64, 48, 52, 2, 8, 3, 0, 2, 6, 8, 23, 184, 1, 10, 0, + 42, 6, 15, 0, 10, 1, 53, 49, 64, 47, 10, 2, 53, 27, 12, 9, 10, 9, + 56, 5, 12, 10, 11, 3, 8, 33, 4, 159, 1, 10, 10, 15, 1, 12, 12, 10, + 10, 15, 2, 12, 13, 10, 10, 15, 3, 12, 14, 10, 10, 15, 4, 12, 15, 10, + 10, 16, 5, 20, 12, 16, 11, 5, 11, 16, 24, 12, 16, 11, 10, 16, 6, 20, + 12, 17, 11, 12, 10, 6, 56, 6, 12, 18, 10, 18, 16, 7, 20, 12, 19, 10, + 19, 11, 4, 33, 4, 149, 1, 10, 7, 50, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 132, 1, 10, 18, 16, 8, 20, 12, + 7, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10, 18, 15, 8, 21, 10, 13, 20, 11, 18, 15, 7, 21, 11, 6, 11, 13, 21, + 10, 19, 11, 17, 24, 12, 21, 10, 15, 20, 11, 21, 22, 11, 15, 21, 11, 19, + 11, 16, 24, 12, 21, 10, 14, 20, 11, 21, 23, 11, 14, 21, 10, 8, 49, 0, + 33, 3, 129, 1, 10, 0, 41, 8, 12, 3, 11, 3, 3, 104, 5, 124, 10, 0, + 42, 8, 15, 9, 12, 23, 10, 23, 46, 10, 9, 56, 7, 4, 126, 11, 23, 11, + 9, 56, 8, 15, 10, 11, 1, 10, 7, 11, 0, 11, 2, 11, 8, 18, 0, 56, + 9, 11, 7, 2, 11, 23, 1, 5, 124, 9, 12, 3, 5, 101, 10, 18, 16, 8, + 20, 10, 7, 33, 4, 139, 1, 5, 62, 11, 13, 1, 11, 14, 1, 11, 15, 1, + 11, 18, 1, 6, 15, 0, 0, 0, 0, 0, 0, 0, 39, 11, 13, 1, 11, 14, + 1, 11, 15, 1, 11, 18, 1, 6, 19, 0, 0, 0, 0, 0, 0, 0, 39, 10, + 10, 15, 11, 12, 12, 10, 10, 15, 12, 12, 13, 10, 10, 15, 13, 12, 14, 10, + 10, 15, 14, 12, 15, 10, 10, 16, 6, 20, 12, 16, 11, 10, 16, 5, 20, 12, + 19, 11, 5, 11, 19, 24, 12, 17, 5, 42, 13, 3, 0, 2, 6, 8, 29, 84, + 10, 0, 42, 6, 15, 0, 10, 1, 53, 49, 64, 47, 10, 2, 53, 27, 12, 9, + 10, 9, 56, 5, 12, 10, 10, 3, 8, 33, 4, 80, 11, 10, 16, 1, 12, 12, + 11, 12, 10, 7, 56, 10, 16, 7, 20, 10, 5, 34, 4, 78, 10, 0, 10, 1, + 10, 2, 10, 3, 11, 4, 10, 6, 10, 7, 10, 8, 49, 0, 17, 8, 1, 10, + 0, 10, 1, 10, 2, 10, 3, 10, 5, 11, 6, 10, 8, 11, 7, 17, 15, 10, + 0, 41, 8, 3, 53, 5, 74, 10, 0, 42, 8, 15, 9, 12, 14, 10, 14, 46, + 10, 9, 56, 7, 4, 75, 11, 14, 11, 9, 56, 8, 15, 15, 11, 1, 11, 8, + 11, 0, 11, 2, 11, 3, 11, 5, 18, 1, 56, 11, 2, 11, 14, 1, 5, 74, + 6, 14, 0, 0, 0, 0, 0, 0, 0, 39, 11, 10, 16, 11, 12, 12, 5, 21, + 16, 3, 0, 0, 1, 7, 11, 0, 11, 1, 11, 2, 11, 3, 11, 4, 18, 0, + 2, 17, 3, 0, 0, 1, 14, 11, 0, 11, 1, 11, 2, 11, 3, 11, 4, 11, + 5, 11, 6, 11, 7, 11, 8, 11, 9, 11, 10, 11, 11, 18, 3, 2, 4, 0, + 0, 2, 2, 6, 35, 153, 1, 10, 0, 41, 6, 4, 151, 1, 10, 0, 42, 6, + 15, 0, 12, 6, 11, 1, 53, 49, 64, 47, 11, 2, 53, 27, 12, 7, 10, 6, + 46, 10, 7, 56, 12, 4, 147, 1, 11, 6, 10, 7, 56, 5, 12, 8, 56, 0, + 12, 9, 10, 8, 16, 16, 20, 12, 10, 10, 9, 11, 10, 33, 4, 125, 10, 8, + 15, 17, 12, 11, 10, 8, 15, 4, 12, 12, 10, 8, 15, 13, 12, 13, 10, 13, + 20, 53, 10, 3, 53, 22, 50, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 37, 4, 115, 10, 11, 20, 10, 3, 22, 11, 11, 21, 10, + 12, 20, 10, 3, 22, 11, 12, 21, 10, 13, 20, 10, 3, 22, 11, 13, 21, 11, + 9, 12, 10, 56, 1, 12, 14, 11, 10, 11, 14, 33, 4, 91, 11, 8, 16, 18, + 20, 12, 15, 11, 5, 11, 15, 33, 4, 89, 11, 4, 56, 13, 2, 6, 6, 0, + 0, 0, 0, 0, 0, 0, 39, 11, 8, 1, 11, 4, 56, 14, 12, 16, 11, 3, + 12, 15, 14, 16, 56, 2, 12, 17, 11, 15, 11, 17, 33, 4, 113, 11, 0, 60, + 0, 54, 0, 11, 7, 56, 15, 11, 16, 56, 16, 5, 88, 6, 16, 0, 0, 0, + 0, 0, 0, 0, 39, 11, 8, 1, 11, 11, 1, 11, 12, 1, 11, 13, 1, 6, + 5, 0, 0, 0, 0, 0, 0, 0, 39, 10, 8, 16, 20, 20, 12, 10, 10, 9, + 11, 10, 33, 4, 143, 1, 10, 8, 15, 21, 12, 11, 10, 8, 15, 14, 12, 12, + 10, 8, 15, 3, 12, 13, 5, 43, 11, 8, 1, 6, 4, 0, 0, 0, 0, 0, + 0, 0, 39, 11, 6, 1, 6, 3, 0, 0, 0, 0, 0, 0, 0, 39, 6, 2, + 0, 0, 0, 0, 0, 0, 0, 39, 21, 3, 0, 2, 2, 6, 1, 13, 10, 0, + 10, 1, 10, 2, 11, 3, 11, 4, 11, 6, 56, 4, 11, 0, 11, 1, 11, 2, + 11, 5, 56, 17, 2, 22, 1, 4, 2, 2, 6, 42, 11, 10, 0, 17, 23, 11, + 0, 11, 3, 56, 18, 12, 4, 11, 1, 11, 2, 11, 4, 56, 19, 2, 25, 1, + 0, 2, 2, 6, 46, 13, 56, 20, 11, 4, 17, 27, 12, 5, 12, 6, 11, 0, + 11, 1, 11, 2, 11, 3, 11, 6, 11, 5, 56, 21, 2, 28, 3, 0, 1, 8, + 51, 188, 1, 10, 1, 41, 8, 4, 185, 1, 10, 1, 42, 8, 15, 9, 12, 13, + 10, 0, 53, 49, 64, 47, 10, 2, 53, 27, 12, 14, 10, 13, 46, 10, 14, 56, + 7, 4, 180, 1, 11, 13, 11, 14, 56, 8, 12, 15, 10, 15, 15, 22, 10, 0, + 10, 1, 10, 2, 11, 3, 11, 4, 11, 5, 11, 6, 11, 7, 11, 8, 11, 9, + 10, 10, 18, 11, 56, 22, 10, 11, 12, 16, 6, 0, 0, 0, 0, 0, 0, 0, + 0, 12, 17, 10, 16, 65, 32, 12, 18, 10, 17, 10, 18, 35, 4, 70, 10, 16, + 10, 17, 66, 32, 20, 12, 20, 13, 20, 15, 23, 12, 21, 10, 10, 11, 21, 21, + 10, 15, 15, 24, 11, 20, 56, 23, 11, 17, 6, 1, 0, 0, 0, 0, 0, 0, + 0, 22, 12, 17, 5, 46, 11, 16, 1, 10, 12, 56, 24, 4, 175, 1, 11, 0, + 12, 17, 10, 10, 12, 23, 11, 1, 12, 24, 11, 2, 12, 18, 11, 12, 56, 25, + 20, 12, 25, 11, 17, 11, 23, 11, 24, 11, 18, 11, 25, 18, 0, 12, 26, 11, + 15, 15, 10, 11, 26, 56, 9, 11, 11, 12, 16, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 12, 17, 10, 16, 65, 32, 12, 18, 10, 17, 10, 18, 35, 4, 172, 1, + 10, 16, 10, 17, 66, 32, 20, 12, 27, 13, 27, 15, 23, 12, 21, 10, 10, 11, + 21, 21, 14, 27, 12, 19, 10, 19, 16, 25, 20, 12, 24, 10, 24, 41, 8, 4, + 169, 1, 11, 24, 42, 8, 15, 9, 12, 28, 10, 19, 16, 26, 20, 10, 19, 16, + 27, 20, 12, 29, 53, 49, 64, 47, 11, 29, 53, 27, 12, 23, 10, 28, 46, 10, + 23, 56, 7, 4, 164, 1, 11, 28, 11, 23, 56, 8, 15, 24, 11, 19, 20, 56, + 23, 11, 17, 6, 1, 0, 0, 0, 0, 0, 0, 0, 22, 12, 17, 5, 105, 11, + 19, 1, 11, 28, 1, 5, 159, 1, 11, 19, 1, 5, 159, 1, 11, 16, 1, 2, + 11, 12, 1, 11, 15, 1, 5, 98, 11, 12, 1, 11, 13, 1, 5, 98, 11, 12, + 1, 5, 98, 31, 3, 0, 1, 8, 54, 170, 1, 10, 1, 41, 8, 4, 167, 1, + 10, 1, 42, 8, 15, 9, 12, 10, 10, 0, 53, 49, 64, 47, 10, 2, 53, 27, + 12, 11, 10, 10, 46, 10, 11, 56, 7, 4, 162, 1, 11, 10, 11, 11, 56, 8, + 12, 12, 10, 12, 15, 28, 10, 0, 10, 1, 10, 2, 11, 3, 11, 4, 11, 5, + 11, 6, 10, 7, 18, 12, 56, 26, 10, 8, 12, 13, 6, 0, 0, 0, 0, 0, + 0, 0, 0, 12, 14, 10, 13, 65, 32, 12, 15, 10, 14, 10, 15, 35, 4, 61, + 10, 13, 10, 14, 66, 32, 12, 16, 10, 12, 15, 24, 11, 16, 20, 56, 23, 11, + 14, 6, 1, 0, 0, 0, 0, 0, 0, 0, 22, 12, 14, 5, 43, 11, 13, 1, + 10, 9, 56, 24, 4, 157, 1, 11, 0, 12, 14, 11, 7, 12, 18, 11, 1, 12, + 19, 11, 2, 12, 15, 11, 9, 56, 25, 20, 12, 20, 11, 14, 11, 18, 11, 19, + 11, 15, 11, 20, 18, 0, 12, 21, 11, 12, 15, 10, 11, 21, 56, 9, 11, 8, + 12, 13, 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, 14, 10, 13, 65, 32, 12, + 15, 10, 14, 10, 15, 35, 4, 154, 1, 10, 13, 10, 14, 66, 32, 12, 16, 10, + 16, 16, 25, 20, 12, 19, 10, 19, 41, 8, 4, 151, 1, 11, 19, 42, 8, 15, + 9, 12, 22, 10, 16, 16, 26, 20, 10, 16, 16, 27, 20, 12, 23, 53, 49, 64, + 47, 11, 23, 53, 27, 12, 18, 10, 22, 46, 10, 18, 56, 7, 4, 146, 1, 11, + 22, 11, 18, 56, 8, 15, 24, 11, 16, 20, 56, 23, 11, 14, 6, 1, 0, 0, + 0, 0, 0, 0, 0, 22, 12, 14, 5, 96, 11, 16, 1, 11, 22, 1, 5, 141, + 1, 11, 16, 1, 5, 141, 1, 11, 13, 1, 2, 11, 9, 1, 11, 12, 1, 5, + 89, 11, 9, 1, 11, 10, 1, 5, 89, 11, 9, 1, 5, 89, 32, 3, 0, 1, + 8, 56, 66, 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, 1, 10, 0, 65, 32, + 12, 2, 10, 1, 10, 2, 35, 4, 63, 10, 0, 10, 1, 66, 32, 12, 3, 10, + 3, 16, 25, 20, 12, 4, 10, 4, 41, 8, 4, 60, 11, 4, 42, 8, 15, 9, + 12, 5, 10, 3, 16, 26, 20, 10, 3, 16, 27, 20, 12, 6, 53, 49, 64, 47, + 11, 6, 53, 27, 12, 8, 10, 5, 46, 10, 8, 56, 7, 4, 55, 11, 5, 11, + 8, 56, 8, 15, 24, 11, 3, 20, 56, 23, 11, 1, 6, 1, 0, 0, 0, 0, + 0, 0, 0, 22, 12, 1, 5, 5, 11, 3, 1, 11, 5, 1, 5, 50, 11, 3, + 1, 5, 50, 11, 0, 1, 2, 33, 3, 0, 2, 2, 6, 63, 209, 1, 10, 0, + 42, 6, 15, 0, 11, 1, 53, 49, 64, 47, 11, 2, 53, 27, 12, 12, 10, 12, + 56, 5, 12, 13, 10, 3, 8, 33, 4, 186, 1, 10, 13, 15, 1, 12, 15, 10, + 13, 15, 2, 12, 16, 10, 11, 12, 1, 10, 13, 15, 21, 12, 17, 10, 13, 15, + 14, 12, 18, 10, 10, 12, 2, 10, 13, 15, 17, 12, 19, 11, 13, 15, 13, 12, + 20, 11, 15, 10, 4, 56, 6, 12, 21, 10, 21, 16, 8, 20, 12, 22, 10, 21, + 16, 7, 20, 11, 5, 33, 4, 172, 1, 11, 7, 4, 161, 1, 50, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 21, 15, 8, 21, + 10, 16, 20, 11, 21, 15, 7, 21, 11, 4, 11, 16, 21, 10, 17, 20, 10, 1, + 22, 11, 17, 21, 10, 18, 20, 11, 1, 22, 11, 18, 21, 10, 19, 20, 10, 2, + 23, 11, 19, 21, 10, 20, 20, 11, 2, 23, 11, 20, 21, 14, 8, 56, 27, 3, + 96, 5, 118, 10, 0, 60, 0, 54, 0, 10, 12, 56, 15, 12, 23, 13, 8, 56, + 28, 12, 24, 10, 3, 8, 33, 4, 153, 1, 11, 24, 12, 25, 11, 23, 11, 10, + 56, 29, 12, 26, 11, 25, 11, 26, 56, 16, 11, 0, 60, 1, 54, 1, 11, 12, + 56, 30, 12, 27, 11, 3, 8, 33, 4, 147, 1, 11, 27, 12, 28, 13, 9, 11, + 11, 56, 31, 12, 29, 11, 28, 11, 29, 56, 32, 11, 8, 12, 30, 11, 9, 12, + 29, 11, 22, 12, 31, 11, 30, 11, 29, 11, 31, 2, 13, 9, 11, 27, 11, 11, + 56, 31, 56, 32, 5, 137, 1, 11, 24, 11, 10, 56, 29, 12, 26, 11, 23, 11, + 26, 56, 16, 5, 118, 11, 16, 1, 10, 21, 16, 7, 20, 11, 6, 23, 11, 21, + 15, 7, 21, 5, 68, 11, 16, 1, 11, 17, 1, 11, 18, 1, 11, 19, 1, 11, + 20, 1, 11, 21, 1, 6, 19, 0, 0, 0, 0, 0, 0, 0, 39, 10, 13, 15, + 11, 12, 15, 10, 13, 15, 12, 12, 16, 10, 10, 12, 1, 10, 13, 15, 17, 12, + 17, 10, 13, 15, 4, 12, 18, 10, 11, 12, 2, 10, 13, 15, 21, 12, 19, 11, + 13, 15, 3, 12, 20, 5, 40, 36, 1, 0, 0, 1, 2, 8, 2, 37, 1, 0, + 0, 1, 2, 9, 2, 38, 1, 0, 0, 1, 2, 49, 1, 2, 39, 1, 0, 0, + 1, 2, 49, 2, 2, 40, 1, 0, 0, 1, 2, 49, 3, 2, 41, 1, 0, 0, + 1, 2, 49, 4, 2, 42, 1, 0, 0, 1, 2, 49, 5, 2, 43, 1, 0, 0, + 1, 2, 49, 6, 2, 44, 1, 0, 0, 1, 2, 49, 7, 2, 45, 1, 0, 0, + 1, 2, 49, 8, 2, 46, 1, 0, 0, 1, 2, 49, 9, 2, 47, 3, 0, 1, + 6, 67, 78, 10, 0, 41, 6, 4, 76, 11, 0, 43, 6, 16, 0, 12, 4, 11, + 1, 53, 49, 64, 47, 11, 2, 53, 27, 12, 5, 10, 4, 10, 5, 56, 12, 4, + 72, 11, 4, 11, 5, 56, 33, 12, 6, 11, 3, 8, 33, 4, 68, 11, 6, 16, + 1, 12, 8, 64, 10, 0, 0, 0, 0, 0, 0, 0, 0, 12, 9, 6, 1, 0, + 0, 0, 0, 0, 0, 0, 12, 10, 10, 8, 56, 34, 12, 11, 10, 10, 10, 11, + 37, 4, 64, 10, 8, 10, 10, 56, 10, 12, 12, 10, 12, 16, 8, 20, 50, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 4, 61, + 13, 9, 11, 12, 16, 8, 20, 68, 10, 11, 10, 6, 1, 0, 0, 0, 0, 0, + 0, 0, 22, 12, 10, 5, 37, 11, 12, 1, 5, 56, 11, 8, 1, 11, 9, 2, + 11, 6, 16, 11, 12, 8, 5, 30, 11, 4, 1, 6, 3, 0, 0, 0, 0, 0, + 0, 0, 39, 6, 2, 0, 0, 0, 0, 0, 0, 0, 39, 50, 1, 0, 1, 6, + 70, 59, 64, 10, 0, 0, 0, 0, 0, 0, 0, 0, 12, 2, 10, 0, 41, 6, + 3, 7, 11, 2, 2, 11, 0, 43, 6, 16, 29, 12, 4, 10, 4, 10, 1, 56, + 35, 3, 19, 11, 4, 1, 11, 2, 2, 11, 4, 10, 1, 56, 36, 12, 5, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 12, 6, 10, 5, 65, 6, 12, 7, 10, 6, + 10, 7, 35, 4, 55, 10, 5, 10, 6, 66, 6, 20, 10, 1, 53, 49, 64, 47, + 12, 8, 53, 12, 10, 11, 8, 11, 10, 27, 12, 8, 13, 2, 11, 8, 68, 10, + 11, 6, 6, 1, 0, 0, 0, 0, 0, 0, 0, 22, 12, 6, 5, 28, 11, 5, + 1, 11, 2, 2, 52, 1, 0, 1, 6, 72, 68, 64, 10, 0, 0, 0, 0, 0, + 0, 0, 0, 12, 1, 10, 0, 41, 6, 3, 7, 11, 1, 2, 11, 0, 43, 6, + 16, 29, 12, 3, 10, 3, 56, 37, 12, 4, 14, 4, 56, 38, 4, 64, 14, 4, + 56, 39, 20, 12, 5, 10, 3, 10, 5, 56, 40, 12, 6, 1, 12, 7, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 12, 8, 10, 7, 65, 6, 12, 9, 10, 8, 10, + 9, 35, 4, 59, 10, 7, 10, 8, 66, 6, 20, 10, 5, 53, 49, 64, 47, 12, + 10, 53, 12, 12, 11, 10, 11, 12, 27, 12, 10, 13, 1, 11, 10, 68, 10, 11, + 8, 6, 1, 0, 0, 0, 0, 0, 0, 0, 22, 12, 8, 5, 32, 11, 7, 1, + 11, 6, 12, 4, 5, 14, 11, 3, 1, 11, 1, 2, 55, 1, 0, 1, 6, 6, + 8, 11, 2, 17, 56, 12, 3, 11, 0, 11, 1, 11, 3, 17, 57, 2, 57, 3, + 0, 1, 6, 77, 48, 10, 0, 41, 6, 4, 46, 11, 0, 43, 6, 16, 0, 12, + 3, 11, 1, 53, 49, 64, 47, 11, 2, 53, 27, 12, 4, 10, 3, 10, 4, 56, + 12, 4, 42, 11, 3, 11, 4, 56, 33, 12, 5, 10, 5, 16, 17, 20, 10, 5, + 16, 4, 20, 10, 5, 16, 13, 20, 10, 5, 16, 21, 20, 10, 5, 16, 14, 20, + 11, 5, 16, 3, 20, 2, 11, 3, 1, 6, 3, 0, 0, 0, 0, 0, 0, 0, + 39, 6, 2, 0, 0, 0, 0, 0, 0, 0, 39, 58, 1, 0, 1, 6, 78, 6, + 11, 0, 17, 23, 11, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 17, 57, 2, + 59, 0, 0, 1, 6, 84, 64, 10, 1, 10, 2, 17, 60, 12, 3, 10, 0, 10, + 3, 17, 61, 4, 62, 11, 0, 42, 6, 15, 0, 11, 3, 56, 5, 12, 4, 11, + 1, 12, 5, 11, 2, 12, 6, 10, 4, 15, 1, 17, 62, 10, 4, 15, 11, 17, + 62, 10, 4, 16, 17, 20, 10, 4, 16, 4, 20, 10, 4, 16, 13, 20, 10, 4, + 16, 21, 20, 10, 4, 16, 14, 20, 11, 4, 16, 3, 20, 12, 7, 12, 8, 12, + 9, 12, 10, 12, 11, 12, 12, 12, 13, 12, 14, 11, 5, 11, 6, 11, 14, 11, + 13, 11, 12, 11, 11, 11, 10, 11, 9, 11, 8, 11, 7, 18, 5, 2, 6, 3, + 0, 0, 0, 0, 0, 0, 0, 39, 60, 1, 0, 0, 1, 8, 11, 0, 53, 49, + 64, 47, 11, 1, 53, 27, 2, 63, 0, 0, 1, 6, 77, 51, 10, 0, 41, 6, + 4, 49, 11, 0, 43, 6, 16, 0, 12, 3, 11, 1, 53, 49, 64, 47, 11, 2, + 53, 27, 12, 4, 10, 3, 10, 4, 56, 12, 4, 45, 11, 3, 11, 4, 56, 33, + 12, 5, 10, 5, 16, 16, 20, 10, 5, 16, 30, 20, 10, 5, 16, 20, 20, 10, + 5, 16, 6, 20, 10, 5, 16, 5, 20, 10, 5, 16, 31, 20, 11, 5, 16, 18, + 20, 2, 11, 3, 1, 6, 3, 0, 0, 0, 0, 0, 0, 0, 39, 6, 2, 0, + 0, 0, 0, 0, 0, 0, 39, 64, 1, 0, 1, 6, 6, 8, 11, 2, 17, 56, + 12, 3, 11, 0, 11, 1, 11, 3, 17, 63, 2, 65, 1, 0, 1, 6, 78, 6, + 11, 0, 17, 23, 11, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 17, 63, 2, + 66, 0, 0, 1, 6, 89, 51, 10, 0, 17, 52, 12, 1, 64, 79, 0, 0, 0, + 0, 0, 0, 0, 0, 12, 2, 14, 1, 56, 41, 4, 10, 11, 2, 2, 11, 1, + 12, 4, 13, 4, 56, 42, 11, 4, 12, 5, 14, 5, 65, 10, 12, 6, 10, 6, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 36, 4, 47, 13, 5, 69, 10, 12, 8, + 10, 8, 17, 7, 11, 8, 17, 6, 13, 2, 12, 9, 12, 10, 12, 7, 10, 0, + 11, 7, 11, 10, 17, 59, 12, 11, 11, 9, 11, 11, 68, 79, 11, 6, 6, 1, + 0, 0, 0, 0, 0, 0, 0, 23, 12, 6, 5, 19, 11, 5, 70, 10, 0, 0, + 0, 0, 0, 0, 0, 0, 11, 2, 2, 69, 0, 0, 1, 8, 94, 48, 10, 0, + 41, 8, 3, 5, 56, 43, 2, 11, 0, 43, 8, 16, 9, 12, 3, 11, 1, 11, + 2, 17, 60, 12, 4, 10, 3, 10, 4, 56, 7, 3, 21, 11, 3, 1, 56, 43, + 2, 11, 3, 11, 4, 56, 44, 12, 5, 10, 5, 16, 10, 56, 45, 17, 71, 10, + 5, 16, 15, 56, 46, 17, 71, 10, 5, 16, 24, 56, 47, 17, 71, 10, 5, 16, + 22, 56, 48, 17, 71, 11, 5, 16, 28, 56, 49, 17, 71, 18, 7, 56, 50, 2, + 72, 3, 0, 1, 6, 95, 66, 10, 0, 41, 6, 4, 64, 11, 0, 43, 6, 16, + 0, 12, 4, 11, 1, 53, 49, 64, 47, 11, 2, 53, 27, 12, 5, 10, 4, 10, + 5, 56, 12, 4, 60, 11, 4, 11, 5, 56, 33, 12, 6, 11, 3, 8, 33, 4, + 53, 10, 6, 16, 1, 12, 8, 11, 6, 16, 2, 12, 9, 10, 9, 20, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 4, 47, 11, 9, 1, 11, 8, 56, 34, 6, + 1, 0, 0, 0, 0, 0, 0, 0, 22, 12, 10, 11, 10, 2, 11, 8, 1, 11, + 9, 20, 12, 10, 5, 45, 10, 6, 16, 11, 12, 8, 11, 6, 16, 12, 12, 9, + 5, 33, 11, 4, 1, 6, 3, 0, 0, 0, 0, 0, 0, 0, 39, 6, 2, 0, + 0, 0, 0, 0, 0, 0, 39, 73, 3, 0, 1, 6, 98, 42, 11, 1, 11, 2, + 17, 60, 12, 5, 10, 0, 10, 5, 17, 61, 3, 10, 56, 51, 2, 11, 0, 43, + 6, 16, 0, 11, 5, 56, 33, 12, 6, 11, 3, 8, 33, 4, 38, 11, 6, 16, + 1, 12, 8, 10, 8, 10, 4, 56, 52, 3, 31, 11, 8, 1, 56, 51, 2, 11, + 8, 11, 4, 56, 10, 16, 8, 20, 56, 53, 2, 11, 6, 16, 11, 12, 8, 5, + 23, 74, 1, 0, 1, 6, 10, 8, 11, 1, 11, 2, 17, 60, 12, 3, 11, 0, + 11, 3, 17, 61, 2, 61, 1, 0, 1, 6, 1, 11, 10, 0, 41, 6, 3, 5, + 9, 2, 11, 0, 43, 6, 16, 0, 11, 1, 56, 12, 2, 75, 1, 0, 1, 6, + 1, 11, 10, 0, 41, 6, 3, 5, 9, 2, 11, 0, 43, 6, 16, 29, 11, 1, + 56, 35, 2, 76, 1, 4, 2, 6, 8, 103, 64, 10, 0, 17, 23, 12, 3, 10, + 3, 10, 1, 10, 2, 17, 74, 4, 60, 10, 0, 17, 23, 41, 8, 4, 13, 5, + 21, 10, 0, 12, 4, 56, 54, 18, 8, 12, 5, 11, 4, 11, 5, 45, 8, 11, + 3, 42, 8, 15, 9, 12, 6, 11, 1, 53, 49, 64, 47, 11, 2, 53, 27, 12, + 7, 10, 6, 46, 10, 7, 56, 7, 3, 55, 10, 0, 56, 55, 10, 0, 56, 56, + 10, 0, 56, 57, 10, 0, 56, 58, 11, 0, 56, 59, 18, 9, 12, 8, 11, 6, + 11, 7, 11, 8, 56, 60, 2, 11, 0, 1, 11, 6, 1, 5, 54, 11, 0, 1, + 6, 3, 0, 0, 0, 0, 0, 0, 0, 39, 15, 3, 0, 1, 6, 105, 199, 1, + 10, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 36, 4, 197, 1, 10, 5, 6, + 255, 255, 255, 255, 0, 0, 0, 0, 37, 4, 195, 1, 11, 0, 42, 6, 15, 0, + 11, 1, 53, 49, 64, 47, 11, 2, 53, 27, 56, 5, 12, 9, 10, 4, 53, 10, + 9, 16, 6, 20, 53, 24, 12, 10, 10, 4, 53, 11, 5, 53, 24, 12, 11, 10, + 11, 50, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 37, 4, 191, 1, 10, 9, 16, 5, 20, 53, 12, 12, 11, 11, 11, 12, 24, 12, + 12, 11, 3, 8, 33, 4, 174, 1, 10, 9, 15, 1, 12, 14, 10, 9, 15, 2, + 12, 15, 10, 9, 15, 3, 12, 16, 11, 9, 15, 4, 12, 17, 11, 12, 12, 18, + 11, 10, 12, 19, 10, 16, 20, 53, 12, 20, 10, 18, 11, 20, 22, 50, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 37, 4, 164, 1, + 10, 17, 20, 53, 12, 20, 10, 19, 11, 20, 37, 4, 154, 1, 10, 16, 20, 11, + 18, 52, 22, 11, 16, 21, 10, 17, 20, 11, 19, 52, 23, 11, 17, 21, 10, 15, + 20, 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 127, 11, 15, 1, 10, 14, + 46, 56, 34, 6, 1, 0, 0, 0, 0, 0, 0, 0, 22, 12, 8, 11, 6, 11, + 4, 18, 10, 12, 21, 11, 14, 10, 8, 11, 21, 56, 61, 11, 8, 11, 7, 33, + 4, 125, 2, 6, 17, 0, 0, 0, 0, 0, 0, 0, 39, 10, 15, 20, 12, 22, + 11, 14, 10, 22, 56, 6, 12, 23, 10, 23, 16, 7, 20, 11, 15, 21, 10, 23, + 15, 8, 12, 24, 11, 6, 11, 24, 21, 11, 23, 15, 7, 12, 25, 11, 4, 11, + 25, 21, 11, 22, 12, 8, 5, 120, 11, 14, 1, 11, 15, 1, 11, 16, 1, 11, + 17, 1, 6, 13, 0, 0, 0, 0, 0, 0, 0, 39, 11, 14, 1, 11, 15, 1, + 11, 16, 1, 11, 17, 1, 6, 12, 0, 0, 0, 0, 0, 0, 0, 39, 10, 9, + 15, 11, 12, 14, 10, 9, 15, 12, 12, 15, 10, 9, 15, 13, 12, 16, 11, 9, + 15, 14, 12, 17, 11, 10, 12, 18, 11, 12, 12, 19, 5, 67, 11, 9, 1, 6, + 11, 0, 0, 0, 0, 0, 0, 0, 39, 6, 9, 0, 0, 0, 0, 0, 0, 0, + 39, 6, 8, 0, 0, 0, 0, 0, 0, 0, 39, 81, 1, 4, 3, 2, 6, 8, + 109, 40, 10, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, 34, 3, 5, 5, 9, + 10, 2, 17, 82, 4, 36, 5, 9, 10, 1, 53, 49, 64, 47, 10, 2, 53, 27, + 12, 4, 10, 0, 10, 4, 10, 1, 10, 2, 56, 62, 56, 63, 3, 25, 5, 28, + 10, 0, 10, 4, 56, 64, 10, 0, 11, 4, 56, 65, 11, 0, 11, 1, 11, 2, + 17, 76, 2, 11, 0, 1, 6, 1, 0, 0, 0, 0, 0, 0, 0, 39, 83, 0, + 0, 1, 6, 114, 116, 10, 0, 17, 23, 12, 4, 56, 0, 12, 5, 56, 66, 12, + 6, 10, 2, 10, 5, 10, 6, 17, 86, 12, 7, 12, 8, 12, 9, 12, 10, 12, + 11, 10, 4, 41, 6, 3, 113, 11, 0, 12, 12, 56, 67, 56, 68, 18, 6, 12, + 13, 11, 12, 11, 13, 45, 6, 10, 4, 42, 6, 15, 0, 12, 14, 10, 14, 46, + 10, 1, 56, 12, 3, 109, 11, 14, 12, 15, 11, 1, 12, 16, 11, 5, 12, 17, + 11, 11, 12, 18, 11, 6, 12, 19, 11, 10, 12, 20, 11, 9, 12, 21, 11, 8, + 12, 22, 11, 7, 12, 23, 56, 69, 56, 69, 12, 32, 12, 33, 11, 17, 11, 18, + 11, 19, 11, 20, 11, 21, 11, 22, 11, 23, 11, 33, 11, 32, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 18, 4, 12, 34, + 11, 15, 11, 16, 11, 34, 56, 70, 11, 4, 42, 6, 15, 29, 12, 35, 10, 35, + 46, 10, 2, 56, 35, 3, 103, 11, 35, 12, 36, 11, 2, 12, 20, 11, 3, 56, + 71, 12, 37, 11, 36, 11, 20, 11, 37, 56, 72, 2, 11, 35, 11, 2, 56, 73, + 11, 3, 68, 6, 5, 102, 11, 14, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 39, 11, 0, 1, 5, 28, 85, 0, 0, 1, 2, 115, 28, 10, 0, 17, 23, 12, + 2, 10, 2, 59, 0, 3, 25, 11, 0, 12, 3, 56, 74, 57, 0, 12, 4, 11, + 3, 11, 4, 63, 0, 11, 2, 60, 0, 54, 0, 11, 1, 12, 5, 56, 75, 12, + 6, 11, 5, 11, 6, 56, 76, 2, 11, 0, 1, 5, 14, 90, 1, 4, 3, 2, + 6, 8, 1, 5, 11, 0, 11, 1, 11, 2, 56, 77, 2, 62, 0, 0, 0, 119, + 42, 64, 117, 0, 0, 0, 0, 0, 0, 0, 0, 12, 1, 10, 0, 46, 56, 78, + 12, 2, 14, 2, 56, 38, 4, 38, 10, 0, 12, 3, 14, 2, 56, 39, 20, 12, + 4, 11, 3, 11, 4, 56, 79, 12, 5, 1, 12, 6, 14, 6, 16, 8, 20, 50, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, + 34, 11, 6, 19, 10, 1, 1, 11, 5, 12, 2, 5, 6, 13, 1, 11, 6, 68, + 117, 5, 31, 11, 0, 1, 11, 1, 2, 92, 0, 0, 2, 2, 6, 122, 140, 1, + 10, 0, 41, 6, 4, 138, 1, 10, 0, 42, 6, 15, 0, 12, 5, 11, 1, 53, + 49, 64, 47, 11, 2, 53, 27, 12, 6, 10, 5, 46, 10, 6, 56, 12, 4, 134, + 1, 11, 5, 10, 6, 56, 5, 12, 7, 56, 0, 12, 8, 10, 7, 16, 16, 20, + 12, 9, 10, 8, 11, 9, 33, 4, 112, 10, 7, 15, 17, 12, 10, 10, 7, 15, + 4, 12, 11, 10, 7, 15, 13, 12, 12, 10, 11, 20, 12, 13, 10, 3, 11, 13, + 37, 4, 102, 10, 10, 20, 10, 3, 23, 11, 10, 21, 10, 11, 20, 10, 3, 23, + 11, 11, 21, 10, 12, 20, 10, 3, 23, 11, 12, 21, 11, 8, 12, 9, 56, 1, + 12, 14, 11, 9, 11, 14, 33, 4, 90, 11, 7, 16, 18, 20, 12, 13, 11, 4, + 11, 13, 33, 4, 88, 56, 80, 12, 15, 11, 15, 2, 6, 6, 0, 0, 0, 0, + 0, 0, 0, 39, 11, 7, 1, 11, 0, 60, 0, 54, 0, 11, 6, 56, 15, 11, + 3, 56, 29, 56, 3, 12, 15, 5, 86, 11, 7, 1, 11, 10, 1, 11, 11, 1, + 11, 12, 1, 6, 7, 0, 0, 0, 0, 0, 0, 0, 39, 10, 7, 16, 20, 20, + 12, 9, 10, 8, 11, 9, 33, 4, 130, 1, 10, 7, 15, 21, 12, 10, 10, 7, + 15, 14, 12, 11, 10, 7, 15, 3, 12, 12, 5, 43, 11, 7, 1, 6, 4, 0, + 0, 0, 0, 0, 0, 0, 39, 11, 5, 1, 6, 3, 0, 0, 0, 0, 0, 0, + 0, 39, 6, 2, 0, 0, 0, 0, 0, 0, 0, 39, 93, 3, 0, 2, 2, 6, + 1, 12, 10, 0, 10, 1, 10, 2, 11, 3, 11, 5, 56, 81, 11, 0, 11, 1, + 11, 2, 11, 4, 56, 82, 2, 94, 0, 0, 2, 2, 6, 6, 8, 11, 0, 11, + 1, 11, 2, 11, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 56, 81, 56, 14, + 2, 95, 1, 0, 2, 2, 6, 6, 9, 11, 3, 17, 56, 12, 4, 11, 0, 11, + 1, 11, 4, 11, 2, 56, 83, 2, 96, 1, 0, 2, 2, 6, 78, 7, 11, 0, + 17, 23, 11, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 11, 2, 56, 83, 2, + 97, 0, 0, 2, 2, 6, 6, 11, 11, 4, 17, 27, 12, 5, 11, 0, 11, 1, + 11, 2, 11, 3, 11, 5, 56, 84, 56, 85, 2, 98, 1, 0, 2, 2, 6, 6, + 10, 11, 3, 17, 56, 12, 5, 11, 0, 11, 1, 11, 5, 11, 2, 11, 4, 17, + 97, 2, 99, 1, 0, 2, 2, 6, 78, 8, 11, 0, 17, 23, 11, 1, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 11, 2, 11, 3, 17, 97, 2, 100, 1, 4, 2, + 2, 6, 1, 15, 10, 0, 17, 23, 56, 86, 4, 5, 5, 7, 10, 0, 56, 87, + 10, 0, 17, 23, 11, 0, 11, 1, 11, 2, 56, 88, 56, 89, 2, 6, 0, 4, + 7, 4, 9, 4, 16, 4, 12, 4, 4, 4, 3, 10, 1, 10, 0, 8, 0, 9, + 0, 4, 8, 4, 10, 4, 13, 4, 15, 9, 1, 4, 0, 4, 11, 4, 6, 2, + 0, 4, 2, 4, 14, 9, 3, 3, 9, 9, 2, 3, 4, 3, 0, 3, 5, 9, + 4, 6, 1, 4, 1, 4, 5, 19, 2, 19, 37, 0, 184, 1, 0, + ] +}); + +#[rustfmt::skip] +pub static MODULE_ECONIA_MARKET: Lazy> = Lazy::new(|| { + vec![ + 161, 28, 235, 11, 7, 0, 0, 10, 12, 1, 0, 34, 2, 34, 143, 1, 3, 177, + 1, 138, 8, 4, 187, 9, 239, 1, 5, 170, 11, 173, 14, 7, 215, 25, 150, 27, + 8, 237, 52, 64, 6, 173, 53, 38, 16, 211, 53, 230, 23, 10, 185, 77, 133, 2, + 12, 190, 79, 172, 62, 13, 234, 141, 1, 44, 0, 0, 1, 4, 1, 23, 0, 9, + 1, 30, 1, 33, 0, 41, 0, 49, 1, 74, 1, 76, 0, 77, 1, 85, 0, 107, + 1, 110, 1, 140, 1, 0, 155, 1, 0, 1, 8, 0, 1, 3, 4, 2, 3, 1, + 0, 1, 0, 5, 4, 0, 0, 6, 4, 0, 0, 12, 6, 0, 0, 17, 3, 0, + 2, 22, 4, 1, 6, 1, 3, 24, 7, 0, 0, 26, 7, 0, 0, 27, 4, 0, + 4, 29, 7, 0, 5, 32, 7, 0, 6, 40, 4, 1, 0, 0, 0, 46, 6, 0, + 0, 47, 8, 0, 7, 48, 4, 2, 7, 0, 4, 0, 0, 50, 3, 0, 0, 53, + 8, 0, 0, 54, 3, 0, 0, 63, 3, 0, 0, 64, 3, 0, 0, 65, 3, 0, + 0, 67, 8, 0, 0, 68, 4, 0, 3, 70, 7, 0, 8, 73, 7, 1, 0, 0, + 9, 75, 4, 1, 0, 1, 12, 106, 4, 0, 14, 141, 1, 6, 0, 12, 195, 1, + 4, 0, 12, 198, 1, 8, 0, 0, 72, 0, 1, 2, 0, 0, 1, 10, 78, 2, + 3, 0, 1, 7, 79, 5, 6, 2, 7, 4, 1, 7, 80, 7, 8, 2, 7, 4, + 1, 4, 81, 2, 10, 1, 0, 1, 0, 82, 13, 14, 2, 0, 0, 1, 10, 83, + 2, 15, 0, 1, 1, 84, 2, 17, 2, 3, 4, 1, 1, 79, 18, 6, 2, 3, + 0, 1, 11, 86, 20, 21, 1, 6, 1, 1, 87, 23, 2, 2, 3, 0, 1, 1, + 80, 24, 8, 2, 3, 0, 1, 8, 88, 9, 26, 1, 0, 1, 8, 89, 27, 6, + 1, 0, 1, 8, 90, 26, 9, 1, 0, 1, 3, 91, 28, 19, 0, 1, 8, 92, + 2, 26, 1, 0, 1, 2, 93, 29, 2, 1, 6, 1, 8, 94, 30, 9, 1, 0, + 1, 3, 95, 31, 2, 0, 1, 8, 96, 32, 2, 1, 0, 1, 0, 97, 20, 2, + 0, 1, 7, 84, 2, 34, 2, 7, 4, 1, 0, 98, 2, 36, 0, 1, 0, 99, + 2, 36, 0, 1, 0, 100, 2, 6, 0, 1, 0, 101, 2, 6, 0, 1, 0, 102, + 37, 2, 0, 1, 3, 103, 37, 38, 0, 1, 0, 104, 40, 2, 0, 1, 0, 105, + 42, 2, 0, 1, 12, 108, 43, 36, 0, 1, 0, 109, 44, 2, 0, 1, 13, 111, + 20, 3, 0, 1, 6, 112, 47, 6, 1, 0, 1, 6, 113, 48, 9, 1, 0, 1, + 3, 114, 49, 39, 0, 1, 0, 115, 51, 2, 0, 1, 0, 116, 52, 2, 0, 1, + 0, 117, 53, 2, 0, 1, 6, 118, 47, 6, 1, 0, 1, 6, 80, 48, 54, 1, + 0, 1, 3, 119, 55, 2, 0, 1, 6, 120, 36, 36, 0, 1, 6, 121, 56, 36, + 1, 0, 1, 0, 122, 58, 2, 0, 1, 0, 123, 59, 2, 0, 1, 0, 124, 2, + 25, 0, 1, 0, 125, 2, 6, 0, 1, 0, 126, 2, 25, 0, 1, 0, 127, 2, + 25, 0, 1, 0, 128, 1, 2, 25, 0, 1, 0, 129, 1, 2, 25, 0, 1, 0, + 130, 1, 2, 36, 0, 1, 0, 131, 1, 2, 25, 0, 1, 0, 132, 1, 2, 36, + 0, 1, 0, 133, 1, 2, 25, 0, 1, 0, 134, 1, 2, 6, 0, 1, 0, 135, + 1, 2, 25, 0, 1, 0, 136, 1, 2, 6, 0, 1, 0, 137, 1, 2, 6, 0, + 1, 0, 138, 1, 36, 60, 0, 1, 1, 139, 1, 18, 62, 2, 3, 0, 1, 2, + 140, 1, 63, 64, 1, 6, 1, 14, 142, 1, 64, 36, 0, 1, 0, 143, 1, 66, + 67, 0, 1, 6, 144, 1, 69, 6, 1, 0, 1, 6, 145, 1, 70, 9, 1, 0, + 1, 3, 146, 1, 71, 72, 0, 1, 0, 147, 1, 74, 75, 0, 1, 6, 148, 1, + 69, 76, 1, 0, 1, 0, 149, 1, 39, 36, 0, 1, 6, 139, 1, 47, 76, 1, + 0, 1, 6, 150, 1, 47, 36, 1, 0, 1, 0, 151, 1, 78, 79, 0, 1, 6, + 152, 1, 69, 81, 1, 0, 1, 8, 79, 82, 6, 1, 0, 1, 0, 153, 1, 74, + 84, 0, 1, 0, 154, 1, 86, 2, 0, 1, 15, 156, 1, 2, 36, 0, 1, 15, + 157, 1, 87, 36, 0, 1, 8, 139, 1, 27, 76, 1, 0, 1, 15, 158, 1, 88, + 89, 1, 0, 1, 6, 159, 1, 70, 54, 1, 0, 1, 3, 160, 1, 90, 91, 2, + 0, 0, 1, 3, 161, 1, 92, 93, 0, 1, 0, 162, 1, 95, 96, 2, 0, 0, + 1, 3, 163, 1, 97, 98, 0, 1, 6, 164, 1, 47, 6, 1, 0, 1, 0, 165, + 1, 99, 2, 0, 1, 3, 166, 1, 100, 101, 2, 0, 0, 1, 3, 167, 1, 102, + 2, 2, 0, 0, 1, 8, 168, 1, 27, 6, 1, 0, 1, 3, 169, 1, 37, 36, + 0, 1, 6, 170, 1, 103, 104, 1, 0, 1, 3, 171, 1, 105, 2, 0, 1, 8, + 172, 1, 26, 2, 1, 0, 1, 3, 173, 1, 106, 2, 0, 1, 0, 174, 1, 108, + 96, 2, 0, 0, 1, 0, 175, 1, 110, 39, 2, 0, 0, 1, 7, 139, 1, 5, + 62, 2, 7, 4, 1, 0, 176, 1, 112, 39, 2, 0, 0, 1, 0, 177, 1, 113, + 39, 2, 0, 0, 1, 0, 178, 1, 113, 2, 2, 0, 0, 1, 0, 179, 1, 114, + 96, 2, 0, 0, 1, 0, 180, 1, 114, 2, 2, 0, 0, 1, 0, 181, 1, 116, + 117, 2, 0, 0, 1, 3, 182, 1, 118, 2, 0, 1, 0, 183, 1, 120, 117, 2, + 0, 0, 1, 0, 184, 1, 121, 117, 2, 0, 0, 1, 0, 185, 1, 121, 2, 2, + 0, 0, 1, 0, 186, 1, 123, 36, 2, 0, 0, 1, 6, 84, 87, 124, 1, 4, + 1, 7, 87, 127, 2, 2, 7, 4, 1, 15, 187, 1, 36, 2, 1, 0, 1, 0, + 188, 1, 129, 1, 36, 3, 0, 0, 0, 1, 12, 189, 1, 129, 1, 36, 3, 0, + 0, 0, 1, 5, 190, 1, 131, 1, 132, 1, 0, 1, 0, 191, 1, 134, 1, 2, + 3, 0, 0, 0, 1, 15, 192, 1, 2, 36, 0, 1, 9, 193, 1, 86, 136, 1, + 1, 0, 1, 0, 194, 1, 138, 1, 36, 2, 0, 0, 1, 12, 196, 1, 139, 1, + 36, 2, 0, 0, 1, 12, 197, 1, 140, 1, 36, 0, 1, 0, 199, 1, 143, 1, + 117, 2, 0, 0, 1, 9, 200, 1, 3, 6, 1, 0, 1, 9, 201, 1, 20, 2, + 1, 0, 1, 9, 202, 1, 3, 36, 1, 0, 1, 9, 203, 1, 2, 136, 1, 1, + 0, 1, 9, 204, 1, 145, 1, 2, 1, 0, 1, 0, 205, 1, 143, 1, 2, 2, + 0, 0, 1, 0, 206, 1, 147, 1, 148, 1, 2, 0, 0, 1, 9, 207, 1, 149, + 1, 36, 1, 0, 1, 9, 94, 150, 1, 136, 1, 1, 0, 1, 9, 208, 1, 151, + 1, 2, 1, 0, 1, 0, 209, 1, 153, 1, 154, 1, 1, 0, 1, 2, 4, 3, + 4, 4, 9, 4, 11, 5, 12, 7, 16, 8, 16, 9, 19, 9, 22, 10, 16, 11, + 16, 12, 25, 13, 25, 14, 25, 12, 19, 16, 22, 17, 22, 18, 19, 17, 19, 20, + 22, 16, 19, 16, 25, 22, 4, 34, 46, 35, 46, 40, 46, 41, 46, 44, 46, 16, + 61, 62, 16, 63, 19, 63, 22, 12, 61, 66, 46, 67, 46, 14, 39, 70, 46, 72, + 46, 73, 46, 75, 46, 13, 36, 14, 36, 76, 36, 81, 36, 82, 11, 83, 46, 84, + 12, 88, 46, 90, 12, 91, 12, 20, 25, 92, 25, 94, 46, 96, 46, 14, 46, 86, + 12, 100, 4, 92, 36, 99, 12, 102, 12, 104, 12, 106, 12, 109, 12, 112, 46, 9, + 125, 9, 126, 113, 4, 114, 11, 116, 130, 1, 111, 12, 120, 135, 1, 115, 130, 1, + 122, 12, 111, 141, 1, 125, 9, 126, 9, 125, 11, 126, 11, 127, 9, 127, 11, 128, + 1, 9, 12, 136, 1, 120, 11, 0, 12, 7, 144, 1, 8, 144, 1, 9, 93, 10, + 144, 1, 11, 144, 1, 14, 22, 17, 93, 13, 19, 14, 19, 14, 136, 1, 129, 1, + 9, 129, 1, 11, 120, 9, 128, 1, 11, 124, 12, 132, 1, 9, 132, 1, 11, 133, + 1, 11, 134, 1, 11, 133, 1, 9, 16, 155, 1, 0, 141, 1, 96, 155, 1, 134, + 1, 9, 13, 7, 10, 8, 24, 5, 3, 3, 5, 1, 3, 3, 3, 3, 3, 11, + 25, 1, 11, 26, 1, 9, 0, 11, 26, 1, 9, 1, 7, 11, 25, 1, 11, 26, + 1, 9, 0, 11, 26, 1, 9, 1, 3, 3, 3, 11, 25, 1, 8, 8, 11, 25, + 1, 8, 7, 0, 1, 5, 2, 3, 8, 9, 2, 6, 11, 15, 2, 9, 0, 9, + 1, 9, 0, 1, 1, 2, 7, 11, 15, 2, 9, 0, 9, 1, 9, 0, 1, 7, + 9, 1, 1, 9, 0, 1, 8, 10, 1, 9, 1, 2, 9, 0, 9, 1, 15, 3, + 7, 10, 8, 24, 7, 8, 9, 5, 3, 5, 1, 3, 3, 3, 3, 3, 2, 11, + 25, 1, 11, 26, 1, 9, 0, 11, 26, 1, 9, 1, 8, 11, 25, 1, 11, 26, + 1, 9, 0, 11, 26, 1, 9, 1, 3, 3, 3, 1, 1, 1, 1, 12, 2, 3, + 8, 2, 1, 11, 1, 2, 9, 0, 9, 1, 2, 6, 11, 1, 2, 9, 0, 9, + 1, 9, 0, 1, 8, 7, 1, 6, 12, 1, 11, 6, 1, 9, 0, 1, 8, 8, + 3, 7, 11, 1, 2, 9, 0, 9, 1, 9, 0, 9, 1, 2, 7, 11, 1, 2, + 9, 0, 9, 1, 9, 0, 1, 2, 1, 11, 25, 1, 9, 0, 1, 6, 11, 25, + 1, 9, 0, 5, 3, 4, 5, 3, 2, 2, 7, 11, 6, 1, 9, 0, 9, 0, + 1, 7, 11, 25, 1, 9, 0, 1, 6, 10, 8, 24, 2, 7, 11, 25, 1, 9, + 0, 9, 0, 36, 5, 7, 11, 15, 2, 3, 8, 9, 7, 8, 9, 3, 2, 1, + 1, 1, 3, 3, 11, 26, 1, 9, 1, 11, 25, 1, 11, 26, 1, 9, 0, 4, + 12, 7, 11, 1, 2, 3, 8, 2, 12, 8, 2, 7, 8, 2, 8, 8, 3, 3, + 3, 1, 11, 25, 1, 2, 1, 3, 4, 5, 3, 11, 25, 1, 8, 7, 11, 25, + 1, 8, 8, 11, 25, 1, 11, 26, 1, 9, 0, 11, 26, 1, 9, 1, 3, 11, + 25, 1, 8, 8, 11, 25, 1, 8, 7, 1, 11, 15, 2, 9, 0, 9, 1, 2, + 12, 8, 14, 1, 3, 4, 5, 3, 3, 1, 1, 10, 4, 1, 4, 5, 5, 3, + 3, 1, 4, 8, 10, 4, 3, 3, 5, 3, 3, 1, 4, 4, 5, 3, 1, 6, + 8, 27, 1, 6, 8, 27, 3, 6, 12, 3, 1, 2, 3, 5, 1, 8, 3, 2, + 6, 11, 12, 1, 9, 0, 3, 2, 7, 11, 12, 1, 9, 0, 3, 9, 5, 3, + 3, 1, 3, 3, 3, 4, 2, 12, 7, 11, 15, 2, 3, 8, 9, 7, 8, 9, + 1, 7, 11, 12, 1, 8, 3, 4, 3, 3, 3, 5, 3, 3, 2, 5, 5, 3, + 1, 4, 6, 8, 27, 4, 6, 12, 3, 1, 4, 6, 5, 3, 3, 1, 4, 3, + 1, 7, 9, 0, 9, 5, 3, 3, 1, 3, 3, 3, 3, 4, 3, 7, 11, 12, + 1, 9, 0, 3, 9, 0, 14, 7, 11, 15, 2, 3, 8, 9, 7, 8, 9, 3, + 1, 7, 11, 12, 1, 8, 3, 4, 7, 8, 3, 5, 3, 3, 3, 1, 7, 3, + 8, 3, 6, 5, 3, 1, 4, 3, 6, 8, 27, 5, 6, 12, 3, 1, 4, 3, + 1, 11, 25, 1, 8, 5, 1, 8, 5, 1, 6, 9, 1, 1, 6, 11, 6, 1, + 9, 0, 1, 6, 8, 28, 4, 5, 6, 11, 1, 2, 3, 8, 2, 6, 8, 2, + 3, 4, 3, 7, 8, 9, 1, 3, 1, 10, 8, 16, 1, 8, 16, 1, 6, 11, + 12, 1, 9, 0, 1, 7, 11, 12, 1, 9, 0, 5, 5, 3, 3, 1, 3, 1, + 11, 25, 1, 4, 12, 10, 8, 16, 1, 7, 11, 12, 1, 8, 3, 3, 3, 5, + 7, 10, 8, 16, 4, 3, 3, 8, 16, 10, 8, 16, 5, 6, 8, 9, 3, 1, + 3, 4, 2, 10, 8, 16, 4, 1, 6, 9, 0, 16, 1, 6, 11, 12, 1, 8, + 3, 10, 8, 16, 10, 8, 16, 4, 4, 6, 8, 3, 3, 3, 5, 3, 3, 3, + 7, 10, 8, 16, 4, 8, 16, 3, 7, 8, 9, 1, 3, 1, 10, 8, 19, 1, + 8, 19, 1, 11, 25, 1, 3, 2, 6, 11, 25, 1, 9, 0, 6, 9, 0, 10, + 10, 8, 19, 1, 7, 11, 12, 1, 8, 3, 4, 11, 25, 1, 3, 3, 11, 25, + 1, 3, 3, 4, 10, 8, 19, 2, 10, 8, 19, 4, 16, 1, 6, 11, 12, 1, + 8, 3, 10, 8, 19, 10, 8, 19, 4, 4, 6, 8, 3, 3, 3, 5, 4, 3, + 4, 3, 6, 8, 3, 3, 2, 6, 12, 3, 3, 1, 3, 3, 5, 3, 5, 3, + 3, 11, 26, 1, 9, 0, 2, 11, 26, 1, 9, 0, 3, 12, 5, 3, 3, 1, + 3, 3, 3, 1, 11, 25, 1, 11, 26, 1, 9, 0, 11, 26, 1, 9, 1, 3, + 3, 3, 11, 25, 1, 11, 26, 1, 9, 0, 11, 26, 1, 9, 1, 4, 12, 3, + 3, 3, 1, 5, 3, 4, 5, 3, 4, 3, 3, 1, 8, 24, 39, 3, 1, 3, + 3, 3, 3, 3, 3, 1, 7, 11, 12, 1, 8, 3, 4, 3, 3, 1, 11, 25, + 1, 3, 3, 1, 1, 3, 3, 3, 11, 26, 1, 9, 1, 1, 11, 25, 1, 11, + 26, 1, 9, 0, 11, 26, 1, 9, 1, 3, 3, 3, 1, 1, 7, 8, 3, 2, + 4, 5, 3, 3, 8, 24, 4, 3, 10, 5, 3, 3, 5, 1, 3, 3, 2, 2, + 2, 4, 4, 3, 3, 3, 3, 5, 3, 3, 6, 3, 3, 3, 3, 3, 3, 9, + 1, 3, 3, 3, 3, 3, 3, 3, 3, 6, 5, 3, 3, 3, 3, 3, 2, 11, + 25, 1, 11, 26, 1, 9, 0, 11, 26, 1, 9, 1, 7, 5, 3, 3, 3, 11, + 25, 1, 11, 26, 1, 9, 0, 11, 26, 1, 9, 1, 3, 4, 7, 11, 12, 1, + 9, 0, 3, 9, 0, 2, 3, 3, 3, 11, 25, 1, 9, 0, 8, 5, 3, 3, + 1, 3, 3, 4, 3, 13, 3, 5, 3, 5, 1, 3, 3, 2, 2, 3, 4, 6, + 10, 8, 24, 6, 11, 25, 1, 2, 49, 2, 3, 3, 3, 3, 3, 7, 8, 9, + 3, 1, 1, 1, 4, 4, 4, 4, 3, 3, 1, 3, 3, 3, 3, 11, 25, 1, + 2, 10, 8, 24, 3, 1, 3, 3, 11, 26, 1, 9, 1, 11, 25, 1, 11, 26, + 1, 9, 0, 3, 7, 10, 8, 24, 1, 1, 3, 1, 7, 11, 12, 1, 8, 3, + 8, 3, 11, 25, 1, 8, 3, 3, 3, 3, 4, 6, 11, 25, 1, 2, 6, 10, + 8, 24, 3, 3, 3, 5, 9, 5, 3, 5, 1, 3, 3, 2, 2, 6, 8, 27, + 2, 2, 3, 8, 5, 3, 3, 5, 1, 3, 1, 3, 18, 6, 11, 15, 2, 3, + 8, 9, 6, 8, 9, 11, 25, 1, 3, 11, 25, 1, 3, 1, 11, 25, 1, 3, + 11, 25, 1, 3, 3, 3, 2, 2, 2, 4, 3, 3, 3, 3, 3, 8, 5, 3, + 5, 1, 3, 1, 3, 6, 8, 27, 7, 6, 12, 3, 5, 1, 3, 1, 3, 8, + 6, 12, 3, 5, 1, 3, 3, 2, 2, 3, 2, 3, 5, 7, 5, 3, 3, 5, + 1, 3, 2, 3, 3, 3, 3, 10, 3, 5, 3, 5, 1, 3, 2, 4, 6, 10, + 8, 24, 6, 11, 25, 1, 2, 38, 3, 3, 3, 3, 7, 8, 9, 3, 4, 4, + 3, 1, 3, 3, 3, 11, 26, 1, 9, 1, 11, 25, 1, 11, 26, 1, 9, 0, + 3, 10, 8, 24, 3, 3, 7, 10, 8, 24, 1, 1, 3, 11, 26, 1, 9, 1, + 11, 25, 1, 11, 26, 1, 9, 0, 1, 3, 3, 3, 1, 3, 1, 11, 25, 1, + 2, 6, 11, 25, 1, 2, 6, 10, 8, 24, 3, 3, 3, 7, 5, 3, 5, 1, + 3, 2, 6, 8, 27, 6, 6, 12, 3, 5, 1, 3, 2, 5, 3, 1, 3, 3, + 3, 6, 3, 8, 11, 3, 3, 3, 3, 1, 11, 12, 1, 9, 0, 1, 8, 4, + 1, 8, 13, 3, 7, 11, 15, 2, 9, 0, 9, 1, 9, 0, 9, 1, 11, 12, + 3, 11, 6, 1, 8, 13, 11, 6, 1, 8, 4, 3, 11, 12, 1, 8, 3, 11, + 12, 1, 8, 3, 8, 10, 8, 10, 8, 9, 7, 11, 15, 2, 3, 8, 9, 4, + 3, 3, 3, 11, 26, 1, 9, 2, 3, 9, 0, 9, 1, 9, 2, 1, 10, 2, + 1, 8, 11, 3, 3, 8, 11, 3, 4, 6, 12, 3, 3, 3, 1, 9, 2, 1, + 11, 26, 1, 9, 0, 2, 3, 11, 26, 1, 9, 2, 6, 8, 11, 3, 3, 3, + 11, 26, 1, 9, 1, 6, 8, 29, 6, 8, 11, 3, 3, 3, 6, 8, 29, 11, + 26, 1, 9, 1, 1, 6, 8, 29, 2, 8, 30, 9, 0, 2, 3, 3, 9, 6, + 12, 3, 5, 1, 3, 3, 3, 3, 3, 2, 3, 8, 23, 2, 5, 11, 26, 1, + 9, 0, 29, 5, 3, 3, 3, 1, 3, 11, 25, 1, 11, 26, 1, 9, 0, 11, + 26, 1, 9, 1, 10, 8, 24, 3, 7, 10, 8, 24, 11, 25, 1, 8, 7, 11, + 25, 1, 8, 8, 3, 3, 11, 26, 1, 9, 1, 11, 25, 1, 11, 26, 1, 9, + 0, 6, 12, 8, 22, 7, 11, 1, 2, 3, 8, 23, 8, 23, 7, 8, 23, 6, + 10, 8, 24, 3, 3, 8, 24, 3, 5, 11, 26, 1, 9, 0, 10, 3, 5, 1, + 3, 3, 3, 3, 3, 11, 26, 1, 9, 0, 11, 26, 1, 9, 1, 5, 11, 26, + 1, 9, 0, 11, 26, 1, 9, 1, 3, 3, 3, 1, 6, 11, 26, 1, 9, 0, + 2, 7, 11, 26, 1, 9, 0, 3, 2, 7, 11, 26, 1, 9, 0, 11, 26, 1, + 9, 0, 13, 3, 3, 11, 25, 1, 11, 26, 1, 9, 0, 1, 3, 11, 26, 1, + 9, 1, 10, 8, 24, 5, 7, 10, 8, 24, 3, 3, 11, 26, 1, 9, 1, 11, + 26, 1, 9, 0, 10, 3, 5, 1, 3, 3, 3, 3, 3, 11, 26, 1, 9, 0, + 6, 8, 29, 4, 11, 26, 1, 9, 0, 3, 3, 3, 1, 11, 26, 1, 8, 30, + 18, 3, 3, 1, 3, 11, 26, 1, 9, 0, 10, 8, 24, 3, 3, 5, 3, 3, + 3, 3, 3, 11, 25, 1, 11, 26, 1, 8, 30, 5, 7, 10, 8, 24, 11, 26, + 1, 9, 0, 6, 109, 97, 114, 107, 101, 116, 18, 77, 97, 114, 107, 101, 116, 69, + 118, 101, 110, 116, 72, 97, 110, 100, 108, 101, 115, 3, 109, 97, 112, 5, 84, 97, + 98, 108, 101, 5, 116, 97, 98, 108, 101, 27, 77, 97, 114, 107, 101, 116, 69, 118, + 101, 110, 116, 72, 97, 110, 100, 108, 101, 115, 70, 111, 114, 77, 97, 114, 107, 101, + 116, 5, 79, 114, 100, 101, 114, 4, 115, 105, 122, 101, 5, 112, 114, 105, 99, 101, + 4, 117, 115, 101, 114, 12, 99, 117, 115, 116, 111, 100, 105, 97, 110, 95, 105, 100, + 16, 111, 114, 100, 101, 114, 95, 97, 99, 99, 101, 115, 115, 95, 107, 101, 121, 10, + 77, 97, 107, 101, 114, 69, 118, 101, 110, 116, 9, 109, 97, 114, 107, 101, 116, 95, + 105, 100, 4, 115, 105, 100, 101, 15, 109, 97, 114, 107, 101, 116, 95, 111, 114, 100, + 101, 114, 95, 105, 100, 4, 116, 121, 112, 101, 29, 77, 97, 114, 107, 101, 116, 69, + 118, 101, 110, 116, 72, 97, 110, 100, 108, 101, 67, 114, 101, 97, 116, 105, 111, 110, + 73, 110, 102, 111, 24, 114, 101, 115, 111, 117, 114, 99, 101, 95, 97, 99, 99, 111, + 117, 110, 116, 95, 97, 100, 100, 114, 101, 115, 115, 39, 99, 97, 110, 99, 101, 108, + 95, 111, 114, 100, 101, 114, 95, 101, 118, 101, 110, 116, 115, 95, 104, 97, 110, 100, + 108, 101, 95, 99, 114, 101, 97, 116, 105, 111, 110, 95, 110, 117, 109, 43, 112, 108, + 97, 99, 101, 95, 115, 119, 97, 112, 95, 111, 114, 100, 101, 114, 95, 101, 118, 101, + 110, 116, 115, 95, 104, 97, 110, 100, 108, 101, 95, 99, 114, 101, 97, 116, 105, 111, + 110, 95, 110, 117, 109, 19, 99, 97, 110, 99, 101, 108, 95, 111, 114, 100, 101, 114, + 95, 101, 118, 101, 110, 116, 115, 11, 69, 118, 101, 110, 116, 72, 97, 110, 100, 108, + 101, 5, 101, 118, 101, 110, 116, 16, 67, 97, 110, 99, 101, 108, 79, 114, 100, 101, + 114, 69, 118, 101, 110, 116, 23, 112, 108, 97, 99, 101, 95, 115, 119, 97, 112, 95, + 111, 114, 100, 101, 114, 95, 101, 118, 101, 110, 116, 115, 19, 80, 108, 97, 99, 101, + 83, 119, 97, 112, 79, 114, 100, 101, 114, 69, 118, 101, 110, 116, 9, 79, 114, 100, + 101, 114, 66, 111, 111, 107, 9, 98, 97, 115, 101, 95, 116, 121, 112, 101, 8, 84, + 121, 112, 101, 73, 110, 102, 111, 9, 116, 121, 112, 101, 95, 105, 110, 102, 111, 17, + 98, 97, 115, 101, 95, 110, 97, 109, 101, 95, 103, 101, 110, 101, 114, 105, 99, 6, + 83, 116, 114, 105, 110, 103, 6, 115, 116, 114, 105, 110, 103, 10, 113, 117, 111, 116, + 101, 95, 116, 121, 112, 101, 8, 108, 111, 116, 95, 115, 105, 122, 101, 9, 116, 105, + 99, 107, 95, 115, 105, 122, 101, 8, 109, 105, 110, 95, 115, 105, 122, 101, 14, 117, + 110, 100, 101, 114, 119, 114, 105, 116, 101, 114, 95, 105, 100, 4, 97, 115, 107, 115, + 8, 65, 86, 76, 113, 117, 101, 117, 101, 9, 97, 118, 108, 95, 113, 117, 101, 117, + 101, 4, 98, 105, 100, 115, 7, 99, 111, 117, 110, 116, 101, 114, 12, 109, 97, 107, + 101, 114, 95, 101, 118, 101, 110, 116, 115, 12, 116, 97, 107, 101, 114, 95, 101, 118, + 101, 110, 116, 115, 10, 84, 97, 107, 101, 114, 69, 118, 101, 110, 116, 10, 79, 114, + 100, 101, 114, 66, 111, 111, 107, 115, 7, 84, 97, 98, 108, 105, 115, 116, 7, 116, + 97, 98, 108, 105, 115, 116, 9, 79, 114, 100, 101, 114, 86, 105, 101, 119, 8, 111, + 114, 100, 101, 114, 95, 105, 100, 14, 114, 101, 109, 97, 105, 110, 105, 110, 103, 95, + 115, 105, 122, 101, 6, 79, 114, 100, 101, 114, 115, 10, 79, 114, 100, 101, 114, 115, + 86, 105, 101, 119, 15, 115, 105, 103, 110, 105, 110, 103, 95, 97, 99, 99, 111, 117, + 110, 116, 10, 105, 110, 116, 101, 103, 114, 97, 116, 111, 114, 9, 100, 105, 114, 101, + 99, 116, 105, 111, 110, 8, 109, 105, 110, 95, 98, 97, 115, 101, 8, 109, 97, 120, + 95, 98, 97, 115, 101, 9, 109, 105, 110, 95, 113, 117, 111, 116, 101, 9, 109, 97, + 120, 95, 113, 117, 111, 116, 101, 11, 108, 105, 109, 105, 116, 95, 112, 114, 105, 99, + 101, 10, 80, 114, 105, 99, 101, 76, 101, 118, 101, 108, 11, 80, 114, 105, 99, 101, + 76, 101, 118, 101, 108, 115, 33, 83, 119, 97, 112, 112, 101, 114, 69, 118, 101, 110, + 116, 72, 97, 110, 100, 108, 101, 67, 114, 101, 97, 116, 105, 111, 110, 78, 117, 109, + 98, 101, 114, 115, 31, 102, 105, 108, 108, 95, 101, 118, 101, 110, 116, 115, 95, 104, + 97, 110, 100, 108, 101, 95, 99, 114, 101, 97, 116, 105, 111, 110, 95, 110, 117, 109, + 19, 83, 119, 97, 112, 112, 101, 114, 69, 118, 101, 110, 116, 72, 97, 110, 100, 108, + 101, 115, 28, 83, 119, 97, 112, 112, 101, 114, 69, 118, 101, 110, 116, 72, 97, 110, + 100, 108, 101, 115, 70, 111, 114, 77, 97, 114, 107, 101, 116, 11, 102, 105, 108, 108, + 95, 101, 118, 101, 110, 116, 115, 9, 70, 105, 108, 108, 69, 118, 101, 110, 116, 5, + 109, 97, 107, 101, 114, 4, 115, 119, 97, 112, 6, 79, 112, 116, 105, 111, 110, 6, + 111, 112, 116, 105, 111, 110, 4, 67, 111, 105, 110, 4, 99, 111, 105, 110, 16, 114, + 101, 115, 111, 117, 114, 99, 101, 95, 97, 99, 99, 111, 117, 110, 116, 11, 103, 101, + 116, 95, 97, 100, 100, 114, 101, 115, 115, 8, 99, 111, 110, 116, 97, 105, 110, 115, + 10, 98, 111, 114, 114, 111, 119, 95, 109, 117, 116, 7, 116, 121, 112, 101, 95, 111, + 102, 5, 109, 97, 116, 99, 104, 10, 103, 101, 116, 95, 115, 105, 103, 110, 101, 114, + 3, 110, 101, 119, 7, 97, 99, 99, 111, 117, 110, 116, 16, 110, 101, 119, 95, 101, + 118, 101, 110, 116, 95, 104, 97, 110, 100, 108, 101, 3, 97, 100, 100, 4, 115, 111, + 109, 101, 7, 105, 115, 95, 115, 111, 109, 101, 12, 100, 101, 115, 116, 114, 111, 121, + 95, 115, 111, 109, 101, 34, 99, 114, 101, 97, 116, 101, 95, 99, 97, 110, 99, 101, + 108, 95, 111, 114, 100, 101, 114, 95, 101, 118, 101, 110, 116, 95, 105, 110, 116, 101, + 114, 110, 97, 108, 4, 110, 111, 110, 101, 10, 101, 109, 105, 116, 95, 101, 118, 101, + 110, 116, 7, 101, 120, 116, 114, 97, 99, 116, 36, 101, 109, 105, 116, 95, 115, 119, + 97, 112, 95, 109, 97, 107, 101, 114, 95, 102, 105, 108, 108, 95, 101, 118, 101, 110, + 116, 115, 95, 105, 110, 116, 101, 114, 110, 97, 108, 4, 102, 105, 108, 108, 11, 105, + 110, 105, 116, 95, 109, 111, 100, 117, 108, 101, 16, 103, 101, 116, 95, 78, 79, 95, + 67, 85, 83, 84, 79, 68, 73, 65, 78, 18, 103, 101, 116, 95, 78, 79, 95, 85, + 78, 68, 69, 82, 87, 82, 73, 84, 69, 82, 7, 103, 101, 116, 95, 65, 83, 75, + 7, 103, 101, 116, 95, 66, 73, 68, 17, 99, 97, 110, 99, 101, 108, 95, 97, 108, + 108, 95, 111, 114, 100, 101, 114, 115, 36, 103, 101, 116, 95, 97, 99, 116, 105, 118, + 101, 95, 109, 97, 114, 107, 101, 116, 95, 111, 114, 100, 101, 114, 95, 105, 100, 115, + 95, 105, 110, 116, 101, 114, 110, 97, 108, 12, 99, 97, 110, 99, 101, 108, 95, 111, + 114, 100, 101, 114, 27, 99, 97, 110, 99, 101, 108, 95, 97, 108, 108, 95, 111, 114, + 100, 101, 114, 115, 95, 99, 117, 115, 116, 111, 100, 105, 97, 110, 19, 67, 117, 115, + 116, 111, 100, 105, 97, 110, 67, 97, 112, 97, 98, 105, 108, 105, 116, 121, 8, 114, + 101, 103, 105, 115, 116, 114, 121, 16, 103, 101, 116, 95, 99, 117, 115, 116, 111, 100, + 105, 97, 110, 95, 105, 100, 22, 99, 97, 110, 99, 101, 108, 95, 97, 108, 108, 95, + 111, 114, 100, 101, 114, 115, 95, 117, 115, 101, 114, 6, 115, 105, 103, 110, 101, 114, + 10, 97, 100, 100, 114, 101, 115, 115, 95, 111, 102, 28, 99, 111, 110, 116, 97, 105, + 110, 115, 95, 97, 99, 116, 105, 118, 101, 95, 108, 105, 115, 116, 95, 110, 111, 100, + 101, 95, 105, 100, 6, 114, 101, 109, 111, 118, 101, 21, 99, 97, 110, 99, 101, 108, + 95, 111, 114, 100, 101, 114, 95, 105, 110, 116, 101, 114, 110, 97, 108, 22, 99, 97, + 110, 99, 101, 108, 95, 111, 114, 100, 101, 114, 95, 99, 117, 115, 116, 111, 100, 105, + 97, 110, 17, 99, 97, 110, 99, 101, 108, 95, 111, 114, 100, 101, 114, 95, 117, 115, + 101, 114, 17, 99, 104, 97, 110, 103, 101, 95, 111, 114, 100, 101, 114, 95, 115, 105, + 122, 101, 13, 105, 115, 95, 108, 111, 99, 97, 108, 95, 116, 97, 105, 108, 26, 99, + 104, 97, 110, 103, 101, 95, 111, 114, 100, 101, 114, 95, 115, 105, 122, 101, 95, 105, + 110, 116, 101, 114, 110, 97, 108, 28, 103, 101, 116, 95, 97, 99, 99, 101, 115, 115, + 95, 107, 101, 121, 95, 105, 110, 115, 101, 114, 116, 105, 111, 110, 95, 107, 101, 121, + 6, 105, 110, 115, 101, 114, 116, 27, 99, 104, 97, 110, 103, 101, 95, 111, 114, 100, + 101, 114, 95, 115, 105, 122, 101, 95, 99, 117, 115, 116, 111, 100, 105, 97, 110, 22, + 99, 104, 97, 110, 103, 101, 95, 111, 114, 100, 101, 114, 95, 115, 105, 122, 101, 95, + 117, 115, 101, 114, 9, 103, 101, 116, 95, 65, 66, 79, 82, 84, 7, 103, 101, 116, + 95, 66, 85, 89, 15, 103, 101, 116, 95, 67, 65, 78, 67, 69, 76, 95, 66, 79, + 84, 72, 16, 103, 101, 116, 95, 67, 65, 78, 67, 69, 76, 95, 77, 65, 75, 69, + 82, 16, 103, 101, 116, 95, 67, 65, 78, 67, 69, 76, 95, 84, 65, 75, 69, 82, + 17, 103, 101, 116, 95, 70, 73, 76, 76, 95, 79, 82, 95, 65, 66, 79, 82, 84, + 12, 103, 101, 116, 95, 72, 73, 95, 80, 82, 73, 67, 69, 23, 103, 101, 116, 95, + 73, 77, 77, 69, 68, 73, 65, 84, 69, 95, 79, 82, 95, 67, 65, 78, 67, 69, + 76, 16, 103, 101, 116, 95, 77, 65, 88, 95, 80, 79, 83, 83, 73, 66, 76, 69, + 18, 103, 101, 116, 95, 78, 79, 95, 82, 69, 83, 84, 82, 73, 67, 84, 73, 79, + 78, 11, 103, 101, 116, 95, 80, 69, 82, 67, 69, 78, 84, 17, 103, 101, 116, 95, + 80, 79, 83, 84, 95, 79, 82, 95, 65, 66, 79, 82, 84, 8, 103, 101, 116, 95, + 83, 69, 76, 76, 9, 103, 101, 116, 95, 84, 73, 67, 75, 83, 37, 103, 101, 116, + 95, 109, 97, 114, 107, 101, 116, 95, 101, 118, 101, 110, 116, 95, 104, 97, 110, 100, + 108, 101, 95, 99, 114, 101, 97, 116, 105, 111, 110, 95, 105, 110, 102, 111, 6, 98, + 111, 114, 114, 111, 119, 4, 103, 117, 105, 100, 4, 71, 85, 73, 68, 12, 99, 114, + 101, 97, 116, 105, 111, 110, 95, 110, 117, 109, 24, 103, 101, 116, 95, 111, 112, 101, + 110, 95, 111, 114, 100, 101, 114, 115, 95, 102, 111, 114, 95, 115, 105, 100, 101, 8, + 105, 115, 95, 101, 109, 112, 116, 121, 8, 112, 111, 112, 95, 104, 101, 97, 100, 26, + 103, 101, 116, 95, 111, 112, 101, 110, 95, 111, 114, 100, 101, 114, 95, 105, 100, 95, + 105, 110, 116, 101, 114, 110, 97, 108, 34, 103, 101, 116, 95, 111, 112, 101, 110, 95, + 111, 114, 100, 101, 114, 115, 95, 102, 111, 114, 95, 115, 105, 100, 101, 95, 112, 97, + 103, 105, 110, 97, 116, 101, 100, 11, 98, 111, 114, 114, 111, 119, 95, 104, 101, 97, + 100, 33, 103, 101, 116, 95, 111, 114, 100, 101, 114, 95, 105, 100, 95, 97, 118, 108, + 95, 113, 117, 101, 117, 101, 95, 97, 99, 99, 101, 115, 115, 95, 107, 101, 121, 31, + 110, 101, 120, 116, 95, 108, 105, 115, 116, 95, 110, 111, 100, 101, 95, 105, 100, 95, + 105, 110, 95, 97, 99, 99, 101, 115, 115, 95, 107, 101, 121, 25, 103, 101, 116, 95, + 112, 114, 105, 99, 101, 95, 108, 101, 118, 101, 108, 115, 95, 102, 111, 114, 95, 115, + 105, 100, 101, 12, 103, 101, 116, 95, 104, 101, 97, 100, 95, 107, 101, 121, 35, 103, + 101, 116, 95, 112, 114, 105, 99, 101, 95, 108, 101, 118, 101, 108, 115, 95, 102, 111, + 114, 95, 115, 105, 100, 101, 95, 112, 97, 103, 105, 110, 97, 116, 101, 100, 16, 105, + 110, 100, 101, 120, 95, 111, 114, 100, 101, 114, 115, 95, 115, 100, 107, 10, 105, 110, + 99, 101, 110, 116, 105, 118, 101, 115, 21, 103, 101, 116, 95, 116, 97, 107, 101, 114, + 95, 102, 101, 101, 95, 100, 105, 118, 105, 115, 111, 114, 25, 99, 97, 108, 99, 117, + 108, 97, 116, 101, 95, 109, 97, 120, 95, 113, 117, 111, 116, 101, 95, 109, 97, 116, + 99, 104, 17, 97, 115, 115, 101, 115, 115, 95, 116, 97, 107, 101, 114, 95, 102, 101, + 101, 115, 15, 98, 111, 114, 114, 111, 119, 95, 104, 101, 97, 100, 95, 109, 117, 116, + 19, 102, 105, 108, 108, 95, 111, 114, 100, 101, 114, 95, 105, 110, 116, 101, 114, 110, + 97, 108, 26, 99, 114, 101, 97, 116, 101, 95, 102, 105, 108, 108, 95, 101, 118, 101, + 110, 116, 95, 105, 110, 116, 101, 114, 110, 97, 108, 17, 112, 108, 97, 99, 101, 95, + 108, 105, 109, 105, 116, 95, 111, 114, 100, 101, 114, 25, 103, 101, 116, 95, 97, 115, + 115, 101, 116, 95, 99, 111, 117, 110, 116, 115, 95, 105, 110, 116, 101, 114, 110, 97, + 108, 17, 119, 111, 117, 108, 100, 95, 117, 112, 100, 97, 116, 101, 95, 104, 101, 97, + 100, 17, 114, 97, 110, 103, 101, 95, 99, 104, 101, 99, 107, 95, 116, 114, 97, 100, + 101, 24, 119, 105, 116, 104, 100, 114, 97, 119, 95, 97, 115, 115, 101, 116, 115, 95, + 105, 110, 116, 101, 114, 110, 97, 108, 23, 100, 101, 112, 111, 115, 105, 116, 95, 97, + 115, 115, 101, 116, 115, 95, 105, 110, 116, 101, 114, 110, 97, 108, 7, 105, 115, 95, + 110, 111, 110, 101, 34, 103, 101, 116, 95, 110, 101, 120, 116, 95, 111, 114, 100, 101, + 114, 95, 97, 99, 99, 101, 115, 115, 95, 107, 101, 121, 95, 105, 110, 116, 101, 114, + 110, 97, 108, 21, 105, 110, 115, 101, 114, 116, 95, 99, 104, 101, 99, 107, 95, 101, + 118, 105, 99, 116, 105, 111, 110, 20, 112, 108, 97, 99, 101, 95, 111, 114, 100, 101, + 114, 95, 105, 110, 116, 101, 114, 110, 97, 108, 12, 100, 101, 115, 116, 114, 111, 121, + 95, 110, 111, 110, 101, 32, 101, 109, 105, 116, 95, 108, 105, 109, 105, 116, 95, 111, + 114, 100, 101, 114, 95, 101, 118, 101, 110, 116, 115, 95, 105, 110, 116, 101, 114, 110, + 97, 108, 27, 112, 108, 97, 99, 101, 95, 108, 105, 109, 105, 116, 95, 111, 114, 100, + 101, 114, 95, 99, 117, 115, 116, 111, 100, 105, 97, 110, 33, 112, 108, 97, 99, 101, + 95, 108, 105, 109, 105, 116, 95, 111, 114, 100, 101, 114, 95, 112, 97, 115, 115, 105, + 118, 101, 95, 97, 100, 118, 97, 110, 99, 101, 43, 112, 108, 97, 99, 101, 95, 108, + 105, 109, 105, 116, 95, 111, 114, 100, 101, 114, 95, 112, 97, 115, 115, 105, 118, 101, + 95, 97, 100, 118, 97, 110, 99, 101, 95, 99, 117, 115, 116, 111, 100, 105, 97, 110, + 38, 112, 108, 97, 99, 101, 95, 108, 105, 109, 105, 116, 95, 111, 114, 100, 101, 114, + 95, 112, 97, 115, 115, 105, 118, 101, 95, 97, 100, 118, 97, 110, 99, 101, 95, 117, + 115, 101, 114, 44, 112, 108, 97, 99, 101, 95, 108, 105, 109, 105, 116, 95, 111, 114, + 100, 101, 114, 95, 112, 97, 115, 115, 105, 118, 101, 95, 97, 100, 118, 97, 110, 99, + 101, 95, 117, 115, 101, 114, 95, 101, 110, 116, 114, 121, 22, 112, 108, 97, 99, 101, + 95, 108, 105, 109, 105, 116, 95, 111, 114, 100, 101, 114, 95, 117, 115, 101, 114, 28, + 112, 108, 97, 99, 101, 95, 108, 105, 109, 105, 116, 95, 111, 114, 100, 101, 114, 95, + 117, 115, 101, 114, 95, 101, 110, 116, 114, 121, 18, 112, 108, 97, 99, 101, 95, 109, + 97, 114, 107, 101, 116, 95, 111, 114, 100, 101, 114, 33, 101, 109, 105, 116, 95, 109, + 97, 114, 107, 101, 116, 95, 111, 114, 100, 101, 114, 95, 101, 118, 101, 110, 116, 115, + 95, 105, 110, 116, 101, 114, 110, 97, 108, 28, 112, 108, 97, 99, 101, 95, 109, 97, + 114, 107, 101, 116, 95, 111, 114, 100, 101, 114, 95, 99, 117, 115, 116, 111, 100, 105, + 97, 110, 23, 112, 108, 97, 99, 101, 95, 109, 97, 114, 107, 101, 116, 95, 111, 114, + 100, 101, 114, 95, 117, 115, 101, 114, 29, 112, 108, 97, 99, 101, 95, 109, 97, 114, + 107, 101, 116, 95, 111, 114, 100, 101, 114, 95, 117, 115, 101, 114, 95, 101, 110, 116, + 114, 121, 15, 114, 101, 103, 105, 115, 116, 101, 114, 95, 109, 97, 114, 107, 101, 116, + 31, 114, 101, 103, 105, 115, 116, 101, 114, 95, 101, 99, 111, 110, 105, 97, 95, 102, + 101, 101, 95, 115, 116, 111, 114, 101, 95, 101, 110, 116, 114, 121, 25, 114, 101, 103, + 105, 115, 116, 101, 114, 95, 109, 97, 114, 107, 101, 116, 95, 98, 97, 115, 101, 95, + 99, 111, 105, 110, 34, 114, 101, 103, 105, 115, 116, 101, 114, 95, 109, 97, 114, 107, + 101, 116, 95, 98, 97, 115, 101, 95, 99, 111, 105, 110, 95, 105, 110, 116, 101, 114, + 110, 97, 108, 4, 117, 116, 102, 56, 40, 114, 101, 103, 105, 115, 116, 101, 114, 95, + 109, 97, 114, 107, 101, 116, 95, 98, 97, 115, 101, 95, 99, 111, 105, 110, 95, 102, + 114, 111, 109, 95, 99, 111, 105, 110, 115, 116, 111, 114, 101, 27, 103, 101, 116, 95, + 109, 97, 114, 107, 101, 116, 95, 114, 101, 103, 105, 115, 116, 114, 97, 116, 105, 111, + 110, 95, 102, 101, 101, 8, 119, 105, 116, 104, 100, 114, 97, 119, 28, 114, 101, 103, + 105, 115, 116, 101, 114, 95, 109, 97, 114, 107, 101, 116, 95, 98, 97, 115, 101, 95, + 103, 101, 110, 101, 114, 105, 99, 21, 85, 110, 100, 101, 114, 119, 114, 105, 116, 101, + 114, 67, 97, 112, 97, 98, 105, 108, 105, 116, 121, 37, 114, 101, 103, 105, 115, 116, + 101, 114, 95, 109, 97, 114, 107, 101, 116, 95, 98, 97, 115, 101, 95, 103, 101, 110, + 101, 114, 105, 99, 95, 105, 110, 116, 101, 114, 110, 97, 108, 18, 103, 101, 116, 95, + 117, 110, 100, 101, 114, 119, 114, 105, 116, 101, 114, 95, 105, 100, 12, 71, 101, 110, + 101, 114, 105, 99, 65, 115, 115, 101, 116, 23, 115, 119, 97, 112, 95, 98, 101, 116, + 119, 101, 101, 110, 95, 99, 111, 105, 110, 115, 116, 111, 114, 101, 115, 21, 105, 115, + 95, 97, 99, 99, 111, 117, 110, 116, 95, 114, 101, 103, 105, 115, 116, 101, 114, 101, + 100, 8, 114, 101, 103, 105, 115, 116, 101, 114, 7, 98, 97, 108, 97, 110, 99, 101, + 4, 122, 101, 114, 111, 7, 100, 101, 112, 111, 115, 105, 116, 29, 115, 119, 97, 112, + 95, 98, 101, 116, 119, 101, 101, 110, 95, 99, 111, 105, 110, 115, 116, 111, 114, 101, + 115, 95, 101, 110, 116, 114, 121, 10, 115, 119, 97, 112, 95, 99, 111, 105, 110, 115, + 5, 118, 97, 108, 117, 101, 5, 109, 101, 114, 103, 101, 12, 115, 119, 97, 112, 95, + 103, 101, 110, 101, 114, 105, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 35, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, + 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, + 0, 20, 99, 111, 109, 112, 105, 108, 97, 116, 105, 111, 110, 95, 109, 101, 116, 97, + 100, 97, 116, 97, 9, 0, 3, 50, 46, 48, 3, 50, 46, 49, 18, 97, 112, 116, + 111, 115, 58, 58, 109, 101, 116, 97, 100, 97, 116, 97, 95, 118, 49, 178, 23, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 12, 69, 95, 77, 65, 88, 95, 66, 65, 83, + 69, 95, 48, 41, 77, 97, 120, 105, 109, 117, 109, 32, 98, 97, 115, 101, 32, 116, + 114, 97, 100, 101, 32, 97, 109, 111, 117, 110, 116, 32, 115, 112, 101, 99, 105, 102, + 105, 101, 100, 32, 97, 115, 32, 48, 46, 1, 0, 0, 0, 0, 0, 0, 0, 13, + 69, 95, 77, 65, 88, 95, 81, 85, 79, 84, 69, 95, 48, 42, 77, 97, 120, 105, + 109, 117, 109, 32, 113, 117, 111, 116, 101, 32, 116, 114, 97, 100, 101, 32, 97, 109, + 111, 117, 110, 116, 32, 115, 112, 101, 99, 105, 102, 105, 101, 100, 32, 97, 115, 32, + 48, 46, 2, 0, 0, 0, 0, 0, 0, 0, 22, 69, 95, 77, 73, 78, 95, 66, + 65, 83, 69, 95, 69, 88, 67, 69, 69, 68, 83, 95, 77, 65, 88, 60, 77, 105, + 110, 105, 109, 117, 109, 32, 98, 97, 115, 101, 32, 116, 114, 97, 100, 101, 32, 97, + 109, 111, 117, 110, 116, 32, 101, 120, 99, 101, 101, 100, 115, 32, 109, 97, 120, 105, + 109, 117, 109, 32, 98, 97, 115, 101, 32, 116, 114, 97, 100, 101, 32, 97, 109, 111, + 117, 110, 116, 46, 3, 0, 0, 0, 0, 0, 0, 0, 23, 69, 95, 77, 73, 78, + 95, 81, 85, 79, 84, 69, 95, 69, 88, 67, 69, 69, 68, 83, 95, 77, 65, 88, + 62, 77, 105, 110, 105, 109, 117, 109, 32, 113, 117, 111, 116, 101, 32, 116, 114, 97, + 100, 101, 32, 97, 109, 111, 117, 110, 116, 32, 101, 120, 99, 101, 101, 100, 115, 32, + 109, 97, 120, 105, 109, 117, 109, 32, 113, 117, 111, 116, 101, 32, 116, 114, 97, 100, + 101, 32, 97, 109, 111, 117, 110, 116, 46, 4, 0, 0, 0, 0, 0, 0, 0, 19, + 69, 95, 79, 86, 69, 82, 70, 76, 79, 87, 95, 65, 83, 83, 69, 84, 95, 73, + 78, 55, 70, 105, 108, 108, 105, 110, 103, 32, 111, 114, 100, 101, 114, 32, 119, 111, + 117, 108, 100, 32, 111, 118, 101, 114, 102, 108, 111, 119, 32, 97, 115, 115, 101, 116, + 32, 114, 101, 99, 101, 105, 118, 101, 100, 32, 102, 114, 111, 109, 32, 116, 114, 97, + 100, 101, 46, 5, 0, 0, 0, 0, 0, 0, 0, 22, 69, 95, 78, 79, 84, 95, + 69, 78, 79, 85, 71, 72, 95, 65, 83, 83, 69, 84, 95, 79, 85, 84, 31, 78, + 111, 116, 32, 101, 110, 111, 117, 103, 104, 32, 97, 115, 115, 101, 116, 32, 116, 111, + 32, 116, 114, 97, 100, 101, 32, 97, 119, 97, 121, 46, 6, 0, 0, 0, 0, 0, + 0, 0, 19, 69, 95, 73, 78, 86, 65, 76, 73, 68, 95, 77, 65, 82, 75, 69, + 84, 95, 73, 68, 24, 78, 111, 32, 109, 97, 114, 107, 101, 116, 32, 119, 105, 116, + 104, 32, 103, 105, 118, 101, 110, 32, 73, 68, 46, 7, 0, 0, 0, 0, 0, 0, + 0, 14, 69, 95, 73, 78, 86, 65, 76, 73, 68, 95, 66, 65, 83, 69, 27, 66, + 97, 115, 101, 32, 97, 115, 115, 101, 116, 32, 116, 121, 112, 101, 32, 105, 115, 32, + 105, 110, 118, 97, 108, 105, 100, 46, 8, 0, 0, 0, 0, 0, 0, 0, 15, 69, + 95, 73, 78, 86, 65, 76, 73, 68, 95, 81, 85, 79, 84, 69, 28, 81, 117, 111, + 116, 101, 32, 97, 115, 115, 101, 116, 32, 116, 121, 112, 101, 32, 105, 115, 32, 105, + 110, 118, 97, 108, 105, 100, 46, 9, 0, 0, 0, 0, 0, 0, 0, 21, 69, 95, + 77, 73, 78, 95, 66, 65, 83, 69, 95, 78, 79, 84, 95, 84, 82, 65, 68, 69, + 68, 52, 77, 105, 110, 105, 109, 117, 109, 32, 98, 97, 115, 101, 32, 97, 115, 115, + 101, 116, 32, 116, 114, 97, 100, 101, 32, 97, 109, 111, 117, 110, 116, 32, 114, 101, + 113, 117, 105, 114, 101, 109, 101, 110, 116, 32, 110, 111, 116, 32, 109, 101, 116, 46, + 10, 0, 0, 0, 0, 0, 0, 0, 22, 69, 95, 77, 73, 78, 95, 81, 85, 79, + 84, 69, 95, 78, 79, 84, 95, 84, 82, 65, 68, 69, 68, 52, 77, 105, 110, 105, + 109, 117, 109, 32, 113, 117, 111, 116, 101, 32, 99, 111, 105, 110, 32, 116, 114, 97, + 100, 101, 32, 97, 109, 111, 117, 110, 116, 32, 114, 101, 113, 117, 105, 114, 101, 109, + 101, 110, 116, 32, 110, 111, 116, 32, 109, 101, 116, 46, 11, 0, 0, 0, 0, 0, + 0, 0, 9, 69, 95, 80, 82, 73, 67, 69, 95, 48, 27, 79, 114, 100, 101, 114, + 32, 112, 114, 105, 99, 101, 32, 115, 112, 101, 99, 105, 102, 105, 101, 100, 32, 97, + 115, 32, 48, 46, 12, 0, 0, 0, 0, 0, 0, 0, 16, 69, 95, 80, 82, 73, + 67, 69, 95, 84, 79, 79, 95, 72, 73, 71, 72, 44, 79, 114, 100, 101, 114, 32, + 112, 114, 105, 99, 101, 32, 101, 120, 99, 101, 101, 100, 115, 32, 109, 97, 120, 105, + 109, 117, 109, 32, 97, 108, 108, 111, 119, 97, 98, 108, 101, 32, 112, 114, 105, 99, + 101, 46, 13, 0, 0, 0, 0, 0, 0, 0, 30, 69, 95, 80, 79, 83, 84, 95, + 79, 82, 95, 65, 66, 79, 82, 84, 95, 67, 82, 79, 83, 83, 69, 83, 95, 83, + 80, 82, 69, 65, 68, 47, 80, 111, 115, 116, 45, 111, 114, 45, 97, 98, 111, 114, + 116, 32, 108, 105, 109, 105, 116, 32, 111, 114, 100, 101, 114, 32, 112, 114, 105, 99, + 101, 32, 99, 114, 111, 115, 115, 101, 115, 32, 115, 112, 114, 101, 97, 100, 46, 14, + 0, 0, 0, 0, 0, 0, 0, 16, 69, 95, 83, 73, 90, 69, 95, 84, 79, 79, + 95, 83, 77, 65, 76, 76, 49, 79, 114, 100, 101, 114, 32, 115, 105, 122, 101, 32, + 100, 111, 101, 115, 32, 110, 111, 116, 32, 109, 101, 101, 116, 32, 109, 105, 110, 105, + 109, 117, 109, 32, 115, 105, 122, 101, 32, 102, 111, 114, 32, 109, 97, 114, 107, 101, + 116, 46, 15, 0, 0, 0, 0, 0, 0, 0, 20, 69, 95, 83, 73, 90, 69, 95, + 66, 65, 83, 69, 95, 79, 86, 69, 82, 70, 76, 79, 87, 55, 76, 105, 109, 105, + 116, 32, 111, 114, 100, 101, 114, 32, 115, 105, 122, 101, 32, 114, 101, 115, 117, 108, + 116, 115, 32, 105, 110, 32, 98, 97, 115, 101, 32, 97, 115, 115, 101, 116, 32, 97, + 109, 111, 117, 110, 116, 32, 111, 118, 101, 114, 102, 108, 111, 119, 46, 16, 0, 0, + 0, 0, 0, 0, 0, 27, 69, 95, 83, 73, 90, 69, 95, 80, 82, 73, 67, 69, + 95, 84, 73, 67, 75, 83, 95, 79, 86, 69, 82, 70, 76, 79, 87, 60, 76, 105, + 109, 105, 116, 32, 111, 114, 100, 101, 114, 32, 115, 105, 122, 101, 32, 97, 110, 100, + 32, 112, 114, 105, 99, 101, 32, 114, 101, 115, 117, 108, 116, 115, 32, 105, 110, 32, + 116, 105, 99, 107, 115, 32, 97, 109, 111, 117, 110, 116, 32, 111, 118, 101, 114, 102, + 108, 111, 119, 46, 17, 0, 0, 0, 0, 0, 0, 0, 27, 69, 95, 83, 73, 90, + 69, 95, 80, 82, 73, 67, 69, 95, 81, 85, 79, 84, 69, 95, 79, 86, 69, 82, + 70, 76, 79, 87, 60, 76, 105, 109, 105, 116, 32, 111, 114, 100, 101, 114, 32, 115, + 105, 122, 101, 32, 97, 110, 100, 32, 112, 114, 105, 99, 101, 32, 114, 101, 115, 117, + 108, 116, 115, 32, 105, 110, 32, 113, 117, 111, 116, 101, 32, 97, 109, 111, 117, 110, + 116, 32, 111, 118, 101, 114, 102, 108, 111, 119, 46, 18, 0, 0, 0, 0, 0, 0, + 0, 21, 69, 95, 73, 78, 86, 65, 76, 73, 68, 95, 82, 69, 83, 84, 82, 73, + 67, 84, 73, 79, 78, 25, 73, 110, 118, 97, 108, 105, 100, 32, 114, 101, 115, 116, + 114, 105, 99, 116, 105, 111, 110, 32, 102, 108, 97, 103, 46, 19, 0, 0, 0, 0, + 0, 0, 0, 12, 69, 95, 83, 69, 76, 70, 95, 77, 65, 84, 67, 72, 54, 65, + 32, 115, 101, 108, 102, 32, 109, 97, 116, 99, 104, 32, 111, 99, 99, 117, 114, 115, + 32, 119, 104, 101, 110, 32, 115, 101, 108, 102, 32, 109, 97, 116, 99, 104, 32, 98, + 101, 104, 97, 118, 105, 111, 114, 32, 105, 115, 32, 97, 98, 111, 114, 116, 46, 20, + 0, 0, 0, 0, 0, 0, 0, 29, 69, 95, 80, 82, 73, 67, 69, 95, 84, 73, + 77, 69, 95, 80, 82, 73, 79, 82, 73, 84, 89, 95, 84, 79, 79, 95, 76, 79, + 87, 58, 78, 111, 32, 114, 111, 111, 109, 32, 116, 111, 32, 105, 110, 115, 101, 114, + 116, 32, 111, 114, 100, 101, 114, 32, 119, 105, 116, 104, 32, 115, 117, 99, 104, 32, + 108, 111, 119, 32, 112, 114, 105, 99, 101, 45, 116, 105, 109, 101, 32, 112, 114, 105, + 111, 114, 105, 116, 121, 46, 21, 0, 0, 0, 0, 0, 0, 0, 21, 69, 95, 73, + 78, 86, 65, 76, 73, 68, 95, 85, 78, 68, 69, 82, 87, 82, 73, 84, 69, 82, + 37, 85, 110, 100, 101, 114, 119, 114, 105, 116, 101, 114, 32, 105, 110, 118, 97, 108, + 105, 100, 32, 102, 111, 114, 32, 103, 105, 118, 101, 110, 32, 109, 97, 114, 107, 101, + 116, 46, 22, 0, 0, 0, 0, 0, 0, 0, 25, 69, 95, 73, 78, 86, 65, 76, + 73, 68, 95, 77, 65, 82, 75, 69, 84, 95, 79, 82, 68, 69, 82, 95, 73, 68, + 24, 77, 97, 114, 107, 101, 116, 32, 111, 114, 100, 101, 114, 32, 73, 68, 32, 105, + 110, 118, 97, 108, 105, 100, 46, 23, 0, 0, 0, 0, 0, 0, 0, 19, 69, 95, + 73, 78, 86, 65, 76, 73, 68, 95, 67, 85, 83, 84, 79, 68, 73, 65, 78, 39, + 67, 117, 115, 116, 111, 100, 105, 97, 110, 32, 110, 111, 116, 32, 97, 117, 116, 104, + 111, 114, 105, 122, 101, 100, 32, 102, 111, 114, 32, 111, 112, 101, 114, 97, 116, 105, + 111, 110, 46, 24, 0, 0, 0, 0, 0, 0, 0, 14, 69, 95, 73, 78, 86, 65, + 76, 73, 68, 95, 85, 83, 69, 82, 37, 73, 110, 118, 97, 108, 105, 100, 32, 117, + 115, 101, 114, 32, 105, 110, 100, 105, 99, 97, 116, 101, 100, 32, 102, 111, 114, 32, + 111, 112, 101, 114, 97, 116, 105, 111, 110, 46, 25, 0, 0, 0, 0, 0, 0, 0, + 32, 69, 95, 70, 73, 76, 76, 95, 79, 82, 95, 65, 66, 79, 82, 84, 95, 78, + 79, 84, 95, 67, 82, 79, 83, 83, 95, 83, 80, 82, 69, 65, 68, 46, 70, 105, + 108, 108, 45, 111, 114, 45, 97, 98, 111, 114, 116, 32, 112, 114, 105, 99, 101, 32, + 100, 111, 101, 115, 32, 110, 111, 116, 32, 99, 114, 111, 115, 115, 32, 116, 104, 101, + 32, 115, 112, 114, 101, 97, 100, 46, 26, 0, 0, 0, 0, 0, 0, 0, 25, 69, + 95, 72, 69, 65, 68, 95, 75, 69, 89, 95, 80, 82, 73, 67, 69, 95, 77, 73, + 83, 77, 65, 84, 67, 72, 53, 65, 86, 76, 32, 113, 117, 101, 117, 101, 32, 104, + 101, 97, 100, 32, 112, 114, 105, 99, 101, 32, 100, 111, 101, 115, 32, 110, 111, 116, + 32, 109, 97, 116, 99, 104, 32, 104, 101, 97, 100, 32, 111, 114, 100, 101, 114, 32, + 112, 114, 105, 99, 101, 46, 27, 0, 0, 0, 0, 0, 0, 0, 24, 69, 95, 78, + 79, 84, 95, 83, 73, 77, 85, 76, 65, 84, 73, 79, 78, 95, 65, 67, 67, 79, + 85, 78, 84, 43, 83, 105, 109, 117, 108, 97, 116, 105, 111, 110, 32, 113, 117, 101, + 114, 121, 32, 99, 97, 108, 108, 101, 100, 32, 98, 121, 32, 105, 110, 118, 97, 108, + 105, 100, 32, 97, 99, 99, 111, 117, 110, 116, 46, 28, 0, 0, 0, 0, 0, 0, + 0, 29, 69, 95, 73, 78, 86, 65, 76, 73, 68, 95, 83, 69, 76, 70, 95, 77, + 65, 84, 67, 72, 95, 66, 69, 72, 65, 86, 73, 79, 82, 33, 73, 110, 118, 97, + 108, 105, 100, 32, 115, 101, 108, 102, 32, 109, 97, 116, 99, 104, 32, 98, 101, 104, + 97, 118, 105, 111, 114, 32, 102, 108, 97, 103, 46, 29, 0, 0, 0, 0, 0, 0, + 0, 17, 69, 95, 73, 78, 86, 65, 76, 73, 68, 95, 80, 69, 82, 67, 69, 78, + 84, 57, 80, 97, 115, 115, 105, 118, 101, 32, 97, 100, 118, 97, 110, 99, 101, 32, + 112, 101, 114, 99, 101, 110, 116, 32, 105, 115, 32, 110, 111, 116, 32, 108, 101, 115, + 115, 32, 116, 104, 97, 110, 32, 111, 114, 32, 101, 113, 117, 97, 108, 32, 116, 111, + 32, 49, 48, 48, 46, 30, 0, 0, 0, 0, 0, 0, 0, 29, 69, 95, 83, 73, + 90, 69, 95, 67, 72, 65, 78, 71, 69, 95, 73, 78, 83, 69, 82, 84, 73, 79, + 78, 95, 69, 82, 82, 79, 82, 84, 79, 114, 100, 101, 114, 32, 115, 105, 122, 101, + 32, 99, 104, 97, 110, 103, 101, 32, 114, 101, 113, 117, 105, 114, 105, 110, 103, 32, + 105, 110, 115, 101, 114, 116, 105, 111, 110, 32, 114, 101, 115, 117, 108, 116, 101, 100, + 32, 105, 110, 32, 97, 110, 32, 65, 86, 76, 32, 113, 117, 101, 117, 101, 10, 32, + 97, 99, 99, 101, 115, 115, 32, 107, 101, 121, 32, 109, 105, 115, 109, 97, 116, 99, + 104, 46, 31, 0, 0, 0, 0, 0, 0, 0, 20, 69, 95, 79, 82, 68, 69, 82, + 95, 68, 73, 68, 95, 78, 79, 84, 95, 80, 79, 83, 84, 51, 79, 114, 100, 101, + 114, 32, 73, 68, 32, 99, 111, 114, 114, 101, 115, 112, 111, 110, 100, 115, 32, 116, + 111, 32, 97, 110, 32, 111, 114, 100, 101, 114, 32, 116, 104, 97, 116, 32, 100, 105, + 100, 32, 110, 111, 116, 32, 112, 111, 115, 116, 46, 32, 0, 0, 0, 0, 0, 0, + 0, 22, 69, 95, 79, 82, 68, 69, 82, 95, 80, 82, 73, 67, 69, 95, 77, 73, + 83, 77, 65, 84, 67, 72, 63, 79, 114, 100, 101, 114, 32, 112, 114, 105, 99, 101, + 32, 102, 105, 101, 108, 100, 32, 100, 111, 101, 115, 32, 110, 111, 116, 32, 109, 97, + 116, 99, 104, 32, 65, 86, 76, 32, 113, 117, 101, 117, 101, 32, 105, 110, 115, 101, + 114, 116, 105, 111, 110, 32, 107, 101, 121, 32, 112, 114, 105, 99, 101, 46, 33, 0, + 0, 0, 0, 0, 0, 0, 28, 69, 95, 83, 73, 90, 69, 95, 67, 72, 65, 78, + 71, 69, 95, 66, 69, 76, 79, 87, 95, 77, 73, 78, 95, 83, 73, 90, 69, 62, + 78, 101, 119, 32, 111, 114, 100, 101, 114, 32, 115, 105, 122, 101, 32, 105, 115, 32, + 108, 101, 115, 115, 32, 116, 104, 97, 110, 32, 116, 104, 101, 32, 109, 105, 110, 105, + 109, 117, 109, 32, 111, 114, 100, 101, 114, 32, 115, 105, 122, 101, 32, 102, 111, 114, + 32, 109, 97, 114, 107, 101, 116, 46, 0, 19, 7, 103, 101, 116, 95, 65, 83, 75, + 1, 1, 0, 7, 103, 101, 116, 95, 66, 73, 68, 1, 1, 0, 7, 103, 101, 116, + 95, 66, 85, 89, 1, 1, 0, 8, 103, 101, 116, 95, 83, 69, 76, 76, 1, 1, + 0, 9, 103, 101, 116, 95, 65, 66, 79, 82, 84, 1, 1, 0, 9, 103, 101, 116, + 95, 84, 73, 67, 75, 83, 1, 1, 0, 11, 103, 101, 116, 95, 80, 69, 82, 67, + 69, 78, 84, 1, 1, 0, 12, 103, 101, 116, 95, 72, 73, 95, 80, 82, 73, 67, + 69, 1, 1, 0, 15, 103, 101, 116, 95, 67, 65, 78, 67, 69, 76, 95, 66, 79, + 84, 72, 1, 1, 0, 16, 103, 101, 116, 95, 67, 65, 78, 67, 69, 76, 95, 77, + 65, 75, 69, 82, 1, 1, 0, 16, 103, 101, 116, 95, 67, 65, 78, 67, 69, 76, + 95, 84, 65, 75, 69, 82, 1, 1, 0, 16, 103, 101, 116, 95, 77, 65, 88, 95, + 80, 79, 83, 83, 73, 66, 76, 69, 1, 1, 0, 16, 103, 101, 116, 95, 78, 79, + 95, 67, 85, 83, 84, 79, 68, 73, 65, 78, 1, 1, 0, 17, 103, 101, 116, 95, + 70, 73, 76, 76, 95, 79, 82, 95, 65, 66, 79, 82, 84, 1, 1, 0, 17, 103, + 101, 116, 95, 80, 79, 83, 84, 95, 79, 82, 95, 65, 66, 79, 82, 84, 1, 1, + 0, 18, 103, 101, 116, 95, 78, 79, 95, 82, 69, 83, 84, 82, 73, 67, 84, 73, + 79, 78, 1, 1, 0, 18, 103, 101, 116, 95, 78, 79, 95, 85, 78, 68, 69, 82, + 87, 82, 73, 84, 69, 82, 1, 1, 0, 23, 103, 101, 116, 95, 73, 77, 77, 69, + 68, 73, 65, 84, 69, 95, 79, 82, 95, 67, 65, 78, 67, 69, 76, 1, 1, 0, + 37, 103, 101, 116, 95, 109, 97, 114, 107, 101, 116, 95, 101, 118, 101, 110, 116, 95, + 104, 97, 110, 100, 108, 101, 95, 99, 114, 101, 97, 116, 105, 111, 110, 95, 105, 110, + 102, 111, 1, 1, 0, 0, 2, 1, 2, 11, 1, 2, 3, 8, 2, 3, 2, 5, + 7, 3, 8, 3, 9, 5, 10, 3, 11, 3, 4, 2, 8, 13, 3, 14, 1, 15, + 4, 9, 5, 10, 3, 16, 2, 7, 3, 8, 3, 5, 2, 3, 18, 5, 19, 3, + 20, 3, 2, 2, 2, 21, 11, 6, 1, 8, 7, 25, 11, 6, 1, 8, 8, 9, + 2, 12, 28, 8, 10, 31, 8, 11, 34, 8, 10, 35, 3, 36, 3, 37, 3, 38, + 3, 39, 11, 12, 1, 8, 3, 42, 11, 12, 1, 8, 3, 43, 3, 44, 11, 6, + 1, 8, 4, 45, 11, 6, 1, 8, 13, 14, 2, 1, 2, 11, 15, 2, 3, 8, + 9, 16, 2, 7, 13, 3, 14, 1, 51, 4, 52, 3, 8, 3, 9, 5, 10, 3, + 17, 2, 2, 39, 10, 8, 3, 42, 10, 8, 3, 18, 2, 2, 39, 10, 8, 16, + 42, 10, 8, 16, 8, 2, 10, 13, 3, 55, 5, 56, 5, 57, 1, 58, 3, 59, + 3, 60, 3, 61, 3, 62, 3, 51, 4, 19, 2, 2, 8, 3, 7, 4, 20, 2, + 3, 13, 3, 39, 10, 8, 19, 42, 10, 8, 19, 21, 2, 3, 19, 3, 66, 3, + 20, 3, 22, 2, 1, 2, 11, 1, 2, 3, 8, 23, 23, 2, 3, 21, 11, 6, + 1, 8, 7, 69, 11, 6, 1, 8, 24, 25, 11, 6, 1, 8, 8, 13, 2, 7, + 13, 3, 14, 1, 15, 4, 71, 5, 10, 3, 7, 3, 8, 3, 0, 0, 0, 2, + 0, 6, 33, 147, 2, 17, 1, 12, 13, 10, 13, 42, 6, 15, 0, 12, 14, 10, + 14, 46, 10, 2, 56, 0, 4, 141, 2, 11, 14, 10, 2, 56, 1, 12, 15, 10, + 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 34, 3, 20, 5, 29, 10, 15, 16, + 1, 20, 12, 16, 11, 3, 11, 16, 33, 4, 135, 2, 5, 29, 56, 2, 10, 15, + 16, 2, 20, 33, 4, 129, 2, 56, 3, 10, 15, 16, 3, 20, 33, 4, 251, 1, + 10, 2, 10, 0, 10, 15, 10, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 10, + 4, 10, 5, 10, 6, 10, 7, 10, 8, 10, 9, 10, 10, 49, 3, 11, 11, 11, + 12, 56, 4, 12, 18, 12, 19, 12, 20, 12, 21, 12, 22, 12, 16, 12, 23, 12, + 24, 10, 15, 16, 4, 20, 53, 49, 64, 47, 12, 25, 10, 13, 41, 0, 4, 76, + 5, 82, 17, 6, 12, 26, 14, 26, 56, 5, 18, 0, 45, 0, 11, 13, 42, 0, + 15, 5, 12, 27, 10, 27, 46, 10, 2, 56, 6, 4, 92, 5, 104, 17, 6, 12, + 28, 14, 28, 56, 7, 14, 28, 56, 8, 18, 4, 12, 29, 10, 27, 10, 2, 11, + 29, 56, 9, 11, 27, 10, 2, 56, 10, 12, 30, 10, 2, 10, 1, 11, 4, 11, + 5, 11, 6, 10, 7, 11, 8, 11, 9, 11, 10, 10, 25, 18, 10, 12, 31, 10, + 16, 12, 32, 11, 7, 12, 33, 11, 15, 16, 6, 20, 12, 34, 10, 20, 4, 246, + 1, 8, 12, 35, 11, 35, 4, 243, 1, 11, 20, 4, 217, 1, 49, 7, 56, 11, + 12, 36, 14, 36, 56, 12, 12, 37, 10, 37, 4, 214, 1, 11, 2, 12, 38, 11, + 25, 12, 39, 10, 1, 12, 40, 6, 0, 0, 0, 0, 0, 0, 0, 0, 11, 36, + 56, 13, 12, 17, 12, 41, 11, 38, 11, 39, 11, 40, 11, 41, 11, 17, 17, 15, + 56, 14, 12, 42, 56, 15, 12, 43, 11, 1, 7, 0, 33, 4, 208, 1, 10, 30, + 15, 7, 11, 31, 56, 16, 11, 37, 4, 205, 1, 11, 30, 15, 8, 13, 42, 56, + 17, 56, 18, 11, 0, 46, 17, 19, 11, 24, 12, 44, 11, 23, 12, 45, 11, 16, + 12, 38, 11, 22, 12, 41, 11, 21, 12, 46, 11, 43, 12, 47, 11, 42, 12, 48, + 11, 44, 11, 45, 11, 38, 11, 41, 11, 46, 11, 47, 11, 48, 2, 11, 30, 1, + 5, 180, 1, 11, 30, 1, 13, 43, 11, 31, 56, 19, 5, 180, 1, 56, 20, 12, + 42, 5, 163, 1, 11, 18, 4, 223, 1, 49, 9, 56, 11, 12, 36, 5, 139, 1, + 11, 19, 4, 229, 1, 49, 5, 56, 11, 12, 36, 5, 139, 1, 11, 33, 11, 32, + 23, 11, 34, 35, 4, 239, 1, 49, 8, 56, 11, 12, 36, 5, 139, 1, 49, 4, + 56, 11, 12, 36, 5, 139, 1, 56, 21, 12, 36, 5, 139, 1, 10, 32, 10, 33, + 35, 12, 35, 5, 132, 1, 11, 0, 1, 11, 15, 1, 6, 8, 0, 0, 0, 0, + 0, 0, 0, 39, 11, 0, 1, 11, 15, 1, 6, 7, 0, 0, 0, 0, 0, 0, + 0, 39, 11, 0, 1, 11, 15, 1, 6, 21, 0, 0, 0, 0, 0, 0, 0, 39, + 11, 0, 1, 11, 14, 1, 6, 6, 0, 0, 0, 0, 0, 0, 0, 39, 21, 0, + 0, 0, 35, 13, 17, 6, 12, 1, 11, 0, 1, 14, 1, 12, 0, 56, 22, 18, + 6, 12, 2, 11, 0, 11, 2, 45, 6, 2, 23, 1, 0, 0, 2, 2, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 2, 24, 1, 0, 0, 2, 2, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 25, 1, 0, 0, 2, 2, 8, 2, 26, 1, 0, 0, + 2, 2, 9, 2, 27, 0, 0, 1, 6, 41, 40, 10, 0, 10, 1, 10, 2, 10, + 3, 17, 28, 12, 4, 14, 4, 65, 39, 12, 5, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 12, 6, 10, 6, 10, 5, 35, 4, 39, 10, 0, 12, 7, 10, 1, 12, + 8, 10, 2, 12, 9, 10, 3, 12, 10, 14, 4, 10, 6, 66, 39, 20, 12, 11, + 11, 7, 11, 8, 11, 9, 11, 10, 11, 11, 17, 29, 11, 6, 6, 1, 0, 0, + 0, 0, 0, 0, 0, 22, 12, 6, 5, 11, 2, 30, 1, 0, 1, 6, 36, 9, + 11, 3, 17, 31, 12, 4, 11, 0, 11, 1, 11, 4, 11, 2, 17, 27, 2, 32, + 1, 4, 1, 6, 45, 7, 11, 0, 17, 33, 11, 1, 6, 0, 0, 0, 0, 0, + 0, 0, 0, 11, 2, 17, 27, 2, 29, 0, 0, 1, 6, 50, 76, 17, 1, 42, + 6, 15, 0, 12, 5, 10, 5, 46, 10, 1, 56, 0, 4, 72, 11, 5, 10, 1, + 56, 1, 12, 6, 10, 3, 8, 33, 4, 68, 11, 6, 15, 9, 12, 8, 10, 4, + 50, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 28, + 52, 12, 10, 10, 8, 46, 10, 10, 56, 23, 4, 65, 11, 8, 11, 10, 56, 24, + 19, 1, 12, 11, 12, 12, 12, 13, 10, 0, 11, 13, 33, 12, 7, 12, 14, 12, + 15, 11, 7, 4, 63, 10, 2, 11, 12, 33, 4, 61, 11, 0, 11, 1, 11, 2, + 11, 3, 11, 15, 11, 14, 11, 11, 11, 4, 49, 3, 17, 36, 1, 2, 6, 23, + 0, 0, 0, 0, 0, 0, 0, 39, 6, 24, 0, 0, 0, 0, 0, 0, 0, 39, + 11, 8, 1, 5, 60, 11, 6, 15, 10, 12, 8, 5, 20, 11, 5, 1, 6, 6, + 0, 0, 0, 0, 0, 0, 0, 39, 37, 1, 0, 1, 6, 36, 10, 11, 4, 17, + 31, 12, 5, 11, 0, 11, 1, 11, 5, 11, 2, 11, 3, 17, 29, 2, 38, 1, + 4, 1, 6, 45, 8, 11, 0, 17, 33, 11, 1, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 11, 2, 11, 3, 17, 29, 2, 39, 0, 0, 1, 6, 57, 162, 1, 17, + 1, 42, 6, 15, 0, 12, 6, 10, 6, 46, 10, 1, 56, 0, 4, 158, 1, 11, + 6, 10, 1, 56, 1, 12, 7, 10, 7, 16, 11, 20, 12, 8, 10, 5, 11, 8, + 38, 4, 154, 1, 10, 3, 8, 33, 4, 150, 1, 11, 7, 15, 9, 12, 10, 10, + 4, 50, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 28, 52, 12, 8, 10, 10, 46, 10, 8, 56, 23, 4, 146, 1, 10, 10, 46, 10, + 8, 56, 25, 12, 9, 10, 10, 10, 8, 56, 26, 12, 12, 10, 12, 16, 12, 20, + 12, 13, 10, 0, 11, 13, 33, 4, 140, 1, 10, 12, 16, 13, 20, 12, 14, 10, + 2, 11, 14, 33, 4, 134, 1, 10, 12, 16, 14, 20, 10, 12, 16, 15, 20, 10, + 12, 16, 16, 20, 12, 15, 12, 16, 12, 14, 11, 0, 11, 1, 11, 2, 11, 3, + 11, 14, 10, 5, 11, 16, 11, 15, 11, 4, 17, 42, 10, 8, 17, 43, 12, 14, + 10, 12, 16, 14, 20, 12, 16, 10, 5, 11, 16, 35, 4, 131, 1, 8, 12, 17, + 11, 17, 4, 109, 11, 10, 1, 11, 12, 15, 14, 12, 18, 11, 5, 11, 18, 21, + 2, 11, 12, 1, 10, 10, 10, 8, 56, 24, 12, 19, 13, 19, 15, 14, 12, 18, + 11, 5, 11, 18, 21, 11, 10, 11, 14, 11, 19, 56, 27, 11, 8, 33, 4, 129, + 1, 5, 108, 6, 30, 0, 0, 0, 0, 0, 0, 0, 39, 11, 9, 12, 17, 5, + 98, 11, 10, 1, 11, 12, 1, 6, 23, 0, 0, 0, 0, 0, 0, 0, 39, 11, + 10, 1, 11, 12, 1, 6, 24, 0, 0, 0, 0, 0, 0, 0, 39, 11, 10, 1, + 6, 22, 0, 0, 0, 0, 0, 0, 0, 39, 11, 7, 15, 10, 12, 10, 5, 28, + 11, 7, 1, 6, 33, 0, 0, 0, 0, 0, 0, 0, 39, 11, 6, 1, 6, 6, + 0, 0, 0, 0, 0, 0, 0, 39, 45, 1, 0, 1, 6, 36, 11, 11, 5, 17, + 31, 12, 6, 11, 0, 11, 1, 11, 6, 11, 2, 11, 3, 11, 4, 17, 39, 2, + 46, 1, 4, 1, 6, 45, 9, 11, 0, 17, 33, 11, 1, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 11, 2, 11, 3, 11, 4, 17, 39, 2, 47, 1, 0, 0, 2, + 2, 49, 0, 2, 48, 1, 0, 0, 2, 2, 9, 2, 49, 1, 0, 0, 2, 2, + 49, 1, 2, 50, 1, 0, 0, 2, 2, 49, 2, 2, 51, 1, 0, 0, 2, 2, + 49, 3, 2, 52, 1, 0, 0, 2, 2, 49, 1, 2, 53, 1, 0, 0, 2, 2, + 6, 255, 255, 255, 255, 0, 0, 0, 0, 2, 54, 1, 0, 0, 2, 2, 49, 2, + 2, 55, 1, 0, 0, 2, 2, 6, 255, 255, 255, 255, 255, 255, 255, 255, 2, 56, + 1, 0, 0, 2, 2, 49, 0, 2, 57, 1, 0, 0, 2, 2, 8, 2, 58, 1, + 0, 0, 2, 2, 49, 3, 2, 59, 1, 0, 0, 2, 2, 8, 2, 60, 1, 0, + 0, 2, 2, 9, 2, 61, 0, 0, 1, 0, 65, 39, 17, 1, 12, 1, 10, 1, + 41, 0, 3, 7, 56, 28, 2, 10, 1, 43, 0, 16, 5, 12, 2, 10, 2, 10, + 0, 56, 6, 3, 19, 11, 2, 1, 56, 28, 2, 11, 2, 11, 0, 56, 29, 12, + 3, 10, 3, 16, 8, 56, 30, 17, 64, 11, 3, 16, 7, 56, 31, 17, 64, 12, + 4, 12, 0, 11, 1, 11, 0, 11, 4, 18, 3, 56, 32, 2, 65, 0, 0, 0, + 73, 63, 64, 68, 0, 0, 0, 0, 0, 0, 0, 0, 12, 4, 10, 2, 8, 33, + 4, 59, 11, 1, 15, 9, 12, 6, 14, 4, 65, 68, 10, 3, 35, 4, 56, 10, + 6, 46, 56, 33, 32, 12, 5, 11, 5, 4, 52, 10, 6, 56, 34, 19, 1, 12, + 7, 12, 8, 12, 9, 10, 9, 10, 0, 10, 8, 10, 2, 11, 7, 17, 68, 56, + 35, 13, 4, 12, 10, 12, 11, 12, 12, 12, 13, 10, 0, 10, 2, 11, 11, 11, + 13, 11, 12, 11, 9, 11, 8, 18, 7, 12, 14, 11, 10, 11, 14, 68, 68, 5, + 9, 11, 6, 1, 11, 4, 2, 9, 12, 5, 5, 19, 11, 1, 15, 10, 12, 6, + 5, 9, 69, 0, 0, 0, 77, 174, 1, 10, 2, 8, 33, 4, 170, 1, 11, 0, + 16, 9, 12, 6, 64, 68, 0, 0, 0, 0, 0, 0, 0, 0, 12, 7, 10, 6, + 56, 33, 4, 165, 1, 8, 12, 5, 11, 5, 4, 23, 11, 6, 1, 11, 7, 12, + 8, 11, 8, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 10, 4, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 33, 4, 162, 1, 10, 6, 56, 36, 12, 11, 10, 11, 16, 12, + 20, 10, 11, 16, 13, 20, 11, 11, 16, 16, 20, 12, 12, 12, 13, 10, 1, 11, + 13, 10, 2, 11, 12, 17, 68, 56, 35, 12, 10, 11, 10, 17, 71, 12, 13, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 10, 12, 10, 3, 35, 3, 58, 5, + 119, 10, 6, 10, 13, 56, 37, 12, 11, 10, 11, 16, 12, 20, 10, 11, 16, 13, + 20, 10, 11, 16, 16, 20, 12, 15, 12, 16, 10, 1, 11, 16, 10, 2, 11, 15, + 17, 68, 56, 35, 13, 7, 10, 11, 16, 14, 20, 10, 11, 16, 15, 20, 10, 11, + 16, 12, 20, 11, 11, 16, 13, 20, 12, 17, 12, 14, 12, 15, 12, 16, 12, 18, + 12, 19, 10, 1, 10, 2, 11, 19, 11, 16, 11, 15, 11, 14, 11, 17, 18, 7, + 12, 20, 11, 18, 11, 20, 68, 68, 10, 6, 11, 13, 56, 38, 12, 13, 10, 13, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 157, 1, 5, 119, 10, 13, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 134, 1, 11, 6, 1, 50, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 19, 11, 7, + 12, 8, 11, 19, 12, 9, 11, 8, 11, 9, 2, 11, 6, 11, 13, 56, 37, 12, + 11, 10, 11, 16, 12, 20, 10, 11, 16, 13, 20, 11, 11, 16, 16, 20, 12, 15, + 12, 16, 11, 1, 11, 16, 11, 2, 11, 15, 17, 68, 56, 35, 12, 19, 5, 127, + 11, 12, 6, 1, 0, 0, 0, 0, 0, 0, 0, 22, 12, 12, 5, 53, 11, 4, + 12, 10, 5, 48, 10, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 12, 5, + 5, 14, 11, 0, 16, 10, 12, 6, 5, 7, 71, 0, 0, 0, 39, 5, 11, 0, + 50, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 28, + 52, 2, 74, 0, 0, 0, 83, 81, 64, 80, 0, 0, 0, 0, 0, 0, 0, 0, + 12, 3, 11, 1, 8, 33, 4, 77, 11, 0, 15, 9, 12, 5, 14, 3, 65, 80, + 10, 2, 35, 4, 74, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 12, 6, 10, 5, 46, 56, 39, 12, 7, 14, 7, 56, 40, 4, + 70, 11, 7, 56, 41, 12, 8, 10, 5, 46, 56, 33, 4, 31, 5, 64, 10, 5, + 46, 56, 39, 12, 9, 14, 9, 14, 8, 56, 42, 3, 40, 5, 64, 10, 5, 56, + 34, 19, 1, 1, 1, 1, 10, 8, 33, 12, 1, 12, 10, 11, 1, 4, 60, 11, + 10, 53, 12, 11, 11, 6, 11, 11, 22, 12, 6, 5, 26, 11, 5, 1, 6, 32, + 0, 0, 0, 0, 0, 0, 0, 39, 13, 3, 11, 8, 11, 6, 18, 11, 68, 80, + 5, 9, 11, 5, 1, 11, 3, 2, 11, 5, 1, 5, 72, 11, 0, 15, 10, 12, + 5, 5, 9, 77, 0, 0, 0, 85, 169, 1, 10, 2, 8, 33, 4, 165, 1, 11, + 0, 16, 9, 12, 6, 64, 80, 0, 0, 0, 0, 0, 0, 0, 0, 12, 7, 10, + 6, 56, 33, 4, 160, 1, 8, 12, 5, 11, 5, 4, 23, 11, 6, 1, 11, 7, + 12, 8, 11, 8, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 10, 4, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 33, 4, 157, 1, 10, 6, 56, 36, 12, 11, 10, 11, 16, + 12, 20, 10, 11, 16, 13, 20, 11, 11, 16, 16, 20, 12, 12, 12, 13, 10, 1, + 11, 13, 10, 2, 11, 12, 17, 68, 56, 35, 12, 10, 11, 10, 17, 71, 12, 13, + 10, 6, 10, 13, 56, 37, 16, 15, 20, 12, 12, 50, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 15, 6, 0, 0, 0, 0, 0, + 0, 0, 0, 12, 16, 10, 16, 10, 3, 35, 3, 66, 5, 94, 10, 6, 10, 13, + 56, 37, 12, 11, 10, 11, 16, 15, 20, 10, 12, 33, 4, 142, 1, 11, 11, 16, + 14, 20, 53, 12, 17, 11, 15, 11, 17, 22, 12, 15, 10, 6, 11, 13, 56, 38, + 12, 13, 10, 13, 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 137, 1, 5, + 94, 13, 7, 11, 12, 11, 15, 18, 11, 68, 80, 10, 13, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 33, 4, 114, 11, 6, 1, 50, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 17, 11, 7, 12, 8, 11, 17, 12, + 9, 11, 8, 11, 9, 2, 11, 6, 11, 13, 56, 37, 12, 19, 10, 19, 16, 12, + 20, 10, 19, 16, 13, 20, 11, 19, 16, 16, 20, 12, 20, 12, 18, 11, 1, 11, + 18, 11, 2, 11, 20, 17, 68, 56, 35, 12, 17, 5, 107, 11, 16, 6, 1, 0, + 0, 0, 0, 0, 0, 0, 22, 12, 16, 5, 61, 13, 7, 11, 12, 11, 15, 18, + 11, 68, 80, 10, 11, 16, 15, 20, 12, 12, 11, 11, 16, 14, 20, 53, 12, 15, + 5, 85, 11, 4, 12, 10, 5, 48, 10, 3, 6, 0, 0, 0, 0, 0, 0, 0, + 0, 33, 12, 5, 5, 14, 11, 0, 16, 10, 12, 6, 5, 7, 78, 1, 4, 0, + 2, 3, 11, 0, 1, 2, 5, 0, 0, 0, 94, 134, 4, 10, 11, 6, 255, 255, + 255, 255, 0, 0, 0, 0, 37, 4, 128, 4, 10, 6, 9, 33, 4, 253, 3, 8, + 12, 16, 10, 2, 16, 6, 20, 12, 15, 10, 2, 16, 17, 20, 12, 17, 17, 79, + 12, 18, 10, 6, 10, 18, 11, 10, 17, 80, 11, 8, 10, 15, 26, 12, 19, 10, + 17, 26, 12, 20, 10, 19, 12, 21, 10, 20, 12, 22, 10, 16, 8, 33, 4, 249, + 3, 10, 2, 15, 9, 12, 24, 9, 12, 23, 10, 2, 16, 4, 20, 6, 1, 0, + 0, 0, 0, 0, 0, 0, 22, 10, 2, 15, 4, 21, 11, 2, 16, 4, 20, 53, + 49, 64, 47, 12, 25, 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, 26, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 12, 27, 9, 12, 28, 10, 24, 46, 56, 33, 3, + 246, 3, 10, 24, 46, 56, 39, 12, 29, 14, 29, 56, 43, 20, 12, 30, 10, 6, + 9, 33, 4, 243, 3, 10, 30, 10, 11, 36, 12, 31, 11, 31, 4, 231, 3, 8, + 12, 32, 11, 32, 4, 175, 1, 11, 1, 1, 8, 12, 28, 11, 19, 11, 21, 23, + 11, 15, 24, 12, 33, 11, 20, 11, 22, 23, 11, 17, 24, 12, 34, 10, 27, 10, + 18, 24, 12, 35, 11, 0, 11, 5, 11, 18, 11, 35, 11, 14, 56, 44, 1, 12, + 36, 11, 6, 9, 33, 4, 170, 1, 11, 34, 10, 27, 22, 12, 35, 10, 33, 11, + 7, 38, 4, 166, 1, 10, 35, 11, 9, 38, 4, 162, 1, 11, 13, 12, 38, 11, + 36, 12, 39, 11, 33, 12, 40, 11, 35, 12, 41, 11, 27, 12, 42, 11, 23, 12, + 37, 11, 24, 46, 56, 33, 11, 28, 12, 43, 12, 44, 11, 38, 11, 39, 11, 40, + 11, 41, 11, 42, 11, 37, 11, 44, 11, 43, 2, 11, 24, 1, 6, 10, 0, 0, + 0, 0, 0, 0, 0, 39, 11, 24, 1, 6, 9, 0, 0, 0, 0, 0, 0, 0, + 39, 11, 34, 10, 27, 23, 12, 35, 5, 127, 10, 22, 10, 30, 26, 12, 40, 10, + 40, 10, 21, 35, 4, 228, 3, 11, 40, 12, 41, 10, 24, 56, 45, 12, 45, 10, + 45, 16, 15, 20, 10, 30, 33, 4, 220, 3, 10, 45, 16, 14, 20, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 33, 4, 222, 1, 11, 45, 1, 10, 24, 56, 34, 19, + 1, 12, 35, 12, 34, 12, 48, 12, 33, 12, 42, 11, 48, 10, 0, 11, 34, 10, + 16, 11, 42, 11, 33, 11, 35, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 49, 1, 17, 36, 1, 5, 65, 10, 45, 16, 14, 20, + 12, 42, 10, 41, 11, 42, 35, 4, 213, 3, 11, 41, 12, 42, 9, 12, 44, 10, + 42, 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 243, 1, 11, 1, 1, 11, + 45, 1, 5, 95, 10, 45, 16, 12, 20, 12, 48, 10, 45, 16, 13, 20, 12, 33, + 10, 3, 10, 48, 33, 4, 210, 3, 10, 4, 10, 33, 33, 12, 43, 11, 43, 4, + 219, 2, 10, 12, 49, 0, 34, 4, 211, 2, 9, 12, 37, 10, 12, 49, 1, 33, + 4, 189, 2, 8, 12, 37, 8, 12, 23, 11, 37, 4, 186, 2, 10, 45, 16, 14, + 20, 11, 45, 16, 16, 20, 12, 35, 12, 34, 11, 48, 10, 0, 11, 33, 10, 16, + 11, 34, 11, 30, 11, 35, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 49, 6, 17, 36, 50, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 34, 10, 24, 11, 34, 56, 24, + 19, 1, 1, 1, 1, 1, 1, 10, 23, 3, 183, 2, 5, 65, 11, 1, 1, 5, + 95, 11, 45, 1, 5, 180, 2, 10, 12, 49, 2, 33, 4, 196, 2, 8, 12, 37, + 5, 147, 2, 10, 12, 49, 3, 33, 4, 203, 2, 8, 12, 23, 5, 147, 2, 11, + 1, 1, 11, 24, 1, 11, 45, 1, 6, 28, 0, 0, 0, 0, 0, 0, 0, 39, + 11, 1, 1, 11, 24, 1, 11, 45, 1, 6, 19, 0, 0, 0, 0, 0, 0, 0, + 39, 10, 42, 10, 30, 24, 12, 34, 10, 34, 10, 17, 24, 12, 35, 11, 21, 10, + 42, 23, 12, 21, 11, 22, 11, 34, 23, 12, 22, 10, 45, 16, 16, 20, 10, 45, + 16, 14, 20, 10, 42, 10, 15, 24, 12, 49, 12, 34, 12, 50, 10, 48, 10, 0, + 10, 33, 10, 16, 11, 50, 11, 34, 10, 42, 10, 44, 11, 13, 11, 14, 11, 49, + 10, 35, 56, 46, 12, 47, 12, 14, 12, 13, 11, 35, 10, 18, 26, 12, 50, 10, + 0, 10, 42, 11, 30, 10, 16, 11, 48, 11, 33, 10, 47, 10, 3, 10, 4, 10, + 25, 10, 50, 10, 26, 17, 85, 12, 51, 10, 1, 11, 51, 68, 93, 11, 26, 6, + 1, 0, 0, 0, 0, 0, 0, 0, 22, 12, 26, 11, 27, 11, 50, 22, 12, 27, + 11, 44, 4, 199, 3, 11, 45, 1, 11, 47, 50, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 28, 52, 12, 50, 10, 24, 11, 50, 56, + 24, 19, 1, 1, 1, 1, 1, 1, 10, 21, 6, 0, 0, 0, 0, 0, 0, 0, + 0, 33, 4, 194, 3, 8, 12, 37, 11, 37, 3, 191, 3, 5, 65, 11, 1, 1, + 5, 95, 10, 22, 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 12, 37, 5, 188, + 3, 11, 1, 1, 10, 45, 16, 14, 20, 11, 42, 23, 11, 45, 15, 14, 21, 5, + 95, 9, 12, 43, 5, 131, 2, 10, 45, 16, 14, 20, 12, 42, 8, 12, 44, 5, + 234, 1, 11, 1, 1, 11, 24, 1, 11, 45, 1, 6, 26, 0, 0, 0, 0, 0, + 0, 0, 39, 10, 21, 12, 41, 5, 185, 1, 10, 6, 8, 33, 4, 240, 3, 10, + 30, 10, 11, 35, 12, 32, 5, 89, 9, 12, 32, 5, 89, 9, 12, 31, 5, 85, + 11, 1, 1, 5, 95, 10, 2, 15, 10, 12, 24, 5, 42, 9, 12, 16, 5, 10, + 11, 1, 1, 11, 2, 1, 6, 12, 0, 0, 0, 0, 0, 0, 0, 39, 86, 0, + 0, 1, 6, 107, 242, 3, 10, 7, 49, 3, 37, 4, 240, 3, 10, 6, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 34, 4, 238, 3, 10, 6, 6, 255, 255, 255, 255, + 0, 0, 0, 0, 37, 4, 236, 3, 10, 0, 10, 1, 10, 2, 17, 87, 12, 12, + 12, 13, 1, 12, 14, 12, 15, 1, 17, 1, 42, 6, 15, 0, 10, 1, 56, 1, + 12, 16, 56, 2, 10, 16, 16, 2, 20, 33, 4, 232, 3, 56, 3, 10, 16, 16, + 3, 20, 33, 4, 228, 3, 10, 16, 16, 11, 20, 12, 17, 10, 5, 11, 17, 38, + 4, 224, 3, 10, 16, 16, 1, 20, 12, 17, 10, 4, 8, 33, 4, 217, 3, 10, + 16, 16, 10, 10, 6, 56, 47, 32, 12, 18, 10, 7, 49, 1, 33, 4, 214, 3, + 10, 18, 32, 12, 19, 11, 19, 3, 210, 3, 10, 7, 49, 3, 33, 4, 207, 3, + 10, 18, 12, 20, 11, 20, 3, 203, 3, 10, 5, 53, 10, 16, 16, 6, 20, 53, + 24, 12, 21, 10, 21, 50, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 37, 4, 199, 3, 10, 5, 53, 10, 6, 53, 24, 12, 22, 10, + 22, 50, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 37, 4, 195, 3, 10, 16, 16, 17, 20, 53, 12, 23, 11, 22, 11, 23, 24, 12, + 23, 10, 23, 50, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 37, 4, 191, 3, 11, 21, 52, 12, 25, 10, 7, 49, 1, 33, 4, 188, + 3, 10, 25, 12, 26, 10, 18, 4, 184, 3, 10, 4, 8, 33, 4, 181, 3, 6, + 255, 255, 255, 255, 255, 255, 255, 255, 10, 12, 23, 12, 28, 10, 4, 8, 33, 4, + 178, 3, 8, 12, 27, 10, 27, 10, 26, 10, 25, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 10, 28, 11, 15, 11, 14, 11, 13, 11, 12, 17, 89, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 12, 29, 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, 30, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, 31, 56, 21, 12, 32, 64, 93, 0, + 0, 0, 0, 0, 0, 0, 0, 12, 33, 10, 5, 12, 34, 11, 18, 4, 161, 3, + 10, 27, 9, 33, 4, 156, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, 36, + 10, 28, 12, 37, 10, 0, 10, 1, 10, 2, 10, 36, 11, 37, 10, 17, 56, 48, + 12, 38, 12, 39, 13, 33, 12, 41, 10, 1, 11, 41, 10, 16, 10, 0, 10, 2, + 10, 3, 10, 27, 11, 26, 11, 25, 6, 0, 0, 0, 0, 0, 0, 0, 0, 11, + 28, 10, 6, 10, 8, 11, 39, 11, 38, 56, 4, 1, 1, 12, 42, 12, 31, 12, + 30, 12, 29, 12, 38, 12, 39, 11, 27, 9, 33, 4, 151, 3, 10, 29, 12, 40, + 10, 0, 10, 1, 10, 2, 11, 40, 11, 39, 11, 38, 11, 17, 56, 49, 10, 16, + 16, 6, 20, 12, 44, 10, 29, 11, 44, 26, 12, 44, 10, 5, 11, 44, 23, 12, + 34, 11, 42, 4, 242, 2, 13, 32, 49, 7, 56, 50, 10, 16, 16, 4, 20, 53, + 49, 64, 47, 12, 24, 14, 32, 56, 51, 4, 239, 2, 10, 34, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 12, 43, 11, 43, 4, 236, 2, 10, 0, 10, 1, 10, + 2, 10, 4, 17, 93, 12, 44, 10, 4, 8, 33, 4, 232, 2, 11, 16, 15, 9, + 12, 46, 10, 34, 10, 6, 10, 0, 10, 2, 10, 44, 18, 1, 12, 47, 11, 46, + 10, 6, 11, 47, 11, 9, 56, 52, 12, 48, 12, 49, 12, 50, 10, 50, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 34, 4, 230, 2, 11, 50, 53, 12, 52, 11, 24, + 11, 52, 27, 12, 24, 10, 0, 10, 1, 10, 2, 10, 4, 10, 34, 10, 6, 10, + 24, 11, 44, 17, 95, 11, 49, 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, + 210, 2, 11, 48, 56, 53, 14, 33, 14, 32, 12, 53, 12, 54, 11, 1, 11, 0, + 11, 2, 11, 3, 11, 4, 11, 5, 11, 6, 11, 7, 11, 8, 11, 34, 10, 24, + 11, 54, 11, 53, 17, 97, 11, 24, 12, 52, 11, 29, 12, 51, 11, 30, 12, 55, + 11, 31, 12, 56, 11, 52, 11, 51, 11, 55, 11, 56, 2, 11, 48, 56, 54, 19, + 1, 12, 57, 12, 51, 12, 58, 12, 56, 12, 55, 11, 58, 10, 1, 11, 51, 10, + 4, 11, 55, 11, 56, 11, 57, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 49, 1, 17, 36, 1, 5, 179, 2, 6, 20, 0, 0, + 0, 0, 0, 0, 0, 39, 11, 16, 15, 10, 12, 46, 5, 138, 2, 11, 16, 1, + 5, 179, 2, 9, 12, 43, 5, 251, 1, 10, 34, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 36, 3, 247, 2, 5, 237, 1, 10, 7, 49, 2, 33, 4, 255, 2, 13, + 32, 49, 2, 56, 50, 5, 237, 1, 10, 4, 8, 33, 4, 144, 3, 10, 16, 16, + 10, 10, 6, 56, 47, 32, 12, 43, 11, 43, 3, 140, 3, 5, 237, 1, 13, 32, + 49, 4, 56, 50, 5, 237, 1, 10, 16, 16, 9, 10, 6, 56, 47, 32, 12, 43, + 5, 137, 3, 11, 36, 10, 29, 23, 12, 40, 5, 212, 1, 10, 25, 12, 36, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 12, 37, 5, 171, 1, 10, 16, 16, 4, 20, + 6, 1, 0, 0, 0, 0, 0, 0, 0, 22, 10, 16, 15, 4, 21, 10, 7, 49, + 2, 33, 3, 174, 3, 5, 237, 1, 13, 32, 49, 2, 56, 50, 5, 237, 1, 9, + 12, 27, 5, 139, 1, 10, 13, 12, 28, 5, 133, 1, 11, 23, 52, 12, 28, 5, + 133, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, 26, 5, 123, 11, 16, 1, + 6, 17, 0, 0, 0, 0, 0, 0, 0, 39, 11, 16, 1, 6, 16, 0, 0, 0, + 0, 0, 0, 0, 39, 11, 16, 1, 6, 15, 0, 0, 0, 0, 0, 0, 0, 39, + 11, 16, 1, 6, 13, 0, 0, 0, 0, 0, 0, 0, 39, 9, 12, 20, 5, 77, + 11, 16, 1, 6, 25, 0, 0, 0, 0, 0, 0, 0, 39, 9, 12, 19, 5, 69, + 10, 16, 16, 9, 10, 6, 56, 47, 32, 12, 18, 5, 62, 11, 16, 1, 6, 14, + 0, 0, 0, 0, 0, 0, 0, 39, 11, 16, 1, 6, 8, 0, 0, 0, 0, 0, + 0, 0, 39, 11, 16, 1, 6, 7, 0, 0, 0, 0, 0, 0, 0, 39, 6, 12, + 0, 0, 0, 0, 0, 0, 0, 39, 6, 11, 0, 0, 0, 0, 0, 0, 0, 39, + 6, 18, 0, 0, 0, 0, 0, 0, 0, 39, 98, 1, 0, 1, 6, 109, 15, 11, + 8, 17, 31, 12, 10, 11, 0, 11, 1, 11, 10, 11, 2, 11, 3, 11, 4, 11, + 5, 11, 6, 11, 7, 49, 18, 56, 55, 2, 99, 0, 0, 1, 6, 111, 205, 1, + 17, 1, 43, 6, 16, 0, 12, 8, 10, 8, 10, 1, 56, 0, 4, 201, 1, 11, + 8, 10, 1, 56, 56, 12, 9, 56, 2, 10, 9, 16, 2, 20, 33, 4, 197, 1, + 56, 3, 10, 9, 16, 3, 20, 33, 4, 193, 1, 10, 9, 16, 10, 56, 39, 12, + 10, 11, 9, 16, 9, 56, 39, 12, 11, 10, 4, 8, 33, 4, 188, 1, 11, 11, + 12, 13, 11, 10, 12, 14, 14, 13, 56, 57, 4, 45, 50, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 14, 13, 56, 43, 20, 12, + 15, 10, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 33, 4, 70, 11, 15, 12, + 16, 11, 0, 11, 1, 11, 2, 11, 3, 11, 4, 11, 5, 11, 16, 49, 3, 49, + 0, 49, 18, 56, 55, 1, 1, 1, 2, 14, 14, 56, 57, 4, 75, 50, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 14, 14, 56, + 43, 20, 12, 21, 10, 4, 8, 33, 4, 174, 1, 11, 21, 6, 1, 0, 0, 0, + 0, 0, 0, 0, 22, 12, 22, 10, 22, 10, 15, 35, 4, 171, 1, 11, 22, 12, + 23, 10, 23, 10, 15, 33, 4, 100, 11, 15, 12, 16, 5, 55, 10, 4, 8, 33, + 4, 166, 1, 10, 15, 10, 23, 23, 12, 24, 11, 6, 8, 33, 4, 145, 1, 10, + 7, 6, 100, 0, 0, 0, 0, 0, 0, 0, 37, 4, 143, 1, 10, 7, 6, 100, + 0, 0, 0, 0, 0, 0, 0, 33, 4, 123, 11, 23, 12, 16, 5, 55, 11, 24, + 11, 7, 24, 6, 100, 0, 0, 0, 0, 0, 0, 0, 26, 12, 25, 10, 4, 8, + 33, 4, 138, 1, 11, 15, 11, 25, 23, 12, 16, 5, 55, 11, 15, 11, 25, 22, + 12, 16, 5, 55, 6, 29, 0, 0, 0, 0, 0, 0, 0, 39, 10, 7, 11, 24, + 38, 4, 152, 1, 11, 23, 12, 16, 5, 55, 10, 4, 8, 33, 4, 161, 1, 11, + 15, 11, 7, 23, 12, 16, 5, 55, 11, 15, 11, 7, 22, 12, 16, 5, 55, 10, + 23, 10, 15, 23, 12, 24, 5, 108, 10, 15, 12, 23, 5, 93, 11, 21, 6, 1, + 0, 0, 0, 0, 0, 0, 0, 23, 12, 16, 10, 16, 10, 15, 36, 4, 185, 1, + 11, 16, 12, 23, 5, 93, 10, 15, 12, 23, 5, 93, 11, 10, 12, 13, 11, 11, + 12, 14, 5, 40, 11, 9, 1, 6, 8, 0, 0, 0, 0, 0, 0, 0, 39, 11, + 9, 1, 6, 7, 0, 0, 0, 0, 0, 0, 0, 39, 11, 8, 1, 6, 6, 0, + 0, 0, 0, 0, 0, 0, 39, 101, 1, 0, 1, 6, 36, 13, 11, 7, 17, 31, + 12, 8, 11, 0, 11, 1, 11, 8, 11, 2, 11, 3, 11, 4, 11, 5, 11, 6, + 56, 58, 2, 102, 1, 0, 1, 6, 45, 11, 11, 0, 17, 33, 11, 1, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 11, 2, 11, 3, 11, 4, 11, 5, 11, 6, 56, + 58, 2, 103, 1, 4, 1, 6, 2, 10, 11, 0, 11, 1, 11, 2, 11, 3, 11, + 4, 11, 5, 11, 6, 56, 59, 1, 2, 104, 1, 0, 1, 6, 115, 13, 11, 0, + 17, 33, 11, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 11, 2, 11, 3, 11, + 4, 11, 5, 11, 6, 11, 7, 49, 18, 56, 55, 2, 105, 1, 4, 1, 6, 2, + 14, 11, 0, 11, 1, 11, 2, 11, 3, 11, 4, 11, 5, 11, 6, 11, 7, 56, + 60, 1, 1, 1, 1, 2, 106, 0, 0, 1, 6, 119, 130, 2, 10, 0, 10, 1, + 10, 2, 17, 87, 12, 7, 12, 8, 1, 12, 9, 12, 10, 1, 17, 1, 42, 6, + 15, 0, 10, 1, 56, 1, 12, 11, 56, 2, 10, 11, 16, 2, 20, 33, 4, 254, + 1, 56, 3, 10, 11, 16, 3, 20, 33, 4, 250, 1, 10, 11, 16, 11, 20, 12, + 12, 10, 5, 11, 12, 38, 4, 246, 1, 10, 5, 53, 10, 11, 16, 6, 20, 53, + 24, 12, 13, 10, 13, 50, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, + 0, 0, 0, 0, 37, 4, 242, 1, 10, 11, 16, 1, 20, 12, 12, 11, 13, 52, + 12, 15, 10, 4, 9, 33, 4, 237, 1, 10, 8, 12, 17, 10, 4, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 10, 15, 6, 0, 0, 0, 0, 0, 0, 0, 0, 10, + 17, 11, 10, 11, 9, 11, 8, 11, 7, 17, 89, 10, 4, 9, 33, 4, 232, 1, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, 19, 10, 17, 12, 18, 10, 0, 10, + 1, 10, 2, 10, 19, 11, 18, 10, 12, 56, 48, 12, 20, 12, 21, 10, 4, 8, + 33, 4, 229, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, 22, 64, 93, 0, + 0, 0, 0, 0, 0, 0, 0, 12, 23, 13, 23, 12, 26, 10, 1, 11, 26, 10, + 11, 10, 0, 10, 2, 10, 3, 10, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 10, 15, 6, 0, 0, 0, 0, 0, 0, 0, 0, 11, 17, 11, 22, 10, 6, 11, + 21, 11, 20, 56, 4, 1, 12, 27, 12, 28, 12, 29, 12, 24, 12, 25, 12, 30, + 12, 31, 10, 11, 16, 4, 20, 53, 49, 64, 47, 12, 14, 10, 4, 9, 33, 4, + 224, 1, 10, 25, 12, 33, 10, 0, 10, 1, 10, 2, 11, 33, 11, 31, 11, 30, + 11, 12, 56, 49, 11, 28, 12, 32, 10, 25, 12, 34, 11, 15, 12, 35, 11, 27, + 12, 36, 11, 11, 16, 6, 20, 12, 37, 10, 32, 4, 219, 1, 8, 12, 38, 11, + 38, 4, 216, 1, 11, 32, 4, 191, 1, 49, 7, 56, 11, 12, 39, 14, 23, 14, + 39, 12, 40, 12, 41, 11, 1, 11, 0, 11, 2, 11, 3, 11, 4, 11, 5, 11, + 6, 11, 14, 11, 41, 11, 40, 17, 107, 11, 25, 12, 42, 11, 24, 12, 43, 11, + 29, 12, 44, 11, 42, 11, 43, 11, 44, 2, 5, 196, 1, 49, 9, 56, 11, 12, + 39, 5, 166, 1, 11, 36, 4, 202, 1, 49, 5, 56, 11, 12, 39, 5, 166, 1, + 11, 35, 11, 34, 23, 11, 37, 35, 4, 212, 1, 49, 8, 56, 11, 12, 39, 5, + 166, 1, 49, 4, 56, 11, 12, 39, 5, 166, 1, 56, 21, 12, 39, 5, 166, 1, + 10, 34, 10, 35, 35, 12, 38, 5, 159, 1, 11, 19, 10, 25, 23, 12, 33, 5, + 135, 1, 6, 255, 255, 255, 255, 0, 0, 0, 0, 12, 22, 5, 94, 10, 15, 12, + 19, 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, 18, 5, 79, 6, 255, 255, 255, + 255, 255, 255, 255, 255, 10, 7, 23, 12, 17, 5, 61, 11, 11, 1, 6, 15, 0, + 0, 0, 0, 0, 0, 0, 39, 11, 11, 1, 6, 14, 0, 0, 0, 0, 0, 0, + 0, 39, 11, 11, 1, 6, 8, 0, 0, 0, 0, 0, 0, 0, 39, 11, 11, 1, + 6, 7, 0, 0, 0, 0, 0, 0, 0, 39, 108, 1, 0, 1, 6, 36, 12, 11, + 6, 17, 31, 12, 7, 11, 0, 11, 1, 11, 7, 11, 2, 11, 3, 11, 4, 11, + 5, 56, 61, 2, 109, 1, 0, 1, 6, 45, 10, 11, 0, 17, 33, 11, 1, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 11, 2, 11, 3, 11, 4, 11, 5, 56, 61, + 2, 110, 1, 4, 1, 6, 2, 11, 11, 0, 11, 1, 11, 2, 11, 3, 11, 4, + 11, 5, 56, 62, 1, 1, 1, 2, 89, 0, 0, 0, 122, 62, 10, 2, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 4, 60, 10, 4, 6, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 4, 58, 11, 1, 10, 2, 37, 4, 56, 11, 3, 10, 4, 37, + 4, 54, 11, 0, 9, 33, 4, 45, 11, 6, 12, 9, 11, 2, 12, 11, 11, 7, + 12, 12, 11, 4, 12, 13, 11, 9, 53, 11, 11, 53, 22, 50, 255, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 37, 4, 43, 11, 13, 11, + 12, 37, 4, 41, 2, 6, 5, 0, 0, 0, 0, 0, 0, 0, 39, 6, 4, 0, + 0, 0, 0, 0, 0, 0, 39, 11, 8, 12, 9, 11, 4, 12, 11, 11, 5, 12, + 12, 11, 2, 12, 13, 5, 28, 6, 3, 0, 0, 0, 0, 0, 0, 0, 39, 6, + 2, 0, 0, 0, 0, 0, 0, 0, 39, 6, 1, 0, 0, 0, 0, 0, 0, 0, + 39, 6, 0, 0, 0, 0, 0, 0, 0, 0, 39, 111, 0, 0, 1, 6, 128, 1, + 49, 17, 6, 12, 6, 14, 6, 17, 33, 42, 6, 15, 0, 10, 0, 12, 7, 56, + 2, 56, 3, 8, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 56, 63, 9, 6, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 56, 63, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 14, 6, 56, 64, 14, 6, 56, 65, 12, 8, 12, 9, 12, 10, 12, 11, 12, 12, + 12, 13, 11, 1, 11, 13, 11, 2, 11, 3, 11, 4, 11, 5, 11, 12, 11, 11, + 11, 10, 11, 9, 11, 8, 18, 5, 12, 15, 11, 7, 11, 15, 56, 66, 10, 0, + 56, 67, 11, 0, 2, 115, 1, 0, 1, 6, 133, 1, 13, 10, 0, 10, 1, 10, + 2, 11, 3, 56, 68, 7, 1, 17, 117, 11, 0, 11, 1, 11, 2, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 56, 69, 2, 118, 1, 4, 1, 6, 137, 1, 13, 17, + 119, 12, 4, 11, 0, 11, 4, 56, 70, 12, 5, 11, 1, 11, 2, 11, 3, 11, + 5, 56, 71, 1, 2, 121, 1, 0, 1, 6, 142, 1, 17, 10, 0, 10, 1, 10, + 2, 10, 3, 10, 5, 11, 4, 56, 72, 11, 5, 17, 123, 12, 6, 11, 0, 11, + 1, 11, 2, 11, 3, 11, 6, 56, 73, 2, 124, 1, 0, 3, 0, 6, 14, 146, + 1, 213, 1, 10, 0, 17, 33, 12, 9, 10, 9, 56, 74, 4, 7, 5, 9, 10, + 0, 56, 75, 10, 9, 56, 76, 4, 13, 5, 15, 10, 0, 56, 77, 10, 9, 56, + 78, 12, 10, 10, 9, 56, 79, 12, 11, 10, 5, 6, 255, 255, 255, 255, 255, 255, + 255, 255, 33, 3, 26, 5, 36, 10, 3, 9, 33, 4, 210, 1, 6, 255, 255, 255, + 255, 255, 255, 255, 255, 10, 10, 23, 12, 12, 11, 12, 12, 5, 10, 7, 6, 255, + 255, 255, 255, 255, 255, 255, 255, 33, 3, 41, 5, 49, 10, 3, 9, 33, 4, 205, + 1, 10, 11, 12, 14, 11, 14, 12, 7, 10, 3, 10, 4, 10, 5, 10, 6, 10, + 7, 10, 10, 11, 10, 10, 11, 11, 11, 17, 89, 10, 3, 9, 33, 4, 197, 1, + 56, 80, 56, 81, 12, 15, 10, 0, 10, 7, 56, 82, 12, 16, 64, 93, 0, 0, + 0, 0, 0, 0, 0, 0, 12, 17, 13, 17, 10, 9, 10, 1, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 11, 2, 11, 3, 11, 4, 11, 5, 11, 6, 11, 7, 11, + 8, 11, 15, 11, 16, 56, 83, 12, 20, 12, 21, 12, 22, 12, 23, 12, 18, 12, + 24, 12, 25, 10, 9, 41, 14, 4, 97, 5, 105, 10, 0, 12, 26, 56, 84, 18, + 14, 12, 27, 11, 26, 11, 27, 45, 14, 10, 9, 42, 14, 15, 18, 12, 28, 10, + 28, 46, 10, 1, 56, 85, 3, 194, 1, 10, 0, 56, 7, 10, 0, 56, 86, 11, + 0, 56, 8, 18, 15, 12, 29, 10, 28, 10, 1, 11, 29, 56, 87, 11, 28, 11, + 1, 56, 88, 12, 30, 10, 30, 15, 19, 11, 21, 56, 89, 56, 16, 14, 17, 12, + 31, 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, 32, 10, 31, 65, 93, 12, 33, + 10, 32, 10, 33, 35, 4, 160, 1, 10, 31, 10, 32, 66, 93, 20, 12, 34, 10, + 30, 15, 20, 11, 34, 56, 90, 11, 32, 6, 1, 0, 0, 0, 0, 0, 0, 0, + 22, 12, 32, 5, 142, 1, 11, 31, 1, 14, 20, 56, 91, 4, 191, 1, 11, 30, + 15, 21, 11, 20, 56, 92, 56, 18, 10, 9, 12, 36, 11, 25, 56, 93, 12, 37, + 11, 36, 11, 37, 56, 94, 11, 9, 11, 24, 56, 95, 11, 18, 12, 32, 11, 23, + 12, 33, 11, 22, 12, 35, 11, 32, 11, 33, 11, 35, 2, 11, 30, 1, 5, 170, + 1, 11, 0, 1, 5, 126, 10, 0, 10, 5, 56, 96, 56, 81, 12, 15, 56, 97, + 12, 16, 5, 70, 6, 255, 255, 255, 255, 255, 255, 255, 255, 10, 11, 23, 12, 14, + 5, 47, 10, 10, 12, 12, 5, 34, 130, 1, 1, 4, 3, 0, 6, 14, 2, 14, + 11, 0, 11, 1, 11, 2, 11, 3, 11, 4, 11, 5, 11, 6, 11, 7, 11, 8, + 56, 98, 1, 1, 1, 2, 131, 1, 1, 0, 2, 0, 6, 152, 1, 83, 14, 8, + 56, 99, 12, 10, 14, 9, 56, 100, 12, 11, 11, 8, 56, 81, 12, 12, 10, 2, + 9, 33, 4, 69, 10, 11, 12, 6, 10, 4, 6, 255, 255, 255, 255, 255, 255, 255, + 255, 33, 3, 20, 5, 24, 6, 255, 255, 255, 255, 255, 255, 255, 255, 10, 10, 23, + 12, 4, 13, 9, 10, 6, 56, 101, 12, 15, 10, 2, 10, 3, 10, 4, 10, 5, + 10, 6, 10, 10, 11, 10, 10, 11, 11, 11, 17, 89, 64, 93, 0, 0, 0, 0, + 0, 0, 0, 0, 12, 16, 13, 16, 7, 0, 11, 0, 6, 0, 0, 0, 0, 0, + 0, 0, 0, 11, 1, 11, 2, 11, 3, 11, 4, 11, 5, 11, 6, 11, 7, 11, + 12, 11, 15, 56, 83, 1, 1, 12, 19, 12, 20, 12, 14, 12, 21, 13, 9, 11, + 21, 56, 102, 56, 93, 11, 9, 11, 14, 11, 20, 11, 19, 2, 10, 10, 12, 4, + 10, 6, 6, 255, 255, 255, 255, 255, 255, 255, 255, 33, 3, 76, 5, 80, 6, 255, + 255, 255, 255, 255, 255, 255, 255, 10, 11, 23, 12, 6, 56, 97, 12, 15, 5, 28, + 135, 1, 1, 0, 2, 0, 6, 156, 1, 93, 11, 9, 17, 123, 12, 10, 14, 8, + 56, 99, 12, 11, 10, 2, 9, 33, 4, 79, 10, 11, 12, 6, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 12, 13, 13, 8, 10, 6, 56, 103, 12, 14, 10, 2, 10, + 3, 10, 4, 10, 5, 10, 6, 10, 13, 11, 13, 10, 11, 11, 11, 17, 89, 64, + 93, 0, 0, 0, 0, 0, 0, 0, 0, 12, 15, 13, 15, 7, 0, 11, 0, 12, + 16, 11, 10, 12, 17, 11, 1, 12, 18, 11, 2, 12, 12, 11, 3, 12, 19, 11, + 4, 12, 20, 11, 5, 12, 21, 11, 6, 12, 22, 11, 7, 12, 23, 56, 104, 12, + 24, 11, 16, 11, 17, 11, 18, 11, 12, 11, 19, 11, 20, 11, 21, 11, 22, 11, + 23, 11, 24, 11, 14, 56, 105, 1, 1, 12, 19, 12, 17, 12, 16, 12, 27, 56, + 106, 13, 8, 11, 27, 56, 107, 11, 8, 11, 16, 11, 17, 11, 19, 2, 10, 6, + 6, 255, 255, 255, 255, 255, 255, 255, 255, 33, 3, 84, 5, 88, 6, 255, 255, 255, + 255, 255, 255, 255, 255, 10, 11, 23, 12, 6, 10, 4, 12, 13, 56, 80, 12, 14, + 5, 18, 6, 0, 5, 6, 5, 0, 5, 2, 5, 9, 0, 0, 5, 3, 4, 1, + 4, 0, 5, 7, 5, 8, 5, 5, 1, 2, 1, 3, 1, 0, 1, 1, 1, 4, + 5, 4, 14, 0, 15, 2, 15, 1, 15, 0, 0, + ] +}); + +#[rustfmt::skip] +pub static MODULE_ECONIA_TXN_GENERATOR_UTILS: Lazy> = Lazy::new(|| { + vec![ + 161, 28, 235, 11, 7, 0, 0, 10, 12, 1, 0, 16, 2, 16, 195, 3, 3, 211, + 3, 156, 1, 4, 239, 4, 236, 1, 5, 219, 6, 192, 5, 7, 155, 12, 172, 7, + 8, 199, 19, 64, 6, 135, 20, 34, 16, 169, 20, 110, 10, 151, 21, 16, 12, 167, + 21, 248, 21, 13, 159, 43, 8, 0, 0, 1, 8, 0, 10, 1, 13, 0, 14, 0, + 16, 0, 21, 1, 30, 0, 1, 6, 0, 0, 5, 10, 0, 3, 12, 4, 1, 0, + 1, 2, 23, 0, 0, 2, 36, 0, 0, 2, 37, 0, 0, 2, 38, 0, 0, 2, + 39, 0, 0, 2, 40, 0, 0, 2, 41, 0, 0, 2, 42, 0, 0, 2, 43, 0, + 0, 2, 44, 0, 0, 2, 45, 0, 0, 2, 46, 0, 0, 2, 47, 0, 0, 2, + 48, 0, 0, 2, 49, 0, 0, 2, 50, 0, 0, 2, 51, 0, 0, 2, 52, 0, + 0, 2, 53, 0, 0, 2, 54, 0, 0, 2, 55, 0, 0, 2, 56, 0, 0, 2, + 57, 0, 0, 2, 58, 0, 0, 2, 59, 0, 0, 2, 60, 0, 0, 2, 61, 0, + 0, 2, 62, 0, 0, 2, 63, 0, 0, 2, 64, 0, 0, 2, 65, 0, 0, 2, + 66, 0, 0, 2, 67, 0, 0, 2, 68, 0, 0, 2, 69, 0, 0, 2, 70, 0, + 0, 2, 71, 0, 0, 2, 72, 0, 0, 2, 73, 0, 0, 2, 74, 0, 0, 2, + 75, 0, 0, 2, 76, 0, 0, 2, 77, 0, 0, 2, 78, 0, 0, 2, 79, 0, + 0, 2, 80, 0, 0, 2, 81, 0, 0, 2, 82, 0, 0, 2, 83, 0, 0, 2, + 84, 0, 0, 2, 85, 0, 0, 2, 86, 0, 0, 2, 87, 0, 0, 2, 88, 0, + 0, 2, 89, 0, 0, 2, 90, 0, 0, 2, 91, 0, 0, 2, 92, 0, 0, 2, + 93, 0, 0, 2, 94, 0, 0, 2, 95, 0, 0, 2, 96, 0, 0, 2, 97, 0, + 0, 2, 98, 0, 0, 2, 99, 0, 0, 2, 100, 0, 0, 2, 101, 0, 0, 2, + 102, 0, 0, 2, 103, 0, 0, 2, 104, 0, 0, 2, 105, 0, 0, 2, 106, 0, + 0, 2, 107, 0, 0, 2, 108, 0, 0, 2, 109, 0, 0, 2, 110, 0, 0, 2, + 111, 0, 0, 2, 112, 0, 0, 2, 113, 0, 0, 2, 114, 0, 0, 2, 115, 0, + 0, 2, 116, 0, 0, 2, 117, 0, 0, 2, 118, 0, 0, 2, 119, 0, 0, 2, + 120, 0, 0, 2, 121, 0, 0, 2, 122, 0, 0, 2, 123, 0, 0, 2, 124, 0, + 0, 2, 125, 0, 0, 2, 126, 0, 0, 2, 127, 0, 0, 2, 128, 1, 0, 0, + 2, 129, 1, 0, 0, 2, 130, 1, 0, 0, 2, 131, 1, 0, 0, 2, 132, 1, + 0, 0, 2, 133, 1, 0, 0, 2, 134, 1, 0, 0, 2, 135, 1, 0, 0, 2, + 136, 1, 0, 0, 2, 137, 1, 0, 0, 2, 138, 1, 0, 0, 2, 139, 1, 0, + 0, 2, 140, 1, 0, 0, 0, 7, 0, 1, 2, 0, 0, 1, 1, 9, 2, 3, + 0, 1, 2, 11, 5, 6, 1, 0, 1, 4, 7, 7, 1, 1, 0, 1, 0, 15, + 10, 1, 2, 0, 0, 1, 5, 17, 12, 13, 2, 0, 0, 1, 0, 18, 16, 1, + 2, 0, 0, 1, 5, 19, 17, 18, 2, 0, 0, 1, 0, 20, 2, 1, 2, 0, + 0, 1, 6, 22, 1, 19, 0, 1, 5, 24, 22, 19, 3, 0, 0, 0, 1, 0, + 25, 24, 1, 2, 0, 0, 1, 0, 26, 26, 1, 2, 0, 0, 1, 0, 27, 24, + 1, 2, 0, 0, 1, 0, 28, 26, 1, 2, 0, 0, 1, 0, 29, 2, 1, 0, + 1, 7, 31, 28, 29, 1, 0, 1, 5, 32, 30, 1, 0, 1, 0, 33, 5, 1, + 2, 0, 0, 1, 4, 34, 26, 1, 2, 0, 0, 1, 0, 35, 5, 1, 0, 1, + 2, 4, 3, 4, 2, 8, 3, 8, 5, 11, 7, 11, 2, 20, 10, 21, 16, 14, + 19, 11, 8, 32, 8, 33, 8, 34, 8, 35, 8, 36, 8, 37, 8, 38, 8, 39, + 8, 40, 8, 41, 8, 42, 8, 43, 8, 44, 8, 45, 8, 46, 8, 47, 8, 48, + 8, 49, 8, 50, 8, 51, 8, 52, 8, 53, 8, 54, 8, 55, 8, 56, 8, 57, + 8, 58, 8, 59, 8, 60, 8, 61, 8, 62, 8, 63, 8, 64, 8, 65, 8, 66, + 8, 67, 8, 68, 8, 69, 8, 70, 8, 71, 8, 72, 8, 73, 8, 74, 8, 75, + 8, 76, 8, 77, 8, 78, 8, 79, 8, 80, 8, 81, 8, 82, 8, 83, 8, 84, + 8, 85, 8, 86, 8, 87, 8, 88, 8, 89, 8, 90, 8, 91, 8, 92, 8, 93, + 8, 94, 8, 95, 8, 96, 8, 97, 8, 98, 8, 99, 8, 100, 8, 101, 8, 102, + 8, 103, 8, 104, 8, 105, 8, 106, 8, 107, 8, 108, 8, 109, 8, 110, 8, 111, + 8, 112, 8, 113, 8, 114, 8, 115, 8, 116, 8, 117, 8, 118, 8, 119, 8, 120, + 8, 121, 8, 122, 8, 123, 8, 124, 8, 125, 8, 126, 8, 127, 8, 128, 1, 8, + 129, 1, 8, 130, 1, 8, 131, 1, 8, 132, 1, 8, 133, 1, 8, 134, 1, 8, + 135, 1, 3, 6, 12, 6, 12, 3, 0, 1, 6, 12, 1, 5, 1, 9, 1, 2, + 6, 12, 3, 1, 11, 2, 1, 9, 0, 4, 5, 3, 3, 11, 2, 1, 9, 0, + 1, 9, 0, 6, 3, 3, 11, 2, 1, 9, 1, 3, 5, 11, 2, 1, 9, 0, + 7, 6, 12, 3, 1, 3, 3, 2, 2, 2, 9, 0, 9, 1, 8, 6, 12, 3, + 5, 1, 3, 3, 2, 2, 4, 4, 3, 3, 3, 1, 8, 0, 4, 5, 4, 10, + 8, 0, 8, 1, 5, 6, 12, 3, 1, 3, 2, 6, 6, 12, 3, 5, 1, 3, + 2, 3, 3, 3, 3, 1, 3, 1, 8, 3, 3, 9, 0, 9, 1, 8, 3, 4, + 3, 3, 3, 11, 2, 1, 9, 2, 5, 3, 3, 3, 3, 11, 2, 1, 8, 3, + 4, 6, 12, 3, 3, 3, 4, 2, 2, 1, 5, 3, 6, 12, 3, 3, 3, 2, + 1, 5, 1, 6, 10, 9, 0, 1, 1, 4, 6, 12, 3, 1, 4, 5, 7, 8, + 1, 8, 0, 4, 1, 3, 2, 8, 4, 8, 5, 2, 8, 6, 8, 5, 2, 8, + 7, 8, 5, 2, 8, 8, 8, 5, 2, 8, 9, 8, 5, 2, 8, 10, 8, 5, + 2, 8, 11, 8, 5, 2, 8, 12, 8, 5, 2, 8, 13, 8, 5, 2, 8, 14, + 8, 5, 2, 8, 15, 8, 5, 2, 8, 16, 8, 5, 2, 8, 17, 8, 5, 2, + 8, 18, 8, 5, 2, 8, 19, 8, 5, 2, 8, 20, 8, 5, 2, 8, 21, 8, + 5, 2, 8, 22, 8, 5, 2, 8, 23, 8, 5, 2, 8, 24, 8, 5, 2, 8, + 25, 8, 5, 2, 8, 26, 8, 5, 2, 8, 27, 8, 5, 2, 8, 28, 8, 5, + 2, 8, 29, 8, 5, 2, 8, 30, 8, 5, 2, 8, 31, 8, 5, 2, 8, 32, + 8, 5, 2, 8, 33, 8, 5, 2, 8, 34, 8, 5, 2, 8, 35, 8, 5, 2, + 8, 36, 8, 5, 2, 8, 37, 8, 5, 2, 8, 38, 8, 5, 2, 8, 39, 8, + 5, 2, 8, 40, 8, 5, 2, 8, 41, 8, 5, 2, 8, 42, 8, 5, 2, 8, + 43, 8, 5, 2, 8, 44, 8, 5, 2, 8, 45, 8, 5, 2, 8, 46, 8, 5, + 2, 8, 47, 8, 5, 2, 8, 48, 8, 5, 2, 8, 49, 8, 5, 2, 8, 50, + 8, 5, 2, 8, 51, 8, 5, 2, 8, 52, 8, 5, 2, 8, 53, 8, 5, 2, + 8, 54, 8, 5, 2, 8, 55, 8, 5, 2, 8, 56, 8, 5, 2, 8, 57, 8, + 5, 2, 8, 58, 8, 5, 2, 8, 59, 8, 5, 2, 8, 60, 8, 5, 2, 8, + 61, 8, 5, 2, 8, 62, 8, 5, 2, 8, 63, 8, 5, 2, 8, 64, 8, 5, + 2, 8, 65, 8, 5, 2, 8, 66, 8, 5, 2, 8, 67, 8, 5, 2, 8, 68, + 8, 5, 2, 8, 69, 8, 5, 2, 8, 70, 8, 5, 2, 8, 71, 8, 5, 2, + 8, 72, 8, 5, 2, 8, 73, 8, 5, 2, 8, 74, 8, 5, 2, 8, 75, 8, + 5, 2, 8, 76, 8, 5, 2, 8, 77, 8, 5, 2, 8, 78, 8, 5, 2, 8, + 79, 8, 5, 2, 8, 80, 8, 5, 2, 8, 81, 8, 5, 2, 8, 82, 8, 5, + 2, 8, 83, 8, 5, 2, 8, 84, 8, 5, 2, 8, 85, 8, 5, 2, 8, 86, + 8, 5, 2, 8, 87, 8, 5, 2, 8, 88, 8, 5, 2, 8, 89, 8, 5, 2, + 8, 90, 8, 5, 2, 8, 91, 8, 5, 2, 8, 92, 8, 5, 2, 8, 93, 8, + 5, 2, 8, 94, 8, 5, 2, 8, 95, 8, 5, 2, 8, 96, 8, 5, 2, 8, + 97, 8, 5, 2, 8, 98, 8, 5, 2, 8, 99, 8, 5, 2, 8, 100, 8, 5, + 2, 8, 101, 8, 5, 2, 8, 102, 8, 5, 2, 8, 103, 8, 5, 2, 8, 104, + 8, 5, 2, 8, 105, 8, 5, 2, 8, 106, 8, 5, 2, 8, 107, 8, 5, 2, + 8, 108, 8, 5, 19, 116, 120, 110, 95, 103, 101, 110, 101, 114, 97, 116, 111, 114, + 95, 117, 116, 105, 108, 115, 5, 79, 114, 100, 101, 114, 9, 109, 97, 114, 107, 101, + 116, 95, 105, 100, 9, 100, 105, 114, 101, 99, 116, 105, 111, 110, 8, 111, 114, 100, + 101, 114, 95, 105, 100, 6, 79, 114, 100, 101, 114, 115, 6, 111, 114, 100, 101, 114, + 115, 13, 100, 101, 112, 111, 115, 105, 116, 95, 99, 111, 105, 110, 115, 6, 115, 105, + 103, 110, 101, 114, 10, 97, 100, 100, 114, 101, 115, 115, 95, 111, 102, 6, 97, 115, + 115, 101, 116, 115, 4, 109, 105, 110, 116, 4, 67, 111, 105, 110, 4, 99, 111, 105, + 110, 4, 117, 115, 101, 114, 17, 112, 108, 97, 99, 101, 95, 108, 105, 109, 105, 116, + 95, 111, 114, 100, 101, 114, 6, 109, 97, 114, 107, 101, 116, 22, 112, 108, 97, 99, + 101, 95, 108, 105, 109, 105, 116, 95, 111, 114, 100, 101, 114, 95, 117, 115, 101, 114, + 18, 112, 108, 97, 99, 101, 95, 109, 97, 114, 107, 101, 116, 95, 111, 114, 100, 101, + 114, 23, 112, 108, 97, 99, 101, 95, 109, 97, 114, 107, 101, 116, 95, 111, 114, 100, + 101, 114, 95, 117, 115, 101, 114, 15, 114, 101, 103, 105, 115, 116, 101, 114, 95, 109, + 97, 114, 107, 101, 116, 10, 105, 110, 99, 101, 110, 116, 105, 118, 101, 115, 27, 103, + 101, 116, 95, 109, 97, 114, 107, 101, 116, 95, 114, 101, 103, 105, 115, 116, 114, 97, + 116, 105, 111, 110, 95, 102, 101, 101, 2, 85, 67, 25, 114, 101, 103, 105, 115, 116, + 101, 114, 95, 109, 97, 114, 107, 101, 116, 95, 98, 97, 115, 101, 95, 99, 111, 105, + 110, 21, 112, 108, 97, 99, 101, 95, 97, 115, 107, 95, 108, 105, 109, 105, 116, 95, + 111, 114, 100, 101, 114, 22, 112, 108, 97, 99, 101, 95, 97, 115, 107, 95, 109, 97, + 114, 107, 101, 116, 95, 111, 114, 100, 101, 114, 21, 112, 108, 97, 99, 101, 95, 98, + 105, 100, 95, 108, 105, 109, 105, 116, 95, 111, 114, 100, 101, 114, 22, 112, 108, 97, + 99, 101, 95, 98, 105, 100, 95, 109, 97, 114, 107, 101, 116, 95, 111, 114, 100, 101, + 114, 18, 112, 108, 97, 99, 101, 95, 99, 97, 110, 99, 101, 108, 95, 111, 114, 100, + 101, 114, 6, 118, 101, 99, 116, 111, 114, 8, 105, 115, 95, 101, 109, 112, 116, 121, + 17, 99, 97, 110, 99, 101, 108, 95, 111, 114, 100, 101, 114, 95, 117, 115, 101, 114, + 24, 114, 101, 103, 105, 115, 116, 101, 114, 95, 109, 97, 114, 107, 101, 116, 95, 97, + 99, 99, 111, 117, 110, 116, 115, 23, 114, 101, 103, 105, 115, 116, 101, 114, 95, 109, + 97, 114, 107, 101, 116, 95, 97, 99, 99, 111, 117, 110, 116, 25, 114, 101, 103, 105, + 115, 116, 101, 114, 95, 109, 117, 108, 116, 105, 112, 108, 101, 95, 109, 97, 114, 107, + 101, 116, 115, 3, 65, 65, 67, 2, 81, 67, 3, 65, 66, 67, 3, 65, 67, 67, + 3, 65, 68, 67, 3, 65, 69, 67, 3, 65, 70, 67, 3, 65, 71, 67, 3, 65, + 72, 67, 3, 65, 73, 67, 3, 65, 74, 67, 3, 65, 75, 67, 3, 65, 76, 67, + 3, 65, 77, 67, 3, 65, 78, 67, 3, 65, 79, 67, 3, 65, 80, 67, 3, 65, + 81, 67, 3, 65, 82, 67, 3, 65, 83, 67, 3, 65, 84, 67, 3, 65, 85, 67, + 3, 65, 86, 67, 3, 65, 87, 67, 3, 65, 88, 67, 3, 65, 89, 67, 3, 65, + 90, 67, 3, 66, 65, 67, 3, 66, 66, 67, 3, 66, 67, 67, 3, 66, 68, 67, + 3, 66, 69, 67, 3, 66, 70, 67, 3, 66, 71, 67, 3, 66, 72, 67, 3, 66, + 73, 67, 3, 66, 74, 67, 3, 66, 75, 67, 3, 66, 76, 67, 3, 66, 77, 67, + 3, 66, 78, 67, 3, 66, 79, 67, 3, 66, 80, 67, 3, 66, 81, 67, 3, 66, + 82, 67, 3, 66, 83, 67, 3, 66, 84, 67, 3, 66, 85, 67, 3, 66, 86, 67, + 3, 66, 87, 67, 3, 66, 88, 67, 3, 66, 89, 67, 3, 66, 90, 67, 3, 67, + 65, 67, 3, 67, 66, 67, 3, 67, 67, 67, 3, 67, 68, 67, 3, 67, 69, 67, + 3, 67, 70, 67, 3, 67, 71, 67, 3, 67, 72, 67, 3, 67, 73, 67, 3, 67, + 74, 67, 3, 67, 75, 67, 3, 67, 76, 67, 3, 67, 77, 67, 3, 67, 78, 67, + 3, 67, 79, 67, 3, 67, 80, 67, 3, 67, 81, 67, 3, 67, 82, 67, 3, 67, + 83, 67, 3, 67, 84, 67, 3, 67, 85, 67, 3, 67, 86, 67, 3, 67, 87, 67, + 3, 67, 88, 67, 3, 67, 89, 67, 3, 67, 90, 67, 3, 68, 65, 67, 3, 68, + 66, 67, 3, 68, 67, 67, 3, 68, 68, 67, 3, 68, 69, 67, 3, 68, 70, 67, + 3, 68, 71, 67, 3, 68, 72, 67, 3, 68, 73, 67, 3, 68, 74, 67, 3, 68, + 75, 67, 3, 68, 76, 67, 3, 68, 77, 67, 3, 68, 78, 67, 3, 68, 79, 67, + 3, 68, 80, 67, 3, 68, 81, 67, 3, 68, 82, 67, 3, 68, 83, 67, 3, 68, + 84, 67, 3, 68, 85, 67, 3, 68, 86, 67, 3, 68, 87, 67, 3, 68, 88, 67, + 3, 68, 89, 67, 3, 68, 90, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 35, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 5, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 35, 69, 20, 99, + 111, 109, 112, 105, 108, 97, 116, 105, 111, 110, 95, 109, 101, 116, 97, 100, 97, 116, + 97, 9, 0, 3, 50, 46, 48, 3, 50, 46, 49, 18, 97, 112, 116, 111, 115, 58, + 58, 109, 101, 116, 97, 100, 97, 116, 97, 95, 118, 49, 59, 2, 101, 0, 0, 0, + 0, 0, 0, 0, 18, 69, 95, 78, 85, 77, 95, 77, 65, 82, 75, 69, 84, 83, + 95, 90, 69, 82, 79, 0, 102, 0, 0, 0, 0, 0, 0, 0, 18, 69, 95, 78, + 85, 77, 95, 77, 65, 82, 75, 69, 84, 83, 95, 72, 73, 71, 72, 0, 0, 0, + 0, 2, 3, 2, 3, 3, 1, 4, 4, 1, 2, 1, 6, 10, 8, 0, 0, 1, + 4, 0, 9, 27, 10, 0, 17, 1, 10, 2, 12, 3, 6, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 1, 6, 0, 228, 11, 84, 2, 0, 0, 0, 56, 0, 12, 5, + 12, 6, 11, 3, 11, 6, 11, 5, 56, 1, 11, 0, 17, 1, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 11, 1, 6, 0, 228, 11, 84, 2, 0, 0, 0, 56, 2, + 12, 8, 12, 3, 11, 2, 11, 3, 11, 8, 56, 3, 2, 4, 1, 4, 1, 1, + 15, 42, 10, 0, 10, 1, 7, 0, 10, 2, 11, 3, 11, 4, 11, 5, 11, 6, + 56, 4, 1, 1, 1, 12, 8, 10, 0, 17, 1, 41, 1, 4, 27, 11, 0, 17, + 1, 42, 1, 15, 0, 11, 1, 11, 2, 11, 8, 18, 0, 68, 14, 2, 64, 14, + 0, 0, 0, 0, 0, 0, 0, 0, 12, 9, 13, 9, 11, 1, 11, 2, 11, 8, + 18, 0, 68, 14, 11, 9, 18, 1, 12, 10, 11, 0, 11, 10, 45, 1, 5, 26, + 6, 1, 4, 0, 3, 11, 11, 0, 11, 1, 7, 0, 11, 2, 11, 3, 11, 4, + 56, 5, 1, 1, 1, 2, 8, 1, 4, 0, 23, 22, 7, 0, 10, 0, 17, 1, + 33, 4, 18, 17, 9, 12, 4, 11, 0, 11, 4, 56, 6, 12, 5, 6, 2, 0, + 0, 0, 0, 0, 0, 0, 6, 3, 0, 0, 0, 0, 0, 0, 0, 6, 4, 0, + 0, 0, 0, 0, 0, 0, 11, 5, 56, 7, 1, 2, 11, 0, 1, 6, 101, 0, + 0, 0, 0, 0, 0, 0, 39, 11, 1, 4, 0, 25, 14, 11, 0, 11, 3, 7, + 0, 8, 11, 1, 11, 2, 49, 3, 49, 0, 56, 4, 1, 1, 1, 1, 2, 12, + 1, 4, 0, 27, 11, 11, 0, 11, 2, 7, 0, 8, 11, 1, 49, 2, 56, 5, + 1, 1, 1, 2, 13, 1, 4, 0, 25, 14, 11, 0, 11, 3, 7, 0, 9, 11, + 1, 11, 2, 49, 3, 49, 0, 56, 4, 1, 1, 1, 1, 2, 14, 1, 4, 0, + 27, 11, 11, 0, 11, 2, 7, 0, 9, 11, 1, 49, 2, 56, 5, 1, 1, 1, + 2, 15, 1, 4, 1, 1, 31, 42, 10, 0, 17, 1, 41, 1, 4, 39, 10, 0, + 17, 1, 42, 1, 12, 1, 10, 1, 16, 0, 56, 8, 3, 34, 11, 1, 15, 0, + 69, 14, 12, 2, 14, 2, 16, 1, 20, 14, 2, 16, 2, 20, 14, 2, 16, 3, + 20, 12, 3, 12, 4, 12, 5, 11, 0, 11, 5, 11, 4, 11, 3, 17, 17, 2, + 11, 0, 1, 11, 1, 1, 5, 33, 11, 0, 1, 5, 33, 18, 1, 4, 0, 19, + 5, 11, 0, 11, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 56, 9, 2, 20, + 1, 4, 0, 19, 235, 5, 10, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 36, + 4, 231, 5, 10, 1, 6, 105, 0, 0, 0, 0, 0, 0, 0, 35, 4, 227, 5, + 10, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 36, 3, 13, 5, 15, 10, 0, + 56, 10, 10, 1, 6, 1, 0, 0, 0, 0, 0, 0, 0, 36, 3, 20, 5, 22, + 10, 0, 56, 11, 10, 1, 6, 2, 0, 0, 0, 0, 0, 0, 0, 36, 3, 27, + 5, 29, 10, 0, 56, 12, 10, 1, 6, 3, 0, 0, 0, 0, 0, 0, 0, 36, + 3, 34, 5, 36, 10, 0, 56, 13, 10, 1, 6, 4, 0, 0, 0, 0, 0, 0, + 0, 36, 3, 41, 5, 43, 10, 0, 56, 14, 10, 1, 6, 5, 0, 0, 0, 0, + 0, 0, 0, 36, 3, 48, 5, 50, 10, 0, 56, 15, 10, 1, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 36, 3, 55, 5, 57, 10, 0, 56, 16, 10, 1, 6, 7, + 0, 0, 0, 0, 0, 0, 0, 36, 3, 62, 5, 64, 10, 0, 56, 17, 10, 1, + 6, 8, 0, 0, 0, 0, 0, 0, 0, 36, 3, 69, 5, 71, 10, 0, 56, 18, + 10, 1, 6, 9, 0, 0, 0, 0, 0, 0, 0, 36, 3, 76, 5, 78, 10, 0, + 56, 19, 10, 1, 6, 10, 0, 0, 0, 0, 0, 0, 0, 36, 3, 83, 5, 85, + 10, 0, 56, 20, 10, 1, 6, 11, 0, 0, 0, 0, 0, 0, 0, 36, 3, 90, + 5, 92, 10, 0, 56, 21, 10, 1, 6, 12, 0, 0, 0, 0, 0, 0, 0, 36, + 3, 97, 5, 99, 10, 0, 56, 22, 10, 1, 6, 13, 0, 0, 0, 0, 0, 0, + 0, 36, 3, 104, 5, 106, 10, 0, 56, 23, 10, 1, 6, 14, 0, 0, 0, 0, + 0, 0, 0, 36, 3, 111, 5, 113, 10, 0, 56, 24, 10, 1, 6, 15, 0, 0, + 0, 0, 0, 0, 0, 36, 3, 118, 5, 120, 10, 0, 56, 25, 10, 1, 6, 16, + 0, 0, 0, 0, 0, 0, 0, 36, 3, 125, 5, 127, 10, 0, 56, 26, 10, 1, + 6, 17, 0, 0, 0, 0, 0, 0, 0, 36, 3, 132, 1, 5, 134, 1, 10, 0, + 56, 27, 10, 1, 6, 18, 0, 0, 0, 0, 0, 0, 0, 36, 3, 139, 1, 5, + 141, 1, 10, 0, 56, 28, 10, 1, 6, 19, 0, 0, 0, 0, 0, 0, 0, 36, + 3, 146, 1, 5, 148, 1, 10, 0, 56, 29, 10, 1, 6, 20, 0, 0, 0, 0, + 0, 0, 0, 36, 3, 153, 1, 5, 155, 1, 10, 0, 56, 30, 10, 1, 6, 21, + 0, 0, 0, 0, 0, 0, 0, 36, 3, 160, 1, 5, 162, 1, 10, 0, 56, 31, + 10, 1, 6, 22, 0, 0, 0, 0, 0, 0, 0, 36, 3, 167, 1, 5, 169, 1, + 10, 0, 56, 32, 10, 1, 6, 23, 0, 0, 0, 0, 0, 0, 0, 36, 3, 174, + 1, 5, 176, 1, 10, 0, 56, 33, 10, 1, 6, 24, 0, 0, 0, 0, 0, 0, + 0, 36, 3, 181, 1, 5, 183, 1, 10, 0, 56, 34, 10, 1, 6, 25, 0, 0, + 0, 0, 0, 0, 0, 36, 3, 188, 1, 5, 190, 1, 10, 0, 56, 35, 10, 1, + 6, 26, 0, 0, 0, 0, 0, 0, 0, 36, 3, 195, 1, 5, 197, 1, 10, 0, + 56, 36, 10, 1, 6, 27, 0, 0, 0, 0, 0, 0, 0, 36, 3, 202, 1, 5, + 204, 1, 10, 0, 56, 37, 10, 1, 6, 28, 0, 0, 0, 0, 0, 0, 0, 36, + 3, 209, 1, 5, 211, 1, 10, 0, 56, 38, 10, 1, 6, 29, 0, 0, 0, 0, + 0, 0, 0, 36, 3, 216, 1, 5, 218, 1, 10, 0, 56, 39, 10, 1, 6, 30, + 0, 0, 0, 0, 0, 0, 0, 36, 3, 223, 1, 5, 225, 1, 10, 0, 56, 40, + 10, 1, 6, 31, 0, 0, 0, 0, 0, 0, 0, 36, 3, 230, 1, 5, 232, 1, + 10, 0, 56, 41, 10, 1, 6, 32, 0, 0, 0, 0, 0, 0, 0, 36, 3, 237, + 1, 5, 239, 1, 10, 0, 56, 42, 10, 1, 6, 33, 0, 0, 0, 0, 0, 0, + 0, 36, 3, 244, 1, 5, 246, 1, 10, 0, 56, 43, 10, 1, 6, 34, 0, 0, + 0, 0, 0, 0, 0, 36, 3, 251, 1, 5, 253, 1, 10, 0, 56, 44, 10, 1, + 6, 35, 0, 0, 0, 0, 0, 0, 0, 36, 3, 130, 2, 5, 132, 2, 10, 0, + 56, 45, 10, 1, 6, 36, 0, 0, 0, 0, 0, 0, 0, 36, 3, 137, 2, 5, + 139, 2, 10, 0, 56, 46, 10, 1, 6, 37, 0, 0, 0, 0, 0, 0, 0, 36, + 3, 144, 2, 5, 146, 2, 10, 0, 56, 47, 10, 1, 6, 38, 0, 0, 0, 0, + 0, 0, 0, 36, 3, 151, 2, 5, 153, 2, 10, 0, 56, 48, 10, 1, 6, 39, + 0, 0, 0, 0, 0, 0, 0, 36, 3, 158, 2, 5, 160, 2, 10, 0, 56, 49, + 10, 1, 6, 40, 0, 0, 0, 0, 0, 0, 0, 36, 3, 165, 2, 5, 167, 2, + 10, 0, 56, 50, 10, 1, 6, 41, 0, 0, 0, 0, 0, 0, 0, 36, 3, 172, + 2, 5, 174, 2, 10, 0, 56, 51, 10, 1, 6, 42, 0, 0, 0, 0, 0, 0, + 0, 36, 3, 179, 2, 5, 181, 2, 10, 0, 56, 52, 10, 1, 6, 43, 0, 0, + 0, 0, 0, 0, 0, 36, 3, 186, 2, 5, 188, 2, 10, 0, 56, 53, 10, 1, + 6, 44, 0, 0, 0, 0, 0, 0, 0, 36, 3, 193, 2, 5, 195, 2, 10, 0, + 56, 54, 10, 1, 6, 45, 0, 0, 0, 0, 0, 0, 0, 36, 3, 200, 2, 5, + 202, 2, 10, 0, 56, 55, 10, 1, 6, 46, 0, 0, 0, 0, 0, 0, 0, 36, + 3, 207, 2, 5, 209, 2, 10, 0, 56, 56, 10, 1, 6, 47, 0, 0, 0, 0, + 0, 0, 0, 36, 3, 214, 2, 5, 216, 2, 10, 0, 56, 57, 10, 1, 6, 48, + 0, 0, 0, 0, 0, 0, 0, 36, 3, 221, 2, 5, 223, 2, 10, 0, 56, 58, + 10, 1, 6, 49, 0, 0, 0, 0, 0, 0, 0, 36, 3, 228, 2, 5, 230, 2, + 10, 0, 56, 59, 10, 1, 6, 50, 0, 0, 0, 0, 0, 0, 0, 36, 3, 235, + 2, 5, 237, 2, 10, 0, 56, 60, 10, 1, 6, 51, 0, 0, 0, 0, 0, 0, + 0, 36, 3, 242, 2, 5, 244, 2, 10, 0, 56, 61, 10, 1, 6, 52, 0, 0, + 0, 0, 0, 0, 0, 36, 3, 249, 2, 5, 251, 2, 10, 0, 56, 62, 10, 1, + 6, 53, 0, 0, 0, 0, 0, 0, 0, 36, 3, 128, 3, 5, 130, 3, 10, 0, + 56, 63, 10, 1, 6, 54, 0, 0, 0, 0, 0, 0, 0, 36, 3, 135, 3, 5, + 137, 3, 10, 0, 56, 64, 10, 1, 6, 55, 0, 0, 0, 0, 0, 0, 0, 36, + 3, 142, 3, 5, 144, 3, 10, 0, 56, 65, 10, 1, 6, 56, 0, 0, 0, 0, + 0, 0, 0, 36, 3, 149, 3, 5, 151, 3, 10, 0, 56, 66, 10, 1, 6, 57, + 0, 0, 0, 0, 0, 0, 0, 36, 3, 156, 3, 5, 158, 3, 10, 0, 56, 67, + 10, 1, 6, 58, 0, 0, 0, 0, 0, 0, 0, 36, 3, 163, 3, 5, 165, 3, + 10, 0, 56, 68, 10, 1, 6, 59, 0, 0, 0, 0, 0, 0, 0, 36, 3, 170, + 3, 5, 172, 3, 10, 0, 56, 69, 10, 1, 6, 60, 0, 0, 0, 0, 0, 0, + 0, 36, 3, 177, 3, 5, 179, 3, 10, 0, 56, 70, 10, 1, 6, 61, 0, 0, + 0, 0, 0, 0, 0, 36, 3, 184, 3, 5, 186, 3, 10, 0, 56, 71, 10, 1, + 6, 62, 0, 0, 0, 0, 0, 0, 0, 36, 3, 191, 3, 5, 193, 3, 10, 0, + 56, 72, 10, 1, 6, 63, 0, 0, 0, 0, 0, 0, 0, 36, 3, 198, 3, 5, + 200, 3, 10, 0, 56, 73, 10, 1, 6, 64, 0, 0, 0, 0, 0, 0, 0, 36, + 3, 205, 3, 5, 207, 3, 10, 0, 56, 74, 10, 1, 6, 65, 0, 0, 0, 0, + 0, 0, 0, 36, 3, 212, 3, 5, 214, 3, 10, 0, 56, 75, 10, 1, 6, 66, + 0, 0, 0, 0, 0, 0, 0, 36, 3, 219, 3, 5, 221, 3, 10, 0, 56, 76, + 10, 1, 6, 67, 0, 0, 0, 0, 0, 0, 0, 36, 3, 226, 3, 5, 228, 3, + 10, 0, 56, 77, 10, 1, 6, 68, 0, 0, 0, 0, 0, 0, 0, 36, 3, 233, + 3, 5, 235, 3, 10, 0, 56, 78, 10, 1, 6, 69, 0, 0, 0, 0, 0, 0, + 0, 36, 3, 240, 3, 5, 242, 3, 10, 0, 56, 79, 10, 1, 6, 70, 0, 0, + 0, 0, 0, 0, 0, 36, 3, 247, 3, 5, 249, 3, 10, 0, 56, 80, 10, 1, + 6, 71, 0, 0, 0, 0, 0, 0, 0, 36, 3, 254, 3, 5, 128, 4, 10, 0, + 56, 81, 10, 1, 6, 72, 0, 0, 0, 0, 0, 0, 0, 36, 3, 133, 4, 5, + 135, 4, 10, 0, 56, 82, 10, 1, 6, 73, 0, 0, 0, 0, 0, 0, 0, 36, + 3, 140, 4, 5, 142, 4, 10, 0, 56, 83, 10, 1, 6, 74, 0, 0, 0, 0, + 0, 0, 0, 36, 3, 147, 4, 5, 149, 4, 10, 0, 56, 84, 10, 1, 6, 75, + 0, 0, 0, 0, 0, 0, 0, 36, 3, 154, 4, 5, 156, 4, 10, 0, 56, 85, + 10, 1, 6, 76, 0, 0, 0, 0, 0, 0, 0, 36, 3, 161, 4, 5, 163, 4, + 10, 0, 56, 86, 10, 1, 6, 77, 0, 0, 0, 0, 0, 0, 0, 36, 3, 168, + 4, 5, 170, 4, 10, 0, 56, 87, 10, 1, 6, 78, 0, 0, 0, 0, 0, 0, + 0, 36, 3, 175, 4, 5, 177, 4, 10, 0, 56, 88, 10, 1, 6, 79, 0, 0, + 0, 0, 0, 0, 0, 36, 3, 182, 4, 5, 184, 4, 10, 0, 56, 89, 10, 1, + 6, 80, 0, 0, 0, 0, 0, 0, 0, 36, 3, 189, 4, 5, 191, 4, 10, 0, + 56, 90, 10, 1, 6, 81, 0, 0, 0, 0, 0, 0, 0, 36, 3, 196, 4, 5, + 198, 4, 10, 0, 56, 91, 10, 1, 6, 82, 0, 0, 0, 0, 0, 0, 0, 36, + 3, 203, 4, 5, 205, 4, 10, 0, 56, 92, 10, 1, 6, 83, 0, 0, 0, 0, + 0, 0, 0, 36, 3, 210, 4, 5, 212, 4, 10, 0, 56, 93, 10, 1, 6, 84, + 0, 0, 0, 0, 0, 0, 0, 36, 3, 217, 4, 5, 219, 4, 10, 0, 56, 94, + 10, 1, 6, 85, 0, 0, 0, 0, 0, 0, 0, 36, 3, 224, 4, 5, 226, 4, + 10, 0, 56, 95, 10, 1, 6, 86, 0, 0, 0, 0, 0, 0, 0, 36, 3, 231, + 4, 5, 233, 4, 10, 0, 56, 96, 10, 1, 6, 87, 0, 0, 0, 0, 0, 0, + 0, 36, 3, 238, 4, 5, 240, 4, 10, 0, 56, 97, 10, 1, 6, 88, 0, 0, + 0, 0, 0, 0, 0, 36, 3, 245, 4, 5, 247, 4, 10, 0, 56, 98, 10, 1, + 6, 89, 0, 0, 0, 0, 0, 0, 0, 36, 3, 252, 4, 5, 254, 4, 10, 0, + 56, 99, 10, 1, 6, 90, 0, 0, 0, 0, 0, 0, 0, 36, 3, 131, 5, 5, + 133, 5, 10, 0, 56, 100, 10, 1, 6, 91, 0, 0, 0, 0, 0, 0, 0, 36, + 3, 138, 5, 5, 140, 5, 10, 0, 56, 101, 10, 1, 6, 92, 0, 0, 0, 0, + 0, 0, 0, 36, 3, 145, 5, 5, 147, 5, 10, 0, 56, 102, 10, 1, 6, 93, + 0, 0, 0, 0, 0, 0, 0, 36, 3, 152, 5, 5, 154, 5, 10, 0, 56, 103, + 10, 1, 6, 94, 0, 0, 0, 0, 0, 0, 0, 36, 3, 159, 5, 5, 161, 5, + 10, 0, 56, 104, 10, 1, 6, 95, 0, 0, 0, 0, 0, 0, 0, 36, 3, 166, + 5, 5, 168, 5, 10, 0, 56, 105, 10, 1, 6, 96, 0, 0, 0, 0, 0, 0, + 0, 36, 3, 173, 5, 5, 175, 5, 10, 0, 56, 106, 10, 1, 6, 97, 0, 0, + 0, 0, 0, 0, 0, 36, 3, 180, 5, 5, 182, 5, 10, 0, 56, 107, 10, 1, + 6, 98, 0, 0, 0, 0, 0, 0, 0, 36, 3, 187, 5, 5, 189, 5, 10, 0, + 56, 108, 10, 1, 6, 99, 0, 0, 0, 0, 0, 0, 0, 36, 3, 194, 5, 5, + 196, 5, 10, 0, 56, 109, 10, 1, 6, 100, 0, 0, 0, 0, 0, 0, 0, 36, + 3, 201, 5, 5, 203, 5, 10, 0, 56, 110, 10, 1, 6, 101, 0, 0, 0, 0, + 0, 0, 0, 36, 3, 208, 5, 5, 210, 5, 10, 0, 56, 111, 10, 1, 6, 102, + 0, 0, 0, 0, 0, 0, 0, 36, 3, 215, 5, 5, 217, 5, 10, 0, 56, 112, + 11, 1, 6, 103, 0, 0, 0, 0, 0, 0, 0, 36, 4, 224, 5, 11, 0, 56, + 113, 2, 11, 0, 1, 5, 223, 5, 11, 0, 1, 6, 102, 0, 0, 0, 0, 0, + 0, 0, 39, 11, 0, 1, 6, 101, 0, 0, 0, 0, 0, 0, 0, 39, 1, 0, + 0, 0, 0, 1, 0, 2, 0, + ] +}); + +#[rustfmt::skip] +pub static MODULES_ECONIA: Lazy>> = Lazy::new(|| { vec![ + MODULE_ECONIA_ASSETS.to_vec(), + MODULE_ECONIA_AVL_QUEUE.to_vec(), + MODULE_ECONIA_FAUCET.to_vec(), + MODULE_ECONIA_TABLIST.to_vec(), + MODULE_ECONIA_RESOURCE_ACCOUNT.to_vec(), + MODULE_ECONIA_INCENTIVES.to_vec(), + MODULE_ECONIA_REGISTRY.to_vec(), + MODULE_ECONIA_USER.to_vec(), + MODULE_ECONIA_MARKET.to_vec(), + MODULE_ECONIA_TXN_GENERATOR_UTILS.to_vec(), +]}); + #[rustfmt::skip] pub static PACKAGE_COMPLEX_METADATA: Lazy> = Lazy::new(|| { vec![ @@ -739,10 +5259,10 @@ pub static MODULES_COMPLEX: Lazy>> = Lazy::new(|| { vec![ pub static PACKAGE_SIMPLE_METADATA: Lazy> = Lazy::new(|| { vec![ 13, 71, 101, 110, 101, 114, 105, 99, 77, 111, 100, 117, 108, 101, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 64, 69, 53, 53, 57, 68, 57, 51, 68, 67, 55, 70, 65, - 56, 70, 70, 66, 70, 53, 70, 65, 57, 48, 49, 52, 57, 69, 49, 65, 56, 48, - 56, 55, 57, 67, 65, 52, 56, 50, 66, 67, 52, 67, 48, 50, 48, 51, 51, 50, - 53, 53, 52, 67, 50, 67, 48, 52, 49, 56, 67, 54, 56, 56, 53, 69, 132, 1, + 0, 0, 0, 0, 0, 64, 66, 48, 55, 66, 66, 56, 51, 50, 51, 70, 49, 57, + 55, 69, 48, 48, 68, 65, 56, 48, 50, 67, 69, 70, 56, 68, 52, 65, 68, 65, + 55, 69, 57, 57, 70, 49, 54, 57, 51, 48, 51, 69, 67, 50, 49, 50, 48, 48, + 49, 55, 67, 48, 67, 68, 70, 68, 51, 48, 65, 68, 67, 55, 70, 68, 132, 1, 31, 139, 8, 0, 0, 0, 0, 0, 2, 255, 77, 139, 59, 14, 194, 48, 16, 68, 251, 61, 133, 229, 30, 135, 11, 80, 208, 64, 197, 9, 162, 20, 43, 123, 64, 86, 156, 93, 203, 134, 80, 32, 238, 142, 45, 1, 138, 102, 154, 249, 188, 49, 179, 159, @@ -881,7 +5401,7 @@ pub static MODULE_SIMPLE_SIMPLE: Lazy> = Lazy::new(|| { 4, 1, 10, 5, 12, 4, 5, 47, 11, 3, 1, 11, 0, 16, 3, 12, 7, 10, 4, 11, 7, 34, 4, 87, 11, 4, 1, 11, 5, 1, 11, 2, 16, 4, 12, 4, 11, 1, 16, 3, 12, 5, 5, 37, 11, 1, 1, 11, 2, 1, 5, 37, 11, 5, - 1, 10, 1, 16, 3, 12, 4, 10, 3, 16, 4, 12, 5, 5, 17, 6, 0, 0, + 1, 10, 1, 16, 3, 12, 4, 10, 3, 16, 4, 12, 5, 5, 17, 6, 1, 4, 1, 3, 18, 38, 10, 0, 17, 1, 12, 2, 10, 2, 41, 3, 3, 35, 10, 0, 12, 3, 11, 0, 56, 0, 18, 3, 12, 4, 11, 3, 11, 4, 45, 3, 11, 2, 42, 3, 12, 5, 10, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 36, 4, 32, @@ -1599,34 +6119,36 @@ pub static MODULES_FRAMEWORK_USECASES: Lazy>> = Lazy::new(|| { vec![ pub static PACKAGE_AMBASSADOR_TOKEN_METADATA: Lazy> = Lazy::new(|| { vec![ 10, 97, 109, 98, 97, 115, 115, 97, 100, 111, 114, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 64, 56, 66, 56, 55, 50, 65, 65, 53, 50, 53, 54, 53, 67, 66, 55, - 48, 65, 65, 57, 48, 48, 55, 51, 48, 56, 67, 67, 49, 50, 51, 57, 50, 66, - 54, 57, 53, 52, 56, 51, 67, 51, 48, 54, 54, 54, 70, 69, 69, 51, 56, 52, - 55, 66, 50, 51, 49, 54, 53, 50, 52, 54, 67, 51, 65, 219, 1, 31, 139, 8, - 0, 0, 0, 0, 0, 2, 255, 141, 141, 61, 110, 195, 48, 12, 70, 119, 157, 130, - 80, 7, 47, 181, 227, 30, 160, 67, 80, 52, 107, 151, 110, 70, 80, 208, 34, 227, - 184, 182, 73, 67, 84, 218, 2, 69, 239, 94, 11, 206, 207, 26, 64, 139, 136, 239, - 189, 215, 204, 24, 6, 236, 120, 239, 4, 39, 134, 103, 40, 112, 106, 209, 12, 73, - 99, 225, 190, 56, 90, 175, 146, 207, 79, 85, 93, 213, 133, 115, 15, 64, 10, 162, - 9, 194, 17, 165, 99, 72, 10, 254, 195, 63, 66, 80, 9, 145, 19, 3, 18, 69, - 54, 3, 97, 38, 38, 56, 104, 132, 73, 233, 52, 114, 57, 159, 218, 177, 183, 35, - 36, 182, 212, 75, 7, 135, 184, 36, 191, 53, 14, 174, 57, 67, 108, 123, 119, 235, - 47, 89, 95, 255, 188, 108, 119, 175, 222, 185, 134, 120, 102, 33, 150, 208, 231, 213, - 118, 78, 106, 187, 139, 96, 89, 254, 194, 168, 1, 199, 204, 84, 213, 102, 125, 215, - 192, 6, 243, 188, 188, 254, 61, 252, 173, 134, 119, 29, 88, 222, 218, 79, 14, 201, - 238, 148, 164, 140, 148, 186, 50, 89, 244, 15, 202, 171, 40, 201, 67, 1, 0, 0, - 1, 10, 97, 109, 98, 97, 115, 115, 97, 100, 111, 114, 0, 0, 0, 4, 0, 0, + 0, 0, 64, 68, 70, 50, 57, 50, 70, 53, 53, 56, 50, 66, 48, 57, 50, 65, + 51, 54, 55, 65, 53, 69, 55, 69, 57, 65, 48, 48, 69, 66, 51, 68, 57, 52, + 48, 55, 56, 67, 49, 54, 69, 65, 66, 65, 48, 50, 52, 69, 65, 65, 50, 52, + 57, 52, 49, 55, 70, 54, 50, 51, 52, 53, 67, 50, 48, 253, 1, 31, 139, 8, + 0, 0, 0, 0, 0, 2, 255, 173, 142, 63, 79, 195, 64, 12, 197, 247, 251, 20, + 86, 24, 186, 144, 63, 172, 72, 12, 21, 162, 43, 11, 91, 84, 161, 203, 157, 155, + 28, 73, 236, 232, 236, 20, 36, 196, 119, 231, 142, 22, 144, 16, 99, 55, 219, 239, + 249, 253, 94, 187, 88, 55, 218, 30, 247, 134, 236, 140, 112, 7, 27, 59, 119, 86, + 196, 122, 142, 27, 115, 196, 40, 129, 41, 159, 111, 170, 166, 106, 54, 198, 92, 129, + 103, 32, 86, 112, 131, 165, 30, 65, 25, 138, 231, 226, 26, 28, 147, 139, 168, 8, + 214, 251, 136, 34, 64, 136, 30, 61, 28, 56, 194, 204, 126, 157, 176, 92, 214, 110, + 10, 50, 128, 162, 104, 160, 30, 14, 49, 33, 95, 57, 142, 166, 61, 63, 161, 236, + 205, 47, 63, 97, 139, 230, 237, 126, 187, 123, 40, 140, 105, 61, 46, 72, 30, 201, + 133, 236, 218, 46, 202, 178, 251, 14, 72, 206, 119, 232, 131, 230, 143, 65, 117, 145, + 219, 186, 78, 235, 176, 118, 149, 227, 185, 182, 217, 92, 78, 182, 147, 243, 248, 67, + 174, 146, 43, 149, 151, 181, 243, 225, 139, 247, 71, 79, 90, 196, 99, 22, 102, 27, + 136, 80, 11, 248, 56, 177, 159, 120, 68, 122, 236, 94, 208, 169, 92, 26, 175, 57, + 187, 228, 83, 248, 191, 21, 62, 1, 7, 30, 19, 174, 183, 1, 0, 0, 1, 10, + 97, 109, 98, 97, 115, 115, 97, 100, 111, 114, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 14, 65, 112, 116, 111, 115, - 70, 114, 97, 109, 101, 119, 111, 114, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 14, 65, 112, 116, 111, 115, 70, 114, + 97, 109, 101, 119, 111, 114, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 11, 65, 112, 116, 111, 115, 83, 116, 100, 108, 105, 98, 0, + 0, 0, 1, 11, 65, 112, 116, 111, 115, 83, 116, 100, 108, 105, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 77, 111, 118, 101, - 83, 116, 100, 108, 105, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 4, 17, 65, 112, 116, 111, 115, 84, 111, 107, 101, 110, 79, 98, 106, 101, 99, - 116, 115, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 77, 111, 118, 101, 83, 116, + 100, 108, 105, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, + 17, 65, 112, 116, 111, 115, 84, 111, 107, 101, 110, 79, 98, 106, 101, 99, 116, 115, + 0, ] }); @@ -1843,29 +6365,31 @@ pub static MODULES_AMBASSADOR_TOKEN: Lazy>> = Lazy::new(|| { vec![ pub static PACKAGE_AGGREGATOR_EXAMPLES_METADATA: Lazy> = Lazy::new(|| { vec![ 19, 65, 103, 103, 114, 101, 103, 97, 116, 111, 114, 32, 101, 120, 97, 109, 112, 108, - 101, 115, 1, 0, 0, 0, 0, 0, 0, 0, 0, 64, 68, 67, 55, 51, 49, 57, - 51, 57, 49, 54, 57, 50, 56, 54, 56, 57, 49, 55, 68, 67, 56, 51, 68, 70, - 52, 70, 65, 55, 66, 50, 65, 51, 67, 68, 52, 55, 55, 49, 49, 54, 69, 57, - 52, 70, 50, 54, 51, 52, 67, 65, 51, 48, 53, 56, 53, 48, 52, 65, 56, 69, - 69, 53, 51, 70, 162, 1, 31, 139, 8, 0, 0, 0, 0, 0, 2, 255, 141, 142, - 65, 10, 195, 32, 16, 69, 247, 158, 66, 220, 215, 244, 2, 93, 72, 105, 46, 208, - 101, 8, 101, 170, 83, 145, 24, 21, 71, 218, 64, 233, 221, 171, 41, 201, 186, 204, - 108, 62, 255, 61, 248, 67, 2, 61, 129, 197, 145, 5, 152, 145, 159, 184, 80, 214, - 102, 180, 80, 98, 230, 184, 192, 156, 60, 146, 96, 79, 204, 228, 98, 104, 253, 81, - 214, 19, 140, 13, 96, 76, 70, 34, 164, 145, 193, 238, 220, 54, 103, 69, 151, 179, - 234, 47, 141, 53, 152, 48, 24, 12, 218, 53, 92, 165, 18, 233, 90, 140, 119, 247, - 138, 189, 185, 143, 26, 124, 19, 164, 236, 234, 63, 114, 157, 242, 138, 121, 234, 160, - 129, 7, 90, 73, 193, 63, 63, 177, 223, 234, 127, 220, 61, 55, 253, 11, 194, 232, - 76, 11, 237, 0, 0, 0, 1, 22, 99, 111, 117, 110, 116, 101, 114, 95, 119, 105, - 116, 104, 95, 109, 105, 108, 101, 115, 116, 111, 110, 101, 0, 0, 0, 3, 0, 0, + 101, 115, 1, 0, 0, 0, 0, 0, 0, 0, 0, 64, 52, 57, 67, 52, 52, 65, + 65, 49, 54, 53, 57, 66, 66, 51, 68, 68, 49, 65, 69, 56, 49, 56, 49, 52, + 53, 53, 68, 48, 69, 57, 57, 56, 56, 50, 48, 53, 50, 52, 51, 57, 70, 48, + 67, 55, 69, 69, 55, 55, 68, 57, 66, 67, 67, 52, 70, 57, 48, 51, 68, 54, + 54, 48, 53, 67, 200, 1, 31, 139, 8, 0, 0, 0, 0, 0, 2, 255, 173, 142, + 193, 14, 130, 48, 12, 134, 239, 123, 138, 101, 103, 5, 207, 38, 30, 136, 209, 23, + 240, 104, 140, 41, 172, 142, 5, 216, 150, 117, 32, 137, 241, 221, 221, 64, 56, 121, + 52, 189, 180, 253, 191, 191, 127, 175, 14, 170, 6, 20, 222, 152, 129, 14, 249, 129, + 139, 66, 41, 143, 10, 130, 245, 28, 71, 232, 92, 139, 36, 216, 128, 158, 180, 53, + 73, 223, 101, 177, 4, 99, 87, 144, 210, 35, 17, 210, 141, 193, 234, 185, 47, 158, + 9, 29, 143, 197, 249, 148, 88, 137, 14, 141, 68, 83, 233, 132, 23, 46, 88, 186, + 4, 217, 234, 50, 98, 47, 174, 116, 72, 120, 29, 130, 163, 125, 158, 199, 177, 238, + 203, 172, 178, 93, 14, 137, 220, 182, 80, 210, 183, 125, 248, 248, 230, 211, 250, 38, + 139, 148, 216, 112, 234, 75, 169, 125, 114, 207, 58, 77, 87, 163, 224, 113, 72, 219, + 14, 180, 49, 24, 4, 127, 207, 169, 231, 197, 255, 239, 224, 85, 255, 153, 253, 1, + 1, 44, 12, 141, 103, 1, 0, 0, 1, 22, 99, 111, 117, 110, 116, 101, 114, 95, + 119, 105, 116, 104, 95, 109, 105, 108, 101, 115, 116, 111, 110, 101, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 14, 65, 112, 116, 111, 115, - 70, 114, 97, 109, 101, 119, 111, 114, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 11, 65, 112, 116, 111, 115, 83, 116, 100, 108, 105, 98, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 14, 65, 112, 116, + 111, 115, 70, 114, 97, 109, 101, 119, 111, 114, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 77, 111, 118, 101, - 83, 116, 100, 108, 105, 98, 0, + 0, 0, 0, 0, 0, 0, 1, 11, 65, 112, 116, 111, 115, 83, 116, 100, 108, 105, + 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 77, 111, + 118, 101, 83, 116, 100, 108, 105, 98, 0, ] }); @@ -1936,24 +6460,26 @@ pub static MODULES_AGGREGATOR_EXAMPLES: Lazy>> = Lazy::new(|| { vec! pub static PACKAGE_BCS_STREAM_METADATA: Lazy> = Lazy::new(|| { vec![ 9, 66, 67, 83, 83, 116, 114, 101, 97, 109, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 64, 51, 53, 54, 53, 51, 54, 51, 69, 56, 68, 49, 69, 50, 52, 56, 51, - 69, 55, 69, 53, 50, 70, 69, 49, 53, 50, 55, 68, 56, 55, 50, 69, 65, 67, - 54, 53, 55, 51, 48, 57, 66, 68, 53, 69, 50, 53, 48, 55, 70, 57, 70, 55, - 50, 67, 50, 48, 56, 67, 49, 54, 66, 66, 54, 57, 145, 1, 31, 139, 8, 0, - 0, 0, 0, 0, 2, 255, 29, 205, 65, 10, 195, 32, 16, 5, 208, 253, 156, 66, - 220, 215, 244, 2, 93, 164, 161, 189, 128, 203, 16, 202, 68, 167, 69, 98, 84, 28, - 105, 11, 165, 119, 143, 202, 204, 234, 243, 62, 127, 78, 104, 54, 124, 209, 2, 1, - 119, 18, 23, 33, 175, 147, 214, 37, 19, 238, 18, 222, 148, 217, 197, 208, 210, 179, - 170, 39, 1, 102, 180, 54, 19, 51, 241, 2, 171, 225, 7, 119, 218, 197, 119, 26, - 239, 183, 70, 44, 37, 10, 150, 130, 113, 77, 141, 169, 68, 214, 197, 122, 183, 86, - 246, 19, 62, 26, 244, 173, 160, 212, 80, 255, 153, 235, 238, 39, 230, 109, 192, 6, - 79, 220, 165, 20, 127, 56, 0, 149, 1, 61, 109, 155, 0, 0, 0, 1, 10, 98, - 99, 115, 95, 115, 116, 114, 101, 97, 109, 0, 0, 0, 2, 0, 0, 0, 0, 0, + 0, 64, 66, 48, 65, 50, 57, 67, 53, 52, 66, 51, 55, 54, 67, 69, 56, 69, + 66, 55, 69, 49, 53, 68, 70, 57, 67, 54, 52, 66, 53, 68, 51, 53, 68, 52, + 65, 68, 67, 51, 50, 65, 57, 52, 65, 69, 57, 66, 56, 49, 50, 48, 57, 57, + 68, 48, 53, 56, 51, 67, 49, 66, 55, 49, 48, 54, 179, 1, 31, 139, 8, 0, + 0, 0, 0, 0, 2, 255, 45, 206, 205, 10, 194, 48, 12, 0, 224, 123, 159, 162, + 244, 172, 155, 103, 193, 131, 14, 125, 129, 29, 199, 144, 180, 141, 91, 217, 250, 67, + 211, 77, 65, 124, 119, 219, 41, 185, 228, 231, 75, 72, 23, 64, 77, 48, 96, 207, + 28, 88, 228, 39, 46, 46, 77, 219, 166, 136, 96, 5, 91, 49, 146, 241, 174, 116, + 15, 85, 14, 193, 88, 7, 90, 71, 36, 66, 234, 153, 84, 116, 167, 141, 110, 226, + 213, 156, 111, 215, 66, 52, 6, 116, 26, 157, 50, 69, 157, 67, 242, 212, 38, 61, + 27, 153, 217, 155, 15, 38, 21, 62, 166, 20, 232, 88, 215, 185, 28, 23, 89, 41, + 111, 107, 40, 114, 63, 131, 164, 127, 250, 136, 249, 167, 167, 143, 83, 149, 149, 216, + 113, 90, 164, 54, 177, 108, 255, 230, 180, 93, 205, 131, 136, 107, 233, 90, 48, 206, + 97, 18, 252, 195, 190, 12, 3, 21, 8, 216, 0, 0, 0, 1, 10, 98, 99, 115, + 95, 115, 116, 114, 101, 97, 109, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 11, 65, 112, 116, 111, 115, 83, 116, 100, - 108, 105, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, - 77, 111, 118, 101, 83, 116, 100, 108, 105, 98, 0, + 0, 0, 0, 0, 0, 0, 1, 11, 65, 112, 116, 111, 115, 83, 116, 100, 108, 105, + 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 77, 111, + 118, 101, 83, 116, 100, 108, 105, 98, 0, ] }); @@ -2159,6 +6685,7 @@ pub static MODULES_BCS_STREAM: Lazy>> = Lazy::new(|| { vec![ #[rustfmt::skip] pub static PACKAGE_TO_METADATA: Lazy>> = Lazy::new(|| { HashMap::from([ + ("econia".to_string(), PACKAGE_ECONIA_METADATA.to_vec()), ("complex".to_string(), PACKAGE_COMPLEX_METADATA.to_vec()), ("simple".to_string(), PACKAGE_SIMPLE_METADATA.to_vec()), ("framework_usecases".to_string(), PACKAGE_FRAMEWORK_USECASES_METADATA.to_vec()), @@ -2169,6 +6696,7 @@ pub static PACKAGE_TO_METADATA: Lazy>> = Lazy::new(|| { #[rustfmt::skip] pub static PACKAGE_TO_MODULES: Lazy>>> = Lazy::new(|| { HashMap::from([ + ("econia".to_string(), MODULES_ECONIA.to_vec()), ("complex".to_string(), MODULES_COMPLEX.to_vec()), ("simple".to_string(), MODULES_SIMPLE.to_vec()), ("framework_usecases".to_string(), MODULES_FRAMEWORK_USECASES.to_vec()), diff --git a/crates/transaction-generator-lib/src/transaction_mix_generator.rs b/crates/transaction-generator-lib/src/transaction_mix_generator.rs index eef89c664cb86..0214d6f7b9fb0 100644 --- a/crates/transaction-generator-lib/src/transaction_mix_generator.rs +++ b/crates/transaction-generator-lib/src/transaction_mix_generator.rs @@ -4,7 +4,7 @@ use crate::{TransactionGenerator, TransactionGeneratorCreator}; use aptos_sdk::types::{transaction::SignedTransaction, LocalAccount}; use rand::{rngs::StdRng, Rng, SeedableRng}; use std::sync::{ - atomic::{AtomicUsize, Ordering}, + atomic::{AtomicU64, AtomicUsize, Ordering}, Arc, }; @@ -40,6 +40,8 @@ impl TransactionGenerator for PhasedTxnMixGenerator { &mut self, account: &LocalAccount, num_to_create: usize, + _history: &[String], + _market_maker: bool, ) -> Vec { let phase = if self.txn_mix_per_phase.len() == 1 { // when only single txn_mix is passed, use it for all phases, for simplicity @@ -51,7 +53,7 @@ impl TransactionGenerator for PhasedTxnMixGenerator { let mut picked = self.rng.gen_range(0, self.total_weight_per_phase[phase]); for (gen, weight) in &mut self.txn_mix_per_phase[phase] { if picked < *weight { - return gen.generate_transactions(account, num_to_create); + return gen.generate_transactions(account, num_to_create, &Vec::new(), false); } picked -= *weight; } @@ -80,12 +82,18 @@ impl PhasedTxnMixGeneratorCreator { } impl TransactionGeneratorCreator for PhasedTxnMixGeneratorCreator { - fn create_transaction_generator(&self) -> Box { + fn create_transaction_generator( + &self, + txn_counter: Arc, + ) -> Box { let mut txn_mix_per_phase = Vec::, usize)>>::new(); for txn_mix_creators in self.txn_mix_per_phase_creators.iter() { let mut txn_mix = Vec::<(Box, usize)>::new(); for (generator_creator, weight) in txn_mix_creators.iter() { - txn_mix.push((generator_creator.create_transaction_generator(), *weight)); + txn_mix.push(( + generator_creator.create_transaction_generator(txn_counter.clone()), + *weight, + )); } txn_mix_per_phase.push(txn_mix); } diff --git a/crates/transaction-generator-lib/src/workflow_delegator.rs b/crates/transaction-generator-lib/src/workflow_delegator.rs index 2252fbb4aab60..5d8dced16b80e 100644 --- a/crates/transaction-generator-lib/src/workflow_delegator.rs +++ b/crates/transaction-generator-lib/src/workflow_delegator.rs @@ -2,11 +2,19 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - account_generator::AccountGeneratorCreator, accounts_pool_wrapper::AccountsPoolWrapperCreator, + account_generator::AccountGeneratorCreator, + accounts_pool_wrapper::{ + AccountsPoolWrapperCreator, AddHistoryWrapperCreator, BucketedAccountsPoolWrapperCreator, MarketMakerPoolWrapperCreator, ReuseAccountsPoolWrapperCreator + }, call_custom_modules::CustomModulesDelegationGeneratorCreator, - entry_points::EntryPointTransactionGenerator, EntryPoints, ObjectPool, - ReliableTransactionSubmitter, RootAccountHandle, TransactionGenerator, - TransactionGeneratorCreator, WorkflowKind, WorkflowProgress, + econia_order_generator::{ + register_econia_markets, EconiaDepositCoinsTransactionGenerator, + EconiaLimitOrderTransactionGenerator, EconiaMarketOrderTransactionGenerator, + EconiaRealOrderTransactionGenerator, EconiaRegisterMarketUserTransactionGenerator, + }, + entry_points::EntryPointTransactionGenerator, + EconiaFlowType, EntryPoints, ObjectPool, BucketedAccountPool, ReliableTransactionSubmitter, RootAccountHandle, + TransactionGenerator, TransactionGeneratorCreator, WorkflowKind, WorkflowProgress, }; use aptos_logger::{info, sample, sample::SampleRate}; use aptos_sdk::{ @@ -19,10 +27,11 @@ use std::{ atomic::{AtomicU64, AtomicUsize, Ordering}, Arc, }, + thread, time::{Duration, SystemTime, UNIX_EPOCH}, }; -#[derive(Clone)] +#[derive(Clone, Debug)] enum StageTracking { // Stage is externally modified. This is used by executor benchmark tests ExternallySet(Arc), @@ -43,22 +52,37 @@ impl StageTracking { .as_secs() } - fn load_current_stage(&self) -> Option { + // fn load_current_stage(&self) -> Option { + // match self { + // StageTracking::ExternallySet(stage_counter) => { + // Some(stage_counter.load(Ordering::Relaxed)) + // }, + // StageTracking::WhenDone { + // stage_counter, + // stage_start_time, + // .. + // } => { + // if stage_start_time.load(Ordering::Relaxed) > Self::current_timestamp() { + // None + // } else { + // Some(stage_counter.load(Ordering::Relaxed)) + // } + // }, + // } + // } +} + +#[derive(Clone)] +pub enum Pool { + AccountPool(Arc>), + AccountWithHistoryPool(Arc)>>), +} + +impl Pool { + fn len(&self) -> usize { match self { - StageTracking::ExternallySet(stage_counter) => { - Some(stage_counter.load(Ordering::Relaxed)) - }, - StageTracking::WhenDone { - stage_counter, - stage_start_time, - .. - } => { - if stage_start_time.load(Ordering::Relaxed) > Self::current_timestamp() { - None - } else { - Some(stage_counter.load(Ordering::Relaxed)) - } - }, + Pool::AccountPool(pool) => pool.len(), + Pool::AccountWithHistoryPool(pool) => pool.len(), } } } @@ -109,16 +133,22 @@ impl TransactionGenerator for WorkflowTxnGenerator { &mut self, account: &LocalAccount, num_to_create: usize, + _history: &[String], + _market_maker: bool, ) -> Vec { assert_ne!(num_to_create, 0); - let stage = match self.stage.load_current_stage() { - Some(stage) => stage, - None => { - sample!( - SampleRate::Duration(Duration::from_secs(2)), - info!("Waiting for delay before next stage"); - ); - return Vec::new(); + let stage = match &self.stage { + StageTracking::ExternallySet(stage_counter) => stage_counter.load(Ordering::Relaxed), + StageTracking::WhenDone { + stage_counter, + stage_start_time, + .. + } => { + if stage_start_time.load(Ordering::Relaxed) > StageTracking::current_timestamp() { + info!("Waiting for next stage for {} seconds", 60); + thread::sleep(Duration::from_secs(60)); + } + stage_counter.load(Ordering::Relaxed) }, }; @@ -165,12 +195,12 @@ impl TransactionGenerator for WorkflowTxnGenerator { } sample!( - SampleRate::Duration(Duration::from_secs(2)), + SampleRate::Duration(Duration::from_millis(1000)), info!("Cur stage: {}, stage switch conditions: {:?}", stage, self.stage_switch_conditions); ); let result = if let Some(generator) = self.generators.get_mut(stage) { - generator.generate_transactions(account, num_to_create) + generator.generate_transactions(account, num_to_create, &Vec::new(), false) } else { Vec::new() }; @@ -248,7 +278,7 @@ impl WorkflowTxnGeneratorCreator { root_account: &dyn RootAccountHandle, txn_executor: &dyn ReliableTransactionSubmitter, num_modules: usize, - _initial_account_pool: Option>>, + initial_account_pool: Option>>, cur_phase: Arc, progress_type: WorkflowProgress, ) -> Self { @@ -289,7 +319,8 @@ impl WorkflowTxnGeneratorCreator { txn_executor, num_modules, mint_entry_point.package_name(), - Some(40_0000_0000), + Some(40_00000000), + true, ) .await; @@ -349,17 +380,322 @@ impl WorkflowTxnGeneratorCreator { StageSwitchCondition::WhenPoolBecomesEmpty(minted_pool), ]) }, + WorkflowKind::Econia { + num_users, + flow_type, + num_markets, + reuse_accounts_for_orders, + publish_packages, + } => { + // let create_accounts = initial_account_pool.is_none(); + let create_accounts = true; + // info!("Create_accounts {:?}", create_accounts); + let created_pool = initial_account_pool.unwrap_or(Arc::new(ObjectPool::new())); + let register_market_accounts_pool = Arc::new(ObjectPool::new()); + let source_addresses: Vec<_> = created_pool.pool.read().iter().map(|a| a.address().clone()).collect(); + info!("Source addresses length: {}", source_addresses.len()); + let deposit_coins_pool = Arc::new(BucketedAccountPool::new(Arc::new(source_addresses.clone()))); + // let place_orders_pool = Arc::new(ObjectPool::new()); + + let mut packages = CustomModulesDelegationGeneratorCreator::publish_package( + init_txn_factory.clone(), + root_account, + txn_executor, + num_modules, + EntryPoints::EconiaRegisterMarket.package_name(), + Some(100_000_000_000_000), + publish_packages, + ) + .await; + + if publish_packages { + register_econia_markets( + init_txn_factory.clone(), + &mut packages, + txn_executor, + num_markets, + ) + .await; + } + + let econia_register_market_user_worker = CustomModulesDelegationGeneratorCreator::create_worker( + init_txn_factory.clone(), + root_account, + txn_executor, + &mut packages, + &mut EconiaRegisterMarketUserTransactionGenerator::new( + num_markets, + true, + ), + ) + .await; + + let econia_deposit_coins_worker = CustomModulesDelegationGeneratorCreator::create_worker( + init_txn_factory.clone(), + root_account, + txn_executor, + &mut packages, + &mut EconiaDepositCoinsTransactionGenerator::new(num_markets, true), + ) + .await; + + let econia_place_orders_worker = match flow_type { + EconiaFlowType::Basic => { + CustomModulesDelegationGeneratorCreator::create_worker( + init_txn_factory.clone(), + root_account, + txn_executor, + &mut packages, + &mut EntryPointTransactionGenerator { + entry_point: EntryPoints::EconiaPlaceRandomLimitOrder, + }, + ) + .await + }, + EconiaFlowType::Mixed => { + CustomModulesDelegationGeneratorCreator::create_worker( + init_txn_factory.clone(), + root_account, + txn_executor, + &mut packages, + &mut EconiaLimitOrderTransactionGenerator::new( + num_markets, + (num_users as u64) * 2, + ), + ) + .await + }, + EconiaFlowType::Market => { + CustomModulesDelegationGeneratorCreator::create_worker( + init_txn_factory.clone(), + root_account, + txn_executor, + &mut packages, + &mut EconiaMarketOrderTransactionGenerator::new( + num_markets, + (num_users as u64) * 2, + ), + ) + .await + }, + }; + + let packages = Arc::new(packages); + + let mut creators: Vec> = vec![]; + let mut stage_switch_conditions = vec![]; + if create_accounts { + info!("Creating {} accounts", num_users); + creators.push(Box::new(AccountGeneratorCreator::new( + txn_factory.clone(), + None, + Some(created_pool.clone()), + num_users, + 400_000_000, + ))); + stage_switch_conditions.push(StageSwitchCondition::MaxTransactions( + Arc::new(AtomicUsize::new(num_users)), + )); + } + + creators.push(Box::new(AccountsPoolWrapperCreator::new( + Box::new(CustomModulesDelegationGeneratorCreator::new_raw( + txn_factory.clone(), + packages.clone(), + econia_register_market_user_worker, + )), + created_pool.clone(), + Some(register_market_accounts_pool.clone()), + ))); + stage_switch_conditions.push(StageSwitchCondition::WhenPoolBecomesEmpty( + created_pool.clone(), + )); + + creators.push(Box::new(BucketedAccountsPoolWrapperCreator::new( + Box::new(CustomModulesDelegationGeneratorCreator::new_raw( + txn_factory.clone(), + packages.clone(), + econia_deposit_coins_worker, + )), + register_market_accounts_pool.clone(), + Some(deposit_coins_pool.clone()), + ))); + stage_switch_conditions.push(StageSwitchCondition::WhenPoolBecomesEmpty( + register_market_accounts_pool.clone(), + )); + + // if reuse_accounts_for_orders { + creators.push(Box::new(ReuseAccountsPoolWrapperCreator::new( + Box::new(CustomModulesDelegationGeneratorCreator::new_raw( + txn_factory.clone(), + packages.clone(), + econia_place_orders_worker, + )), + deposit_coins_pool.clone(), + ))); + stage_switch_conditions.push(StageSwitchCondition::MaxTransactions( + Arc::new(AtomicUsize::new(2_000_000)), + )); + // } + // else { + // creators.push(Box::new(AccountsPoolWrapperCreator::new( + // Box::new(CustomModulesDelegationGeneratorCreator::new_raw( + // txn_factory.clone(), + // packages.clone(), + // econia_place_orders_worker, + // )), + // deposit_coins_pool.clone(), + // Some(place_orders_pool.clone()), + // ))); + // stage_switch_conditions.push(StageSwitchCondition::WhenPoolBecomesEmpty( + // deposit_coins_pool.clone(), + // )); + // } + + Self::new(stage_tracking, creators, stage_switch_conditions) + }, + WorkflowKind::EconiaReal { + num_users, + publish_packages, + } => { + // let create_accounts = initial_account_pool.is_none(); + let create_accounts = true; + // info!("Create_accounts {:?}", create_accounts); + let created_pool = initial_account_pool.unwrap_or(Arc::new(ObjectPool::new())); + let register_market_accounts_pool = Arc::new(ObjectPool::new()); + let deposit_coins_pool = Arc::new(ObjectPool::new()); + let deposit_coins_pool_with_added_history = Arc::new(ObjectPool::new()); + let num_markets = 2; + + let mut packages = CustomModulesDelegationGeneratorCreator::publish_package( + init_txn_factory.clone(), + root_account, + txn_executor, + num_modules, + EntryPoints::EconiaRegisterMarket.package_name(), + Some(100_000_000_000_000), + publish_packages, + ) + .await; + + if publish_packages { + register_econia_markets( + init_txn_factory.clone(), + &mut packages, + txn_executor, + num_markets, + ) + .await; + } + + let econia_register_market_user_worker = CustomModulesDelegationGeneratorCreator::create_worker( + init_txn_factory.clone(), + root_account, + txn_executor, + &mut packages, + &mut EconiaRegisterMarketUserTransactionGenerator::new( + num_markets, + false, + ), + ) + .await; + + let econia_deposit_coins_worker = CustomModulesDelegationGeneratorCreator::create_worker( + init_txn_factory.clone(), + root_account, + txn_executor, + &mut packages, + &mut EconiaDepositCoinsTransactionGenerator::new(num_markets, false), + ) + .await; + + let econia_place_orders_worker = CustomModulesDelegationGeneratorCreator::create_worker( + init_txn_factory.clone(), + root_account, + txn_executor, + &mut packages, + &mut EconiaRealOrderTransactionGenerator::default(), + ) + .await; + + let packages = Arc::new(packages); + + let mut creators: Vec> = vec![]; + let mut stage_switch_conditions = vec![]; + if create_accounts { + creators.push(Box::new(AccountGeneratorCreator::new( + txn_factory.clone(), + None, + Some(created_pool.clone()), + num_users, + 400_000_000, + ))); + stage_switch_conditions.push(StageSwitchCondition::MaxTransactions( + Arc::new(AtomicUsize::new(num_users)), + )); + } + + creators.push(Box::new(AccountsPoolWrapperCreator::new( + Box::new(CustomModulesDelegationGeneratorCreator::new_raw( + txn_factory.clone(), + packages.clone(), + econia_register_market_user_worker, + )), + created_pool.clone(), + Some(register_market_accounts_pool.clone()), + ))); + stage_switch_conditions.push(StageSwitchCondition::WhenPoolBecomesEmpty( + created_pool.clone(), + )); + + creators.push(Box::new(AccountsPoolWrapperCreator::new( + Box::new(CustomModulesDelegationGeneratorCreator::new_raw( + txn_factory.clone(), + packages.clone(), + econia_deposit_coins_worker, + )), + register_market_accounts_pool.clone(), + Some(deposit_coins_pool.clone()), + ))); + stage_switch_conditions.push(StageSwitchCondition::WhenPoolBecomesEmpty( + register_market_accounts_pool.clone(), + )); + + creators.push(Box::new(AddHistoryWrapperCreator::new( + deposit_coins_pool.clone(), + deposit_coins_pool_with_added_history.clone(), + ))); + stage_switch_conditions.push(StageSwitchCondition::WhenPoolBecomesEmpty( + deposit_coins_pool.clone(), + )); + creators.push(Box::new(MarketMakerPoolWrapperCreator::new( + Box::new(CustomModulesDelegationGeneratorCreator::new_raw( + txn_factory.clone(), + packages.clone(), + econia_place_orders_worker, + )), + deposit_coins_pool_with_added_history.clone(), + ))); + stage_switch_conditions.push(StageSwitchCondition::MaxTransactions( + Arc::new(AtomicUsize::new(2_000_000)), + )); + + Self::new(stage_tracking, creators, stage_switch_conditions) + }, } } } impl TransactionGeneratorCreator for WorkflowTxnGeneratorCreator { - fn create_transaction_generator(&self) -> Box { + fn create_transaction_generator( + &self, + txn_counter: Arc, + ) -> Box { Box::new(WorkflowTxnGenerator::new( self.stage.clone(), self.creators .iter() - .map(|c| c.create_transaction_generator()) + .map(|c| c.create_transaction_generator(txn_counter.clone())) .collect(), self.stage_switch_conditions.clone(), )) diff --git a/execution/executor-benchmark/src/lib.rs b/execution/executor-benchmark/src/lib.rs index 09f2a5b91558b..6be0f48582ad4 100644 --- a/execution/executor-benchmark/src/lib.rs +++ b/execution/executor-benchmark/src/lib.rs @@ -48,7 +48,10 @@ use std::{ collections::HashMap, fs, path::Path, - sync::{atomic::AtomicUsize, Arc}, + sync::{ + atomic::{AtomicU64, AtomicUsize}, + Arc, + }, time::Instant, }; use tokio::runtime::Runtime; @@ -123,6 +126,7 @@ pub fn run_benchmark( config.storage.dir = checkpoint_dir.as_ref().to_path_buf(); config.storage.storage_pruner_config = pruner_config; config.storage.rocksdb_configs.enable_storage_sharding = enable_storage_sharding; + let txn_counter = Arc::new(AtomicU64::new(0)); let (db, executor) = init_db_and_executor::(&config); let root_account = TransactionGenerator::read_root_account(genesis_key, &db); let root_account = Arc::new(root_account); @@ -162,7 +166,7 @@ pub fn run_benchmark( &PipelineConfig::default(), ); // need to initialize all workers and finish with all transactions before we start the timer: - ((0..pipeline_config.num_generator_workers).map(|_| transaction_generator_creator.create_transaction_generator()).collect::>(), phase) + ((0..pipeline_config.num_generator_workers).map(|_| transaction_generator_creator.create_transaction_generator(txn_counter.clone())).collect::>(), phase) }); let start_version = db.reader.expect_synced_version(); @@ -596,6 +600,7 @@ struct OverallMeasuring { start_time: Instant, start_execution: ExecutionTimeMeasurement, start_gas: GasMeasurement, + event_summary: HashMap, } impl OverallMeasuring { @@ -604,9 +609,14 @@ impl OverallMeasuring { start_time: Instant::now(), start_execution: ExecutionTimeMeasurement::now(), start_gas: GasMeasurement::now(), + event_summary: HashMap::new(), } } + pub fn event_summary_mut(&mut self) -> &mut HashMap { + &mut self.event_summary + } + pub fn print_end(self, prefix: &str, num_txns: u64) { let elapsed = self.start_time.elapsed().as_secs_f64(); let num_txns = num_txns as f64; @@ -715,6 +725,18 @@ impl OverallMeasuring { delta_execution.commit_total / elapsed, num_txns / delta_execution.commit_total ); + + info!("Summary of events emitted during the execution"); + for (key, value) in self.event_summary { + println!( + "event : {}, #generated : {}", + key.replace( + "0000000000000000000000000000000000000000000000000000000000000001", + "0x1" + ), + value + ); + } } } @@ -763,8 +785,8 @@ mod tests { println!("run_benchmark"); super::run_benchmark::( - 10, /* block_size */ - 30, /* num_blocks */ + 10, /* block_size */ + 200, /* num_blocks */ transaction_type .map(|t| vec![(t.materialize(1, true, WorkflowProgress::MoveByPhases), 1)]), 2, /* transactions per sender */ @@ -794,10 +816,7 @@ mod tests { AptosVM::set_concurrency_level_once(4); AptosVM::set_processed_transactions_detailed_counters(); NativeExecutor::set_concurrency_level_once(4); - test_generic_benchmark::( - Some(TransactionTypeArg::ModifyGlobalMilestoneAggV2), - true, - ); + test_generic_benchmark::(Some(TransactionTypeArg::EconiaBasic1Market), true); } #[test] diff --git a/execution/executor-benchmark/src/main.rs b/execution/executor-benchmark/src/main.rs index 9d3e23722e68b..df47d18999069 100644 --- a/execution/executor-benchmark/src/main.rs +++ b/execution/executor-benchmark/src/main.rs @@ -369,7 +369,7 @@ enum Command { #[clap(long, default_value_t = 1000000)] num_new_accounts: usize, - #[clap(long, default_value_t = 1000000)] + #[clap(long, default_value_t = 100_0000_0000_0000)] init_account_balance: u64, }, } diff --git a/execution/executor-benchmark/src/pipeline.rs b/execution/executor-benchmark/src/pipeline.rs index c61fd79dfecbf..ea5de389bb745 100644 --- a/execution/executor-benchmark/src/pipeline.rs +++ b/execution/executor-benchmark/src/pipeline.rs @@ -149,7 +149,7 @@ where .name("txn_executor".to_string()) .spawn(move || { start_execution_rx.map(|rx| rx.recv()); - let overall_measuring = OverallMeasuring::start(); + let mut overall_measuring = OverallMeasuring::start(); let mut executed = 0; let mut stage_index = 0; @@ -169,7 +169,12 @@ where info!("Received block of size {:?} to execute", block_size); executed += block_size; stage_executed += block_size; - exe.execute_block(current_block_start_time, partition_time, block); + exe.execute_block( + current_block_start_time, + partition_time, + block, + overall_measuring.event_summary_mut(), + ); info!("Finished executing block"); // Empty blocks indicate the end of a stage. diff --git a/execution/executor-benchmark/src/transaction_executor.rs b/execution/executor-benchmark/src/transaction_executor.rs index 2d4079a3db848..5bc84383fbe42 100644 --- a/execution/executor-benchmark/src/transaction_executor.rs +++ b/execution/executor-benchmark/src/transaction_executor.rs @@ -7,10 +7,9 @@ use aptos_crypto::hash::HashValue; use aptos_executor::block_executor::{BlockExecutor, TransactionBlockExecutor}; use aptos_executor_types::BlockExecutorTrait; use aptos_logger::info; -use aptos_types::block_executor::{ - config::BlockExecutorConfigFromOnchain, partitioner::ExecutableBlock, -}; +use aptos_types::block_executor::{config::BlockExecutorConfigFromOnchain, partitioner::ExecutableBlock}; use std::{ + collections::HashMap, sync::{mpsc, Arc}, time::{Duration, Instant}, }; @@ -49,6 +48,7 @@ where current_block_start_time: Instant, partition_time: Duration, executable_block: ExecutableBlock, + event_summary: &mut HashMap, ) { let execution_start_time = Instant::now(); if self.maybe_first_block_start_time.is_none() { diff --git a/execution/executor-benchmark/src/transaction_generator.rs b/execution/executor-benchmark/src/transaction_generator.rs index dde5b1a40454f..52224b66b58b5 100644 --- a/execution/executor-benchmark/src/transaction_generator.rs +++ b/execution/executor-benchmark/src/transaction_generator.rs @@ -332,9 +332,10 @@ impl TransactionGenerator { }) .borrow_mut(); transaction_generator - .generate_transactions(sender, 1) - .pop() - .map(Transaction::UserTransaction) + .generate_transactions(sender, 1, &Vec::new(), false) + .iter() + .map(|txn| Transaction::UserTransaction(txn.clone())) + .collect() }, |sender_idx| *sender_idx, ); @@ -438,7 +439,7 @@ impl TransactionGenerator { init_account_balance, ), ); - Some(Transaction::UserTransaction(txn)) + vec![Transaction::UserTransaction(txn)] }, |(sender_idx, _)| *sender_idx, ); @@ -635,7 +636,7 @@ impl TransactionGenerator { self.transaction_factory .transfer(account_cache.accounts[receiver_idx].address(), 1), ); - Some(Transaction::UserTransaction(txn)) + vec![Transaction::UserTransaction(txn)] }, |(sender_idx, _)| *sender_idx, ); @@ -652,7 +653,7 @@ impl TransactionGenerator { ) -> bool where T: Send, - F: Fn(T, &AccountCache) -> Option + Send + Sync, + F: Fn(T, &AccountCache) -> Vec + Send + Sync, S: Fn(&T) -> usize, { let _timer = TIMER.with_label_values(&["generate_block"]).start_timer(); @@ -669,9 +670,11 @@ impl TransactionGenerator { let tx = tx.clone(); scope.spawn(move |_| { for (index, job) in per_worker_jobs { - if let Some(txn) = job() { - tx.send((index, txn)).unwrap(); - } + let txns = job(); + tx.send((index, txns)).unwrap(); + // if let Some(txn) = job() { + // tx.send((index, txn)).unwrap(); + // } } }); } @@ -684,8 +687,8 @@ impl TransactionGenerator { let mut transactions = Vec::new(); for i in 0..block_size { - if let Some(txn) = transactions_by_index.get(&i) { - transactions.push(txn.clone()); + if let Some(txns) = transactions_by_index.get(&i) { + transactions.extend(txns.clone()); } } diff --git a/execution/executor-types/src/state_checkpoint_output.rs b/execution/executor-types/src/state_checkpoint_output.rs index c611d6c2c90a0..a36fe0acc848a 100644 --- a/execution/executor-types/src/state_checkpoint_output.rs +++ b/execution/executor-types/src/state_checkpoint_output.rs @@ -42,6 +42,10 @@ impl TransactionsByStatus { self.statuses_for_input_txns.len() } + pub fn to_commit(&self) -> &TransactionsWithOutput { + &self.to_commit + } + pub fn into_inner( self, ) -> ( @@ -96,6 +100,7 @@ impl StateCheckpointOutput { Self::new_empty(Arc::new(StateDelta::new_empty())) } + fn new_impl(inner: Inner) -> Self { Self { inner: Arc::new(DropHelper::new(inner)), diff --git a/testsuite/forge-cli/src/suites/land_blocking.rs b/testsuite/forge-cli/src/suites/land_blocking.rs index dbae2019a9a43..142b349056c4f 100644 --- a/testsuite/forge-cli/src/suites/land_blocking.rs +++ b/testsuite/forge-cli/src/suites/land_blocking.rs @@ -2,9 +2,9 @@ // Parts of the project are originally copyright © Meta Platforms, Inc. // SPDX-License-Identifier: Apache-2.0 -use super::ungrouped::mixed_emit_job; -use crate::{suites::realistic_environment::realistic_env_max_load_test, TestCommand}; -use aptos_forge::{success_criteria::SuccessCriteria, ForgeConfig}; +use super::{pfn::pfn_const_tps, ungrouped::mixed_emit_job}; +use crate::TestCommand; +use aptos_forge::{success_criteria::SuccessCriteria, ForgeConfig, args::TransactionTypeArg}; use aptos_testcases::{ compatibility_test::SimpleValidatorUpgrade, framework_upgrade::FrameworkUpgrade, }; @@ -14,11 +14,12 @@ use std::{num::NonZeroUsize, sync::Arc, time::Duration}; pub(crate) fn get_land_blocking_test( test_name: &str, duration: Duration, - test_cmd: &TestCommand, + _test_cmd: &TestCommand, ) -> Option { let test = match test_name { "land_blocking" | "realistic_env_max_load" => { - realistic_env_max_load_test(duration, test_cmd, 7, 5) + pfn_const_tps(duration, true, true, false, TransactionTypeArg::EconiaMarket1MarketReuseAccounts.materialize_default(), 10000) + // realistic_env_max_load_test(duration, test_cmd, 7, 5) }, "compat" => compat(), "framework_upgrade" => framework_upgrade(), diff --git a/testsuite/forge-cli/src/suites/pfn.rs b/testsuite/forge-cli/src/suites/pfn.rs index 81f721698069d..372ade11be90e 100644 --- a/testsuite/forge-cli/src/suites/pfn.rs +++ b/testsuite/forge-cli/src/suites/pfn.rs @@ -5,8 +5,9 @@ use super::ungrouped::RELIABLE_PROGRESS_THRESHOLD; use aptos_config::config::NodeConfig; use aptos_forge::{ + args::TransactionTypeArg, success_criteria::{LatencyType, SuccessCriteria}, - EmitJobMode, EmitJobRequest, ForgeConfig, OverrideNodeConfigFn, + EmitJobMode, EmitJobRequest, ForgeConfig, OverrideNodeConfigFn, TransactionType, }; use aptos_testcases::public_fullnode_performance::PFNPerformance; use std::{num::NonZeroUsize, sync::Arc, time::Duration}; @@ -14,9 +15,20 @@ use std::{num::NonZeroUsize, sync::Arc, time::Duration}; /// Attempts to match the test name to a PFN test pub fn get_pfn_test(test_name: &str, duration: Duration) -> Option { let test = match test_name { - "pfn_const_tps" => pfn_const_tps(duration, false, false, true), - "pfn_const_tps_with_network_chaos" => pfn_const_tps(duration, false, true, false), - "pfn_const_tps_with_realistic_env" => pfn_const_tps(duration, true, true, false), + "pfn_const_tps" => pfn_const_tps(duration, false, false, true, TransactionType::default(), 10000), + "pfn_const_tps_with_network_chaos" => pfn_const_tps(duration, false, true, false, TransactionType::default(), 10000), + "pfn_const_tps_with_realistic_env" => pfn_const_tps(duration, true, true, false, TransactionType::default(), 10000), + + "pfn_const_tps_with_realistic_env_econia_basic_1market" => pfn_const_tps(duration, true, true, false, TransactionTypeArg::EconiaBasic1MarketReuseAccounts.materialize_default(), 10000), + "pfn_const_tps_with_realistic_env_econia_mixed_1market" => pfn_const_tps(duration, true, true, false, TransactionTypeArg::EconiaMixed1MarketReuseAccounts.materialize_default(), 10000), + "pfn_const_tps_with_realistic_env_econia_mixed_10market" => pfn_const_tps(duration, true, true, false, TransactionTypeArg::EconiaMixed10MarketReuseAccounts.materialize_default(), 10000), + "pfn_const_tps_with_realistic_env_econia_mixed_100market" => pfn_const_tps(duration, true, true, false, TransactionTypeArg::EconiaMixed100MarketReuseAccounts.materialize_default(), 10000), + + "pfn_const_tps_with_realistic_env_econia_market_1market" => pfn_const_tps(duration, true, true, false, TransactionTypeArg::EconiaMarket1MarketReuseAccounts.materialize_default(), 10000), + "pfn_const_tps_with_realistic_env_econia_market_10market" => pfn_const_tps(duration, true, true, false, TransactionTypeArg::EconiaMarket10MarketReuseAccounts.materialize_default(), 10000), + "pfn_const_tps_with_realistic_env_econia_market_100market" => pfn_const_tps(duration, true, true, false, TransactionTypeArg::EconiaMarket100MarketReuseAccounts.materialize_default(), 10000), + "pfn_const_tps_with_realistic_env_econia_real" => pfn_const_tps(duration, true, true, false, TransactionTypeArg::EconiaReal.materialize_default(), 10000), + "pfn_performance" => pfn_performance(duration, false, false, true, 7, 1, false), "pfn_performance_with_network_chaos" => { pfn_performance(duration, false, true, false, 7, 1, false) @@ -36,11 +48,13 @@ pub fn get_pfn_test(test_name: &str, duration: Duration) -> Option /// /// Note: If `add_cpu_chaos` is true, CPU chaos is enabled on the entire swarm. /// Likewise, if `add_network_emulation` is true, network chaos is enabled. -fn pfn_const_tps( +pub fn pfn_const_tps( duration: Duration, add_cpu_chaos: bool, add_network_emulation: bool, epoch_changes: bool, + transaction_type: TransactionType, + mempool_backlog: usize, ) -> ForgeConfig { let epoch_duration_secs = if epoch_changes { 300 // 5 minutes @@ -51,7 +65,12 @@ fn pfn_const_tps( ForgeConfig::default() .with_initial_validator_count(NonZeroUsize::new(7).unwrap()) .with_initial_fullnode_count(7) - .with_emit_job(EmitJobRequest::default().mode(EmitJobMode::ConstTps { tps: 5000 })) + .with_emit_job(EmitJobRequest::default() + .mode(EmitJobMode::MaxLoad { mempool_backlog }) + .transaction_type(transaction_type) + .coins_per_account_override(1_0000_0000_0000) + .num_accounts_mode(aptos_forge::emitter::NumAccountsMode::NumAccounts(2_500)), + ) .add_network_test(PFNPerformance::new( 7, add_cpu_chaos, diff --git a/testsuite/module-publish/src/packages/econia/Move.toml b/testsuite/module-publish/src/packages/econia/Move.toml new file mode 100644 index 0000000000000..560713a4fe74c --- /dev/null +++ b/testsuite/module-publish/src/packages/econia/Move.toml @@ -0,0 +1,17 @@ +[package] +name = "Econia" +version = "4.2.1" +upgrade_policy = "compatible" +authors = ["Econia Labs (developers@econialabs.com)"] + +[addresses] +econia = "0x12345" + +# Mock addresses for testing. +user = "0x1234" +user_0 = "0x2345" +user_1 = "0x3456" +integrator = "0x4567" + +[dependencies] +AptosFramework = { local = "../../../../../aptos-move/framework/aptos-framework" } \ No newline at end of file diff --git a/testsuite/module-publish/src/packages/econia/sources/assets.move b/testsuite/module-publish/src/packages/econia/sources/assets.move new file mode 100644 index 0000000000000..c7542b7a6f275 --- /dev/null +++ b/testsuite/module-publish/src/packages/econia/sources/assets.move @@ -0,0 +1,604 @@ +/// Mock asset types for on- and off-chain testing. +module econia::assets { + + // Uses >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + use aptos_framework::coin; + use std::signer::address_of; + use std::string::utf8; + + // Uses <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Test-only uses >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test_only] + use aptos_framework::account; + #[test_only] + use aptos_framework::aptos_coin; + + // Test-only uses <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Structs >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Stores mock coin type capabilities. + struct CoinCapabilities has key { + burn_capability: coin::BurnCapability, + freeze_capability: coin::FreezeCapability, + mint_capability: coin::MintCapability + } + + /// Base coin type. + struct BC{} + + /// Quote coin type. + struct QC{} + + /// Utility coin type. + struct UC{} + + /// Aditional coin types + struct AC{} + + struct DC{} + + struct EC{} + + struct FC{} + + struct GC{} + + struct HC{} + + struct IC{} + + struct JC{} + + struct KC{} + + struct LC{} + + struct MC{} + + struct NC{} + + struct OC{} + + struct PC{} + + struct RC{} + + struct SC{} + + struct TC{} + + struct VC{} + + struct WC{} + + struct XC{} + + struct AAC{} + + struct ABC{} + + struct ACC{} + + struct ADC{} + + struct AEC{} + + struct AFC{} + + struct AGC{} + + struct AHC{} + + struct AIC{} + + struct AJC{} + + struct AKC{} + + struct ALC{} + + struct AMC{} + + struct ANC{} + + struct AOC{} + + struct APC{} + + struct AQC{} + + struct ARC{} + + struct ASC{} + + struct ATC{} + + struct AUC{} + + struct AVC{} + + struct AWC{} + + struct AXC{} + + struct AYC{} + + struct AZC{} + + struct BAC{} + + struct BBC{} + + struct BCC{} + + struct BDC{} + + struct BEC{} + + struct BFC{} + + struct BGC{} + + struct BHC{} + + struct BIC{} + + struct BJC{} + + struct BKC{} + + struct BLC{} + + struct BMC{} + + struct BNC{} + + struct BOC{} + + struct BPC{} + + struct BQC{} + + struct BRC{} + + struct BSC{} + + struct BTC{} + + struct BUC{} + + struct BVC{} + + struct BWC{} + + struct BXC{} + + struct BYC{} + + struct BZC{} + + struct CAC{} + + struct CBC{} + + struct CCC{} + + struct CDC{} + + struct CEC{} + + struct CFC{} + + struct CGC{} + + struct CHC{} + + struct CIC{} + + struct CJC{} + + struct CKC{} + + struct CLC{} + + struct CMC{} + + struct CNC{} + + struct COC{} + + struct CPC{} + + struct CQC{} + + struct CRC{} + + struct CSC{} + + struct CTC{} + + struct CUC{} + + struct CVC{} + + struct CWC{} + + struct CXC{} + + struct CYC{} + + struct CZC{} + + struct DAC{} + + struct DBC{} + + struct DCC{} + + struct DDC{} + + struct DEC{} + + struct DFC{} + + struct DGC{} + + struct DHC{} + + struct DIC{} + + struct DJC{} + + struct DKC{} + + struct DLC{} + + struct DMC{} + + struct DNC{} + + struct DOC{} + + struct DPC{} + + struct DQC{} + + struct DRC{} + + struct DSC{} + + struct DTC{} + + struct DUC{} + + struct DVC{} + + struct DWC{} + + struct DXC{} + + struct DYC{} + + struct DZC{} + + + + // Structs <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Error codes >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Caller is not Econia. + const E_NOT_ECONIA: u64 = 0; + /// Coin capabilities have already been initialized. + const E_HAS_CAPABILITIES: u64 = 1; + + // Error codes <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Constants >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Base coin name. + const BASE_COIN_NAME: vector = b"Base coin"; + /// Base coin symbol. + const BASE_COIN_SYMBOL: vector = b"BC"; + /// Base coin decimals. + const BASE_COIN_DECIMALS: u8 = 4; + /// Quote coin name. + const QUOTE_COIN_NAME: vector = b"Quote coin"; + /// Quote coin symbol. + const QUOTE_COIN_SYMBOL: vector = b"QC"; + /// Quote coin decimals. + const QUOTE_COIN_DECIMALS: u8 = 12; + /// Utility coin name. + const UTILITY_COIN_NAME: vector = b"Utility coin"; + /// Utility coin symbol. + const UTILITY_COIN_SYMBOL: vector = b"UC"; + /// Utility coin decimals. + const UTILITY_COIN_DECIMALS: u8 = 10; + + // Constants <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Public functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Burn `coins` for which `CoinType` is defined at Econia account. + public fun burn( + coins: coin::Coin + ) acquires CoinCapabilities { + // Borrow immutable reference to burn capability. + let burn_capability = &borrow_global>( + @econia).burn_capability; + coin::burn(coins, burn_capability); // Burn coins. + } + + /// Mint new `amount` of `CoinType`, aborting if not called by + /// Econia account. + public fun mint( + account: &signer, + amount: u64 + ): coin::Coin + acquires CoinCapabilities { + // Get account address. + let account_address = address_of(account); // Get account address. + // Assert caller is Econia. + assert!(account_address == @econia, E_NOT_ECONIA); + // Borrow immutable reference to mint capability. + let mint_capability = &borrow_global>( + account_address).mint_capability; + // Mint specified amount. + coin::mint(amount, mint_capability) + } + + // Public functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Private functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Initialize given coin type under Econia account. + fun init_coin_type( + account: &signer, + coin_name: vector, + coin_symbol: vector, + decimals: u8, + ) { + // Assert caller is Econia. + // assert!(address_of(account) == @econia, E_NOT_ECONIA); + // Assert Econia does not already have coin capabilities stored. + assert!(!exists>(address_of(account)), + E_HAS_CAPABILITIES); + // Initialize coin, storing capabilities. + let (burn_capability, freeze_capability, mint_capability) = + coin::initialize( + account, utf8(coin_name), utf8(coin_symbol), decimals, false); + move_to>(account, + CoinCapabilities{ + burn_capability, + freeze_capability, + mint_capability + }); // Store capabilities under Econia account. + } + + /// Initialize mock base, quote, and utility coin types upon genesis + /// publication. + fun init_module( + account: &signer + ) { + init_coin_type(account, BASE_COIN_NAME, BASE_COIN_SYMBOL, + BASE_COIN_DECIMALS); // Initialize mock base coin. + init_coin_type(account, QUOTE_COIN_NAME, QUOTE_COIN_SYMBOL, + QUOTE_COIN_DECIMALS); // Initialize mock quote coin. + init_coin_type(account, UTILITY_COIN_NAME, UTILITY_COIN_SYMBOL, + UTILITY_COIN_DECIMALS); // Initialize mock utility coin. + if (!exists>(address_of(account))) init_coin_type(account, b"A Coin", b"AC", 10); // Initialize A coin + if (!exists>(address_of(account))) init_coin_type(account, b"D Coin", b"DC", 10); // Initialize D coin + if (!exists>(address_of(account))) init_coin_type(account, b"F Coin", b"FC", 10); // Initialize F coin + if (!exists>(address_of(account))) init_coin_type(account, b"E Coin", b"EC", 10); // Initialize E coin + if (!exists>(address_of(account))) init_coin_type(account, b"G Coin", b"GC", 10); // Initialize G coin + if (!exists>(address_of(account))) init_coin_type(account, b"H Coin", b"HC", 10); // Initialize H coin + if (!exists>(address_of(account))) init_coin_type(account, b"I Coin", b"IC", 10); // Initialize I coin + if (!exists>(address_of(account))) init_coin_type(account, b"J Coin", b"JC", 10); // Initialize J coin + if (!exists>(address_of(account))) init_coin_type(account, b"K Coin", b"KC", 10); // Initialize K coin + if (!exists>(address_of(account))) init_coin_type(account, b"L Coin", b"LC", 10); // Initialize L coin + if (!exists>(address_of(account))) init_coin_type(account, b"M Coin", b"MC", 10); // Initialize M coin + if (!exists>(address_of(account))) init_coin_type(account, b"N Coin", b"NC", 10); // Initialize N coin + if (!exists>(address_of(account))) init_coin_type(account, b"O Coin", b"OC", 10); // Initialize O coin + if (!exists>(address_of(account))) init_coin_type(account, b"P Coin", b"PC", 10); // Initialize P coin + if (!exists>(address_of(account))) init_coin_type(account, b"R Coin", b"RC", 10); // Initialize R coin + if (!exists>(address_of(account))) init_coin_type(account, b"S Coin", b"SC", 10); // Initialize S coin + if (!exists>(address_of(account))) init_coin_type(account, b"T Coin", b"TC", 10); // Initialize T coin + if (!exists>(address_of(account))) init_coin_type(account, b"V Coin", b"VC", 10); // Initialize V coin + if (!exists>(address_of(account))) init_coin_type(account, b"W Coin", b"WC", 10); // Initialize W coin + if (!exists>(address_of(account))) init_coin_type(account, b"X Coin", b"XC", 10); // Initialize X coin + if (!exists>(address_of(account))) init_coin_type(account, b"AA Coin", b"AAC", 10); // Initialize AA coin + if (!exists>(address_of(account))) init_coin_type(account, b"AB Coin", b"ABC", 10); // Initialize AB coin + if (!exists>(address_of(account))) init_coin_type(account, b"AC Coin", b"ACC", 10); // Initialize AC coin + if (!exists>(address_of(account))) init_coin_type(account, b"AD Coin", b"ADC", 10); // Initialize AD coin + if (!exists>(address_of(account))) init_coin_type(account, b"AE Coin", b"AEC", 10); // Initialize AE coin + if (!exists>(address_of(account))) init_coin_type(account, b"AF Coin", b"AFC", 10); // Initialize AF coin + if (!exists>(address_of(account))) init_coin_type(account, b"AG Coin", b"AGC", 10); // Initialize AG coin + if (!exists>(address_of(account))) init_coin_type(account, b"AH Coin", b"AHC", 10); // Initialize AH coin + if (!exists>(address_of(account))) init_coin_type(account, b"AI Coin", b"AIC", 10); // Initialize AI coin + if (!exists>(address_of(account))) init_coin_type(account, b"AJ Coin", b"AJC", 10); // Initialize AJ coin + if (!exists>(address_of(account))) init_coin_type(account, b"AK Coin", b"AKC", 10); // Initialize AK coin + if (!exists>(address_of(account))) init_coin_type(account, b"AL Coin", b"ALC", 10); // Initialize AL coin + if (!exists>(address_of(account))) init_coin_type(account, b"AM Coin", b"AMC", 10); // Initialize AM coin + if (!exists>(address_of(account))) init_coin_type(account, b"AN Coin", b"ANC", 10); // Initialize AN coin + if (!exists>(address_of(account))) init_coin_type(account, b"AO Coin", b"AOC", 10); // Initialize AO coin + if (!exists>(address_of(account))) init_coin_type(account, b"AP Coin", b"APC", 10); // Initialize AP coin + if (!exists>(address_of(account))) init_coin_type(account, b"AQ Coin", b"AQC", 10); // Initialize AQ coin + if (!exists>(address_of(account))) init_coin_type(account, b"AR Coin", b"ARC", 10); // Initialize AR coin + if (!exists>(address_of(account))) init_coin_type(account, b"AS Coin", b"ASC", 10); // Initialize AS coin + if (!exists>(address_of(account))) init_coin_type(account, b"AT Coin", b"ATC", 10); // Initialize AT coin + if (!exists>(address_of(account))) init_coin_type(account, b"AU Coin", b"AUC", 10); // Initialize AU coin + if (!exists>(address_of(account))) init_coin_type(account, b"AV Coin", b"AVC", 10); // Initialize AV coin + if (!exists>(address_of(account))) init_coin_type(account, b"AW Coin", b"AWC", 10); // Initialize AW coin + if (!exists>(address_of(account))) init_coin_type(account, b"AX Coin", b"AXC", 10); // Initialize AX coin + if (!exists>(address_of(account))) init_coin_type(account, b"AY Coin", b"AYC", 10); // Initialize AY coin + if (!exists>(address_of(account))) init_coin_type(account, b"AZ Coin", b"AZC", 10); // Initialize AZ coin + if (!exists>(address_of(account))) init_coin_type(account, b"BA Coin", b"BAC", 10); // Initialize BA coin + if (!exists>(address_of(account))) init_coin_type(account, b"BB Coin", b"BBC", 10); // Initialize BB coin + if (!exists>(address_of(account))) init_coin_type(account, b"BC Coin", b"BCC", 10); // Initialize BC coin + if (!exists>(address_of(account))) init_coin_type(account, b"BD Coin", b"BDC", 10); // Initialize BD coin + if (!exists>(address_of(account))) init_coin_type(account, b"BE Coin", b"BEC", 10); // Initialize BE coin + if (!exists>(address_of(account))) init_coin_type(account, b"BF Coin", b"BFC", 10); // Initialize BF coin + if (!exists>(address_of(account))) init_coin_type(account, b"BG Coin", b"BGC", 10); // Initialize BG coin + if (!exists>(address_of(account))) init_coin_type(account, b"BH Coin", b"BHC", 10); // Initialize BH coin + if (!exists>(address_of(account))) init_coin_type(account, b"BI Coin", b"BIC", 10); // Initialize BI coin + if (!exists>(address_of(account))) init_coin_type(account, b"BJ Coin", b"BJC", 10); // Initialize BJ coin + if (!exists>(address_of(account))) init_coin_type(account, b"BK Coin", b"BKC", 10); // Initialize BK coin + if (!exists>(address_of(account))) init_coin_type(account, b"BL Coin", b"BLC", 10); // Initialize BL coin + if (!exists>(address_of(account))) init_coin_type(account, b"BM Coin", b"BMC", 10); // Initialize BM coin + if (!exists>(address_of(account))) init_coin_type(account, b"BN Coin", b"BNC", 10); // Initialize BN coin + if (!exists>(address_of(account))) init_coin_type(account, b"BO Coin", b"BOC", 10); // Initialize BO coin + if (!exists>(address_of(account))) init_coin_type(account, b"BP Coin", b"BPC", 10); // Initialize BP coin + if (!exists>(address_of(account))) init_coin_type(account, b"BQ Coin", b"BQC", 10); // Initialize BQ coin + if (!exists>(address_of(account))) init_coin_type(account, b"BR Coin", b"BRC", 10); // Initialize BR coin + if (!exists>(address_of(account))) init_coin_type(account, b"BS Coin", b"BSC", 10); // Initialize BS coin + if (!exists>(address_of(account))) init_coin_type(account, b"BT Coin", b"BTC", 10); // Initialize BT coin + if (!exists>(address_of(account))) init_coin_type(account, b"BU Coin", b"BUC", 10); // Initialize BU coin + if (!exists>(address_of(account))) init_coin_type(account, b"BV Coin", b"BVC", 10); // Initialize BV coin + if (!exists>(address_of(account))) init_coin_type(account, b"BW Coin", b"BWC", 10); // Initialize BW coin + if (!exists>(address_of(account))) init_coin_type(account, b"BX Coin", b"BXC", 10); // Initialize BX coin + if (!exists>(address_of(account))) init_coin_type(account, b"BY Coin", b"BYC", 10); // Initialize BY coin + if (!exists>(address_of(account))) init_coin_type(account, b"BZ Coin", b"BZC", 10); // Initialize BZ coin + if (!exists>(address_of(account))) init_coin_type(account, b"CA Coin", b"CAC", 10); // Initialize CA coin + if (!exists>(address_of(account))) init_coin_type(account, b"CB Coin", b"CBC", 10); // Initialize CB coin + if (!exists>(address_of(account))) init_coin_type(account, b"CC Coin", b"CCC", 10); // Initialize CC coin + if (!exists>(address_of(account))) init_coin_type(account, b"CD Coin", b"CDC", 10); // Initialize CD coin + if (!exists>(address_of(account))) init_coin_type(account, b"CE Coin", b"CEC", 10); // Initialize CE coin + if (!exists>(address_of(account))) init_coin_type(account, b"CF Coin", b"CFC", 10); // Initialize CF coin + if (!exists>(address_of(account))) init_coin_type(account, b"CG Coin", b"CGC", 10); // Initialize CG coin + if (!exists>(address_of(account))) init_coin_type(account, b"CH Coin", b"CHC", 10); // Initialize CH coin + if (!exists>(address_of(account))) init_coin_type(account, b"CI Coin", b"CIC", 10); // Initialize CI coin + if (!exists>(address_of(account))) init_coin_type(account, b"CJ Coin", b"CJC", 10); // Initialize CJ coin + if (!exists>(address_of(account))) init_coin_type(account, b"CK Coin", b"CKC", 10); // Initialize CK coin + if (!exists>(address_of(account))) init_coin_type(account, b"CL Coin", b"CLC", 10); // Initialize CL coin + if (!exists>(address_of(account))) init_coin_type(account, b"CM Coin", b"CMC", 10); // Initialize CM coin + if (!exists>(address_of(account))) init_coin_type(account, b"CN Coin", b"CNC", 10); // Initialize CN coin + if (!exists>(address_of(account))) init_coin_type(account, b"CO Coin", b"COC", 10); // Initialize CO coin + if (!exists>(address_of(account))) init_coin_type(account, b"CP Coin", b"CPC", 10); // Initialize CP coin + if (!exists>(address_of(account))) init_coin_type(account, b"CQ Coin", b"CQC", 10); // Initialize CQ coin + if (!exists>(address_of(account))) init_coin_type(account, b"CR Coin", b"CRC", 10); // Initialize CR coin + if (!exists>(address_of(account))) init_coin_type(account, b"CS Coin", b"CSC", 10); // Initialize CS coin + if (!exists>(address_of(account))) init_coin_type(account, b"CT Coin", b"CTC", 10); // Initialize CT coin + if (!exists>(address_of(account))) init_coin_type(account, b"CU Coin", b"CUC", 10); // Initialize CU coin + if (!exists>(address_of(account))) init_coin_type(account, b"CV Coin", b"CVC", 10); // Initialize CV coin + if (!exists>(address_of(account))) init_coin_type(account, b"CW Coin", b"CWC", 10); // Initialize CW coin + if (!exists>(address_of(account))) init_coin_type(account, b"CX Coin", b"CXC", 10); // Initialize CX coin + if (!exists>(address_of(account))) init_coin_type(account, b"CY Coin", b"CYC", 10); // Initialize CY coin + if (!exists>(address_of(account))) init_coin_type(account, b"CZ Coin", b"CZC", 10); // Initialize CZ coin + if (!exists>(address_of(account))) init_coin_type(account, b"DA Coin", b"DAC", 10); // Initialize DA coin + if (!exists>(address_of(account))) init_coin_type(account, b"DB Coin", b"DBC", 10); // Initialize DB coin + if (!exists>(address_of(account))) init_coin_type(account, b"DC Coin", b"DCC", 10); // Initialize DC coin + if (!exists>(address_of(account))) init_coin_type(account, b"DD Coin", b"DDC", 10); // Initialize DD coin + if (!exists>(address_of(account))) init_coin_type(account, b"DE Coin", b"DEC", 10); // Initialize DE coin + if (!exists>(address_of(account))) init_coin_type(account, b"DF Coin", b"DFC", 10); // Initialize DF coin + if (!exists>(address_of(account))) init_coin_type(account, b"DG Coin", b"DGC", 10); // Initialize DG coin + if (!exists>(address_of(account))) init_coin_type(account, b"DH Coin", b"DHC", 10); // Initialize DH coin + if (!exists>(address_of(account))) init_coin_type(account, b"DI Coin", b"DIC", 10); // Initialize DI coin + if (!exists>(address_of(account))) init_coin_type(account, b"DJ Coin", b"DJC", 10); // Initialize DJ coin + if (!exists>(address_of(account))) init_coin_type(account, b"DK Coin", b"DKC", 10); // Initialize DK coin + if (!exists>(address_of(account))) init_coin_type(account, b"DL Coin", b"DLC", 10); // Initialize DL coin + if (!exists>(address_of(account))) init_coin_type(account, b"DM Coin", b"DMC", 10); // Initialize DM coin + if (!exists>(address_of(account))) init_coin_type(account, b"DN Coin", b"DNC", 10); // Initialize DN coin + if (!exists>(address_of(account))) init_coin_type(account, b"DO Coin", b"DOC", 10); // Initialize DO coin + if (!exists>(address_of(account))) init_coin_type(account, b"DP Coin", b"DPC", 10); // Initialize DP coin + if (!exists>(address_of(account))) init_coin_type(account, b"DQ Coin", b"DQC", 10); // Initialize DQ coin + if (!exists>(address_of(account))) init_coin_type(account, b"DR Coin", b"DRC", 10); // Initialize DR coin + if (!exists>(address_of(account))) init_coin_type(account, b"DS Coin", b"DSC", 10); // Initialize DS coin + if (!exists>(address_of(account))) init_coin_type(account, b"DT Coin", b"DTC", 10); // Initialize DT coin + if (!exists>(address_of(account))) init_coin_type(account, b"DU Coin", b"DUC", 10); // Initialize DU coin + if (!exists>(address_of(account))) init_coin_type(account, b"DV Coin", b"DVC", 10); // Initialize DV coin + if (!exists>(address_of(account))) init_coin_type(account, b"DW Coin", b"DWC", 10); // Initialize DW coin + if (!exists>(address_of(account))) init_coin_type(account, b"DX Coin", b"DXC", 10); // Initialize DX coin + if (!exists>(address_of(account))) init_coin_type(account, b"DY Coin", b"DYC", 10); // Initialize DY coin + if (!exists>(address_of(account))) init_coin_type(account, b"DZ Coin", b"DZC", 10); // Initialize DZ coin + } + + // Private functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Test-only functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test_only] + /// Wrapper for `init_module()`, not requiring signature. + /// + /// Similarly initializes the Aptos coin, destroying capabilities. + public fun init_coin_types_test() { + // Initialize Econia test coin types. + init_module(&account::create_signer_with_capability( + &account::create_test_signer_cap(@econia))); + // Initialize Aptos coin type, storing capabilities. + let (burn_cap, mint_cap) = aptos_coin::initialize_for_test( + &account::create_signer_with_capability( + &account::create_test_signer_cap(@aptos_framework))); + // Destroy Aptos coin burn capability. + coin::destroy_burn_cap(burn_cap); + // Destroy Aptos coin mint capability. + coin::destroy_mint_cap(mint_cap); + } + + #[test_only] + /// Wrapper for `mint()`, not requiring signature. + public fun mint_test( + amount: u64 + ): coin::Coin + acquires CoinCapabilities { + // Get Econia account. + let econia = account::create_signer_with_capability( + &account::create_test_signer_cap(@econia)); + // Initialize coin types if they have not been initialized yet. + if (!exists>(@econia)) init_module(&econia); + mint(&econia, amount) // Mint and return amount. + } + + // Test-only functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Tests >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test(econia = @econia)] + #[expected_failure(abort_code = E_HAS_CAPABILITIES)] + /// Verify failure for capabilities already registered. + fun test_init_has_caps( + econia: &signer + ) { + init_module(econia); // Initialize coin types. + init_module(econia); // Attempt invalid re-init. + } + + #[test(account = @user)] + #[expected_failure(abort_code = E_NOT_ECONIA)] + /// Verify failure for unauthorized caller. + fun test_init_not_econia( + account: &signer + ) { + init_module(account); // Attempt invalid init. + } + + #[test(account = @econia)] + /// Verify successful mint, then burn. + fun test_mint_and_burn( + account: &signer + ) acquires CoinCapabilities { + init_module(account); // Initialize coin types. + let base_coin = mint(account, 20); // Mint base coin. + // Assert correct value minted. + assert!(coin::value(&base_coin) == 20, 0); + burn(base_coin); // Burn coins. + // Assert can burn another coin that has now been initialized. + burn(mint(account, 1)); + } + + #[test(account = @user)] + #[expected_failure(abort_code = E_NOT_ECONIA)] + /// Verify failure for unauthorized caller. + fun test_mint_not_econia( + account: &signer + ): coin::Coin + acquires CoinCapabilities { + mint(account, 20) // Attempt invalid mint. + } + + // Tests <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +} \ No newline at end of file diff --git a/testsuite/module-publish/src/packages/econia/sources/avl_queue.move b/testsuite/module-publish/src/packages/econia/sources/avl_queue.move new file mode 100644 index 0000000000000..6c57b884189af --- /dev/null +++ b/testsuite/module-publish/src/packages/econia/sources/avl_queue.move @@ -0,0 +1,9065 @@ +/// AVL queue: a hybrid between an AVL tree and a queue. +/// +/// The present implementation involves an Adelson-Velsky and Landis +/// (AVL) tree, where each tree node has an enclosed doubly linked list. +/// Tree nodes correspond to keys from key-value insertion pairs, and +/// list nodes correspond to distinct insertion values sharing the same +/// insertion key. Hence tree node insertion keys are sorted in +/// lexicographical order, while list node insertion values are sorted +/// in order of insertion within a corresponding doubly linked list, to +/// the effect that key-value insertion pairs can be popped from the +/// head of the AVL queue in: +/// +/// 1. Either ascending or descending order of insertion key (with +/// sort order set upon initialization), then by +/// 2. Ascending order of insertion within a doubly linked list. +/// +/// Like an AVL tree, the present implementation also allows for +/// insertions and removals from anywhere inside the data structure. +/// +/// # General overview sections +/// +/// [AVL trees](#avl-trees) +/// +/// * [Height invariant](#height-invariant) +/// * [Rotations](#rotations) +/// * [Retracing](#retracing) +/// * [As a map](#as-a-map) +/// +/// [AVL queues](#avl-queues) +/// +/// * [Key storage multiplicity](#key-storage-multiplicity) +/// * [Sort order](#sort-order) +/// * [Node structure](#node-structure) +/// * [Node provisioning](#node-provisioning) +/// * [Access keys](#access-keys) +/// * [Height](#height) +/// +/// [Implementation analysis](#implementation-analysis) +/// +/// * [Gas considerations](#gas-considerations) +/// * [Test development](#test-development) +/// * [Public function index](#public-function-index) +/// * [Dependency charts](#dependency-charts) +/// +/// [Bit conventions](#bit-conventions) +/// +/// * [Number](#number) +/// * [Status](#status) +/// * [Masking](#masking) +/// +/// [References](#references) +/// +/// [Complete DocGen index](#complete-docgen-index) +/// +/// # AVL trees +/// +/// ## Height invariant +/// +/// An AVL tree is a self-balancing binary search tree where the height +/// of a node's two child subtrees differ by at most one. For example: +/// +/// > 3 +/// > / \ +/// > 2 5 +/// > / / \ +/// > 1 4 7 +/// > / \ +/// > 6 8 +/// +/// Here, node 3's left child subtree has height 1 while its right child +/// subtree has height 2. Similarly, all other nodes satisfy the AVL +/// height invariant. +/// +/// ## Rotations +/// +/// Continuing the above example, if node 4 were to be removed then node +/// 5 would violate the height invariant: +/// +/// > 3 +/// > / \ +/// > 2 5 +/// > / \ +/// > 1 7 +/// > / \ +/// > 6 8 +/// +/// Here, a left rotation is necessary to rebalance the tree, yielding: +/// +/// > 3 +/// > / \ +/// > 2 7 +/// > / / \ +/// > 1 5 8 +/// > \ +/// > 6 +/// +/// Rotations are required whenever an insertion or removal leads to a +/// violation of the AVL height invariant. +/// +/// ## Retracing +/// +/// Similarly, insertion and removal operations may require retracing up +/// to the root, a process that updates node-wise state pertaining to +/// the height invariant. +/// +/// Here, some implementations encode in each node a "balance factor" +/// that describes whether a given node is left-heavy, right-heavy, or +/// balanced, while others track the height at a given node, as in the +/// present implementation. For example, consider the following tree: +/// +/// > 2 +/// > / \ +/// > 1 3 +/// +/// Here, nodes 1 and 3 have height 0, while node 2 has height 1. +/// Inserting 4 yields: +/// +/// > 2 +/// > / \ +/// > 1 3 +/// > \ +/// > 4 +/// +/// Now node 4 has height 0, and the heights of nodes 3 and 2 have both +/// increased by 1. In practice, this means that inserting node 4 will +/// require modifying state in node 3 and in node 2, by looping back up +/// to the root to update heights, checking along the way whether a +/// height modification leads to an invariant violation. +/// +/// ## As a map +/// +/// AVL trees can be used as an associative array that maps from keys to +/// values, simply by storing values in the leaves of the tree. For +/// example, the insertion sequence +/// +/// 1. $\langle 2, a \rangle$ +/// 2. $\langle 3, b \rangle$ +/// 3. $\langle 1, c \rangle$ +/// 4. $\langle 4, d \rangle$ +/// +/// produces: +/// +/// > <2, a> +/// > / \ +/// > <1, c> <3, b> +/// > \ +/// > <4, d> +/// +/// Notably, in an AVL tree, keys can only be inserted once, such that +/// inserting $\langle 2, e \rangle$ to the above tree would be invalid +/// unless $\langle 2, a \rangle$ were first removed. +/// +/// # AVL queues +/// +/// ## Key storage multiplicity +/// +/// Unlike an AVL tree, which can only store one instance of a given +/// key, AVL queues can store multiple instances. For example, the +/// following insertion sequence, without intermediate removals, is +/// invalid in an AVL tree but valid in an AVL queue: +/// +/// 1. $p_{3, 0} = \langle 3, 5 \rangle$ +/// 2. $p_{3, 1} = \langle 3, 8 \rangle$ +/// 3. $p_{3, 2} = \langle 3, 2 \rangle$ +/// 4. $p_{3, 3} = \langle 3, 5 \rangle$ +/// +/// Here, the "key-value insertion pair" +/// $p_{i, j} = \langle i, v_j \rangle$ has: +/// +/// * "Insertion key" $i$: the inserted key. +/// * "Insertion count" $j$: the number of key-value insertion pairs, +/// having the same insertion key, that were previously inserted. +/// * "Insertion value" $v_j$: the value from the key-value +/// insertion pair having insertion count $j$. +/// +/// ## Sort order +/// +/// Key-value insertion pairs in an AVL queue are sorted by: +/// +/// 1. Either ascending or descending order of insertion key, then by +/// 2. Ascending order of insertion count. +/// +/// For example, consider the key-value pair insertion pairs inserted in +/// the following sequence: +/// +/// 1. $p_{1, 0} = \langle 1, a \rangle$ +/// 2. $p_{3, 0} = \langle 3, b \rangle$ +/// 3. $p_{3, 1} = \langle 3, c \rangle$ +/// 4. $p_{1, 1} = \langle 1, d \rangle$ +/// 5. $p_{2, 0} = \langle 2, e \rangle$ +/// +/// In an ascending AVL queue, the dequeue sequence would be: +/// +/// 1. $p_{1, 0} = \langle 1, a \rangle$ +/// 2. $p_{1, 1} = \langle 1, d \rangle$ +/// 3. $p_{2, 0} = \langle 2, e \rangle$ +/// 4. $p_{3, 0} = \langle 3, b \rangle$ +/// 5. $p_{3, 1} = \langle 3, c \rangle$ +/// +/// In a descending AVL queue, the dequeue sequence would instead be: +/// +/// 1. $p_{3, 0} = \langle 3, b \rangle$ +/// 2. $p_{3, 1} = \langle 3, c \rangle$ +/// 3. $p_{2, 0} = \langle 2, e \rangle$ +/// 4. $p_{1, 0} = \langle 1, a \rangle$ +/// 5. $p_{1, 1} = \langle 1, d \rangle$ +/// +/// ## Node structure +/// +/// Continuing the above example, key-value insertion pairs would be +/// stored in an ascending AVL queue as follows: +/// +/// > 2 [e] +/// > / \ +/// > [a -> d] 1 3 [b -> c] +/// > AVL queue head ^ ^ AVL queue tail +/// +/// In a descending AVL queue: +/// +/// > 2 [e] +/// > / \ +/// > [a -> d] 1 3 [b -> c] +/// > AVL queue tail ^ ^ AVL queue head +/// +/// For each case, the tree node with insertion key 1 has a doubly +/// linked list where the head list node has insertion value a, and the +/// tail list node has insertion value d. Similarly, the tree node with +/// insertion key 3 has a doubly linked list where the head list node +/// has insertion value b, and the tail list node has insertion value c. +/// +/// ## Node provisioning +/// +/// Tree nodes and list nodes are stored as hash table entries, and thus +/// incur per-item global storage costs on the Aptos blockchain. As of +/// the time of this writing, per-item creation costs are by far the +/// most expensive per-item operation, and moreover, there is no +/// incentive to deallocate from memory. Hence the typical approach of +/// allocating a node upon insertion and deallocating upon removal is +/// more costly than the approach taken in the present implementation, +/// which involves re-using nodes once they have been allocated. +/// +/// More specifically, when a tree node or list node is removed from the +/// AVL queue, it is pushed onto a stack of inactive nodes for the +/// corresponding type. Then, when an insertion operation requires a new +/// node, the inactive node can be popped off the top of the stack and +/// overwritten. Rather than allocating a new node for each insertion +/// and deallocating for each removal, this approach minimizes per-item +/// creation costs. Additionally, nodes can be pre-allocated upon AVL +/// queue initialization and pushed directly on the inactive nodes stack +/// so as to reduce per-item costs for future operations. +/// +/// Since each active tree node contains a doubly linked list having at +/// least one active list node, the number of active tree nodes is +/// less than or equal to the number of active list nodes. +/// +/// Tree nodes and list nodes are each assigned a 1-indexed 14-bit +/// serial ID known as a node ID. Node ID 0 is reserved for null, such +/// that the maximum number of allocated nodes for each node type is +/// thus $2^{14} - 1 = 16383$. +/// +/// To additionally reduce costs, insertion values are not stored in +/// list nodes, but are also stored as hash table entries, accessed via +/// the corresponding list node ID. This approach reduces per-byte costs +/// associated with list node operations: if a list node is removed from +/// the middle of a doubly linked list, for example, per-byte write +/// costs will only be assessed on the next and last fields of the +/// removed node's neighbors, and will not be assessed on the neighbors' +/// insertion values. +/// +/// ## Access keys +/// +/// When a key-value insertion pair is inserted to the AVL queue, an +/// "access key" is returned, and can be used for subsequent lookup +/// operations. Access keys have the following bit structure: +/// +/// | Bit(s) | Data | +/// |--------|----------------------------------------------| +/// | 47-60 | Tree node ID | +/// | 33-46 | List node ID | +/// | 32 | If set, ascending AVL queue, else descending | +/// | 0-31 | Insertion key | +/// +/// Insertion values are indexed by list node ID, and since the list +/// node ID for an insertion value is encoded in the access key +/// returned upon insertion, access keys can be used for $O(1)$ list +/// node lookup. +/// +/// With the exception of list nodes at the head or tail of their +/// corresponding doubly linked list, list nodes do not, however, +/// indicate the corresponding tree node in which their doubly linked +/// list is located. This means that the corresponding tree node ID +/// and insertion key encoded in an access key are not verified from the +/// provided access key during lookup, as this process would require +/// $O(\log_2 n)$ lookup on the tree node. +/// +/// Lookup operations thus assume that the provided access key +/// corresponds to a valid list node in the given AVL queue, and are +/// subject to undefined behavior if this condition is not met. +/// +/// Notably, access keys are guaranteed to be unique within an AVL queue +/// at any given time, but are not guaranteed to be unique within an +/// AVL queue across time: since node IDs are reused per the stack-based +/// allocation strategy above, the same access key can be issued +/// multiple times. Hence it is up to callers to ensure appropriate +/// management of access keys, which effectively function as pointers +/// into AVL queue memory. Notably, if a caller wishes to uniquely +/// identify issued access keys, the caller can simply concatenate +/// access keys with a global counter. +/// +/// Bits 0-32 are not required for lookup operations, but rather, are +/// included in access keys simply to provide additional metadata. +/// +/// ## Height +/// +/// In the present implementation, left or right height denotes the +/// height of a node's left or right subtree, respectively, plus one. +/// Subtree height is adjusted by one to avoid negative numbers, with +/// the resultant value denoting the height of a tree rooted at the +/// given node, accounting only for height to the given side. The height +/// of a node is denoted as the larger of its left height and right +/// height: +/// +/// > 2 +/// > / \ +/// > 1 3 +/// > \ +/// > 4 +/// +/// | Key | Left height | Right height | Height | +/// |-----|-------------|--------------|--------| +/// | 1 | 0 | 0 | 0 | +/// | 2 | 1 | 2 | 2 | +/// | 3 | 0 | 1 | 1 | +/// | 4 | 0 | 0 | 0 | +/// +/// The overall height $h$ of a tree (the height of the root node) is +/// related to the number of levels $l$ in the tree by the equation +/// $h = l - 1$, and for an AVL tree of size $n \geq 1$ nodes, the +/// number of levels in the tree lies in the interval +/// +/// $$\log_2(n + 1) \leq l \leq c \log_2(n + d) + b$$ +/// +/// where +/// +/// * $\varphi = \frac{1 + \sqrt{5}}{2} \approx 1.618$ (the golden +/// ratio), +/// * $c = \frac{1}{\log_2 \varphi} \approx 1.440$ , +/// * $b = \frac{c}{2} \log_2 5 - 2 \approx -0.3277$ , and +/// * $d = 1 + \frac{1}{\varphi^4 \sqrt{5}} \approx 1.065$ . +/// +/// With a maximum node count of $n_{max} = 2^{14} - 1 = 16383$, the +/// the maximum height $h_{max}$ of an AVL tree in the present +/// implementation is thus +/// +/// $$h_{max} = \lfloor c \log_2(n_{max} + d) + b \rfloor - 1 = 18$$ +/// +/// such that left height and right height can always be encoded in +/// $b_{max} = \lceil \log_2 h_{max} \rceil = 5$ bits each. +/// +/// Similarly, for a given height the size of an AVL tree is at most +/// +/// $$log_2(n + 1) \leq h + 1$$ +/// +/// $$n + 1 \leq 2^{h + 1}$$ +/// +/// $$n \leq 2^{h + 1} - 1$$ +/// +/// and at least +/// +/// $$c \log_2(n + d) + b \geq h + 1$$ +/// +/// $$\log_{\varphi}(n + d) + b \geq h + 1$$ +/// +/// $$\log_{\varphi}(n + d) \geq h + 1 - b$$ +/// +/// $$n + d \geq \varphi^{h + 1 - b}$$ +/// +/// $$n \geq \varphi^{h + 1 - b} - d$$ +/// +/// such that size lies in the interval +/// +/// $$\varphi ^ {h +1 - b} - d \leq n \leq 2^{h + 1} - 1$$ +/// +/// which, for the special case of $h = 1$, results in the integer lower +/// bound +/// +/// $$n_{h = 1} \geq \varphi ^ {1 + 1 - b} - d$$ +/// +/// $$n_{h = 1} \geq \varphi ^ {2 - b} - d$$ +/// +/// $$n_{h = 1} \geq \varphi ^ {2 - (\frac{c}{2}\log_2 5 - 2)} - d$$ +/// +/// $$n_{h = 1} \geq \varphi ^ {4 - \frac{c}{2}\log_2 5} - d$$ +/// +/// $$n_{h = 1} \geq \varphi ^ {4 - \frac{1}{2}\log_\varphi 5} - d$$ +/// +/// $$n_{h = 1} \geq \varphi ^ {4 - \log_\varphi \sqrt{5}} - d$$ +/// +/// $$n_{h = 1} \geq \varphi^4 / \varphi^{\log_\varphi \sqrt{5}} - d$$ +/// +/// $$n_{h = 1} \geq \varphi^4 / \sqrt{5} - d$$ +/// +/// $$n_{h = 1} \geq \varphi^4/\sqrt{5}-(1+1/(\varphi^4 \sqrt{5}))$$ +/// +/// $$n_{h=1}\geq(1+s)^4/(2^4s)-1-2^4/((1+s)^4s), s = \sqrt{5}$$ +/// +/// $$n_{h=1}\geq\frac{(1+s)^4}{2^4s}-\frac{2^4}{s(1+s)^4}-1$$ +/// +/// $$n_{h = 1} \geq 2$$ +/// +/// with the final step verifiable via a computer algebra system like +/// WolframAlpha. Thus for the heights possible in the present +/// implementation (and for one height higher): +/// +/// | Height | Minimum size | Maximum size | +/// |--------------|--------------|-----------------| +/// | 0 | 1 | 1 | +/// | 1 | 2 | 3 | +/// | 2 | 4 | 7 | +/// | 3 | 7 | 15 | +/// | 4 | 12 | 31 | +/// | 5 | 20 | 63 | +/// | 6 | 33 | 127 | +/// | 7 | 54 | 255 | +/// | 8 | 88 | 511 | +/// | 9 | 143 | 1023 | +/// | 10 | 232 | 2047 | +/// | 11 | 376 | 4095 | +/// | 12 | 609 | 8191 | +/// | 13 | 986 | 16383 (`n_max`) | +/// | 14 | 1596 | 32767 | +/// | 15 | 2583 | 65535 | +/// | 16 | 4180 | 131071 | +/// | 17 | 6764 | 262143 | +/// | 18 (`h_max`) | 10945 | 524287 | +/// | 19 | 17710 | 1048575 | +/// +/// Supporting Python calculations: +/// +/// ```python +/// >>> import math +/// >>> phi = (1 + math.sqrt(5)) / 2 +/// >>> phi +/// 1.618033988749895 +/// >>> c = 1 / math.log(phi, 2) +/// >>> c +/// 1.4404200904125564 +/// >>> b = c / 2 * math.log(5, 2) - 2 +/// >>> b +/// -0.32772406181544556 +/// >>> d = 1 + 1 / (phi ** 4 * math.sqrt(5)) +/// >>> d +/// 1.0652475842498528 +/// >>> n_max = 2 ** 14 - 1 +/// >>> n_max +/// 16383 +/// >>> h_max = math.floor(c * math.log(n_max + d, 2) + b) - 1 +/// >>> h_max +/// 18 +/// >>> b_max = math.ceil(math.log(h_max, 2)) +/// >>> b_max +/// 5 +/// >>> for h in range(h_max + 2): +/// ... if h == 1: +/// ... n_min = 2 +/// ... else: +/// ... n_min = phi ** (h + 1 - b) - d +/// ... n_max = 2 ** (h + 1) - 1 +/// ... n_min_ceil = math.ceil(n_min) +/// ... print(f"h: {h}, n_min: {n_min_ceil}, n_max: {n_max}, " +/// ... f"n_min_raw: {n_min}") +/// ... +/// h: 0, n_min: 1, n_max: 1, n_min_raw: 0.8291796067500634 +/// h: 1, n_min: 2, n_max: 3, n_min_raw: 2 +/// h: 2, n_min: 4, n_max: 7, n_min_raw: 3.894427190999917 +/// h: 3, n_min: 7, n_max: 15, n_min_raw: 6.959674775249768 +/// h: 4, n_min: 12, n_max: 31, n_min_raw: 11.919349550499538 +/// h: 5, n_min: 20, n_max: 63, n_min_raw: 19.944271909999163 +/// h: 6, n_min: 33, n_max: 127, n_min_raw: 32.92886904474855 +/// h: 7, n_min: 54, n_max: 255, n_min_raw: 53.93838853899757 +/// h: 8, n_min: 88, n_max: 511, n_min_raw: 87.93250516799598 +/// h: 9, n_min: 143, n_max: 1023, n_min_raw: 142.93614129124342 +/// h: 10, n_min: 232, n_max: 2047, n_min_raw: 231.93389404348926 +/// h: 11, n_min: 376, n_max: 4095, n_min_raw: 375.9352829189826 +/// h: 12, n_min: 609, n_max: 8191, n_min_raw: 608.9344245467217 +/// h: 13, n_min: 986, n_max: 16383, n_min_raw: 985.9349550499542 +/// h: 14, n_min: 1596, n_max: 32767, n_min_raw: 1595.9346271809259 +/// h: 15, n_min: 2583, n_max: 65535, n_min_raw: 2582.93482981513 +/// h: 16, n_min: 4180, n_max: 131071, n_min_raw: 4179.934704580306 +/// h: 17, n_min: 6764, n_max: 262143, n_min_raw: 6763.934781979686 +/// h: 18, n_min: 10945, n_max: 524287, n_min_raw: 10944.93473414424 +/// h: 19, n_min: 17710, n_max: 1048575, n_min_raw: 17709.934763708177 +/// ``` +/// +/// # Implementation analysis +/// +/// ## Gas considerations +/// +/// The present implementation relies on bit packing in assorted forms +/// to minimize per-byte storage costs: insertion keys are at most 32 +/// bits and node IDs are 14 bits, for example, for maximum data +/// compression. Notably, associated bit packing operations are manually +/// inlined to reduce the number of function calls: as of the time of +/// this writing, instruction gas for 15 function calls costs the same +/// as a single per-item read out of global storage. Hence inlined +/// bit packing significantly reduces the number of function calls when +/// compared against an implementation with frequent calls to helper +/// functions of the form `mask_in_bits(target, incoming, shift)`. +/// +/// As of the time of this writing, per-item reads and per-item writes +/// cost the same amount of storage gas, per-item writes cost 60 times +/// as much as per-byte writes, and per-byte writes cost approximately +/// 16.7 times as much as per-item writes. Hence with tree nodes only +/// occupying 128 bits (16 bytes), writing to a tree node only costs +/// about 25% more then reading a tree node. +/// +/// With storage gas only assessed on a per-transaction basis, this +/// means that inserting a tree node and retracing all the way back up +/// to the root only costs 25% more than does the $O(\log_2 n)$ lookup +/// required for a key search, assuming no rebalancing takes place: +/// per-item read costs are assessed on the way down, then replaced by +/// per-item write costs on the way back up. +/// +/// As for rebalancing, this process is only (potentially) required for +/// operations that alter the number of tree nodes: if key-value +/// insertion pair operations consistently involve the same insertion +/// keys, then tree retracing and rebalancing operations are minimized. +/// +/// In the case that rebalancing does occur, per-item write costs on the +/// affected nodes are essentially amortized against the gas reductions +/// afforded by an AVL tree's height guarantees: the height of a +/// red-black tree is at most $2 \log_2 n$, for example, but the +/// height of an AVL tree is at most approximately $1.44 \log_2 n$ +/// (per above). This means that fewer per-item costs are assessed on +/// key lookup for the latter, and moreover, re-coloring operations +/// required for the former may still require looping back up to the +/// root in the worst case, resulting in higher per-item write costs. +/// +/// ## Test development +/// +/// Unit tests for the present implementation were written alongside +/// source code, with some integration refactors applied along the way. +/// For example, rotation tests were first devised based on manual +/// allocation of nodes, then some were later updated for specific +/// insertion and deletion scenarios. As such, syntax may vary +/// slightly between some test cases depending on the level to which +/// they were later scoped for integration. +/// +/// ## Public function index +/// +/// * `borrow()` +/// * `borrow_head()` +/// * `borrow_head_mut()` +/// * `borrow_mut()` +/// * `borrow_tail()` +/// * `borrow_tail_mut()` +/// * `contains_active_list_node_id()` +/// * `get_access_key_insertion_key()` +/// * `get_head_key()` +/// * `get_height()` +/// * `get_tail_key()` +/// * `has_key()` +/// * `insert()` +/// * `insert_check_eviction()` +/// * `insert_evict_tail()` +/// * `is_ascending()` +/// * `is_ascending_access_key()` +/// * `is_empty()` +/// * `is_local_tail()` +/// * `new()` +/// * `next_list_node_id_in_access_key()` +/// * `pop_head()` +/// * `pop_tail()` +/// * `remove()` +/// * `would_update_head()` +/// * `would_update_tail()` +/// +/// ## Dependency charts +/// +/// The below dependency charts use `mermaid.js` syntax, which can be +/// automatically rendered into a diagram (depending on the browser) +/// when viewing the documentation file generated from source code. If +/// a browser renders the diagrams with coloring that makes it difficult +/// to read, try a different browser. +/// +/// `retrace()`: +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// retrace --> retrace_update_heights +/// retrace --> retrace_rebalance +/// retrace --> retrace_prep_iterate +/// +/// retrace_rebalance --> retrace_rebalance_rotate_left_right +/// retrace_rebalance --> retrace_rebalance_rotate_right +/// retrace_rebalance --> retrace_rebalance_rotate_right_left +/// retrace_rebalance --> retrace_rebalance_rotate_left +/// +/// ``` +/// +/// `insert()`: +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// insert --> search +/// insert --> insert_list_node +/// insert --> insert_tree_node +/// insert --> retrace +/// insert --> insert_check_head_tail +/// +/// insert_list_node --> insert_list_node_get_last_next +/// insert_list_node --> insert_list_node_assign_fields +/// +/// insert_tree_node --> insert_tree_node_update_parent_edge +/// +/// ``` +/// +/// `remove()`: +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// remove --> remove_list_node +/// remove --> remove_update_head +/// remove --> remove_update_tail +/// remove --> remove_tree_node +/// +/// remove_list_node --> remove_list_node_update_edges +/// +/// remove_update_head --> traverse +/// +/// remove_update_tail --> traverse +/// +/// remove_tree_node --> remove_tree_node_with_children +/// remove_tree_node --> remove_tree_node_follow_up +/// +/// remove_tree_node_follow_up --> retrace +/// +/// ``` +/// +/// Assorted: +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// insert_evict_tail --> insert +/// insert_evict_tail --> remove +/// +/// insert_check_eviction --> remove +/// insert_check_eviction --> insert +/// +/// next_list_node_id_in_access_key --> traverse +/// +/// has_key --> search +/// +/// pop_head --> remove +/// +/// pop_tail --> remove +/// +/// ``` +/// +/// # Bit conventions +/// +/// ## Number +/// +/// Bit numbers are 0-indexed from the least-significant bit (LSB): +/// +/// > 11101...1010010101 +/// > bit 5 = 0 ^ ^ bit 0 = 1 +/// +/// ## Status +/// +/// `0` is considered an "unset" bit, and `1` is considered a "set" bit. +/// Hence `11101` is set at bit 0 and unset at bit 1. +/// +/// ## Masking +/// +/// In the present implementation, a bitmask refers to a bitstring that +/// is only set at the indicated bit. For example, a bitmask with bit 0 +/// set corresponds to `000...001`, and a bitmask with bit 3 set +/// corresponds to `000...01000`. +/// +/// # References +/// +/// * [Adelson-Velsky and Landis 1962] (original paper) +/// * [Galles 2011] (interactive visualizer) +/// * [Wikipedia 2022] +/// +/// [Adelson-Velsky and Landis 1962]: +/// https://zhjwpku.com/assets/pdf/AED2-10-avl-paper.pdf +/// [Galles 2011]: +/// https://www.cs.usfca.edu/~galles/visualization/AVLtree.html +/// [Wikipedia 2022]: +/// https://en.wikipedia.org/wiki/AVL_tree +/// +/// # Complete DocGen index +/// +/// The below index is automatically generated from source code: +module econia::avl_queue { + + // Uses >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + use aptos_std::table::{Self, Table}; + use aptos_std::table_with_length::{Self, TableWithLength}; + use std::option::{Self, Option}; + + // Uses <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Test-only uses >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test_only] + use std::vector; + + // Test-only uses <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Structs >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// A hybrid between an AVL tree and a queue. See above. + /// + /// Most non-table fields stored compactly in `bits` as follows: + /// + /// | Bit(s) | Data | + /// |---------|----------------------------------------------------| + /// | 126 | If set, ascending AVL queue, else descending | + /// | 112-125 | Tree node ID at top of inactive stack | + /// | 98-111 | List node ID at top of inactive stack | + /// | 84-97 | AVL queue head list node ID | + /// | 52-83 | AVL queue head insertion key (if node ID not null) | + /// | 38-51 | AVL queue tail list node ID | + /// | 6-37 | AVL queue tail insertion key (if node ID not null) | + /// | 0-5 | Bits 8-13 of tree root node ID | + /// + /// Bits 0-7 of the tree root node ID are stored in `root_lsbs`. + struct AVLqueue has store { + bits: u128, + root_lsbs: u8, + /// Map from tree node ID to tree node. + tree_nodes: TableWithLength, + /// Map from list node ID to list node. + list_nodes: TableWithLength, + /// Map from list node ID to optional insertion value. + values: Table> + } + + /// A tree node in an AVL queue. + /// + /// All fields stored compactly in `bits` as follows: + /// + /// | Bit(s) | Data | + /// |--------|--------------------------------------| + /// | 94-125 | Insertion key | + /// | 89-93 | Left height | + /// | 84-88 | Right height | + /// | 70-83 | Parent node ID | + /// | 56-69 | Left child node ID | + /// | 42-55 | Right child node ID | + /// | 28-41 | List head node ID | + /// | 14-27 | List tail node ID | + /// | 0-13 | Next inactive node ID, when in stack | + /// + /// All fields except next inactive node ID are ignored when the + /// node is in the inactive nodes stack. + struct TreeNode has store { + bits: u128 + } + + /// A list node in an AVL queue. + /// + /// For compact storage, a "virtual last field" and a "virtual next + /// field" are split into two `u8` fields each: one for + /// most-significant bits (`last_msbs`, `next_msbs`), and one for + /// least-significant bits (`last_lsbs`, `next_lsbs`). + /// + /// When set at bit 14, the 16-bit concatenated result of `_msbs` + /// and `_lsbs` fields, in either case, refers to a tree node ID: If + /// `last_msbs` and `last_lsbs` indicate a tree node ID, then the + /// list node is the head of the list at the given tree node. If + /// `next_msbs` and `next_lsbs` indicate a tree node ID, then the + /// list node is the tail of the list at the given tree node. + /// + /// If not set at bit 14, the corresponding node ID is either the + /// last or the next list node in the doubly linked list. + /// + /// If list node is in the inactive list node stack, next node ID + /// indicates next inactive node in the stack. + struct ListNode has store { + last_msbs: u8, + last_lsbs: u8, + next_msbs: u8, + next_lsbs: u8 + } + + // Structs <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Error codes >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Number of allocated tree nodes is too high. + const E_TOO_MANY_TREE_NODES: u64 = 0; + /// Number of allocated list nodes is too high. + const E_TOO_MANY_LIST_NODES: u64 = 1; + /// Insertion key is too large. + const E_INSERTION_KEY_TOO_LARGE: u64 = 2; + /// Attempted insertion with eviction from empty AVL queue. + const E_EVICT_EMPTY: u64 = 3; + /// Attempted insertion with eviction for key-value insertion pair + /// that would become new tail. + const E_EVICT_NEW_TAIL: u64 = 4; + /// Specified height exceeds max height. + const E_INVALID_HEIGHT: u64 = 5; + + // Error codes <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Constants >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Ascending AVL queue flag. + const ASCENDING: bool = true; + /// Bit flag denoting ascending AVL queue. + const BIT_FLAG_ASCENDING: u8 = 1; + /// Bit flag denoting a tree node. + const BIT_FLAG_TREE_NODE: u8 = 1; + /// Number of bits in a byte. + const BITS_PER_BYTE: u8 = 8; + /// Flag for decrement to height during retrace. + const DECREMENT: bool = false; + /// Descending AVL queue flag. + const DESCENDING: bool = false; + /// `u64` bitmask with all bits set, generated in Python via + /// `hex(int('1' * 64, 2))`. + const HI_64: u64 = 0xffffffffffffffff; + /// `u128` bitmask with all bits set, generated in Python via + /// `hex(int('1' * 128, 2))`. + const HI_128: u128 = 0xffffffffffffffffffffffffffffffff; + /// Single bit set in integer of width required to encode bit flag. + const HI_BIT: u8 = 1; + /// All bits set in integer of width required to encode a byte. + /// Generated in Python via `hex(int('1' * 8, 2))`. + const HI_BYTE: u64 = 0xff; + /// All bits set in integer of width required to encode left or + /// right height. Generated in Python via `hex(int('1' * 5, 2))`. + const HI_HEIGHT: u8 = 0x1f; + /// All bits set in integer of width required to encode insertion + /// key. Generated in Python via `hex(int('1' * 32, 2))`. + const HI_INSERTION_KEY: u64 = 0xffffffff; + /// All bits set in integer of width required to encode node ID. + /// Generated in Python via `hex(int('1' * 14, 2))`. + const HI_NODE_ID: u64 = 0x3fff; + /// Flag for increment to height during retrace. + const INCREMENT: bool = true; + /// Flag for left direction. + const LEFT: bool = true; + /// Maximum tree height. + const MAX_HEIGHT: u8 = 18; + /// Flag for null value when null defined as 0. + const NIL: u8 = 0; + /// $2^{14} - 1$, the maximum number of nodes that can be allocated + /// for either node type. + const N_NODES_MAX: u64 = 16383; + /// Flag for inorder predecessor traversal. + const PREDECESSOR: bool = true; + /// Flag for right direction. + const RIGHT: bool = false; + /// Number of bits sort order bit flag is shifted in an access key. + const SHIFT_ACCESS_SORT_ORDER: u8 = 32; + /// Number of bits list node ID is shifted in an access key. + const SHIFT_ACCESS_LIST_NODE_ID: u8 = 33; + /// Number of bits tree node ID is shifted in an access key. + const SHIFT_ACCESS_TREE_NODE_ID: u8 = 47; + /// Number of bits sort order is shifted in `AVLqueue.bits`. + const SHIFT_SORT_ORDER: u8 = 126; + /// Number of bits left child node ID is shifted in `TreeNode.bits`. + const SHIFT_CHILD_LEFT: u8 = 56; + /// Number of bits right child node ID is shifted in + /// `TreeNode.bits`. + const SHIFT_CHILD_RIGHT: u8 = 42; + /// Number of bits AVL queue head insertion key is shifted in + /// `AVLqueue.bits`. + const SHIFT_HEAD_KEY: u8 = 52; + /// Number of bits AVL queue head list node ID is shifted in + /// `AVLqueue.bits`. + const SHIFT_HEAD_NODE_ID: u8 = 84; + /// Number of bits left height is shifted in `TreeNode.bits`. + const SHIFT_HEIGHT_LEFT: u8 = 89; + /// Number of bits right height is shifted in `TreeNode.bits`. + const SHIFT_HEIGHT_RIGHT: u8 = 84; + /// Number of bits insertion key is shifted in `TreeNode.bits`. + const SHIFT_INSERTION_KEY: u8 = 94; + /// Number of bits inactive list node stack top is shifted in + /// `AVLqueue.bits`. + const SHIFT_LIST_STACK_TOP: u8 = 98; + /// Number of bits node type bit flag is shifted in `ListNode` + /// virtual last and next fields. + const SHIFT_NODE_TYPE: u8 = 14; + /// Number of bits list head node ID is shifted in `TreeNode.bits`. + const SHIFT_LIST_HEAD: u8 = 28; + /// Number of bits list tail node ID is shifted in `TreeNode.bits`. + const SHIFT_LIST_TAIL: u8 = 14; + /// Number of bits parent node ID is shifted in `AVLqueue.bits`. + const SHIFT_PARENT: u8 = 70; + /// Number of bits AVL queue tail insertion key is shifted in + /// `AVLqueue.bits`. + const SHIFT_TAIL_KEY: u8 = 6; + /// Number of bits AVL queue tail list node ID is shifted in + /// `AVLqueue.bits`. + const SHIFT_TAIL_NODE_ID: u8 = 38; + /// Number of bits inactive tree node stack top is shifted in + /// `AVLqueue.bits`. + const SHIFT_TREE_STACK_TOP: u8 = 112; + /// Flag for inorder successor traversal. + const SUCCESSOR: bool = false; + + // Constants <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Public functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Immutably borrow insertion value corresponding to access key, + /// aborting if invalid key. + /// + /// # Assumptions + /// + /// * Provided access key corresponds to a valid list node in the + /// given AVL queue. + /// + /// # Testing + /// + /// * `test_borrow_borrow_mut()` + public fun borrow( + avlq_ref: &AVLqueue, + access_key: u64 + ): &V { + let list_node_id = // Extract list node ID from access key. + (access_key >> SHIFT_ACCESS_LIST_NODE_ID) & HI_NODE_ID; + // Immutably borrow corresponding insertion value. + option::borrow(table::borrow(&avlq_ref.values, list_node_id)) + } + + /// Immutably borrow AVL queue head insertion value, aborting if + /// empty. + /// + /// # Testing + /// + /// * `test_borrow_borrow_mut()` + public fun borrow_head( + avlq_ref: &AVLqueue + ): &V { + let (list_node_id) = (((avlq_ref.bits >> SHIFT_HEAD_NODE_ID) & + (HI_NODE_ID as u128)) as u64); // Get head list node ID. + // Immutably borrow corresponding insertion value. + option::borrow(table::borrow(&avlq_ref.values, list_node_id)) + } + + /// Mutably borrow AVL queue head insertion value, aborting if + /// empty. + /// + /// # Testing + /// + /// * `test_borrow_borrow_mut()` + public fun borrow_head_mut( + avlq_ref_mut: &mut AVLqueue + ): &mut V { + let (list_node_id) = (((avlq_ref_mut.bits >> SHIFT_HEAD_NODE_ID) & + (HI_NODE_ID as u128)) as u64); // Get head list node ID. + // Mutably borrow corresponding insertion value. + option::borrow_mut( + table::borrow_mut(&mut avlq_ref_mut.values, list_node_id)) + } + + /// Mutably borrow insertion value corresponding to access key, + /// aborting if invalid key. + /// + /// # Assumptions + /// + /// * Provided access key corresponds to a valid list node in the + /// given AVL queue. + /// + /// # Testing + /// + /// * `test_borrow_borrow_mut()` + public fun borrow_mut( + avlq_ref_mut: &mut AVLqueue, + access_key: u64 + ): &mut V { + let list_node_id = // Extract list node ID from access key. + (access_key >> SHIFT_ACCESS_LIST_NODE_ID) & HI_NODE_ID; + // Mutably borrow corresponding insertion value. + option::borrow_mut( + table::borrow_mut(&mut avlq_ref_mut.values, list_node_id)) + } + + /// Immutably borrow AVL queue tail insertion value, aborting if + /// empty. + /// + /// # Testing + /// + /// * `test_borrow_borrow_mut()` + public fun borrow_tail( + avlq_ref: &AVLqueue + ): &V { + let (list_node_id) = (((avlq_ref.bits >> SHIFT_TAIL_NODE_ID) & + (HI_NODE_ID as u128)) as u64); // Get tail list node ID. + // Immutably borrow corresponding insertion value. + option::borrow(table::borrow(&avlq_ref.values, list_node_id)) + } + + /// Mutably borrow AVL queue tail insertion value, aborting if + /// empty. + /// + /// # Testing + /// + /// * `test_borrow_borrow_mut()` + public fun borrow_tail_mut( + avlq_ref_mut: &mut AVLqueue + ): &mut V { + let (list_node_id) = (((avlq_ref_mut.bits >> SHIFT_TAIL_NODE_ID) & + (HI_NODE_ID as u128)) as u64); // Get tail list node ID. + // Mutably borrow corresponding insertion value. + option::borrow_mut( + table::borrow_mut(&mut avlq_ref_mut.values, list_node_id)) + } + + /// Return `true` if list node ID encoded in `access_key` is active. + /// + /// # Testing + /// + /// * `test_contains_active_list_node_id()` + public fun contains_active_list_node_id( + avlq_ref: &AVLqueue, + access_key: u64 + ): bool { + let list_node_id = // Extract list node ID from access key. + (access_key >> SHIFT_ACCESS_LIST_NODE_ID) & HI_NODE_ID; + // Return false if no list node in AVL queue with list node ID, + if (!table::contains(&avlq_ref.values, list_node_id)) false else + // Otherwise, return if there is an insertion value for + // given list node ID. + option::is_some(table::borrow(&avlq_ref.values, list_node_id)) + } + + /// Get insertion key encoded in an access key. + /// + /// # Testing + /// + /// * `test_access_key_getters()` + public fun get_access_key_insertion_key( + access_key: u64 + ): u64 { + access_key & HI_INSERTION_KEY + } + + /// Return none if AVL queue empty, else head insertion key. + /// + /// # Testing + /// + /// * `test_get_head_tail_key()` + public fun get_head_key( + avlq_ref: &AVLqueue + ): Option { + let bits = avlq_ref.bits; // Get AVL queue bits. + // Get AVL queue head node ID and insertion key fields. + let (avlq_head_node_id, avlq_head_insertion_key) = + ((((bits >> SHIFT_HEAD_NODE_ID) & (HI_NODE_ID as u128)) as u64), + (((bits >> SHIFT_HEAD_KEY) & (HI_INSERTION_KEY as u128)) as u64)); + // If no AVL queue head return none, else head insertion key. + if (avlq_head_node_id == (NIL as u64)) option::none() else + option::some(avlq_head_insertion_key) + } + + /// Return none if empty AVL queue, else tree height. + /// + /// # Reference diagram + /// + /// Height 0 for sole node at root. + /// + /// > 4 + /// + /// Insert 5, increasing right height to 1: + /// + /// > 4 + /// > \ + /// > 5 + /// + /// Insert 3, increasing left height to 1 as well: + /// + /// > 4 + /// > / \ + /// > 3 5 + /// + /// Insert 1, increasing left height to 2: + /// + /// > 4 + /// > / \ + /// > 3 5 + /// > / + /// > 1 + /// + /// # Testing + /// + /// * `test_get_height()` + public fun get_height( + avlq_ref: &AVLqueue + ): Option { + // Get root MSBs. + let msbs = avlq_ref.bits & ((HI_NODE_ID as u128) >> BITS_PER_BYTE); + let root = ((msbs << BITS_PER_BYTE) as u64) | + (avlq_ref.root_lsbs as u64); // Mask in root LSBs. + // Return none if no root and thus empty AVL queue. + if (root == (NIL as u64)) return option::none(); + // Immutably borrow root node. + let root_ref = table_with_length::borrow(&avlq_ref.tree_nodes, root); + let bits = root_ref.bits; // Get root bits. + let (height_left, height_right) = // Get left and right height. + ((((bits >> SHIFT_HEIGHT_LEFT ) & (HI_HEIGHT as u128)) as u8), + (((bits >> SHIFT_HEIGHT_RIGHT) & (HI_HEIGHT as u128)) as u8)); + let height = // Height is greater of left and right height. + if (height_left >= height_right) height_left else height_right; + option::some(height) // Return option-packed height. + } + + /// Return none if AVL queue empty, else tail insertion key. + /// + /// # Testing + /// + /// * `test_get_head_tail_key()` + public fun get_tail_key( + avlq_ref: &AVLqueue + ): Option { + let bits = avlq_ref.bits; // Get AVL queue bits. + // Get AVL queue tail node ID and insertion key fields. + let (avlq_tail_node_id, avlq_tail_insertion_key) = + ((((bits >> SHIFT_TAIL_NODE_ID) & (HI_NODE_ID as u128)) as u64), + (((bits >> SHIFT_TAIL_KEY) & (HI_INSERTION_KEY as u128)) as u64)); + // If no AVL queue tail return none, else tail insertion key. + if (avlq_tail_node_id == (NIL as u64)) option::none() else + option::some(avlq_tail_insertion_key) + } + + /// Return `true` if insertion `key` in AVL queue, else `false`. + /// + /// # Aborts + /// + /// * `E_INSERTION_KEY_TOO_LARGE`: Insertion key is too large. + /// + /// # Testing + /// + /// * `test_has_key()` + /// * `test_has_key_too_big()` + public fun has_key( + avlq_ref: &AVLqueue, + key: u64 + ): bool { + // Assert insertion key is not too many bits. + assert!(key <= HI_INSERTION_KEY, E_INSERTION_KEY_TOO_LARGE); + // Search for key, storing match flags. + let (nil_if_empty, none_if_found_or_empty) = search(avlq_ref, key); + // Return true if found, else false. + if ((nil_if_empty != (NIL as u64)) && + option::is_none(&none_if_found_or_empty)) true else false + } + + /// Insert a key-value pair into an AVL queue. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `key`: Key to insert. + /// * `value`: Value to insert. + /// + /// # Returns + /// + /// * `u64`: Access key used for lookup. + /// + /// # Aborts + /// + /// * `E_INSERTION_KEY_TOO_LARGE`: Insertion key is too large. + /// + /// # Failure testing + /// + /// * `test_insert_insertion_key_too_large()` + /// * `test_insert_too_many_list_nodes()` + /// + /// # State verification testing + /// + /// See `test_insert()` for state verification testing of the + /// below insertion sequence. + /// + /// Insert $\langle 3, 9 \rangle$: + /// + /// > 3 [9] + /// + /// Insert $\langle 4, 8 \rangle$: + /// + /// > 3 [9] + /// > \ + /// > 4 [8] + /// + /// Insert $\langle 5, 7 \rangle$: + /// + /// > 4 [8] + /// > / \ + /// > [9] 3 5 [7] + /// + /// Insert $\langle 3, 6 \rangle$ + /// + /// > 4 [8] + /// > / \ + /// > [9 -> 6] 3 5 [7] + /// + /// Insert $\langle 5, 5 \rangle$ + /// + /// > 4 [8] + /// > / \ + /// > [9 -> 6] 3 5 [7 -> 5] + public fun insert( + avlq_ref_mut: &mut AVLqueue, + key: u64, + value: V + ): u64 { + // Assert insertion key is not too many bits. + assert!(key <= HI_INSERTION_KEY, E_INSERTION_KEY_TOO_LARGE); + // Search for key, storing match node ID, and optional side on + // which a new leaf would be inserted relative to match node. + let (match_node_id, new_leaf_side) = search(avlq_ref_mut, key); + // If search returned null from the root, or if search flagged + // that a new tree node will have to be inserted as child, flag + // that the inserted list node will be the sole node in the + // corresponding doubly linked list. + let solo = match_node_id == (NIL as u64) || + option::is_some(&new_leaf_side); + // If a solo list node, flag no anchor tree node yet inserted, + // otherwise set anchor tree node as match node from search. + let anchor_tree_node_id = if (solo) (NIL as u64) else match_node_id; + let list_node_id = // Insert list node, storing its node ID. + insert_list_node(avlq_ref_mut, anchor_tree_node_id, value); + // Get corresponding tree node: if solo list node, insert a tree + // node and store its ID. Otherwise tree node is match node from + // search. + let tree_node_id = if (solo) insert_tree_node( + avlq_ref_mut, key, match_node_id, list_node_id, new_leaf_side) else + match_node_id; + // If just inserted new tree node that is not root, retrace + // starting at the parent to the inserted tree node. + if (solo && (match_node_id != (NIL as u64))) + retrace(avlq_ref_mut, match_node_id, INCREMENT, + *option::borrow(&new_leaf_side)); + // Check AVL queue head and tail. + insert_check_head_tail(avlq_ref_mut, key, list_node_id); + let order_bit = // Get sort order bit from AVL queue bits. + (avlq_ref_mut.bits >> SHIFT_SORT_ORDER) & (HI_BIT as u128); + // Return bit-packed access key. + key | ((order_bit as u64) << SHIFT_ACCESS_SORT_ORDER) | + ((list_node_id ) << SHIFT_ACCESS_LIST_NODE_ID) | + ((tree_node_id ) << SHIFT_ACCESS_TREE_NODE_ID) + } + + /// Try inserting key-value pair, evicting AVL queue tail as needed. + /// + /// If AVL queue is empty then no eviction is required, and a + /// standard insertion is performed. + /// + /// If AVL queue is not empty, then eviction is required if the AVL + /// queue is above the provided critical height or if the maximum + /// number of list nodes have already been allocated and all are + /// active. Here, insertion is not permitted if attempting to insert + /// a new tail. Otherwise, the tail of the AVL queue is removed then + /// the provided key-value pair is inserted. + /// + /// If AVL queue is not empty but eviction is not required, a + /// standard insertion is performed. + /// + /// Does not guarantee that height will be less than or equal to + /// critical height post-insertion, since there is no limit on the + /// number of list nodes with a given insertion key: evicting the + /// tail node does not guarantee removing a corresponding tree node. + /// Rather, critical height is simply a threshold for determining + /// whether height-driven eviction is required. + /// + /// Does not check number of active tree nodes because the number of + /// active tree nodes is less than or equal to the number of active + /// list nodes. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `key`: Key to insert. + /// * `value`: Value to insert. + /// * `critical_height`: Tree height above which eviction should + /// take place. + /// + /// # Returns + /// + /// * `u64`: Access key of key-value pair just inserted, otherwise + /// `NIL` if an invalid insertion. + /// * `u64`: `NIL` if no eviction required, otherwise access key of + /// evicted key-value insertion pair. + /// * `Option`: None if no eviction required. If an invalid + /// insertion, the insertion value that could not be inserted. + /// Otherwise, the evicted insertion value. + /// + /// # Aborts + /// + /// * `E_INVALID_HEIGHT`: Specified height exceeds max height. + /// + /// # Reference diagrams + /// + /// ## Case 1 + /// + /// * Ascending AVL queue. + /// * Left height greater than or equal to right height. + /// * Max list nodes active. + /// + /// > [1] 2 + /// > / \ + /// > [2] 1 3 [3 -> 4 -> ... N_NODES_MAX] + /// + /// 1. Attempting to insert with insertion key 3, critical height 2 + /// is invalid (not too tall, max list nodes active, attempting + /// to insert tail). + /// 2. Attempting to insert with insertion key 2, critical height 2 + /// then evicts tail (not too tall, max list nodes active, not + /// attempting to insert tail). + /// + /// ## Case 2 + /// + /// * Descending AVL queue. + /// * Left height not greater than or equal to right height. + /// * Not max list nodes active. + /// + /// > [123] 2 + /// > / \ + /// > [456] 1 3 [789] + /// > \ + /// > 4 [321] + /// + /// 1. Attempting to insert with insertion key 1, critical height 1 + /// is invalid (too tall, not max list nodes active, attempting + /// to insert tail). + /// 2. Attempting to insert with insertion key 2, critical height 1 + /// then evicts tail (too tall, not max list nodes active, not + /// attempting to insert tail). + /// 3. Attempting to insert with insertion key 1, critical height + /// 10 then results in standard insertion at tail (not too tall, + /// not max list nodes active). + /// + /// # Testing + /// + /// * `test_insert_check_eviction_case_1()` + /// * `test_insert_check_eviction_case_2()` + /// * `test_insert_check_eviction_empty()` + /// * `test_insert_check_eviction_invalid_height()` + public fun insert_check_eviction( + avlq_ref_mut: &mut AVLqueue, + key: u64, + value: V, + critical_height: u8 + ): ( + u64, + u64, + Option + ) { + // Assert specified critical height is a valid height. + assert!(critical_height <= MAX_HEIGHT, E_INVALID_HEIGHT); + let bits = avlq_ref_mut.bits; // Get AVL queue bits. + let tail_list_node_id = // Get AVL queue tail list node id. + (((bits >> SHIFT_TAIL_NODE_ID) & (HI_NODE_ID as u128)) as u64); + // If empty, return result of standard insertion. + if (tail_list_node_id == (NIL as u64)) return + (insert(avlq_ref_mut, key, value), (NIL as u64), option::none()); + // Get inactive list nodes stack top and root MSBs. + let (list_top, root_msbs) = + ((((bits >> SHIFT_LIST_STACK_TOP) & (HI_NODE_ID as u128)) as u64), + (((bits & ((HI_NODE_ID as u128) >> BITS_PER_BYTE))))); + // Get root field by masking in root LSBs. + let root = ((root_msbs << BITS_PER_BYTE) as u64) | + (avlq_ref_mut.root_lsbs as u64); + let root_ref = // Immutably borrow root node. + table_with_length::borrow(&avlq_ref_mut.tree_nodes, root); + let r_bits = root_ref.bits; // Get root node bits. + let (height_left, height_right) = // Get left and right height. + ((((r_bits >> SHIFT_HEIGHT_LEFT) & (HI_HEIGHT as u128)) as u8), + (((r_bits >> SHIFT_HEIGHT_RIGHT) & (HI_HEIGHT as u128)) as u8)); + let height = // Height is greater of left and right height. + if (height_left >= height_right) height_left else height_right; + let too_tall = height > critical_height; // Check if too tall. + // Get number of allocated list nodes. + let n_list_nodes = table_with_length::length(&avlq_ref_mut.list_nodes); + let max_list_nodes_active = // Check if max list nodes active. + (n_list_nodes == N_NODES_MAX) && (list_top == (NIL as u64)); + // Declare tail access key and insertion value. + let (tail_access_key, tail_value); + // If above critical height or max list nodes active: + if (too_tall || max_list_nodes_active) { // If need to evict: + let order_bit = // Get sort order bit flag. + (((bits >> SHIFT_SORT_ORDER) & (HI_BIT as u128)) as u8); + // Determine if ascending AVL queue. + let ascending = order_bit == BIT_FLAG_ASCENDING; + // Get AVL queue tail insertion key. + let tail_key = (((bits >> SHIFT_TAIL_KEY) & + (HI_INSERTION_KEY as u128)) as u64); + // If ascending and insertion key greater than or equal to + // tail key, or descending and insertion key less than or + // equal to tail key, attempting to insert new tail: invalid + // when above critical height or max list nodes active. + if (( ascending && (key >= tail_key)) || + (!ascending && (key <= tail_key))) return + ((NIL as u64), (NIL as u64), option::some(value)); + // Immutably borrow tail list node. + let tail_list_node_ref = table_with_length::borrow( + &avlq_ref_mut.list_nodes, tail_list_node_id); + let next = // Get virtual next field from node. + ((tail_list_node_ref.next_msbs as u64) << BITS_PER_BYTE) | + (tail_list_node_ref.next_lsbs as u64); + // Get tree node ID encoded in next field. + let tail_tree_node_id = next & (HI_NODE_ID as u64); + tail_access_key = tail_key | // Get tail access key. + ((order_bit as u64 ) << SHIFT_ACCESS_SORT_ORDER) | + ((tail_list_node_id) << SHIFT_ACCESS_LIST_NODE_ID) | + ((tail_tree_node_id) << SHIFT_ACCESS_TREE_NODE_ID); + // Get tail insertion value from evicted tail. + tail_value = option::some(remove(avlq_ref_mut, tail_access_key)); + } else { // If no potential for eviction: + // Flag no evicted tail return values. + (tail_access_key, tail_value) = ((NIL as u64), option::none()); + }; // Optional eviction now complete. + // Return access key for new key-value insertion pair, optional + // access key and insertion value for evicted tail. + (insert(avlq_ref_mut, key, value), tail_access_key, tail_value) + } + + /// Insert key-value insertion pair, evicting AVL queue tail. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `key`: Key to insert. + /// * `value`: Value to insert. + /// + /// # Returns + /// + /// * `u64`: Access key for lookup of inserted pair. + /// * `u64`: Access key of evicted tail. + /// * `V`: Evicted tail insertion value. + /// + /// # Aborts + /// + /// * `E_EVICT_EMPTY`: AVL queue is empty. + /// * `E_EVICT_NEW_TAIL`: Key-value insertion pair would itself + /// become new tail. + /// + /// # Testing + /// + /// * `test_insert_evict_tail()` + /// * `test_insert_evict_tail_empty()` + /// * `test_insert_evict_tail_new_tail_ascending()` + /// * `test_insert_evict_tail_new_tail_descending()` + public fun insert_evict_tail( + avlq_ref_mut: &mut AVLqueue, + key: u64, + value: V + ): ( + u64, + u64, + V + ) { + let bits = avlq_ref_mut.bits; // Get AVL queue bits. + // Get AVL queue sort order bit, tail list node ID, and + // tail insertion key. + let (order_bit, tail_list_node_id, tail_key) = + ((((bits >> SHIFT_SORT_ORDER) & (HI_BIT as u128)) as u8), + (((bits >> SHIFT_TAIL_NODE_ID) & (HI_NODE_ID as u128)) as u64), + (((bits >> SHIFT_TAIL_KEY) & (HI_INSERTION_KEY as u128)) as u64)); + // Assert not trying to evict from empty AVL queue. + assert!(tail_list_node_id != (NIL as u64), E_EVICT_EMPTY); + // Determine if AVL queue is ascending. + let ascending = order_bit == BIT_FLAG_ASCENDING; + // Assert not trying to evict when new entry would become tail + // (assert ascending and insertion key is less than tail key, or + // descending and insertion key is greater than tail key). + assert!((( ascending && (key < tail_key)) || + (!ascending && (key > tail_key))), E_EVICT_NEW_TAIL); + // Immutably borrow tail list node. + let tail_list_node_ref = table_with_length::borrow( + &avlq_ref_mut.list_nodes, tail_list_node_id); + // Get virtual next field from node. + let next = ((tail_list_node_ref.next_msbs as u64) << BITS_PER_BYTE) | + (tail_list_node_ref.next_lsbs as u64); + // Get tree node ID encoded in next field. + let tail_tree_node_id = next & (HI_NODE_ID as u64); + let tail_access_key = tail_key | // Get tail access key. + ((order_bit as u64 ) << SHIFT_ACCESS_SORT_ORDER) | + ((tail_list_node_id) << SHIFT_ACCESS_LIST_NODE_ID) | + ((tail_tree_node_id) << SHIFT_ACCESS_TREE_NODE_ID); + // Insert new key-value insertion pair, storing access key. + let new_access_key = insert(avlq_ref_mut, key, value); + // Remove tail access key, storing insertion value. + let tail_value = remove(avlq_ref_mut, tail_access_key); + (new_access_key, tail_access_key, tail_value) + } + + /// Return `true` if given AVL queue has ascending sort order. + /// + /// # Testing + /// + /// * `test_is_ascending()` + public fun is_ascending( + avlq_ref: &AVLqueue + ): bool { + ((avlq_ref.bits >> SHIFT_SORT_ORDER) & (BIT_FLAG_ASCENDING as u128)) == + (BIT_FLAG_ASCENDING as u128) + } + + /// Return `true` if ascending access key, else `false`. + /// + /// # Testing + /// + /// * `test_access_key_getters()` + public fun is_ascending_access_key( + access_key: u64 + ): bool { + ((access_key >> SHIFT_ACCESS_SORT_ORDER) & (HI_BIT as u64) as u8) == + BIT_FLAG_ASCENDING + } + + /// Return `true` if given AVL queue is empty. + /// + /// # Testing + /// + /// * `test_is_empty()` + public fun is_empty( + avlq_ref: &AVLqueue + ): bool { + ((avlq_ref.bits >> SHIFT_HEAD_NODE_ID) & (HI_NODE_ID as u128)) == + (NIL as u128) // Return true if no AVL queue head. + } + + /// Return `true` if access key corresponds to a list node at the + /// tail of its corresponding doubly linked list, aborting for an + /// invalid list node ID. + /// + /// # Testing + /// + /// * `test_insert()` + /// * `test_remove_2()` + public fun is_local_tail( + avlq_ref: &AVLqueue, + access_key: u64 + ): bool { + let list_node_id = // Extract list node ID from access key. + (access_key >> SHIFT_ACCESS_LIST_NODE_ID) & HI_NODE_ID; + let list_node_ref = // Immutably borrow corresponding list node. + table_with_length::borrow(&avlq_ref.list_nodes, list_node_id); + // Get virtual next field from node. + let next = ((list_node_ref.next_msbs as u64) << BITS_PER_BYTE) | + (list_node_ref.next_lsbs as u64); + let tree_node_flag_bit = // Get tree node flag bit. + (((next >> SHIFT_NODE_TYPE) & (BIT_FLAG_TREE_NODE as u64)) as u8); + // Return true if next node is flagged as a tree node. + return (tree_node_flag_bit == BIT_FLAG_TREE_NODE) + } + + /// Return a new AVL queue, optionally allocating inactive nodes. + /// + /// # Parameters + /// + /// * `sort_order`: `ASCENDING` or `DESCENDING`. + /// * `n_inactive_tree_nodes`: The number of inactive tree nodes + /// to allocate. + /// * `n_inactive_list_nodes`: The number of inactive list nodes + /// to allocate. + /// + /// # Returns + /// + /// * `AVLqueue`: A new AVL queue. + /// + /// # Aborts + /// + /// * `E_TOO_MANY_TREE_NODES`: Too many tree nodes specified. + /// * `E_TOO_MANY_LIST_NODES`: Too many list nodes specified. + /// + /// # Testing + /// + /// * `test_new_no_nodes()` + /// * `test_new_some_nodes()` + /// * `test_new_some_nodes_loop()` + /// * `test_new_too_many_list_nodes()` + /// * `test_new_too_many_tree_nodes()` + public fun new( + sort_order: bool, + n_inactive_tree_nodes: u64, + n_inactive_list_nodes: u64, + ): AVLqueue { + // Assert not trying to allocate too many tree nodes. + assert!(n_inactive_tree_nodes <= N_NODES_MAX, E_TOO_MANY_TREE_NODES); + // Assert not trying to allocate too many list nodes. + assert!(n_inactive_list_nodes <= N_NODES_MAX, E_TOO_MANY_LIST_NODES); + // Initialize bits field based on sort order. + let bits = if (sort_order == DESCENDING) (NIL as u128) else + ((BIT_FLAG_ASCENDING as u128) << SHIFT_SORT_ORDER); + // Mask in 1-indexed node ID at top of each inactive node stack. + bits = bits | ((n_inactive_tree_nodes as u128) << SHIFT_TREE_STACK_TOP) + | ((n_inactive_list_nodes as u128) << SHIFT_LIST_STACK_TOP); + // Declare empty AVL queue. + let avlq = AVLqueue{bits, + root_lsbs: NIL, + tree_nodes: table_with_length::new(), + list_nodes: table_with_length::new(), + values: table::new()}; + // If need to allocate at least one tree node: + if (n_inactive_tree_nodes > 0) { + let i = 0; // Declare loop counter. + // While nodes to allocate: + while (i < n_inactive_tree_nodes) { + // Add to tree nodes table a node having 1-indexed node + // ID derived from counter, indicating next inactive + // node in stack has ID of last allocated node (or null + // in the case of the first loop iteration). + table_with_length::add( + &mut avlq.tree_nodes, i + 1, TreeNode{bits: (i as u128)}); + i = i + 1; // Increment loop counter. + }; + }; + // If need to allocate at least one list node: + if (n_inactive_list_nodes > 0) { + let i = 0; // Declare loop counter. + // While nodes to allocate: + while (i < n_inactive_list_nodes) { + // Add to list nodes table a node having 1-indexed node + // ID derived from counter, indicating next inactive + // node in stack has ID of last allocated node (or null + // in the case of the first loop iteration). + table_with_length::add(&mut avlq.list_nodes, i + 1, ListNode{ + last_msbs: 0, + last_lsbs: 0, + next_msbs: ((i >> BITS_PER_BYTE) as u8), + next_lsbs: ((i & HI_BYTE) as u8)}); + // Allocate optional insertion value entry. + table::add(&mut avlq.values, i + 1, option::none()); + i = i + 1; // Increment loop counter. + }; + }; + avlq // Return AVL queue. + } + + /// Get list node ID of the next list node in AVL queue, encoded in + /// an otherwise blank access key. + /// + /// This function is optimized for performance and leaves access key + /// validity checking to calling functions. + /// + /// # Parameters + /// + /// * `avlq_ref`: Immutable reference to AVL queue. + /// * `access_key`: Access key containing list node ID of an active + /// list node, relative to which the next list node ID should be + /// returned. + /// + /// # Returns + /// + /// * `u64`: The list node ID of the next active list node in the + /// AVL queue, if there is one, encoded in an otherwise blank + /// access key, otherwise `NIL`. + /// + /// # Testing + /// + /// * `test_next_list_node_id_in_access_key()` + public fun next_list_node_id_in_access_key( + avlq_ref: &AVLqueue, + access_key: u64, + ): ( + u64 + ) { + let list_node_id = // Extract list node ID from access key. + (access_key >> SHIFT_ACCESS_LIST_NODE_ID) & HI_NODE_ID; + // Immutably borrow list node. + let list_node_ref = table_with_length::borrow( + &avlq_ref.list_nodes, list_node_id); + // Get virtual next field from node. + let next = ((list_node_ref.next_msbs as u64) << BITS_PER_BYTE) | + (list_node_ref.next_lsbs as u64); + // Determine if next node is flagged as tree node. + let next_is_tree = ((next >> SHIFT_NODE_TYPE) & + (BIT_FLAG_TREE_NODE as u64)) == (BIT_FLAG_TREE_NODE as u64); + let next_node_id = next & HI_NODE_ID; // Get next node ID. + let target_list_node_id = if (next_is_tree) { + let target = if (is_ascending(avlq_ref)) + SUCCESSOR else PREDECESSOR; + let (_, target_tree_node_list_head, _) = + traverse(avlq_ref, next_node_id, target); + target_tree_node_list_head + } else { + next_node_id + }; + (target_list_node_id << SHIFT_ACCESS_LIST_NODE_ID) + } + + /// Return insertion value at head of AVL queue, aborting if empty. + /// + /// # Testing + /// + /// * `test_pop_head_tail()` + public fun pop_head( + avlq_ref_mut: &mut AVLqueue + ): V { + let (list_node_id) = ((avlq_ref_mut.bits >> SHIFT_HEAD_NODE_ID) & + (HI_NODE_ID as u128) as u64); // Get head list node ID. + // Immutably borrow head list node. + let list_node_ref = table_with_length::borrow( + &mut avlq_ref_mut.list_nodes, list_node_id); + // Get virtual last field from node. + let last = ((list_node_ref.last_msbs as u64) << BITS_PER_BYTE) | + (list_node_ref.last_lsbs as u64); + // Get tree node ID encoded in last field. + let tree_node_id = last & (HI_NODE_ID as u64); + // Encode list node and tree node IDs in partial access key. + let access_key = (list_node_id << SHIFT_ACCESS_LIST_NODE_ID) | + (tree_node_id << SHIFT_ACCESS_TREE_NODE_ID); + remove(avlq_ref_mut, access_key) // Remove from AVL queue. + } + + /// Return insertion value at tail of AVL queue, aborting if empty. + /// + /// # Testing + /// + /// * `test_pop_head_tail()` + public fun pop_tail( + avlq_ref_mut: &mut AVLqueue + ): V { + let (list_node_id) = ((avlq_ref_mut.bits >> SHIFT_TAIL_NODE_ID) & + (HI_NODE_ID as u128) as u64); // Get tail list node ID. + // Immutably borrow tail list node. + let list_node_ref = table_with_length::borrow( + &mut avlq_ref_mut.list_nodes, list_node_id); + // Get virtual next field from node. + let next = ((list_node_ref.next_msbs as u64) << BITS_PER_BYTE) | + (list_node_ref.next_lsbs as u64); + // Get tree node ID encoded in next field. + let tree_node_id = next & (HI_NODE_ID as u64); + // Encode list node and tree node IDs in partial access key. + let access_key = (list_node_id << SHIFT_ACCESS_LIST_NODE_ID) | + (tree_node_id << SHIFT_ACCESS_TREE_NODE_ID); + remove(avlq_ref_mut, access_key) // Remove from AVL queue. + } + + /// Remove node having given access key, return insertion value. + /// + /// Update AVL queue head, tail, root fields as needed. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `access_key`: Access key returned by `insert()`. + /// + /// # Assumptions + /// + /// * Provided access key corresponds to a valid list node in the + /// given AVL queue. + /// + /// # Reference diagram + /// + /// Consider the following AVL queue: + /// + /// > 2 [3 -> 4] + /// > / + /// > 1 [5 -> 6] + /// + /// ## Case 1 (ascending head updates) + /// + /// * Ascending AVL queue. + /// * Remove insertion value 5, updating AVL queue head to node + /// having insertion value 6. + /// * Remove insertion value 6, updating AVL queue head to node + /// having insertion value 3. + /// + /// ## Case 2 (ascending tail updates) + /// + /// * Ascending AVL queue. + /// * Remove insertion value 4, updating AVL queue tail to node + /// having insertion value 3. + /// * Remove insertion value 3, updating AVL queue tail to node + /// having insertion value 6. + /// + /// ## Case 3 (descending head updates) + /// + /// * Descending AVL queue. + /// * Remove insertion value 3, updating AVL queue head to node + /// having insertion value 4. + /// * Remove insertion value 4, updating AVL queue head to node + /// having insertion value 5. + /// + /// ## Case 4 (descending tail updates) + /// + /// * Descending AVL queue. + /// * Remove insertion value 6, updating AVL queue tail to node + /// having insertion value 5. + /// * Remove insertion value 5, updating AVL queue tail to node + /// having insertion value 4. + /// + /// # Testing + /// + /// * `test_remove_mid_list()` tests no modification to doubly + /// linked list or tail. + /// * `test_remove_1()`, `test_remove_3()`, and `test_remove_root()` + /// test updates to AVL queue head. + /// * `test_remove_2()`, `test_remove_4()`, and `test_remove_root()` + /// test updates to AVL queue tail. + /// * `test_remove_1()`, `test_remove_2()`, `test_remove_3()`, + /// `test_remove_4()`, and `test_remove_root()` test a doubly + /// linked list head and tail modified, and a tree node removed. + public fun remove( + avlq_ref_mut: &mut AVLqueue, + access_key: u64 + ): V { + let list_node_id = // Extract list node ID from access key. + (access_key >> SHIFT_ACCESS_LIST_NODE_ID) & HI_NODE_ID; + // Remove list node, storing insertion value, optional new list + // head, and optional new list tail. + let (value, new_list_head_option, new_list_tail_option) = + remove_list_node(avlq_ref_mut, list_node_id); + // Check if doubly linked list head modified. + let list_head_modified = option::is_some(&new_list_head_option); + // Check if doubly linked list tail modified. + let list_tail_modified = option::is_some(&new_list_tail_option); + // If doubly linked list head or tail modified: + if (list_head_modified || list_tail_modified) { + let bits = avlq_ref_mut.bits; // Get AVL queue bits. + // Get AVL queue head and tail node IDs, sort order bit. + let (avlq_head_node_id, avlq_tail_node_id, order_bit) = ( + (((bits >> SHIFT_HEAD_NODE_ID) & (HI_NODE_ID as u128)) as u64), + (((bits >> SHIFT_TAIL_NODE_ID) & (HI_NODE_ID as u128)) as u64), + (((bits >> SHIFT_SORT_ORDER ) & (HI_BIT as u128)) as u8)); + // Determine if AVL queue head, tail were modified. + let (avlq_head_modified, avlq_tail_modified) = + ((avlq_head_node_id == list_node_id), + (avlq_tail_node_id == list_node_id)); + // Determine if ascending AVL queue. + let ascending = order_bit == BIT_FLAG_ASCENDING; + let tree_node_id = // Get tree node ID from access key. + (access_key >> SHIFT_ACCESS_TREE_NODE_ID) & HI_NODE_ID; + // If AVL queue head modified, update accordingly. + if (avlq_head_modified) remove_update_head( + avlq_ref_mut, *option::borrow(&new_list_head_option), + ascending, tree_node_id); + // If AVL queue tail modified, update accordingly. + if (avlq_tail_modified) remove_update_tail( + avlq_ref_mut, *option::borrow(&new_list_tail_option), + ascending, tree_node_id); + // If list head and tail both modified, then just removed + // the sole list node in a tree node, so remove tree node: + if (list_head_modified && list_tail_modified) + remove_tree_node(avlq_ref_mut, tree_node_id); + }; + value // Return insertion value. + } + + /// Return `true` if inserting `key` would update AVL queue head. + /// + /// # Aborts + /// + /// * `E_INSERTION_KEY_TOO_LARGE`: Insertion key is too large. + /// + /// # Testing + /// + /// * `test_would_update_head_tail()` + /// * `test_would_update_head_too_big()` + public fun would_update_head( + avlq_ref: &AVLqueue, + key: u64 + ): bool { + // Assert insertion key is not too many bits. + assert!(key <= HI_INSERTION_KEY, E_INSERTION_KEY_TOO_LARGE); + let bits = avlq_ref.bits; // Get AVL queue field bits. + // Extract relevant fields. + let (order_bit, head_node_id, head_key) = + ((((bits >> SHIFT_SORT_ORDER) & (HI_BIT as u128)) as u8), + (((bits >> SHIFT_HEAD_NODE_ID) & (HI_NODE_ID as u128)) as u64), + (((bits >> SHIFT_HEAD_KEY) & (HI_INSERTION_KEY as u128)) as u64)); + // Determine if AVL queue is ascending. + let ascending = order_bit == BIT_FLAG_ASCENDING; + // Return true if empty AVL queue with no head node. + if (head_node_id == (NIL as u64)) return true; + // Return true if ascending and key less than head key, or + // descending and key greater than head key. + return (( ascending && (key < head_key)) || + (!ascending && (key > head_key))) + } + + /// Return `true` if inserting `key` would update AVL queue tail. + /// + /// # Aborts + /// + /// * `E_INSERTION_KEY_TOO_LARGE`: Insertion key is too large. + /// + /// # Testing + /// + /// * `test_would_update_head_tail()` + /// * `test_would_update_tail_too_big()` + public fun would_update_tail( + avlq_ref: &AVLqueue, + key: u64 + ): bool { + // Assert insertion key is not too many bits. + assert!(key <= HI_INSERTION_KEY, E_INSERTION_KEY_TOO_LARGE); + let bits = avlq_ref.bits; // Get AVL queue field bits. + // Extract relevant fields. + let (order_bit, tail_node_id, tail_key) = + ((((bits >> SHIFT_SORT_ORDER) & (HI_BIT as u128)) as u8), + (((bits >> SHIFT_TAIL_NODE_ID) & (HI_NODE_ID as u128)) as u64), + (((bits >> SHIFT_TAIL_KEY) & (HI_INSERTION_KEY as u128)) as u64)); + // Determine if AVL queue is ascending. + let ascending = order_bit == BIT_FLAG_ASCENDING; + // Return true if empty AVL queue with no tail node. + if (tail_node_id == (NIL as u64)) return true; + // Return true if ascending and key greater than or equal to + // tail key, or descending and key less than or equal to tail + // key. + return (( ascending && (key >= tail_key)) || + (!ascending && (key <= tail_key))) + } + + // Public functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Private functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Check head and tail of AVL queue during insertion. + /// + /// Update fields as needed based on sort order. + /// + /// Inner function for `insert()`. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `key`: Insertion key just inserted. + /// * `list_node_id`: ID of list node just inserted. + /// + /// # Testing + /// + /// * `test_insert_check_head_tail_ascending()` + /// * `test_insert_check_head_tail_descending()` + fun insert_check_head_tail( + avlq_ref_mut: &mut AVLqueue, + key: u64, + list_node_id: u64 + ) { + let bits = avlq_ref_mut.bits; // Get AVL queue field bits. + // Extract relevant fields. + let (order_bit, head_node_id, head_key, tail_node_id, tail_key) = + ((((bits >> SHIFT_SORT_ORDER) & (HI_BIT as u128)) as u8), + (((bits >> SHIFT_HEAD_NODE_ID) & (HI_NODE_ID as u128)) as u64), + (((bits >> SHIFT_HEAD_KEY) & (HI_INSERTION_KEY as u128)) as u64), + (((bits >> SHIFT_TAIL_NODE_ID) & (HI_NODE_ID as u128)) as u64), + (((bits >> SHIFT_TAIL_KEY) & (HI_INSERTION_KEY as u128)) as u64)); + // Determine if AVL queue is ascending. + let ascending = order_bit == BIT_FLAG_ASCENDING; + let reassign_head = false; // Assume not reassigning head. + if (head_node_id == (NIL as u64)) { // If no head node: + reassign_head = true; // Mark should reassign head. + } else { // Otherwise if AVL queue already had head: + // If ascending AVL queue and insertion key less than head + // key, + if ((ascending && (key < head_key)) || + // Or if descending AVL queue and insertion key greater than + // head key, mark should reassign head. + (!ascending && (key > head_key))) reassign_head = true; + }; + // Reassign bits for head key and node ID if needed: + if (reassign_head) avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ (((HI_NODE_ID as u128) << SHIFT_HEAD_NODE_ID) | + ((HI_INSERTION_KEY as u128) << SHIFT_HEAD_KEY))) | + // Mask in new bits. + ((list_node_id as u128) << SHIFT_HEAD_NODE_ID) | + ((key as u128) << SHIFT_HEAD_KEY); + let reassign_tail = false; // Assume not reassigning tail. + if (tail_node_id == (NIL as u64)) { // If no tail node: + reassign_tail = true; // Mark should reassign tail. + } else { // Otherwise if AVL queue already had tail: + // If ascending AVL queue and insertion key greater than or + // equal to tail key, + if ((ascending && (key >= tail_key)) || + // Or if descending AVL queue and insertion key less than or + // equal to tail key, mark should reassign tail. + (!ascending && (key <= tail_key))) reassign_tail = true; + }; + // Reassign bits for tail key and node ID if needed: + if (reassign_tail) avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ (((HI_NODE_ID as u128) << SHIFT_TAIL_NODE_ID) | + ((HI_INSERTION_KEY as u128) << SHIFT_TAIL_KEY))) | + // Mask in new bits. + ((list_node_id as u128) << SHIFT_TAIL_NODE_ID) | + ((key as u128) << SHIFT_TAIL_KEY); + } + + /// Insert a list node and return its node ID. + /// + /// Inner function for `insert()`. + /// + /// In the case of inserting a list node to a doubly linked list in + /// an existing tree node, known as the "anchor tree node", the list + /// node becomes the new list tail. + /// + /// In the other case of inserting a "solo node" as the sole list + /// node in a doubly linked list in a new tree leaf, the list node + /// becomes the head and tail of the new list. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `anchor_tree_node_id`: Node ID of anchor tree node, `NIL` if + /// inserting a list node as the sole list node in a new tree + /// node. + /// * `value`: Insertion value for list node to insert. + /// + /// # Returns + /// + /// * `u64`: Node ID of inserted list node. + /// + /// # Testing + /// + /// * `test_insert_list_node_not_solo()` + /// * `test_insert_list_node_solo()` + fun insert_list_node( + avlq_ref_mut: &mut AVLqueue, + anchor_tree_node_id: u64, + value: V + ): u64 { + let (last, next) = // Get virtual last and next fields for node. + insert_list_node_get_last_next(avlq_ref_mut, anchor_tree_node_id); + let list_node_id = // Assign fields, store inserted node ID. + insert_list_node_assign_fields(avlq_ref_mut, last, next, value); + // If inserting a new list tail that is not solo: + if (anchor_tree_node_id != (NIL as u64)) { + // Mutably borrow tree nodes table. + let tree_nodes_ref_mut = &mut avlq_ref_mut.tree_nodes; + // Mutably borrow list nodes table. + let list_nodes_ref_mut = &mut avlq_ref_mut.list_nodes; + let last_node_ref_mut = // Mutably borrow old tail. + table_with_length::borrow_mut(list_nodes_ref_mut, last); + last_node_ref_mut.next_msbs = // Reassign its next MSBs. + ((list_node_id >> BITS_PER_BYTE) as u8); + // Reassign its next LSBs to those of inserted list node. + last_node_ref_mut.next_lsbs = ((list_node_id & HI_BYTE) as u8); + // Mutably borrow anchor tree node. + let anchor_node_ref_mut = table_with_length::borrow_mut( + tree_nodes_ref_mut, anchor_tree_node_id); + // Reassign bits for list tail node: + anchor_node_ref_mut.bits = anchor_node_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_LIST_TAIL)) | + // Mask in new bits. + ((list_node_id as u128) << SHIFT_LIST_TAIL); + }; + list_node_id // Return inserted list node ID. + } + + /// Assign fields when inserting a list node. + /// + /// Inner function for `insert_list_node()`. + /// + /// If inactive list node stack is empty, allocate a new list node, + /// otherwise pop one off the inactive stack. + /// + /// # Parameters + /// + /// * `avlq_ref`: Immutable reference to AVL queue. + /// * `last`: Virtual last field from + /// `insert_list_node_get_last_next()`. + /// * `next`: Virtual next field from + /// `insert_list_node_get_last_next()`. + /// * `value`: Insertion value. + /// + /// # Returns + /// + /// * `u64`: Node ID of inserted list node. + /// + /// # Aborts + /// + /// `E_TOO_MANY_LIST_NODES`: Too many list nodes allocated. + /// + /// # Testing + /// + /// * `test_insert_list_node_assign_fields_allocate()` + /// * `test_insert_list_node_assign_fields_stacked()` + /// * `test_insert_too_many_list_nodes()` + fun insert_list_node_assign_fields( + avlq_ref_mut: &mut AVLqueue, + last: u64, + next: u64, + value: V + ): u64 { + // Mutably borrow list nodes table. + let list_nodes_ref_mut = &mut avlq_ref_mut.list_nodes; + // Mutably borrow insertion values table. + let values_ref_mut = &mut avlq_ref_mut.values; + // Split last and next arguments into byte fields. + let (last_msbs, last_lsbs, next_msbs, next_lsbs) = ( + ((last >> BITS_PER_BYTE) as u8), ((last & HI_BYTE) as u8), + ((next >> BITS_PER_BYTE) as u8), ((next & HI_BYTE) as u8)); + // Get top of inactive list nodes stack. + let list_node_id = (((avlq_ref_mut.bits >> SHIFT_LIST_STACK_TOP) & + (HI_NODE_ID as u128)) as u64); + // If will need to allocate a new list node: + if (list_node_id == (NIL as u64)) { + // Get new 1-indexed list node ID. + list_node_id = table_with_length::length(list_nodes_ref_mut) + 1; + assert!( // Verify list nodes not over-allocated. + list_node_id <= N_NODES_MAX, E_TOO_MANY_LIST_NODES); + // Allocate a new list node with given fields. + table_with_length::add(list_nodes_ref_mut, list_node_id, ListNode{ + last_msbs, last_lsbs, next_msbs, next_lsbs}); + // Allocate a new list node value option. + table::add(values_ref_mut, list_node_id, option::some(value)); + } else { // If can pop inactive node off stack: + // Mutably borrow inactive node at top of stack. + let node_ref_mut = table_with_length::borrow_mut( + list_nodes_ref_mut, list_node_id); + let new_list_stack_top = // Get new list stack top node ID. + ((node_ref_mut.next_msbs as u128) << BITS_PER_BYTE) | + (node_ref_mut.next_lsbs as u128); + // Reassign bits for inactive list node stack top: + avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_LIST_STACK_TOP)) | + // Mask in new bits. + (new_list_stack_top << SHIFT_LIST_STACK_TOP); + node_ref_mut.last_msbs = last_msbs; // Reassign last MSBs. + node_ref_mut.last_lsbs = last_lsbs; // Reassign last LSBs. + node_ref_mut.next_msbs = next_msbs; // Reassign next MSBs. + node_ref_mut.next_lsbs = next_lsbs; // Reassign next LSBs. + // Mutably borrow empty value option for node ID. + let value_option_ref_mut = + table::borrow_mut(values_ref_mut, list_node_id); + // Fill the empty value option with the insertion value. + option::fill(value_option_ref_mut, value); + }; + list_node_id // Return list node ID. + } + + /// Get virtual last and next fields when inserting a list node. + /// + /// Inner function for `insert_list_node()`. + /// + /// If inserted list node will be the only list node in a doubly + /// linked list, a "solo list node", then it will have to indicate + /// for next and last node IDs a new tree node, which will be + /// inserted via a call to `insert_tree_node()` from within + /// `insert()`. This only happens after `insert()` calls + /// `insert_list_node()` and `insert_list_node()` calls + /// `insert_list_node_assign_fields()`, which verifies that the + /// number of allocated list nodes does not exceed the maximum + /// permissible amount. + /// + /// Since the number of active tree nodes is always less than or + /// equal to the number of active list nodes, it is thus not + /// necessary to check on the number of allocated tree nodes here + /// when getting a new 1-indexed tree node ID: a list node + /// allocation violation precedes a tree node allocation violation, + /// and `insert_list_node_assign_fields()` already checks the + /// number of allocated list nodes during insertion (since the + /// inactive node stack is emptied before allocating new nodes, this + /// means that if the number of allocated list nodes is valid, then + /// the number of allocated tree nodes is also valid). + /// + /// # Parameters + /// + /// * `avlq_ref`: Immutable reference to AVL queue. + /// * `anchor_tree_node_id`: Node ID of anchor tree node, `NIL` if + /// inserting a solo list node. + /// + /// # Returns + /// + /// * `u64`: Virtual last field of inserted list node. + /// * `u64`: Virtual next field of inserted list node. + /// + /// # Testing + /// + /// * `test_insert_list_node_get_last_next_new_tail()` + /// * `test_insert_list_node_get_last_next_solo_allocate()` + /// * `test_insert_list_node_get_last_next_solo_stacked()` + fun insert_list_node_get_last_next( + avlq_ref: &AVLqueue, + anchor_tree_node_id: u64, + ): ( + u64, + u64 + ) { + // Declare bitmask for flagging a tree node. + let is_tree_node = ((BIT_FLAG_TREE_NODE as u64) << SHIFT_NODE_TYPE); + // Immutably borrow tree nodes table. + let tree_nodes_ref = &avlq_ref.tree_nodes; + let last; // Declare virtual last field for inserted list node. + // If inserting a solo list node: + if (anchor_tree_node_id == (NIL as u64)) { + // Get top of inactive tree nodes stack. + anchor_tree_node_id = (((avlq_ref.bits >> SHIFT_TREE_STACK_TOP) & + (HI_NODE_ID as u128)) as u64); + // If will need to allocate a new tree node, get new + // 1-indexed tree node ID. + if (anchor_tree_node_id == (NIL as u64)) anchor_tree_node_id = + table_with_length::length(tree_nodes_ref) + 1; + // Set virtual last field as flagged anchor tree node ID. + last = anchor_tree_node_id | is_tree_node; + } else { // If not inserting a solo list node: + // Immutably borrow anchor tree node. + let anchor_node_ref = table_with_length::borrow( + tree_nodes_ref, anchor_tree_node_id); + // Set virtual last field as anchor node list tail. + last = (((anchor_node_ref.bits >> SHIFT_LIST_TAIL) & + (HI_NODE_ID as u128)) as u64); + }; + // Return virtual last field per above, and virtual next field + // as flagged anchor tree node ID. + (last, (anchor_tree_node_id | is_tree_node)) + } + + /// Insert a tree node and return its node ID. + /// + /// Inner function for `insert()`. + /// + /// If inactive tree node stack is empty, allocate a new tree node, + /// otherwise pop one off the inactive stack. + /// + /// Should only be called when `insert_list_node()` inserts the + /// sole list node in new AVL tree node, thus checking the number + /// of allocated list nodes per `insert_list_node_assign_fields()`. + /// As discussed in `insert_list_node_get_last_next()`, this check + /// verifies the number of allocated tree nodes, since the number + /// of active tree nodes is less than or equal to the number of + /// active list nodes. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `key`: Insertion key for inserted node. + /// * `parent`: Node ID of parent to inserted node, `NIL` when + /// inserted node is to become root. + /// * `solo_node_id`: Node ID of sole list node in tree node's + /// doubly linked list. + /// * `new_leaf_side`: None if inserted node is root, `LEFT` if + /// inserted node is left child of its parent, and `RIGHT` if + /// inserted node is right child of its parent. + /// + /// # Returns + /// + /// * `u64`: Node ID of inserted tree node. + /// + /// # Assumptions + /// + /// * Node is a leaf in the AVL tree and has a single list node in + /// its doubly linked list. + /// * The number of allocated tree nodes has already been checked + /// via `insert_list_node_get_last_next()`. + /// * All `u64` fields correspond to valid node IDs. + /// + /// # Testing + /// + /// * `test_insert_tree_node_empty()` + /// * `test_insert_tree_node_stacked()` + fun insert_tree_node( + avlq_ref_mut: &mut AVLqueue, + key: u64, + parent: u64, + solo_node_id: u64, + new_leaf_side: Option + ): u64 { + // Pack field bits. + let bits = ((key as u128) << SHIFT_INSERTION_KEY) | + ((parent as u128) << SHIFT_PARENT) | + ((solo_node_id as u128) << SHIFT_LIST_HEAD) | + ((solo_node_id as u128) << SHIFT_LIST_TAIL); + // Get top of inactive tree nodes stack. + let tree_node_id = (((avlq_ref_mut.bits >> SHIFT_TREE_STACK_TOP) & + (HI_NODE_ID as u128)) as u64); + // Mutably borrow tree nodes table. + let tree_nodes_ref_mut = &mut avlq_ref_mut.tree_nodes; + // If need to allocate new tree node: + if (tree_node_id == (NIL as u64)) { + // Get new 1-indexed tree node ID. + tree_node_id = table_with_length::length(tree_nodes_ref_mut) + 1; + table_with_length::add( // Allocate new packed tree node. + tree_nodes_ref_mut, tree_node_id, TreeNode{bits}) + } else { // If can pop inactive node off stack: + // Mutably borrow inactive node at top of stack. + let node_ref_mut = table_with_length::borrow_mut( + tree_nodes_ref_mut, tree_node_id); + // Get new inactive tree nodes stack top node ID. + let new_tree_stack_top = node_ref_mut.bits & (HI_NODE_ID as u128); + // Reassign bits for inactive tree node stack top: + avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_TREE_STACK_TOP)) | + // Mask in new bits. + (new_tree_stack_top << SHIFT_TREE_STACK_TOP); + node_ref_mut.bits = bits; // Reassign inserted node bits. + }; + insert_tree_node_update_parent_edge( // Update parent edge. + avlq_ref_mut, tree_node_id, parent, new_leaf_side); + tree_node_id // Return inserted tree node ID. + } + + /// Update the parent edge for a tree node just inserted. + /// + /// Inner function for `insert_tree_node()`. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `tree_node_id`: Node ID of tree node just inserted in + /// `insert_tree_node()`. + /// * `parent`: Node ID of parent to inserted node, `NIL` when + /// inserted node is root. + /// * `new_leaf_side`: None if inserted node is root, `LEFT` if + /// inserted node is left child of its parent, and `RIGHT` if + /// inserted node is right child of its parent. + /// + /// # Testing + /// + /// * `test_insert_tree_node_update_parent_edge_left()` + /// * `test_insert_tree_node_update_parent_edge_right()` + /// * `test_insert_tree_node_update_parent_edge_root()` + fun insert_tree_node_update_parent_edge( + avlq_ref_mut: &mut AVLqueue, + tree_node_id: u64, + parent: u64, + new_leaf_side: Option + ) { + if (option::is_none(&new_leaf_side)) { // If inserting root: + // Reassign bits for root MSBs: + avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID >> BITS_PER_BYTE) as u128)) | + // Mask in new bits. + ((tree_node_id as u128) >> BITS_PER_BYTE); + // Set root LSBs. + avlq_ref_mut.root_lsbs = ((tree_node_id & HI_BYTE) as u8); + } else { // If inserting child to existing node: + // Mutably borrow tree nodes table. + let tree_nodes_ref_mut = &mut avlq_ref_mut.tree_nodes; + // Mutably borrow parent. + let parent_ref_mut = table_with_length::borrow_mut( + tree_nodes_ref_mut, parent); + // Determine if inserting left child. + let left_child = *option::borrow(&new_leaf_side) == LEFT; + // Get child node ID field shift amounts for given side; + let child_shift = if (left_child) SHIFT_CHILD_LEFT else + SHIFT_CHILD_RIGHT; + // Reassign bits for child field on given side. + parent_ref_mut.bits = parent_ref_mut.bits & + // Clear out all bits via mask unset at relevant bits. + (HI_128 ^ ((HI_NODE_ID as u128) << child_shift)) | + // Mask in new bits. + ((tree_node_id as u128) << child_shift); + }; + } + + /// Remove list node for given access key, return insertion value. + /// + /// Inner function for `remove()`. + /// + /// Updates last and next nodes in doubly linked list, optionally + /// updating head or tail field in corresponding tree node if list + /// node was head or tail of doubly linked list. Does not modify + /// corresponding tree node if list node was sole node in doubly + /// linked list. + /// + /// Pushes inactive list node onto inactive list nodes stack. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `list_node_id`: List node ID of node to remove. + /// + /// # Returns + /// + /// * `V`: Corresponding insertion value. + /// * `Option`: New list head node ID, if any, with `NIL` + /// indicating that corresponding doubly linked list has been + /// cleared out. + /// * `Option`: New list tail node ID, if any, with `NIL` + /// indicating that corresponding doubly linked list has been + /// cleared out. + /// + /// # Testing + /// + /// * `test_remove_list_node()` + fun remove_list_node( + avlq_ref_mut: &mut AVLqueue, + list_node_id: u64 + ): ( + V, + Option, + Option + ) { + // Mutably borrow list nodes table. + let list_nodes_ref_mut = &mut avlq_ref_mut.list_nodes; + let list_node_ref_mut = // Mutably borrow list node. + table_with_length::borrow_mut(list_nodes_ref_mut, list_node_id); + // Get virtual last field. + let last = ((list_node_ref_mut.last_msbs as u64) << BITS_PER_BYTE) | + (list_node_ref_mut.last_lsbs as u64); + // Get virtual next field. + let next = ((list_node_ref_mut.next_msbs as u64) << BITS_PER_BYTE) | + (list_node_ref_mut.next_lsbs as u64); + // Determine if last node is flagged as tree node. + let last_is_tree = ((last >> SHIFT_NODE_TYPE) & + (BIT_FLAG_TREE_NODE as u64)) == (BIT_FLAG_TREE_NODE as u64); + // Determine if next node is flagged as tree node. + let next_is_tree = ((next >> SHIFT_NODE_TYPE) & + (BIT_FLAG_TREE_NODE as u64)) == (BIT_FLAG_TREE_NODE as u64); + let last_node_id = last & HI_NODE_ID; // Get last node ID. + let next_node_id = next & HI_NODE_ID; // Get next node ID. + // Get inactive list nodes stack top. + let list_top = (((avlq_ref_mut.bits >> SHIFT_LIST_STACK_TOP) & + (HI_NODE_ID as u128)) as u64); + list_node_ref_mut.last_msbs = 0; // Clear node's last MSBs. + list_node_ref_mut.last_lsbs = 0; // Clear node's last LSBs. + // Set node's next MSBs to those of inactive stack top. + list_node_ref_mut.next_msbs = ((list_top >> BITS_PER_BYTE) as u8); + // Set node's next LSBs to those of inactive stack top. + list_node_ref_mut.next_lsbs = ((list_top & (HI_BYTE as u64)) as u8); + // Reassign bits for inactive list node stack top: + avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_LIST_STACK_TOP)) | + // Mask in new bits. + ((list_node_id as u128) << SHIFT_LIST_STACK_TOP); + // Update node edges, storing optional new head and tail. + let (new_head, new_tail) = remove_list_node_update_edges( + avlq_ref_mut, last, next, last_is_tree, next_is_tree, last_node_id, + next_node_id); + // Mutably borrow insertion values table. + let values_ref_mut = &mut avlq_ref_mut.values; + let value = option::extract( // Extract insertion value. + table::borrow_mut(values_ref_mut, list_node_id)); + // Return insertion value, optional new head, optional new tail. + (value, new_head, new_tail) + } + + /// Update node edges when removing a list node. + /// + /// Inner function for `remove_list_node()`. + /// + /// Update last and next edges relative to removed list node, + /// returning optional new list head and tail list node IDs. If + /// removed list node was sole node in doubly linked list, does not + /// modify corresponding tree node. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `last`: Virtual last field from removed list node. + /// * `next`: Virtual next field from removed list node. + /// * `last_is_tree`: `true` if last node is flagged as tree node. + /// * `next_is_tree`: `true` if next node is flagged as tree node. + /// * `last_node_id`: Node ID of last node. + /// * `next_node_id`: Node ID of next node. + /// + /// # Returns + /// + /// * `Option`: New list head node ID, if any, with `NIL` + /// indicating that corresponding doubly linked list has been + /// cleared out. + /// * `Option`: New list tail node ID, if any, with `NIL` + /// indicating that corresponding doubly linked list has been + /// cleared out. + /// + /// # Testing + /// + /// * `test_remove_list_node()` + fun remove_list_node_update_edges( + avlq_ref_mut: &mut AVLqueue, + last: u64, + next: u64, + last_is_tree: bool, + next_is_tree: bool, + last_node_id: u64, + next_node_id: u64 + ): ( + Option, + Option + ) { + // If node was sole list node in doubly linked list, return that + // the doubly linked list has been cleared out. + if (last_is_tree && next_is_tree) return + (option::some((NIL as u64)), option::some((NIL as u64))); + // Otherwise, assume no new list head or tail. + let (new_head, new_tail) = (option::none(), option::none()); + // Mutably borrow tree nodes table. + let tree_nodes_ref_mut = &mut avlq_ref_mut.tree_nodes; + // Mutably borrow list nodes table. + let list_nodes_ref_mut = &mut avlq_ref_mut.list_nodes; + if (last_is_tree) { // If removed node was list head: + // Mutably borrow corresponding tree node. + let tree_node_ref_mut = table_with_length::borrow_mut( + tree_nodes_ref_mut, last_node_id); + // Reassign bits for list head to next node ID: + tree_node_ref_mut.bits = tree_node_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_LIST_HEAD)) | + // Mask in new bits. + ((next_node_id as u128) << SHIFT_LIST_HEAD); + new_head = option::some(next_node_id); // Flag new head. + } else { // If node was not list head: + // Mutably borrow last list node. + let list_node_ref_mut = table_with_length::borrow_mut( + list_nodes_ref_mut, last_node_id); + // Set node's next MSBs to those of virtual next field. + list_node_ref_mut.next_msbs = ((next >> BITS_PER_BYTE) as u8); + // Set node's next LSBs to those of virtual next field. + list_node_ref_mut.next_lsbs = ((next & (HI_BYTE as u64)) as u8); + }; + if (next_is_tree) { // If removed node was list tail: + // Mutably borrow corresponding tree node. + let tree_node_ref_mut = table_with_length::borrow_mut( + tree_nodes_ref_mut, next_node_id); + // Reassign bits for list tail to last node ID: + tree_node_ref_mut.bits = tree_node_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_LIST_TAIL)) | + // Mask in new bits. + ((last_node_id as u128) << SHIFT_LIST_TAIL); + new_tail = option::some(last_node_id); // Flag new tail. + } else { // If node was not list tail: + // Mutably borrow next list node. + let list_node_ref_mut = table_with_length::borrow_mut( + list_nodes_ref_mut, next_node_id); + // Set node's last MSBs to those of virtual next field. + list_node_ref_mut.last_msbs = ((last >> BITS_PER_BYTE) as u8); + // Set node's last LSBs to those of virtual next field. + list_node_ref_mut.last_lsbs = ((last & (HI_BYTE as u64)) as u8); + }; + (new_head, new_tail) // Return optional new head and tail. + } + + /// Remove tree node from an AVL queue. + /// + /// Inner function for `remove()`. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `node_x_id`: Node ID of node to remove. + /// + /// Here, node x refers to the node to remove from the tree. Node + /// x may have a parent or may be the tree root, and may have 0, 1, + /// or 2 children. + /// + /// > | + /// > x + /// > / \ + /// + /// # Case 1 + /// + /// Node x has no children. Here, the parent to node x gets updated + /// to have a null subtree as its child on the side that node x used + /// to be a child at. If node x has no parent the tree is completely + /// cleared out and no retrace takes place, otherwise a decrement + /// retrace starts from node x's pre-removal parent on the side that + /// node x used to be a child at. + /// + /// # Case 2 + /// + /// Node x has a single child node. Here, the parent to node x gets + /// updated to have node x's sole child as its child on the side + /// that node x used to be a child at. If node x has no parent then + /// the child becomes the root of the tree and no retrace takes + /// place, otherwise a decrement retrace starts from node x's + /// pre-removal parent on the side that node x used to be a child + /// at. + /// + /// ## Left child + /// + /// Pre-removal: + /// + /// > | + /// > x + /// > / + /// > l + /// + /// Post-removal: + /// + /// > | + /// > l + /// + /// ## Right child + /// + /// Pre-removal: + /// + /// > | + /// > x + /// > \ + /// > r + /// + /// Post-removal: + /// + /// > | + /// > r + /// + /// # Case 3 + /// + /// Node x has two children. Handled by + /// `remove_tree_node_with_children()`. + /// + /// # Testing + /// + /// * `test_remove_root_twice()` and `test_rotate_left_2()` test + /// case 1. + /// * `test_rotate_right_left_2()` tests case 2 left child variant. + /// * `test_remove_root_twice()` and `test_rotate_left_right_1()` + /// test case 2 right child variant. + /// * `test_remove_children_1()`, `test_remove_children_2()`, and + /// `test_remove_children_3()` test case 3. + /// + /// See tests for more information on their corresponding reference + /// diagrams. + fun remove_tree_node( + avlq_ref_mut: &mut AVLqueue, + node_x_id: u64 + ) { + let node_x_ref = // Immutably borrow node x. + table_with_length::borrow(&mut avlq_ref_mut.tree_nodes, node_x_id); + let bits = node_x_ref.bits; // Get node x bits. + // Get node x's left height, right height, parent, and children + // fields. + let (node_x_height_left, node_x_height_right, node_x_parent, + node_x_child_left , node_x_child_right) = + ((((bits >> SHIFT_HEIGHT_LEFT ) & (HI_HEIGHT as u128)) as u8), + (((bits >> SHIFT_HEIGHT_RIGHT) & (HI_HEIGHT as u128)) as u8), + (((bits >> SHIFT_PARENT ) & (HI_NODE_ID as u128)) as u64), + (((bits >> SHIFT_CHILD_LEFT ) & (HI_NODE_ID as u128)) as u64), + (((bits >> SHIFT_CHILD_RIGHT ) & (HI_NODE_ID as u128)) as u64)); + // Determine if node x has left child. + let has_child_left = node_x_child_left != (NIL as u64); + // Determine if node x has right child. + let has_child_right = node_x_child_right != (NIL as u64); + // Assume case 1: node x is leaf node replaced by null subtree, + // potentially requiring decrement retrace on side that node x + // was child at (retrace side reassigned later). + let (new_subtree_root, retrace_node_id, retrace_side) = + ((NIL as u64) , node_x_parent , false ); + if (( has_child_left && !has_child_right) || + (!has_child_left && has_child_right)) { // If only 1 child: + new_subtree_root = if (has_child_left) node_x_child_left else + node_x_child_right; // New subtree root is the child. + // Mutably borrow child. + let child_ref_mut = table_with_length::borrow_mut( + &mut avlq_ref_mut.tree_nodes, new_subtree_root); + // Reassign bits for new parent field. + child_ref_mut.bits = child_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_PARENT)) | + // Mask in new bits. + ((node_x_parent as u128) << SHIFT_PARENT); + }; // Case 2 handled. + // If node x has two children, remove node per case 3, storing + // new subtree root, retrace node ID, and retrace side. + if (has_child_left && has_child_right) + (new_subtree_root, retrace_node_id, retrace_side) = + remove_tree_node_with_children( + avlq_ref_mut, node_x_height_left, node_x_height_right, + node_x_parent, node_x_child_left, node_x_child_right); + // Clean up parent edge, optionally retrace, push onto stack. + remove_tree_node_follow_up( + avlq_ref_mut, node_x_id, node_x_parent, new_subtree_root, + retrace_node_id, retrace_side); + } + + /// Clean up parent edge, optionally retrace, push onto stack. + /// + /// Inner function for `remove_tree_node()`, following up on removal + /// of node x. + /// + /// Follow up on tree node removal re-ordering operations, updating + /// parent to node x (if there is one). Retrace as needed, then push + /// node x onto the inactive tree nodes stack. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `node_x_id`: Node ID of removed node. + /// * `node_x_parent`: Parent field of node x before it was removed, + /// `NIL` if x was root. + /// * `new_subtree_root`: New root of subtree where node x was root + /// pre-removal, `NIL` if node x was a leaf node. + /// * `retrace_node_id`: Node ID to retrace from, `NIL` if node x + /// was at the root and had less than two children before it was + /// removed. + /// * `retrace_side`: Side of decrement retrace for the case of node + /// x having two children, reassigned if node x was not at root + /// before removal and had less than two children. + /// + /// # Testing + /// + /// * `test_remove_root_twice()`, `test_remove_children_2()`, and + /// `test_remove_children_3()` test node x is tree root. + /// * `test_rotate_left_2()` and `test_rotate_right_left_2()` test + /// node x is left child. + /// * `test_remove_children_1()` and `test_rotate_left_right_1()` + /// test node x is right child. + /// * `test_rotate_left_2()` and `test_rotate_right_left_2()` test + /// retrace node ID is node x's parent, for node x is left child + /// and has less than two children. + /// * `test_rotate_left_right_1()` tests retrace node ID is node + /// x's parent, for node x is right child and has less than two + /// children. + /// * `test_remove_children_1()` tests retrace node ID is not node + /// x's parent, for node x is not root and has two children. + /// * `test_remove_root_twice()` tests node x as root with less than + /// two children, where retrace node ID is null and no retrace + /// needed. + /// * Above tests vary whether the inactive tree node stack is empty + /// before removal, with `test_remove_root_twice()` in particular + /// covering both cases. + fun remove_tree_node_follow_up( + avlq_ref_mut: &mut AVLqueue, + node_x_id: u64, + node_x_parent: u64, + new_subtree_root: u64, + retrace_node_id: u64, + retrace_side: bool + ) { + if (node_x_parent == (NIL as u64)) { // If node x was tree root: + // Reassign bits for root MSBs: + avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) >> BITS_PER_BYTE)) | + // Mask in new bits. + ((new_subtree_root as u128) >> BITS_PER_BYTE); + avlq_ref_mut.root_lsbs = // Set AVL queue root LSBs. + ((new_subtree_root & HI_BYTE) as u8); + } else { // If node x was not root: + // Mutably borrow node x's parent. + let parent_ref_mut = table_with_length::borrow_mut( + &mut avlq_ref_mut.tree_nodes, node_x_parent); + // Get parent's left child. + let parent_left_child = (((parent_ref_mut.bits >> SHIFT_CHILD_LEFT) + & (HI_NODE_ID as u128)) as u64); + // Get child shift based on node x's side as a child. + let child_shift = if (parent_left_child == node_x_id) + SHIFT_CHILD_LEFT else SHIFT_CHILD_RIGHT; + // Reassign bits for new child field. + parent_ref_mut.bits = parent_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << child_shift)) | + // Mask in new bits. + ((new_subtree_root as u128) << child_shift); + // If retrace node id is node x's parent, then node x had + // less than two children before removal, so retrace side + // is the side on which node x was previously a child. + if (retrace_node_id == node_x_parent) retrace_side = + if (parent_left_child == node_x_id) LEFT else RIGHT; + }; // Parent edge updated, retrace side assigned if needed. + // Retrace if node x was not root having less than two children. + if (retrace_node_id != (NIL as u64)) + retrace(avlq_ref_mut, retrace_node_id, DECREMENT, retrace_side); + // Get inactive tree nodes stack top. + let tree_top = (((avlq_ref_mut.bits >> SHIFT_TREE_STACK_TOP) & + (HI_NODE_ID as u128)) as u64); + // Mutably borrow node x. + let node_x_ref_mut = table_with_length::borrow_mut( + &mut avlq_ref_mut.tree_nodes, node_x_id); + // Set node x to indicate the next inactive tree node in stack. + node_x_ref_mut.bits = (tree_top as u128); + // Reassign bits for inactive tree node stack top: + avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_TREE_STACK_TOP)) | + // Mask in new bits. + ((node_x_id as u128) << SHIFT_TREE_STACK_TOP); + } + + /// Replace node x with its predecessor in preparation for retrace. + /// + /// Inner function for `remove_tree_node()` in the case of removing + /// a node with two children. Here, node x is the node to remove, + /// having left child node l and right child node r. + /// + /// > | + /// > x + /// > / \ + /// > l r + /// + /// Does not modify state of node x, which is updated later via + /// `remove_tree_node_follow_up()`. Similarly does not modify state + /// for parent of node x or of AVL queue root field. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `node_x_height_left`: Node x's left height. + /// * `node_x_height_right`: Node x's right height. + /// * `node_x_parent`: Node x's parent field. + /// * `node_l_id`: Node ID of node x's left child. + /// * `node_r_id`: Node ID of node x's right child. + /// + /// # Returns + /// + /// * `u64`: Node ID of new root subtree where node x was root + /// pre-removal. + /// * `u64`: Node ID of node to begin decrement retrace from in + /// `remove_tree_node_follow_up()`. + /// * `bool`: `LEFT` or `RIGHT`, the side on which the decrement + /// retrace should take place. + /// + /// # Predecessor is immediate child + /// + /// Node l does not have a right child, but has left child tree l + /// which may or may not be empty. + /// + /// > | + /// > x + /// > / \ + /// > l r + /// > / + /// > t_l + /// + /// Here, node l takes the place of node x, with node l's left + /// height and right height set to those of node x pre-removal. Then + /// a left decrement retrace is initiated at node l. + /// + /// > | + /// > l + /// > / \ + /// > t_l r + /// + /// # Predecessor is not immediate child + /// + /// Node l has a right child, with node y as the maximum node in the + /// corresponding subtree. Node y has no right child, but has as its + /// left child tree y, which may or may not be empty. Node y may or + /// may not have node l as its parent. + /// + /// > | + /// > x + /// > / \ + /// > l r + /// > / \ + /// > t_l ~ + /// > \ + /// > y + /// > / + /// > t_y + /// + /// Here, node y takes the place of node x, with node y's left + /// height and right height set to those of node x pre-removal. Tree + /// y then takes the place of y, and a right decrement retrace is + /// initiated at node y's pre-removal parent. + /// + /// > | + /// > y + /// > / \ + /// > l r + /// > / \ + /// > t_l ~ + /// > \ + /// > t_y + /// + /// # Reference diagrams + /// + /// ## Case 1 + /// + /// * Node x is not root. + /// * Node x has insertion key 4. + /// * Predecessor is node x's child. + /// * Node l has insertion key 3. + /// + /// Pre-removal: + /// + /// > 2 + /// > / \ + /// > 1 4 <- node x + /// > / \ + /// > node l -> 3 5 <- node r + /// + /// Post-removal: + /// + /// > 2 + /// > / \ + /// > 1 3 + /// > \ + /// > 5 + /// + /// ## Case 2 + /// + /// * Node x is root. + /// * Node x has insertion key 5. + /// * Predecessor is not node x's child. + /// * Predecessor is node l's child. + /// * Node y has insertion key 4. + /// * Subtree y is not empty. + /// + /// Pre-removal: + /// + /// > 5 <- node x + /// > / \ + /// > node l -> 2 6 <- node r + /// > / \ \ + /// > tree l -> 1 4 7 + /// > / + /// > tree y -> 3 + /// + /// Post-removal: + /// + /// > 4 + /// > / \ + /// > 2 6 + /// > / \ \ + /// > 1 3 7 + /// + /// ## Case 3 + /// + /// * Node x is root. + /// * Node x has insertion key 5. + /// * Predecessor is not node x's child. + /// * Predecessor is not node l's child. + /// * Node y has insertion key 4. + /// * Subtree y is empty. + /// + /// Pre-removal: + /// + /// > 5 <- node x + /// > / \ + /// > node l -> 2 6 <- node r + /// > / \ \ + /// > tree l -> 1 3 7 + /// > \ + /// > 4 <- node y + /// + /// Post-removal: + /// + /// > 4 + /// > / \ + /// > 2 6 + /// > / \ \ + /// > 1 3 7 + /// + /// # Testing + /// + /// * `test_remove_children_1()` + /// * `test_remove_children_2()` + /// * `test_remove_children_3()` + fun remove_tree_node_with_children( + avlq_ref_mut: &mut AVLqueue, + node_x_height_left: u8, + node_x_height_right: u8, + node_x_parent: u64, + node_l_id: u64, + node_r_id: u64, + ): ( + u64, + u64, + bool + ) { + // Declare returns. + let (new_subtree_root, retrace_node_id, retrace_side); + // Mutably borrow tree nodes table. + let tree_nodes_ref_mut = &mut avlq_ref_mut.tree_nodes; + let node_l_ref_mut = // Mutably borrow node l. + table_with_length::borrow_mut(tree_nodes_ref_mut, node_l_id); + let bits = node_l_ref_mut.bits; // Get node l bits. + let node_l_child_right = // Get node l's right child field. + (((bits >> SHIFT_CHILD_RIGHT) & (HI_NODE_ID as u128)) as u64); + // If node l has no right child (if is immediate predecessor): + if (node_l_child_right == (NIL as u64)) { + // Reassign node l bits for parent, heights, right child. + node_l_ref_mut.bits = node_l_ref_mut.bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ (((HI_HEIGHT as u128) << SHIFT_HEIGHT_LEFT) | + ((HI_HEIGHT as u128) << SHIFT_HEIGHT_RIGHT) | + ((HI_NODE_ID as u128) << SHIFT_PARENT) | + ((HI_NODE_ID as u128) << SHIFT_CHILD_RIGHT))) | + // Mask in new bits. + ((node_x_height_left as u128) << SHIFT_HEIGHT_LEFT) | + ((node_x_height_right as u128) << SHIFT_HEIGHT_RIGHT) | + ((node_x_parent as u128) << SHIFT_PARENT) | + ((node_r_id as u128) << SHIFT_CHILD_RIGHT); + // Assign returns accordingly. + (new_subtree_root, retrace_node_id, retrace_side) = + ( node_l_id, node_l_id, LEFT); + } else { // If node x predecessor is in node l's right subtree: + // Assign node l's right child as a candidate for node y. + let node_y_id = node_l_child_right; + let node_y_ref_mut; // Declare mutable reference to node y. + loop { // Loop down node l's right subtree + // Mutably borrow node y candidate. + node_y_ref_mut = table_with_length::borrow_mut( + tree_nodes_ref_mut, node_y_id); + let child_right = // Get candidate's right child field. + (((node_y_ref_mut.bits >> SHIFT_CHILD_RIGHT) & + (HI_NODE_ID as u128)) as u64); + // Break if no right child, since have found node y. + if (child_right == (NIL as u64)) break; + // Otherwise child is candidate for new iteration. + node_y_id = child_right; + }; // Node y found. + let bits = node_y_ref_mut.bits; // Get node y bits. + // Get node y's parent ID and tree y ID. + let (node_y_parent_id, tree_y_id) = + ((((bits >> SHIFT_PARENT ) & (HI_NODE_ID as u128)) as u64), + (((bits >> SHIFT_CHILD_LEFT) & (HI_NODE_ID as u128)) as u64)); + // Reassign node y bits for parent, heights, children. + node_y_ref_mut.bits = bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ (((HI_HEIGHT as u128) << SHIFT_HEIGHT_LEFT) | + ((HI_HEIGHT as u128) << SHIFT_HEIGHT_RIGHT) | + ((HI_NODE_ID as u128) << SHIFT_PARENT) | + ((HI_NODE_ID as u128) << SHIFT_CHILD_LEFT) | + ((HI_NODE_ID as u128) << SHIFT_CHILD_RIGHT))) | + // Mask in new bits. + ((node_x_height_left as u128) << SHIFT_HEIGHT_LEFT) | + ((node_x_height_right as u128) << SHIFT_HEIGHT_RIGHT) | + ((node_x_parent as u128) << SHIFT_PARENT) | + ((node_l_id as u128) << SHIFT_CHILD_LEFT) | + ((node_r_id as u128) << SHIFT_CHILD_RIGHT); + // Mutably borrow node y's parent. + let node_y_parent_ref_mut = table_with_length::borrow_mut( + tree_nodes_ref_mut, node_y_parent_id); + // Reassign bits for parent's right child field: + node_y_parent_ref_mut.bits = node_y_parent_ref_mut.bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_CHILD_RIGHT)) | + // Mask in new bits. + ((tree_y_id as u128) << SHIFT_CHILD_RIGHT); + // Mutably borrow node l, which may be node y's parent. + node_l_ref_mut = if (node_y_parent_id == node_l_id) + node_y_parent_ref_mut else table_with_length::borrow_mut( + tree_nodes_ref_mut, node_l_id); + // Reassign bits for node l's parent field: + node_l_ref_mut.bits = node_l_ref_mut.bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_PARENT)) | + // Mask in new bits. + ((node_y_id as u128) << SHIFT_PARENT); + if (tree_y_id != (NIL as u64)) { // If tree y not null: + // Mutably borrow tree y's root. + let tree_y_ref_mut = table_with_length::borrow_mut( + tree_nodes_ref_mut, tree_y_id); + // Reassign bits for corresponding parent field: + tree_y_ref_mut.bits = tree_y_ref_mut.bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_PARENT)) | + // Mask in new bits. + ((node_y_parent_id as u128) << SHIFT_PARENT); + }; + // Assign returns accordingly. + (new_subtree_root, retrace_node_id, retrace_side) = + ( node_y_id, node_y_parent_id, RIGHT); + }; + let node_r_ref_mut = // Mutably borrow node r. + table_with_length::borrow_mut(tree_nodes_ref_mut, node_r_id); + // Reassign bits for node r parent field: + node_r_ref_mut.bits = node_r_ref_mut.bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_PARENT)) | + // Mask in new bits. + ((new_subtree_root as u128) << SHIFT_PARENT); + // Return new subtree root, node ID to retrace from, side to + // retrace on. + (new_subtree_root, retrace_node_id, retrace_side) + } + + /// Update AVL queue head during removal. + /// + /// Inner function for `remove()`, should only be called if AVL + /// queue head is modified. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `new_list_head`: New head of corresponding doubly linked list, + /// `NIL` if doubly linked list is cleared out by removal. + /// * `ascending`: `true` if ascending AVL queue, else `false`. + /// * `tree_node_id`: Node ID of corresponding tree node. + /// + /// # Testing + /// + /// * `test_remove_1()` and `test_remove_3()` test new list head is + /// a list node ID. + /// * `test_remove_1()` and `test_remove_3()` test new list head is + /// not a list node ID, for traversal where start node is not sole + /// leaf at root. + /// * `test_remove_1()` tests ascending AVL queue. + /// * `test_remove_3()` tests descending AVL queue. + /// * `test_remove_root()` tests new list head is not a list node + /// ID, for traversal where start node is sole leaf at root. + fun remove_update_head( + avlq_ref_mut: &mut AVLqueue, + new_list_head: u64, + ascending: bool, + tree_node_id: u64 + ) { + // Declare new AVL queue head node ID. + let new_avlq_head_node_id; + // If new list head is a list node ID: + if (new_list_head != (NIL as u64)) { + // Then it becomes the new AVL queue head node ID. + new_avlq_head_node_id = new_list_head; + // Otherwise, if new list head is null, then just cleared out + // sole list node in a doubly linked list: + } else { + // Declare target tree node to traverse to. + let target = if (ascending) SUCCESSOR else PREDECESSOR; + // Declare new AVL queue head insertion key. + let new_avlq_head_insertion_key; + // Get new AVL queue head insertion key and node ID by + // traversing to corresponding tree node (both null if start + // node is sole leaf at root). + (new_avlq_head_insertion_key, new_avlq_head_node_id, _) + = traverse(avlq_ref_mut, tree_node_id, target); + // Reassign bits for AVL queue head insertion key: + avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_INSERTION_KEY as u128) << SHIFT_HEAD_KEY)) | + // Mask in new bits. + ((new_avlq_head_insertion_key as u128) << SHIFT_HEAD_KEY); + }; + // Reassign bits for AVL queue head node ID: + avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_HEAD_NODE_ID)) | + // Mask in new bits. + ((new_avlq_head_node_id as u128) << SHIFT_HEAD_NODE_ID); + } + + /// Update AVL queue tail during removal. + /// + /// Inner function for `remove()`, should only be called if AVL + /// queue tail is modified. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `new_list_tail`: New tail of corresponding doubly linked list, + /// `NIL` if doubly linked list is cleared out by removal. + /// * `ascending`: `true` if ascending AVL queue, else `false`. + /// * `tree_node_id`: Node ID of corresponding tree node. + /// + /// # Testing + /// + /// * `test_remove_2()` and `test_remove_4()` test new list tail is + /// a list node ID. + /// * `test_remove_2()` and `test_remove_4()` test new list tail is + /// not a list node ID, for traversal where start node is not sole + /// leaf at root. + /// * `test_remove_2()` tests ascending AVL queue. + /// * `test_remove_4()` tests descending AVL queue. + /// * `test_remove_root()` tests new list tail is not a list node + /// ID, for traversal where start node is sole leaf at root. + fun remove_update_tail( + avlq_ref_mut: &mut AVLqueue, + new_list_tail: u64, + ascending: bool, + tree_node_id: u64 + ) { + // Declare new AVL queue tail node ID. + let new_avlq_tail_node_id; + // If new list tail is a list node ID: + if (new_list_tail != (NIL as u64)) { + // Then it becomes the new AVL queue tail node ID. + new_avlq_tail_node_id = new_list_tail; + // Otherwise, if new list tail is null, then just cleared out + // sole list node in a doubly linked list: + } else { + // Declare target tree node to traverse to. + let target = if (ascending) PREDECESSOR else SUCCESSOR; + // Declare new AVL queue tail insertion key. + let new_avlq_tail_insertion_key; + // Get new AVL queue tail insertion key and node ID by + // traversing to corresponding tree node (both null if start + // node is sole leaf at root). + (new_avlq_tail_insertion_key, _, new_avlq_tail_node_id) + = traverse(avlq_ref_mut, tree_node_id, target); + // Reassign bits for AVL queue tail insertion key: + avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_INSERTION_KEY as u128) << SHIFT_TAIL_KEY)) | + // Mask in new bits. + ((new_avlq_tail_insertion_key as u128) << SHIFT_TAIL_KEY); + }; + // Reassign bits for AVL queue tail node ID: + avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_TAIL_NODE_ID)) | + // Mask in new bits. + ((new_avlq_tail_node_id as u128) << SHIFT_TAIL_NODE_ID); + } + + /// Retrace ancestor heights after tree node insertion or removal. + /// + /// Should only be called by `insert()` or + /// `remove_tree_node_follow_up()`. + /// + /// When a tree node is inserted or removed, a parent-child edge is + /// updated with corresponding node IDs for both parent and optional + /// child. Then the corresponding change in height at the parent + /// node, on the affected side, must be updated, along with any + /// affected heights up to the root. If the process results in an + /// imbalance of more than one between the left height and right + /// height of a node in the ancestor chain, the corresponding + /// subtree must be rebalanced. + /// + /// Parent-child edge updates are handled in `insert_tree_node()` + /// and `remove_tree_node()`, while the height retracing process is + /// handled here. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `node_id` : Node ID of affected tree node. + /// * `operation`: `INCREMENT` or `DECREMENT`, the change in height + /// on the affected side. + /// * `side`: `LEFT` or `RIGHT`, the side on which the height is + /// affected. + /// + /// # Testing + /// + /// Tests are designed to evaluate both true and false outcomes for + /// all logical branches, with each relevant test covering multiple + /// conditional branches, optionally via a retrace back to the root. + /// + /// See `test_rotate_right_1()` and `test_rotate_left_2()` for more + /// information on their corresponding reference diagrams. + /// + /// `if (height_left != height_right)` + /// + /// | Exercises `true` | Exercises `false` | + /// |------------------------|--------------------------------| + /// | `test_rotate_left_2()` | `test_retrace_insert_remove()` | + /// + /// `if (height_left > height_right)` + /// + /// | Exercises `true` | Exercises `false` | + /// |-------------------------|------------------------| + /// | `test_rotate_right_1()` | `test_rotate_left_2()` | + /// + /// `if (imbalance > 1)` + /// + /// | Exercises `true` | Exercises `false` | + /// |------------------------|--------------------------------| + /// | `test_rotate_left_2()` | `test_retrace_insert_remove()` | + /// + /// `if (left_heavy)` + /// + /// | Exercises `true` | Exercises `false` | + /// |-------------------------|------------------------| + /// | `test_rotate_right_1()` | `test_rotate_left_2()` | + /// + /// `if (parent == (NIL as u64))` + /// + /// | Exercises `true` | Exercises `false` | + /// |-------------------------|------------------------| + /// | `test_rotate_right_1()` | `test_rotate_left_2()` | + /// + /// `if (new_subtree_root != (NIL as u64))` + /// + /// | Exercises `true` | Exercises `false` | + /// |-------------------------|--------------------------------| + /// | `test_rotate_right_1()` | `test_retrace_insert_remove()` | + /// + /// `if (delta == 0)` + /// + /// | Exercises `true` | Exercises `false` | + /// |------------------------|--------------------------------| + /// | `test_rotate_left_2()` | `test_retrace_insert_remove()` | + /// + /// Assorted tests indexed at `remove_tree_node_follow_up()` + /// additionally exercise retracing logic. + /// + /// ## Reference diagram + /// + /// For `test_retrace_insert_remove()`, insert node d and retrace + /// from node c, then remove node d and retrace from c again. + /// + /// Pre-insertion: + /// + /// > 4 + /// > / \ + /// > 3 5 + /// + /// Pre-removal: + /// + /// > node b -> 4 + /// > / \ + /// > node a -> 3 5 <- node c + /// > \ + /// > 6 <- node d + /// + /// Post-removal: + /// + /// > 4 + /// > / \ + /// > 3 5 + fun retrace( + avlq_ref_mut: &mut AVLqueue, + node_id: u64, + operation: bool, + side: bool + ) { + let delta = 1; // Mark height change of one for first iteration. + // Mutably borrow tree nodes table. + let nodes_ref_mut = &mut avlq_ref_mut.tree_nodes; + // Mutably borrow node under consideration. + let node_ref_mut = + table_with_length::borrow_mut(nodes_ref_mut, node_id); + loop { + // Get parent field of node under review. + let parent = (((node_ref_mut.bits >> SHIFT_PARENT) & + (HI_NODE_ID as u128)) as u64); + let (height_left, height_right, height, height_old) = + retrace_update_heights(node_ref_mut, side, operation, delta); + // Flag no rebalancing via null new subtree root. + let new_subtree_root = (NIL as u64); + if (height_left != height_right) { // If node not balanced: + // Determine if node is left-heavy, and calculate the + // imbalance of the node (the difference in height + // between node's two subtrees). + let (left_heavy, imbalance) = if (height_left > height_right) + (true, height_left - height_right) else + (false, height_right - height_left); + if (imbalance > 1) { // If imbalance greater than 1: + // Get shift amount for child on heavy side. + let child_shift = if (left_heavy) SHIFT_CHILD_LEFT else + SHIFT_CHILD_RIGHT; + // Get child ID from node bits. + let child_id = (((node_ref_mut.bits >> child_shift) & + (HI_NODE_ID as u128)) as u64); + // Rebalance, storing node ID of new subtree root + // and new subtree height. + (new_subtree_root, height) = retrace_rebalance( + avlq_ref_mut, node_id, child_id, left_heavy); + }; + }; // Corresponding subtree has been optionally rebalanced. + if (parent == (NIL as u64)) { // If just retraced root: + // If just rebalanced at root: + if (new_subtree_root != (NIL as u64)) { + // Reassign bits for root MSBs: + avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) >> BITS_PER_BYTE)) | + // Mask in new bits. + ((new_subtree_root as u128) >> BITS_PER_BYTE); + avlq_ref_mut.root_lsbs = // Set AVL queue root LSBs. + ((new_subtree_root & HI_BYTE) as u8); + }; // AVL queue root now current for actual root. + return // Stop looping. + } else { // If just retraced node not at root: + // Prepare to optionally iterate again. + (node_ref_mut, operation, side, delta) = + retrace_prep_iterate(avlq_ref_mut, parent, node_id, + new_subtree_root, height, height_old); + // Return if current iteration did not result in height + // change for corresponding subtree. + if (delta == 0) return; + // Store parent ID as node ID for next iteration. + node_id = parent; + }; + } + } + + /// Prepare for an optional next retrace iteration. + /// + /// Inner function for `retrace()`, should only be called if just + /// retraced below the root of the AVL queue. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `parent_id`: Node ID of next ancestor in retrace chain. + /// * `node_id`: Node ID at root of subtree just retraced, before + /// any optional rebalancing took place. + /// * `new_subtree_root`: Node ID of new subtree root for when + /// rebalancing took place, `NIL` if no rebalancing. + /// * `height`: Height of subtree after retrace. + /// * `height_old`: Height of subtree before retrace. + /// + /// # Returns + /// + /// * `&mut TreeNode`: Mutable reference to next ancestor. + /// * `bool`: `INCREMENT` or `DECREMENT`, the change in height for + /// the subtree just retraced. Evaluates to `DECREMENT` when + /// height does not change. + /// * `bool`: `LEFT` or `RIGHT`, the side on which the retraced + /// subtree was a child to the next ancestor. + /// * `u8`: Change in height of subtree due to retrace, evaluates to + /// 0 when height does not change. + /// + /// # Testing + /// + /// * `test_retrace_prep_iterate_1()` + /// * `test_retrace_prep_iterate_2()` + /// * `test_retrace_prep_iterate_3()` + /// + /// ## Case 1 + /// + /// * Side is `LEFT`. + /// * Subtree rebalanced. + /// * Operation is `DECREMENT`. + /// * Actual change in height. + /// + /// ## Case 2 + /// + /// * Side is `RIGHT`. + /// * Subtree rebalanced. + /// * Operation is `DECREMENT`. + /// * No change in height. + /// + /// ## Case 3 + /// + /// * Side is `RIGHT`. + /// * Subtree not rebalanced. + /// * Operation is `INCREMENT`. + fun retrace_prep_iterate( + avlq_ref_mut: &mut AVLqueue, + parent_id: u64, + node_id: u64, + new_subtree_root: u64, + height: u8, + height_old: u8, + ): ( + &mut TreeNode, + bool, + bool, + u8 + ) { + // Mutably borrow tree nodes table. + let nodes_ref_mut = &mut avlq_ref_mut.tree_nodes; + // Mutably borrow parent to subtree just retraced. + let node_ref_mut = + table_with_length::borrow_mut(nodes_ref_mut, parent_id); + // Get parent's left child. + let left_child = ((node_ref_mut.bits >> SHIFT_CHILD_LEFT) & + (HI_NODE_ID as u128) as u64); + // Flag side on which retracing operation took place. + let side = if (left_child == node_id) LEFT else RIGHT; + // If subtree rebalanced: + if (new_subtree_root != (NIL as u64)) { + // Get corresponding child field shift amount. + let child_shift = if (side == LEFT) + SHIFT_CHILD_LEFT else SHIFT_CHILD_RIGHT; + // Reassign bits for new child field. + node_ref_mut.bits = node_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << child_shift)) | + // Mask in new bits. + ((new_subtree_root as u128) << child_shift) + }; // Parent-child edge updated. + // Determine retrace operation type and height delta. + let (operation, delta) = if (height > height_old) + (INCREMENT, height - height_old) else + (DECREMENT, height_old - height); + // Return mutable reference to parent node, operation performed, + // side of operation, and corresponding change in height. + (node_ref_mut, operation, side, delta) + } + + /// Rebalance a subtree, returning new root and height. + /// + /// Inner function for `retrace()`. + /// + /// Updates state for nodes in subtree, but not for potential parent + /// to subtree. + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `node_id_x`: Node ID of subtree root. + /// * `node_id_z`: Node ID of child to subtree root, on subtree + /// root's heavy side. + /// * `node_x_left_heavy`: `true` if node x is left-heavy. + /// + /// # Returns + /// + /// * `u64`: Tree node ID of new subtree root after rotation. + /// * `u8`: Height of subtree after rotation. + /// + /// # Node x status + /// + /// Node x can be either left-heavy or right heavy. In either case, + /// consider that node z has left child and right child fields. + /// + /// ## Node x left-heavy + /// + /// > n_x + /// > / + /// > n_z + /// > / \ + /// > z_c_l z_c_r + /// + /// ## Node x right-heavy + /// + /// > n_x + /// > \ + /// > n_z + /// > / \ + /// > z_c_l z_c_r + /// + /// # Testing + /// + /// * `test_rotate_left_1()` + /// * `test_rotate_left_2()` + /// * `test_rotate_left_right_1()` + /// * `test_rotate_left_right_2()` + /// * `test_rotate_right_1()` + /// * `test_rotate_right_2()` + /// * `test_rotate_right_left_1()` + /// * `test_rotate_right_left_2()` + fun retrace_rebalance( + avlq_ref_mut: &mut AVLqueue, + node_x_id: u64, + node_z_id: u64, + node_x_left_heavy: bool, + ): ( + u64, + u8 + ) { + let node_z_ref = // Immutably borrow node z. + table_with_length::borrow(&avlq_ref_mut.tree_nodes, node_z_id); + let bits = node_z_ref.bits; // Get node z bits. + // Get node z's left height, right height, and child fields. + let (node_z_height_left, node_z_height_right, + node_z_child_left , node_z_child_right ) = + ((((bits >> SHIFT_HEIGHT_LEFT ) & (HI_HEIGHT as u128)) as u8), + (((bits >> SHIFT_HEIGHT_RIGHT) & (HI_HEIGHT as u128)) as u8), + (((bits >> SHIFT_CHILD_LEFT ) & (HI_NODE_ID as u128)) as u64), + (((bits >> SHIFT_CHILD_RIGHT ) & (HI_NODE_ID as u128)) as u64)); + // Return result of rotation. If node x is left-heavy: + return (if (node_x_left_heavy) + // If node z is right-heavy, rotate left-right + (if (node_z_height_right > node_z_height_left) + retrace_rebalance_rotate_left_right( + avlq_ref_mut, node_x_id, node_z_id, node_z_child_right, + node_z_height_left) + // Otherwise node z is not right-heavy so rotate right. + else retrace_rebalance_rotate_right( + avlq_ref_mut, node_x_id, node_z_id, node_z_child_right, + node_z_height_right)) + else // If node x is right-heavy: + // If node z is left-heavy, rotate right-left + (if (node_z_height_left > node_z_height_right) + retrace_rebalance_rotate_right_left( + avlq_ref_mut, node_x_id, node_z_id, node_z_child_left, + node_z_height_right) + // Otherwise node z is not left-heavy so rotate left. + else retrace_rebalance_rotate_left( + avlq_ref_mut, node_x_id, node_z_id, node_z_child_left, + node_z_height_left))) + } + + /// Rotate left during rebalance. + /// + /// Inner function for `retrace_rebalance()`. + /// + /// Updates state for nodes in subtree, but not for potential parent + /// to subtree. + /// + /// Here, subtree root node x is right-heavy, with right child + /// node z that is not left-heavy. Node x has an optional tree 1 + /// as its left child subtree, and node z has optional trees 2 and + /// 3 as its left and right child subtrees, respectively. + /// + /// Pre-rotation: + /// + /// > n_x + /// > / \ + /// > t_1 n_z + /// > / \ + /// > t_2 t_3 + /// + /// Post-rotation: + /// + /// > n_z + /// > / \ + /// > n_x t_3 + /// > / \ + /// > t_1 t_2 + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `node_x_id`: Node ID of subtree root pre-rotation. + /// * `node_z_id`: Node ID of subtree root post-rotation. + /// * `tree_2_id`: Node z's left child field. + /// * `node_z_height_left`: Node z's left height. + /// + /// # Returns + /// + /// * `u64`: Node z's ID. + /// * `u8`: The height of the subtree rooted at node z, + /// post-rotation. + /// + /// # Reference rotations + /// + /// ## Case 1 + /// + /// * Tree 2 null. + /// * Node x left height greater than or equal to right height + /// post-rotation. + /// * Node z right height greater than or equal to left height + /// post-rotation. + /// + /// Pre-rotation: + /// + /// > 4 <- node x + /// > \ + /// > 6 <- node z + /// > \ + /// > 8 <- tree 3 + /// + /// Post-rotation: + /// + /// > 6 <- node z + /// > / \ + /// > node x -> 4 8 <- tree 3 + /// + /// ## Case 2 + /// + /// * Tree 2 not null. + /// * Node x left height not greater than or equal to right height + /// post-rotation. + /// * Node z right height not greater than or equal to left height + /// post-rotation. + /// * Remove node d, then retrace from node x. + /// + /// Pre-removal: + /// + /// > 3 <- node a + /// > / \ + /// > node b -> 2 5 + /// > / / \ + /// > node c -> 1 4 7 + /// > node d ^ / \ + /// > 6 8 + /// + /// Pre-rotation: + /// + /// > 3 + /// > / \ + /// > 2 5 <- node x + /// > / \ + /// > 1 7 <- node z + /// > / \ + /// > tree 2 -> 6 8 <- tree 3 + /// + /// Post-rotation: + /// + /// > 3 + /// > / \ + /// > 2 7 <- node z + /// > / / \ + /// > 1 5 8 <- tree 3 + /// > \ + /// > 6 <- tree 2 + /// + /// # Testing + /// + /// * `test_rotate_left_1()` + /// * `test_rotate_left_2()` + fun retrace_rebalance_rotate_left( + avlq_ref_mut: &mut AVLqueue, + node_x_id: u64, + node_z_id: u64, + tree_2_id: u64, + node_z_height_left: u8 + ): ( + u64, + u8 + ) { + // Mutably borrow tree nodes table. + let nodes_ref_mut = &mut avlq_ref_mut.tree_nodes; + if (tree_2_id != (NIL as u64)) { // If tree 2 is not empty: + let tree_2_ref_mut = // Mutably borrow tree 2 root. + table_with_length::borrow_mut(nodes_ref_mut, tree_2_id); + // Reassign bits for new parent field: + tree_2_ref_mut.bits = tree_2_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_PARENT)) | + // Mask in new bits. + ((node_x_id as u128) << SHIFT_PARENT); + }; + let node_x_ref_mut = // Mutably borrow node x. + table_with_length::borrow_mut(nodes_ref_mut, node_x_id); + let node_x_height_left = (((node_x_ref_mut.bits >> SHIFT_HEIGHT_LEFT) & + (HI_HEIGHT as u128)) as u8); // Get node x left height. + // Node x's right height is from transferred tree 2. + let node_x_height_right = node_z_height_left; + let node_x_parent = (((node_x_ref_mut.bits >> SHIFT_PARENT) & + (HI_NODE_ID as u128)) as u64); // Get node x parent field. + // Reassign bits for right child, right height, and parent: + node_x_ref_mut.bits = node_x_ref_mut.bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ (((HI_NODE_ID as u128) << SHIFT_CHILD_RIGHT) | + ((HI_HEIGHT as u128) << SHIFT_HEIGHT_RIGHT) | + ((HI_NODE_ID as u128) << SHIFT_PARENT))) | + // Mask in new bits. + ((tree_2_id as u128) << SHIFT_CHILD_RIGHT) | + ((node_x_height_right as u128) << SHIFT_HEIGHT_RIGHT) | + ((node_z_id as u128) << SHIFT_PARENT); + // Determine height of tree rooted at x. + let node_x_height = if (node_x_height_left >= node_x_height_right) + node_x_height_left else node_x_height_right; + // Get node z left height. + let node_z_height_left = node_x_height + 1; + let node_z_ref_mut = // Mutably borrow node z. + table_with_length::borrow_mut(nodes_ref_mut, node_z_id); + // Reassign bits for left child, left height, and parent: + node_z_ref_mut.bits = node_z_ref_mut.bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ (((HI_NODE_ID as u128) << SHIFT_CHILD_LEFT) | + ((HI_HEIGHT as u128) << SHIFT_HEIGHT_LEFT) | + ((HI_NODE_ID as u128) << SHIFT_PARENT))) | + // Mask in new bits. + ((node_x_id as u128) << SHIFT_CHILD_LEFT) | + ((node_z_height_left as u128) << SHIFT_HEIGHT_LEFT) | + ((node_x_parent as u128) << SHIFT_PARENT); + let node_z_height_right = (((node_z_ref_mut.bits >> SHIFT_HEIGHT_RIGHT) + & (HI_HEIGHT as u128)) as u8); // Get node z right height. + // Determine height of tree rooted at z. + let node_z_height = if (node_z_height_right >= node_z_height_left) + node_z_height_right else node_z_height_left; + (node_z_id, node_z_height) // Return new subtree root, height. + } + + /// Rotate left-right during rebalance. + /// + /// Inner function for `retrace_rebalance()`. + /// + /// Updates state for nodes in subtree, but not for potential parent + /// to subtree. + /// + /// Here, subtree root node x is left-heavy, with left child node + /// z that is right-heavy. Node z has as its right child node y. + /// + /// Node z has an optional tree 1 as its left child subtree, node + /// y has optional trees 2 and 3 as its left and right child + /// subtrees, respectively, and node x has an optional tree 4 as its + /// right child subtree. + /// + /// Double rotations result in a subtree root with a balance factor + /// of zero, such that node y is has the same left and right height + /// post-rotation. + /// + /// Pre-rotation: + /// + /// > n_x + /// > / \ + /// > n_z t_4 + /// > / \ + /// > t_1 n_y + /// > / \ + /// > t_2 t_3 + /// + /// Post-rotation: + /// + /// > n_y + /// > ___/ \___ + /// > n_z n_x + /// > / \ / \ + /// > t_1 t_2 t_3 t_4 + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `node_x_id`: Node ID of subtree root pre-rotation. + /// * `node_z_id`: Node ID of subtree left child pre-rotation. + /// * `node_y_id`: Node ID of subtree root post-rotation. + /// * `node_z_height_left`: Node z's left height pre-rotation. + /// + /// # Procedure + /// + /// * Inspect node y's fields. + /// * Optionally update tree 2's parent field. + /// * Optionally update tree 3's parent field. + /// * Update node x's left child and parent fields. + /// * Update node z's right child and parent fields. + /// * Update node y's children and parent fields. + /// + /// # Reference rotations + /// + /// ## Case 1 + /// + /// * Tree 2 null. + /// * Tree 3 not null. + /// * Node z right height not greater than or equal to left height + /// post-rotation. + /// * Remove node r, then retrace from node x. + /// + /// Pre-removal: + /// + /// > 5 + /// > / \ + /// > 2 6 <- node r + /// > / \ \ + /// > 1 3 7 + /// > \ + /// > 4 + /// + /// Pre-rotation: + /// + /// > 5 <- node x + /// > / \ + /// > node z -> 2 7 <- tree 4 + /// > / \ + /// > tree 1 -> 1 3 <- node y + /// > \ + /// > 4 <- tree 3 + /// + /// Post-rotation: + /// + /// > 3 <- node y + /// > / \ + /// > node z -> 2 5 <- node x + /// > / / \ + /// > tree 1 -> 1 4 7 <- tree 4 + /// > ^ tree 3 + /// + /// ## Case 2 + /// + /// * Tree 2 not null. + /// * Tree 3 null. + /// * Node z right height greater than or equal to left height + /// post-rotation. + /// + /// Pre-rotation: + /// + /// > 8 <- node x + /// > / \ + /// > node z -> 2 9 <- tree 4 + /// > / \ + /// > tree 1 -> 1 6 <- node y + /// > / + /// > tree 2 -> 5 + /// + /// Post-rotation: + /// + /// > 6 <- node y + /// > / \ + /// > node z -> 2 8 <- node x + /// > / \ \ + /// > tree 1 -> 1 5 9 <- tree 4 + /// > ^ tree 2 + /// + /// # Testing + /// + /// * `test_rotate_left_right_1()` + /// * `test_rotate_left_right_2()` + fun retrace_rebalance_rotate_left_right( + avlq_ref_mut: &mut AVLqueue, + node_x_id: u64, + node_z_id: u64, + node_y_id: u64, + node_z_height_left: u8 + ): ( + u64, + u8 + ) { + // Mutably borrow tree nodes table. + let nodes_ref_mut = &mut avlq_ref_mut.tree_nodes; + // Immutably borrow node y. + let node_y_ref = table_with_length::borrow(nodes_ref_mut, node_y_id); + let y_bits = node_y_ref.bits; // Get node y bits. + // Get node y's left and right height, and tree 2 and 3 IDs. + let (node_y_height_left, node_y_height_right, tree_2_id, tree_3_id) = + ((((y_bits >> SHIFT_HEIGHT_LEFT ) & (HI_HEIGHT as u128)) as u8), + (((y_bits >> SHIFT_HEIGHT_RIGHT) & (HI_HEIGHT as u128)) as u8), + (((y_bits >> SHIFT_CHILD_LEFT ) & (HI_NODE_ID as u128)) as u64), + (((y_bits >> SHIFT_CHILD_RIGHT ) & (HI_NODE_ID as u128)) as u64)); + if (tree_2_id != (NIL as u64)) { // If tree 2 not null: + let tree_2_ref_mut = // Mutably borrow tree 2 root. + table_with_length::borrow_mut(nodes_ref_mut, tree_2_id); + // Reassign bits for new parent field: + tree_2_ref_mut.bits = tree_2_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_PARENT)) | + // Mask in new bits. + ((node_z_id as u128) << SHIFT_PARENT); + }; + if (tree_3_id != (NIL as u64)) { // If tree 3 not null: + let tree_3_ref_mut = // Mutably borrow tree 3 root. + table_with_length::borrow_mut(nodes_ref_mut, tree_3_id); + // Reassign bits for new parent field: + tree_3_ref_mut.bits = tree_3_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_PARENT)) | + // Mask in new bits. + ((node_x_id as u128) << SHIFT_PARENT); + }; + let node_x_ref_mut = // Mutably borrow node x. + table_with_length::borrow_mut(nodes_ref_mut, node_x_id); + // Node x's left height is from transferred tree 3. + let node_x_height_left = node_y_height_right; + let node_x_parent = (((node_x_ref_mut.bits >> SHIFT_PARENT) & + (HI_NODE_ID as u128)) as u64); // Store node x parent field. + // Reassign bits for left child, left height, and parent: + node_x_ref_mut.bits = node_x_ref_mut.bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ (((HI_NODE_ID as u128) << SHIFT_CHILD_LEFT) | + ((HI_HEIGHT as u128) << SHIFT_HEIGHT_LEFT) | + ((HI_NODE_ID as u128) << SHIFT_PARENT))) | + // Mask in new bits. + ((tree_3_id as u128) << SHIFT_CHILD_LEFT) | + ((node_x_height_left as u128) << SHIFT_HEIGHT_LEFT) | + ((node_y_id as u128) << SHIFT_PARENT); + let node_z_ref_mut = // Mutably borrow node z. + table_with_length::borrow_mut(nodes_ref_mut, node_z_id); + // Node z's right height is from transferred tree 2. + let node_z_height_right = node_y_height_left; + // Reassign bits for right child, right height, and parent: + node_z_ref_mut.bits = node_z_ref_mut.bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ (((HI_NODE_ID as u128) << SHIFT_CHILD_RIGHT) | + ((HI_HEIGHT as u128) << SHIFT_HEIGHT_RIGHT) | + ((HI_NODE_ID as u128) << SHIFT_PARENT))) | + // Mask in new bits. + ((tree_2_id as u128) << SHIFT_CHILD_RIGHT) | + ((node_z_height_right as u128) << SHIFT_HEIGHT_RIGHT) | + ((node_y_id as u128) << SHIFT_PARENT); + // Determine height of tree rooted at z. + let node_z_height = if (node_z_height_right >= node_z_height_left) + node_z_height_right else node_z_height_left; + // Get node y's post-rotation height (same on left and right). + let node_y_height = node_z_height + 1; + let node_y_ref_mut = // Mutably borrow node y. + table_with_length::borrow_mut(nodes_ref_mut, node_y_id); + // Reassign bits for both child edges, and parent. + node_y_ref_mut.bits = node_y_ref_mut.bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ (((HI_NODE_ID as u128) << SHIFT_CHILD_LEFT) | + ((HI_NODE_ID as u128) << SHIFT_CHILD_RIGHT) | + ((HI_HEIGHT as u128) << SHIFT_HEIGHT_LEFT) | + ((HI_HEIGHT as u128) << SHIFT_HEIGHT_RIGHT) | + ((HI_NODE_ID as u128) << SHIFT_PARENT))) | + // Mask in new bits. + ((node_z_id as u128) << SHIFT_CHILD_LEFT) | + ((node_x_id as u128) << SHIFT_CHILD_RIGHT) | + ((node_y_height as u128) << SHIFT_HEIGHT_LEFT) | + ((node_y_height as u128) << SHIFT_HEIGHT_RIGHT) | + ((node_x_parent as u128) << SHIFT_PARENT); + (node_y_id, node_y_height) // Return new subtree root, height. + } + + /// Rotate right during rebalance. + /// + /// Inner function for `retrace_rebalance()`. + /// + /// Updates state for nodes in subtree, but not for potential parent + /// to subtree. + /// + /// Here, subtree root node x is left-heavy, with left child + /// node z that is not right-heavy. Node x has an optional tree 3 + /// as its right child subtree, and node z has optional trees 1 and + /// 2 as its left and right child subtrees, respectively. + /// + /// Pre-rotation: + /// + /// > n_x + /// > / \ + /// > n_z t_3 + /// > / \ + /// > t_1 t_2 + /// + /// Post-rotation: + /// + /// > n_z + /// > / \ + /// > t_1 n_x + /// > / \ + /// > t_2 t_3 + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `node_x_id`: Node ID of subtree root pre-rotation. + /// * `node_z_id`: Node ID of subtree root post-rotation. + /// * `tree_2_id`: Node z's right child field. + /// * `node_z_height_right`: Node z's right height. + /// + /// # Returns + /// + /// * `u64`: Node z's ID. + /// * `u8`: The height of the subtree rooted at node z, + /// post-rotation. + /// + /// # Reference rotations + /// + /// ## Case 1 + /// + /// * Tree 2 null. + /// * Node x right height greater than or equal to left height + /// post-rotation. + /// * Node z left height greater than or equal to right height + /// post-rotation. + /// * Simulates inserting tree 1, then retracing from node z. + /// + /// Pre-insertion: + /// + /// > 8 + /// > / + /// > 6 + /// + /// Pre-rotation: + /// + /// > 8 <- node x + /// > / + /// > 6 <- node z + /// > / + /// > 4 <- tree 1 + /// + /// Post-rotation: + /// + /// > 6 <- node z + /// > / \ + /// > tree 1 -> 4 8 <- node x + /// + /// ## Case 2 + /// + /// * Tree 2 not null. + /// * Node x right height not greater than or equal to left height + /// post-rotation. + /// * Node z left height not greater than or equal to right height + /// post-rotation. + /// + /// Pre-rotation: + /// + /// > 7 <- node x + /// > / + /// > 4 <- node z + /// > / \ + /// > tree 1 -> 3 5 <- tree 2 + /// + /// Post-rotation: + /// + /// > 4 <- node z + /// > / \ + /// > tree 1 -> 3 7 <- node x + /// > / + /// > 5 <- tree 2 + /// + /// # Testing + /// + /// * `test_rotate_right_1()` + /// * `test_rotate_right_2()` + fun retrace_rebalance_rotate_right( + avlq_ref_mut: &mut AVLqueue, + node_x_id: u64, + node_z_id: u64, + tree_2_id: u64, + node_z_height_right: u8 + ): ( + u64, + u8 + ) { + // Mutably borrow tree nodes table. + let nodes_ref_mut = &mut avlq_ref_mut.tree_nodes; + if (tree_2_id != (NIL as u64)) { // If tree 2 is not empty: + let tree_2_ref_mut = // Mutably borrow tree 2 root. + table_with_length::borrow_mut(nodes_ref_mut, tree_2_id); + // Reassign bits for new parent field: + tree_2_ref_mut.bits = tree_2_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_PARENT)) | + // Mask in new bits. + ((node_x_id as u128) << SHIFT_PARENT); + }; + let node_x_ref_mut = // Mutably borrow node x. + table_with_length::borrow_mut(nodes_ref_mut, node_x_id); + let node_x_height_right = (((node_x_ref_mut.bits >> SHIFT_HEIGHT_RIGHT) + & (HI_HEIGHT as u128)) as u8); // Get node x right height. + // Node x's left height is from transferred tree 2. + let node_x_height_left = node_z_height_right; + let node_x_parent = (((node_x_ref_mut.bits >> SHIFT_PARENT) & + (HI_NODE_ID as u128)) as u64); // Get node x parent field. + // Reassign bits for left child, left height, and parent: + node_x_ref_mut.bits = node_x_ref_mut.bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ (((HI_NODE_ID as u128) << SHIFT_CHILD_LEFT) | + ((HI_HEIGHT as u128) << SHIFT_HEIGHT_LEFT) | + ((HI_NODE_ID as u128) << SHIFT_PARENT))) | + // Mask in new bits. + ((tree_2_id as u128) << SHIFT_CHILD_LEFT) | + ((node_x_height_left as u128) << SHIFT_HEIGHT_LEFT) | + ((node_z_id as u128) << SHIFT_PARENT); + // Determine height of tree rooted at x. + let node_x_height = if (node_x_height_right >= node_x_height_left) + node_x_height_right else node_x_height_left; + // Get node z right height. + let node_z_height_right = node_x_height + 1; + let node_z_ref_mut = // Mutably borrow node z. + table_with_length::borrow_mut(nodes_ref_mut, node_z_id); + // Reassign bits for right child, right height, and parent: + node_z_ref_mut.bits = node_z_ref_mut.bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ (((HI_NODE_ID as u128) << SHIFT_CHILD_RIGHT) | + ((HI_HEIGHT as u128) << SHIFT_HEIGHT_RIGHT) | + ((HI_NODE_ID as u128) << SHIFT_PARENT))) | + // Mask in new bits. + ((node_x_id as u128) << SHIFT_CHILD_RIGHT) | + ((node_z_height_right as u128) << SHIFT_HEIGHT_RIGHT) | + ((node_x_parent as u128) << SHIFT_PARENT); + let node_z_height_left = (((node_z_ref_mut.bits >> SHIFT_HEIGHT_LEFT) & + (HI_HEIGHT as u128)) as u8); // Get node z left height. + // Determine height of tree rooted at z. + let node_z_height = if (node_z_height_left >= node_z_height_right) + node_z_height_left else node_z_height_right; + (node_z_id, node_z_height) // Return new subtree root, height. + } + + /// Rotate right-left during rebalance. + /// + /// Inner function for `retrace_rebalance()`. + /// + /// Updates state for nodes in subtree, but not for potential parent + /// to subtree. + /// + /// Here, subtree root node x is right-heavy, with right child node + /// z that is left-heavy. Node z has as its left child node y. + /// + /// Node x has an optional tree 1 as its left child subtree, node + /// y has optional trees 2 and 3 as its left and right child + /// subtrees, respectively, and node z has an optional tree 4 as its + /// right child subtree. + /// + /// Double rotations result in a subtree root with a balance factor + /// of zero, such that node y is has the same left and right height + /// post-rotation. + /// + /// Pre-rotation: + /// + /// > n_x + /// > / \ + /// > t_1 n_z + /// > / \ + /// > n_y t_4 + /// > / \ + /// > t_2 t_3 + /// + /// Post-rotation: + /// + /// > n_y + /// > ___/ \___ + /// > n_x n_z + /// > / \ / \ + /// > t_1 t_2 t_3 t_4 + /// + /// # Parameters + /// + /// * `avlq_ref_mut`: Mutable reference to AVL queue. + /// * `node_x_id`: Node ID of subtree root pre-rotation. + /// * `node_z_id`: Node ID of subtree right child pre-rotation. + /// * `node_y_id`: Node ID of subtree root post-rotation. + /// * `node_z_height_right`: Node z's right height pre-rotation. + /// + /// # Procedure + /// + /// * Inspect node y's fields. + /// * Optionally update tree 2's parent field. + /// * Optionally update tree 3's parent field. + /// * Update node x's right child and parent fields. + /// * Update node z's left child and parent fields. + /// * Update node y's children and parent fields. + /// + /// # Reference rotations + /// + /// ## Case 1 + /// + /// * Tree 2 not null. + /// * Tree 3 null. + /// * Node z left height not greater than or equal to right height + /// post-rotation. + /// + /// Pre-rotation: + /// + /// > 2 <- node x + /// > / \ + /// > tree 1 -> 1 8 <- node z + /// > / \ + /// > node y -> 4 9 <- tree 4 + /// > / + /// > 3 <- tree 2 + /// + /// Post-rotation: + /// + /// > 4 <- node y + /// > / \ + /// > node x -> 2 8 <- node z + /// > / \ \ + /// > tree 1 -> 1 3 9 <- tree 4 + /// > ^ tree 2 + /// + /// ## Case 2 + /// + /// * Tree 2 null. + /// * Tree 3 not null. + /// * Node z left height greater than or equal to right height + /// post-rotation. + /// * Remove node r, then retrace from node x. + /// + /// Pre-removal: + /// + /// > 3 + /// > / \ + /// > node r -> 2 6 + /// > / / \ + /// > 1 4 7 + /// > \ + /// > 5 + /// + /// Pre-rotation: + /// + /// > 3 <- node x + /// > / \ + /// > tree 1 -> 1 6 <- node z + /// > / \ + /// > node y -> 4 7 <- tree 4 + /// > \ + /// > 5 <- tree 3 + /// + /// Post-rotation: + /// + /// > 4 <- node y + /// > / \ + /// > node x -> 3 6 <- node z + /// > / / \ + /// > tree 1 -> 1 5 7 <- tree 4 + /// > ^ tree 3 + /// + /// # Testing + /// + /// * `test_rotate_right_left_1()` + /// * `test_rotate_right_left_2()` + fun retrace_rebalance_rotate_right_left( + avlq_ref_mut: &mut AVLqueue, + node_x_id: u64, + node_z_id: u64, + node_y_id: u64, + node_z_height_right: u8 + ): ( + u64, + u8 + ) { + // Mutably borrow tree nodes table. + let nodes_ref_mut = &mut avlq_ref_mut.tree_nodes; + // Immutably borrow node y. + let node_y_ref = table_with_length::borrow(nodes_ref_mut, node_y_id); + let y_bits = node_y_ref.bits; // Get node y bits. + // Get node y's left and right height, and tree 2 and 3 IDs. + let (node_y_height_left, node_y_height_right, tree_2_id, tree_3_id) = + ((((y_bits >> SHIFT_HEIGHT_LEFT ) & (HI_HEIGHT as u128)) as u8), + (((y_bits >> SHIFT_HEIGHT_RIGHT) & (HI_HEIGHT as u128)) as u8), + (((y_bits >> SHIFT_CHILD_LEFT ) & (HI_NODE_ID as u128)) as u64), + (((y_bits >> SHIFT_CHILD_RIGHT ) & (HI_NODE_ID as u128)) as u64)); + if (tree_2_id != (NIL as u64)) { // If tree 2 not null: + let tree_2_ref_mut = // Mutably borrow tree 2 root. + table_with_length::borrow_mut(nodes_ref_mut, tree_2_id); + // Reassign bits for new parent field: + tree_2_ref_mut.bits = tree_2_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_PARENT)) | + // Mask in new bits. + ((node_x_id as u128) << SHIFT_PARENT); + }; + if (tree_3_id != (NIL as u64)) { // If tree 3 not null: + let tree_3_ref_mut = // Mutably borrow tree 3 root. + table_with_length::borrow_mut(nodes_ref_mut, tree_3_id); + // Reassign bits for new parent field: + tree_3_ref_mut.bits = tree_3_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_PARENT)) | + // Mask in new bits. + ((node_z_id as u128) << SHIFT_PARENT); + }; + let node_x_ref_mut = // Mutably borrow node x. + table_with_length::borrow_mut(nodes_ref_mut, node_x_id); + // Node x's right height is from transferred tree 2. + let node_x_height_right = node_y_height_left; + let node_x_parent = (((node_x_ref_mut.bits >> SHIFT_PARENT) & + (HI_NODE_ID as u128)) as u64); // Store node x parent field. + // Reassign bits for right child, right height, and parent: + node_x_ref_mut.bits = node_x_ref_mut.bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ (((HI_NODE_ID as u128) << SHIFT_CHILD_RIGHT) | + ((HI_HEIGHT as u128) << SHIFT_HEIGHT_RIGHT) | + ((HI_NODE_ID as u128) << SHIFT_PARENT))) | + // Mask in new bits. + ((tree_2_id as u128) << SHIFT_CHILD_RIGHT) | + ((node_x_height_right as u128) << SHIFT_HEIGHT_RIGHT) | + ((node_y_id as u128) << SHIFT_PARENT); + let node_z_ref_mut = // Mutably borrow node z. + table_with_length::borrow_mut(nodes_ref_mut, node_z_id); + // Node z's left height is from transferred tree 3. + let node_z_height_left = node_y_height_right; + // Reassign bits for left child, left height, and parent: + node_z_ref_mut.bits = node_z_ref_mut.bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ (((HI_NODE_ID as u128) << SHIFT_CHILD_LEFT) | + ((HI_HEIGHT as u128) << SHIFT_HEIGHT_LEFT) | + ((HI_NODE_ID as u128) << SHIFT_PARENT))) | + // Mask in new bits. + ((tree_3_id as u128) << SHIFT_CHILD_LEFT) | + ((node_z_height_left as u128) << SHIFT_HEIGHT_LEFT) | + ((node_y_id as u128) << SHIFT_PARENT); + // Determine height of tree rooted at z. + let node_z_height = if (node_z_height_left >= node_z_height_right) + node_z_height_left else node_z_height_right; + // Get node y's post-rotation height (same on left and right). + let node_y_height = node_z_height + 1; + let node_y_ref_mut = // Mutably borrow node y. + table_with_length::borrow_mut(nodes_ref_mut, node_y_id); + // Reassign bits for both child edges, and parent. + node_y_ref_mut.bits = node_y_ref_mut.bits & + // Clear out fields via mask unset at field bits. + (HI_128 ^ (((HI_NODE_ID as u128) << SHIFT_CHILD_LEFT) | + ((HI_NODE_ID as u128) << SHIFT_CHILD_RIGHT) | + ((HI_HEIGHT as u128) << SHIFT_HEIGHT_LEFT) | + ((HI_HEIGHT as u128) << SHIFT_HEIGHT_RIGHT) | + ((HI_NODE_ID as u128) << SHIFT_PARENT))) | + // Mask in new bits. + ((node_x_id as u128) << SHIFT_CHILD_LEFT) | + ((node_z_id as u128) << SHIFT_CHILD_RIGHT) | + ((node_y_height as u128) << SHIFT_HEIGHT_LEFT) | + ((node_y_height as u128) << SHIFT_HEIGHT_RIGHT) | + ((node_x_parent as u128) << SHIFT_PARENT); + (node_y_id, node_y_height) // Return new subtree root, height. + } + + /// Update height fields during retracing. + /// + /// Inner function for `retrace()`. + /// + /// # Parameters + /// + /// * `node_ref_mut`: Mutable reference to a node that needs to have + /// its height fields updated during retrace. + /// * `side`: `LEFT` or `RIGHT`, the side on which the node's height + /// needs to be updated. + /// * `operation`: `INCREMENT` or `DECREMENT`, the kind of change in + /// the height field for the given side. + /// * `delta`: The amount of height change for the operation. + /// + /// # Returns + /// + /// * `u8`: The left height of the node after updating height. + /// * `u8`: The right height of the node after updating height. + /// * `u8`: The height of the node before updating height. + /// * `u8`: The height of the node after updating height. + /// + /// # Testing + /// + /// * `test_retrace_update_heights_1()` + /// * `test_retrace_update_heights_2()` + /// + /// ## Case 1 + /// + /// * Left height is greater than or equal to right height + /// pre-retrace. + /// * Side is `LEFT`. + /// * Operation is `DECREMENT`. + /// * Left height is greater than or equal to right height + /// post-retrace. + /// + /// ## Case 2 + /// + /// * Left height is not greater than or equal to right height + /// pre-retrace. + /// * Side is `RIGHT`. + /// * Operation is `INCREMENT`. + /// * Left height is not greater than or equal to right height + /// post-retrace. + fun retrace_update_heights( + node_ref_mut: &mut TreeNode, + side: bool, + operation: bool, + delta: u8 + ): ( + u8, + u8, + u8, + u8 + ) { + let bits = node_ref_mut.bits; // Get node's field bits. + // Get node's left height, right height, and parent fields. + let (height_left, height_right) = + ((((bits >> SHIFT_HEIGHT_LEFT ) & (HI_HEIGHT as u128)) as u8), + (((bits >> SHIFT_HEIGHT_RIGHT) & (HI_HEIGHT as u128)) as u8)); + let height_old = if (height_left >= height_right) height_left else + height_right; // Get height of node before retracing. + // Get height field and shift amount for operation side. + let (height_field, height_shift) = if (side == LEFT) + (height_left , SHIFT_HEIGHT_LEFT ) else + (height_right, SHIFT_HEIGHT_RIGHT); + // Get updated height field for side. + let height_field = if (operation == INCREMENT) height_field + delta + else height_field - delta; + // Reassign bits for corresponding height field: + node_ref_mut.bits = bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_HEIGHT as u128) << height_shift)) | + // Mask in new bits. + ((height_field as u128) << height_shift); + // Reassign local height to that of indicated field. + if (side == LEFT) height_left = height_field else + height_right = height_field; + let height = if (height_left >= height_right) height_left else + height_right; // Get height of node after update. + (height_left, height_right, height, height_old) + } + + /// Search in AVL queue for closest match to seed key. + /// + /// Return immediately if empty tree, otherwise get node ID of root + /// node. Then start walking down nodes, branching left whenever the + /// seed key is less than a node's key, right whenever the seed + /// key is greater than a node's key, and returning when the seed + /// key equals a node's key. Also return if there is no child to + /// branch to on a given side. + /// + /// The "match" node is the node last walked before returning. + /// + /// # Parameters + /// + /// * `avlq_ref`: Immutable reference to AVL queue. + /// * `seed_key`: Seed key to search for. + /// + /// # Returns + /// + /// * `u64`: Node ID of match node, or `NIL` if empty tree. + /// * `Option`: None if empty tree or if match key equals seed + /// key, `LEFT` if seed key is less than match key but match node + /// has no left child, `RIGHT` if seed key is greater than match + /// key but match node has no right child. + /// + /// # Assumptions + /// + /// * Seed key fits in 32 bits. + /// + /// # Reference diagram + /// + /// > 4 <- ID 1 + /// > / \ + /// > ID 5 -> 2 8 <- ID 2 + /// > / \ + /// > ID 4 -> 6 10 <- ID 3 + /// + /// | Seed key | Match key | Node ID | Side | + /// |----------|-----------|---------|-------| + /// | 2 | 2 | 5 | None | + /// | 7 | 6 | 4 | Right | + /// | 9 | 10 | 3 | Left | + /// | 4 | 4 | 1 | None | + /// + /// # Testing + /// + /// * `test_search()` + fun search( + avlq_ref: &AVLqueue, + seed_key: u64 + ): ( + u64, + Option + ) { + let root_msbs = // Get root MSBs. + ((avlq_ref.bits & ((HI_NODE_ID as u128) >> BITS_PER_BYTE)) as u64); + let node_id = // Shift over, mask in LSBs, store as search node. + (root_msbs << BITS_PER_BYTE) | (avlq_ref.root_lsbs as u64); + // If no node at root, return as such, with empty option. + if (node_id == (NIL as u64)) return (node_id, option::none()); + // Mutably borrow tree nodes table. + let nodes_ref = &avlq_ref.tree_nodes; + loop { // Begin walking down tree nodes: + let node_ref = // Mutably borrow node having given ID. + table_with_length::borrow(nodes_ref, node_id); + // Get insertion key encoded in search node's bits. + let node_key = (((node_ref.bits >> SHIFT_INSERTION_KEY) & + (HI_INSERTION_KEY as u128)) as u64); + // If search key equals seed key, return node's ID and + // empty option. + if (seed_key == node_key) return (node_id, option::none()); + // Get bitshift for child node ID and side based on + // inequality comparison between seed key and node key. + let (child_shift, child_side) = if (seed_key < node_key) + (SHIFT_CHILD_LEFT, LEFT) else (SHIFT_CHILD_RIGHT, RIGHT); + let child_id = (((node_ref.bits >> child_shift) & + (HI_NODE_ID as u128)) as u64); // Get child node ID. + // If no child on given side, return match node's ID + // and option with given side. + if (child_id == (NIL as u64)) return + (node_id, option::some(child_side)); + // Otherwise continue walk at given child. + node_id = child_id; + } + } + + /// Traverse from tree node to inorder predecessor or successor. + /// + /// # Parameters + /// + /// * `avlq_ref`: Immutable reference to AVL queue. + /// * `start_node_id`: Tree node ID of node to traverse from. + /// * `target`: Either `PREDECESSOR` or `SUCCESSOR`. + /// + /// # Conventions + /// + /// Traversal starts at the "start node" and ends at the "target + /// node", if any. + /// + /// # Returns + /// + /// * `u64`: Insertion key of target node, or `NIL`. + /// * `u64`: List node ID for head of doubly linked list in + /// target node, or `NIL`. + /// * `u64`: List node ID for tail of doubly linked list in + /// target node, or `NIL`. + /// + /// # Membership considerations + /// + /// * Aborts if no tree node in AVL queue with given start node ID. + /// * Returns all `NIL` if start node is sole node at root. + /// * Returns all `NIL` if no predecessor or successor. + /// * Returns all `NIL` if start node ID indicates inactive node. + /// + /// # Predecessor + /// + /// 1. If start node has left child, return maximum node in left + /// child's right subtree. + /// 2. Otherwise, walk upwards until reaching a node that had last + /// walked node as the root of its right subtree. + /// + /// # Successor + /// + /// 1. If start node has right child, return minimum node in right + /// child's left subtree. + /// 2. Otherwise, walk upwards until reaching a node that had last + /// walked node as the root of its left subtree. + /// + /// # Reference diagram + /// + /// > 5 + /// > ____/ \____ + /// > 2 8 + /// > / \ / \ + /// > 1 3 7 9 + /// > \ / + /// > 4 6 + /// + /// Inserted in following sequence: + /// + /// | Insertion key | Sequence number | + /// |---------------|-----------------| + /// | 5 | 1 | + /// | 8 | 2 | + /// | 2 | 3 | + /// | 1 | 4 | + /// | 3 | 5 | + /// | 7 | 6 | + /// | 9 | 7 | + /// | 4 | 8 | + /// | 6 | 9 | + /// + /// # Testing + /// + /// * `test_traverse()` + fun traverse( + avlq_ref: &AVLqueue, + start_node_id: u64, + target: bool + ): ( + u64, + u64, + u64 + ) { + // Immutably borrow tree nodes table. + let nodes_ref = &avlq_ref.tree_nodes; + // Immutably borrow start node. + let node_ref = table_with_length::borrow(nodes_ref, start_node_id); + // Determine child and subtree side based on target. + let (child_shift, subtree_shift) = if (target == PREDECESSOR) + (SHIFT_CHILD_LEFT , SHIFT_CHILD_RIGHT) else + (SHIFT_CHILD_RIGHT, SHIFT_CHILD_LEFT ); + let bits = node_ref.bits; // Get node bits. + // Get node ID of relevant child to start node. + let child = (((bits >> child_shift) & (HI_NODE_ID as u128)) as u64); + if (child == (NIL as u64)) { // If no such child: + child = start_node_id; // Set child as start node. + loop { // Start upward walk. + let parent = // Get parent field from node bits. + (((bits >> SHIFT_PARENT) & (HI_NODE_ID as u128)) as u64); + // Return all null if no parent. + if (parent == (NIL as u64)) return + ((NIL as u64), (NIL as u64), (NIL as u64)); + // Otherwise, immutably borrow parent node. + node_ref = table_with_length::borrow(nodes_ref, parent); + bits = node_ref.bits; // Get node bits. + let subtree = // Get subtree field for break side. + (((bits >> subtree_shift) & (HI_NODE_ID as u128)) as u64); + // If child from indicated subtree, break out of loop. + if (subtree == child) break; + // Otherwise store node ID for next iteration. + child = parent; + }; + } else { // If start node has child on relevant side: + loop { // Start downward walk. + // Immutably borrow child node. + node_ref = table_with_length::borrow(nodes_ref, child); + bits = node_ref.bits; // Get node bits. + child = // Get node ID of child in relevant subtree. + (((bits >> subtree_shift) & (HI_NODE_ID as u128)) as u64); + // If no subtree left to check, break out of loop. + if (child == (NIL as u64)) break; // Else iterate again. + } + }; + let bits = node_ref.bits; // Get node bits. + // Return insertion key, list head, and list tail. + ((((bits >> SHIFT_INSERTION_KEY) & (HI_INSERTION_KEY as u128)) as u64), + (((bits >> SHIFT_LIST_HEAD ) & (HI_NODE_ID as u128)) as u64), + (((bits >> SHIFT_LIST_TAIL ) & (HI_NODE_ID as u128)) as u64)) + } + + // Private functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Test-only error codes >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test_only] + /// When a char in a bytestring is neither 0 nor 1. + const E_BIT_NOT_0_OR_1: u64 = 100; + + // Test-only error codes <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Test-only functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test_only] + /// Immutably borrow list node having given node ID. + fun borrow_list_node_test( + avlq_ref: &AVLqueue, + node_id: u64 + ): &ListNode { + table_with_length::borrow(&avlq_ref.list_nodes, node_id) + } + + #[test_only] + /// Immutably borrow tree node having given node ID. + fun borrow_tree_node_test( + avlq_ref: &AVLqueue, + node_id: u64 + ): &TreeNode { + table_with_length::borrow(&avlq_ref.tree_nodes, node_id) + } + + #[test_only] + /// Immutably borrow value option having given node ID. + public fun borrow_value_option_test( + avlq_ref: &AVLqueue, + node_id: u64 + ): &Option { + table::borrow(&avlq_ref.values, node_id) + } + + #[test_only] + /// Drop AVL queue. + fun drop_avlq_test( + avlq: AVLqueue + ) { + // Unpack all fields, dropping those that are not tables. + let AVLqueue{bits: _, root_lsbs: _, tree_nodes, list_nodes, values} = + avlq; + // Drop all tables. + table_with_length::drop_unchecked(tree_nodes); + table_with_length::drop_unchecked(list_nodes); + table::drop_unchecked(values); + } + + #[test_only] + /// Flip access key sort order bit. + public fun flip_access_key_sort_order_bit_test( + access_key: u64 + ): u64 { + access_key ^ ((BIT_FLAG_ASCENDING as u64) << (SHIFT_ACCESS_SORT_ORDER)) + } + + #[test_only] + /// Get list node ID encoded in an access key. + /// + /// # Testing + /// + /// * `test_access_key_getters()` + public fun get_access_key_list_node_id_test( + access_key: u64 + ): u64 { + (access_key >> SHIFT_ACCESS_LIST_NODE_ID) & HI_NODE_ID + } + + #[test_only] + /// Get tree node ID encoded in an access key. + /// + /// # Testing + /// + /// * `test_access_key_getters()` + fun get_access_key_tree_node_id_test( + access_key: u64 + ): u64 { + (access_key >> SHIFT_ACCESS_TREE_NODE_ID) & HI_NODE_ID + } + + #[test_only] + /// Like `get_child_left_test()`, but accepts tree node ID inside + /// given AVL queue. + fun get_child_left_by_id_test( + avlq_ref: &AVLqueue, + tree_node_id: u64 + ): u64 { + let tree_node_ref = // Immutably borrow tree node. + table_with_length::borrow(&avlq_ref.tree_nodes, tree_node_id); + get_child_left_test(tree_node_ref) // Return left child field. + } + + #[test_only] + /// Return left child node ID indicated by given tree node. + /// + /// # Testing + /// + /// * `test_get_child_left_test()` + fun get_child_left_test( + tree_node_ref: &TreeNode + ): u64 { + (((tree_node_ref.bits >> SHIFT_CHILD_LEFT) & + (HI_NODE_ID as u128)) as u64) + } + + #[test_only] + /// Return right child node ID indicated by given tree node. + /// + /// # Testing + /// + /// * `test_get_child_right_test()` + fun get_child_right_test( + tree_node_ref: &TreeNode + ): u64 { + (((tree_node_ref.bits >> SHIFT_CHILD_RIGHT)) & + (HI_NODE_ID as u128) as u64) + } + + #[test_only] + /// Like `get_child_right_test()`, but accepts tree node ID inside + /// given AVL queue. + fun get_child_right_by_id_test( + avlq_ref: &AVLqueue, + tree_node_id: u64 + ): u64 { + let tree_node_ref = // Immutably borrow tree node. + table_with_length::borrow(&avlq_ref.tree_nodes, tree_node_id); + get_child_right_test(tree_node_ref) // Return right child field. + } + + #[test_only] + /// Return head insertion key indicated by given AVL queue. + /// + /// # Testing + /// + /// * `test_set_get_head_tail_test()` + fun get_head_key_test( + avlq_ref: &AVLqueue + ): u64 { + (((avlq_ref.bits >> SHIFT_HEAD_KEY) & + (HI_INSERTION_KEY as u128)) as u64) + } + + #[test_only] + /// Return head list node ID indicated by given AVL queue. + /// + /// # Testing + /// + /// * `test_set_get_head_tail_test()` + fun get_head_node_id_test( + avlq_ref: &AVLqueue + ): u64 { + (((avlq_ref.bits >> SHIFT_HEAD_NODE_ID) & (HI_NODE_ID as u128)) as u64) + } + + #[test_only] + /// Like `get_height_left_test()`, but accepts tree node ID inside + /// given AVL queue. + fun get_height_left_by_id_test( + avlq_ref: &AVLqueue, + tree_node_id: u64 + ): u8 { + let tree_node_ref = // Immutably borrow tree node. + table_with_length::borrow(&avlq_ref.tree_nodes, tree_node_id); + get_height_left_test(tree_node_ref) // Return left height. + } + + #[test_only] + /// Return left height indicated by given tree node. + /// + /// # Testing + /// + /// * `test_get_height_left_test()` + fun get_height_left_test( + tree_node_ref: &TreeNode + ): u8 { + (((tree_node_ref.bits >> SHIFT_HEIGHT_LEFT) & + (HI_HEIGHT as u128)) as u8) + } + + #[test_only] + /// Return right height indicated by given tree node. + /// + /// # Testing + /// + /// * `test_get_height_right_test()` + fun get_height_right_test( + tree_node_ref: &TreeNode + ): u8 { + (((tree_node_ref.bits >> SHIFT_HEIGHT_RIGHT) & + (HI_HEIGHT as u128)) as u8) + } + + #[test_only] + /// Like `get_height_right_test()`, but accepts tree node ID inside + /// given AVL queue. + fun get_height_right_by_id_test( + avlq_ref: &AVLqueue, + tree_node_id: u64 + ): u8 { + let tree_node_ref = // Immutably borrow tree node. + table_with_length::borrow(&avlq_ref.tree_nodes, tree_node_id); + get_height_right_test(tree_node_ref) // Return right height. + } + + #[test_only] + /// Like `get_insertion_key_test()`, but accepts tree node ID inside + /// given AVL queue. + fun get_insertion_key_by_id_test( + avlq_ref: &AVLqueue, + tree_node_id: u64 + ): u64 { + let tree_node_ref = // Immutably borrow tree node. + table_with_length::borrow(&avlq_ref.tree_nodes, tree_node_id); + get_insertion_key_test(tree_node_ref) // Return insertion key. + } + + #[test_only] + /// Return insertion key indicated by given tree node. + /// + /// # Testing + /// + /// * `test_get_insertion_key_test()` + fun get_insertion_key_test( + tree_node_ref: &TreeNode + ): u64 { + (((tree_node_ref.bits >> SHIFT_INSERTION_KEY) & + (HI_INSERTION_KEY as u128)) as u64) + } + + #[test_only] + /// Like `get_list_head_test()`, but accepts tree node ID inside + /// given AVL queue. + fun get_list_head_by_id_test( + avlq_ref: &AVLqueue, + tree_node_id: u64 + ): u64 { + let tree_node_ref = // Immutably borrow tree node. + table_with_length::borrow(&avlq_ref.tree_nodes, tree_node_id); + get_list_head_test(tree_node_ref) // Return list head. + } + + #[test_only] + /// Return list head node ID indicated by given tree node. + /// + /// # Testing + /// + /// * `test_get_list_head_test()` + fun get_list_head_test( + tree_node_ref: &TreeNode + ): u64 { + (((tree_node_ref.bits >> SHIFT_LIST_HEAD) & + (HI_NODE_ID as u128)) as u64) + } + + #[test_only] + /// Return node ID of last node and if last node is a tree node, + /// for given list node. + /// + /// # Testing + /// + /// * `test_get_list_last_test()` + fun get_list_last_test( + list_node_ref: &ListNode + ): ( + u64, + bool + ) { + // Get virtual last field. + let last_field = ((list_node_ref.last_msbs as u64) << BITS_PER_BYTE) | + (list_node_ref.last_lsbs as u64); + let tree_node_flag = (((last_field >> SHIFT_NODE_TYPE) & + (BIT_FLAG_TREE_NODE as u64)) as u8); // Get tree node flag. + // Return node ID, and if last node is a tree node. + ((last_field & HI_NODE_ID), tree_node_flag == BIT_FLAG_TREE_NODE) + } + + #[test_only] + /// Like `get_list_last_test()`, but accepts list node ID inside + /// given AVL queue. + fun get_list_last_by_id_test( + avlq_ref: &AVLqueue, + list_node_id: u64 + ): ( + u64, + bool + ) { + let list_node_ref = // Immutably borrow list node. + table_with_length::borrow(&avlq_ref.list_nodes, list_node_id); + get_list_last_test(list_node_ref) // Return last field data. + } + + #[test_only] + /// Return only node ID from `get_list_last_by_id_test()`. + fun get_list_last_node_id_by_id_test( + avlq_ref: &AVLqueue, + list_node_id: u64 + ): u64 { + // Get last node ID. + let (node_id, _) = get_list_last_by_id_test(avlq_ref, list_node_id); + node_id // Return it. + } + + #[test_only] + /// Return node ID of next node and if next node is a tree node, + /// for given list node. + /// + /// # Testing + /// + /// * `test_get_list_next_test()` + fun get_list_next_test( + list_node_ref: &ListNode + ): ( + u64, + bool + ) { + // Get virtual next field. + let next_field = ((list_node_ref.next_msbs as u64) << BITS_PER_BYTE) | + (list_node_ref.next_lsbs as u64); + let tree_node_flag = (((next_field >> SHIFT_NODE_TYPE) & + (BIT_FLAG_TREE_NODE as u64)) as u8); // Get tree node flag. + // Return node ID, and if next node is a tree node. + ((next_field & HI_NODE_ID), tree_node_flag == BIT_FLAG_TREE_NODE) + } + + #[test_only] + /// Like `get_list_next_test()`, but accepts list node ID inside + /// given AVL queue. + fun get_list_next_by_id_test( + avlq_ref: &AVLqueue, + list_node_id: u64 + ): ( + u64, + bool + ) { + let list_node_ref = // Immutably borrow list node. + table_with_length::borrow(&avlq_ref.list_nodes, list_node_id); + get_list_next_test(list_node_ref) // Return next field data. + } + + #[test_only] + /// Return only node ID from `get_list_next_by_id_test()`. + fun get_list_next_node_id_by_id_test( + avlq_ref: &AVLqueue, + list_node_id: u64 + ): u64 { + // Get next node ID. + let (node_id, _) = get_list_next_by_id_test(avlq_ref, list_node_id); + node_id // Return it. + } + + #[test_only] + /// Like `get_list_tail_test()`, but accepts tree node ID inside + /// given AVL queue. + fun get_list_tail_by_id_test( + avlq_ref: &AVLqueue, + tree_node_id: u64 + ): u64 { + let tree_node_ref = // Immutably borrow tree node. + table_with_length::borrow(&avlq_ref.tree_nodes, tree_node_id); + get_list_tail_test(tree_node_ref) // Return list tail. + } + + #[test_only] + /// Return list tail node ID indicated by given tree node. + /// + /// # Testing + /// + /// * `test_get_list_tail_test()` + fun get_list_tail_test( + tree_node_ref: &TreeNode + ): u64 { + (((tree_node_ref.bits >> SHIFT_LIST_TAIL) & + (HI_NODE_ID as u128)) as u64) + } + + #[test_only] + /// Return node ID at top of inactive list node stack indicated by + /// given AVL queue. + /// + /// # Testing + /// + /// * `test_get_list_top_test()` + fun get_list_top_test( + avlq_ref: &AVLqueue + ): u64 { + (((avlq_ref.bits >> SHIFT_LIST_STACK_TOP) & + (HI_NODE_ID as u128)) as u64) + } + + #[test_only] + /// Like `get_parent_test()`, but accepts tree node ID inside given + /// AVL queue. + fun get_parent_by_id_test( + avlq_ref: &AVLqueue, + tree_node_id: u64 + ): u64 { + let tree_node_ref = // Immutably borrow tree node. + table_with_length::borrow(&avlq_ref.tree_nodes, tree_node_id); + get_parent_test(tree_node_ref) // Return parent field. + } + + #[test_only] + /// Return parent node ID indicated by given tree node. + /// + /// # Testing + /// + /// * `test_get_parent_test()` + fun get_parent_test( + tree_node_ref: &TreeNode + ): u64 { + (((tree_node_ref.bits >> SHIFT_PARENT) & + (HI_NODE_ID as u128)) as u64) + } + + #[test_only] + /// Return tail insertion key indicated by given AVL queue. + /// + /// # Testing + /// + /// * `test_set_get_head_tail_test()` + fun get_tail_key_test( + avlq_ref: &AVLqueue + ): u64 { + (((avlq_ref.bits >> SHIFT_TAIL_KEY) & + (HI_INSERTION_KEY as u128)) as u64) + } + + #[test_only] + /// Return tail list node ID indicated by given AVL queue. + /// + /// # Testing + /// + /// * `test_set_get_head_tail_test()` + fun get_tail_node_id_test( + avlq_ref: &AVLqueue + ): u64 { + (((avlq_ref.bits >> SHIFT_TAIL_NODE_ID) & (HI_NODE_ID as u128)) as u64) + } + + #[test_only] + /// Like `get_tree_next_test()`, but accepts tree node ID inside + /// given AVL queue. + fun get_tree_next_by_id_test( + avlq_ref: &AVLqueue, + tree_node_id: u64 + ): u64 { + let tree_node_ref = // Immutably borrow tree node. + table_with_length::borrow(&avlq_ref.tree_nodes, tree_node_id); + get_tree_next_test(tree_node_ref) // Return parent field. + } + + #[test_only] + /// Return node ID of next inactive tree node in stack, indicated + /// by given tree node. + /// + /// # Testing + /// + /// * `test_get_tree_next_test()` + fun get_tree_next_test( + tree_node_ref: &TreeNode + ): u64 { + ((tree_node_ref.bits & (HI_64 as u128)) as u64) & HI_NODE_ID + } + + #[test_only] + /// Return node ID at top of inactive tree node stack indicated by + /// given AVL queue. + /// + /// # Testing + /// + /// * `test_get_tree_top_test()` + fun get_tree_top_test( + avlq_ref: &AVLqueue + ): u64 { + (((avlq_ref.bits >> SHIFT_TREE_STACK_TOP) & + (HI_NODE_ID as u128)) as u64) + } + + #[test_only] + /// Return root node ID indicated by AVL queue. + /// + /// # Testing + /// + /// * `test_set_get_root_test()` + fun get_root_test( + avlq_ref: &AVLqueue + ): u64 { + // Get MSBs. + let msbs = avlq_ref.bits & ((HI_NODE_ID as u128) >> BITS_PER_BYTE); + // Mask in LSBs and return. + ((msbs << BITS_PER_BYTE) as u64) | (avlq_ref.root_lsbs as u64) + } + + #[test_only] + /// Return copy of value for given node ID. + fun get_value_test( + avlq_ref: &AVLqueue, + node_id: u64 + ): V { + // Borrow value option. + let value_option_ref = borrow_value_option_test(avlq_ref, node_id); + // Return copy of value. + *option::borrow(value_option_ref) + } + + #[test_only] + /// Return only is tree node flag from `get_list_last_by_id_test()`. + fun is_tree_node_list_last_by_id_test( + avlq_ref: &AVLqueue, + list_node_id: u64 + ): bool { + let (_, is_tree_node) = // Check if last node is tree node. + get_list_last_by_id_test(avlq_ref, list_node_id); + is_tree_node // Return flag. + } + + #[test_only] + /// Return only is tree node flag from `get_list_next_by_id_test()`. + fun is_tree_node_list_next_by_id_test( + avlq_ref: &AVLqueue, + list_node_id: u64 + ): bool { + let (_, is_tree_node) = // Check if next node is tree node. + get_list_next_by_id_test(avlq_ref, list_node_id); + is_tree_node // Return flag. + } + + #[test_only] + /// Set head insertion key in given AVL queue. + /// + /// # Testing + /// + /// * `test_set_get_head_tail_test()` + fun set_head_key_test( + avlq_ref_mut: &mut AVLqueue, + key: u64 + ) { + // Reassign bits: + avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_INSERTION_KEY as u128) << SHIFT_HEAD_KEY as u128)) | + // Mask in new bits. + ((key as u128) << SHIFT_HEAD_KEY) + } + + #[test_only] + /// Set head list node ID in given AVL queue. + /// + /// # Testing + /// + /// * `test_set_get_head_tail_test()` + fun set_head_node_id_test( + avlq_ref_mut: &mut AVLqueue, + node_id: u64 + ) { + // Reassign bits: + avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID as u128) << SHIFT_HEAD_NODE_ID as u128)) | + // Mask in new bits. + ((node_id as u128) << SHIFT_HEAD_NODE_ID) + } + + #[test_only] + /// Set root node ID. + /// + /// # Testing + /// + /// * `test_set_get_root_test()` + fun set_root_test( + avlq_ref_mut: &mut AVLqueue, + root_node_id: u64 + ) { + // Reassign bits for root MSBs: + avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ ((HI_NODE_ID >> BITS_PER_BYTE) as u128)) | + // Mask in new bits. + ((root_node_id as u128) >> BITS_PER_BYTE); + // Set root LSBs. + avlq_ref_mut.root_lsbs = ((root_node_id & HI_BYTE) as u8); + } + + #[test_only] + /// Set tail insertion key in given AVL queue. + /// + /// # Testing + /// + /// * `test_set_get_head_tail_test()` + fun set_tail_key_test( + avlq_ref_mut: &mut AVLqueue, + key: u64 + ) { + // Reassign bits: + avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ (((HI_INSERTION_KEY as u128) << SHIFT_TAIL_KEY) as u128)) + // Mask in new bits. + | ((key as u128) << SHIFT_TAIL_KEY) + } + + #[test_only] + /// Set tail list node ID in given AVL queue. + /// + /// # Testing + /// + /// * `test_set_get_head_tail_test()` + fun set_tail_node_id_test( + avlq_ref_mut: &mut AVLqueue, + node_id: u64 + ) { + // Reassign bits: + avlq_ref_mut.bits = avlq_ref_mut.bits & + // Clear out field via mask unset at field bits. + (HI_128 ^ (((HI_NODE_ID as u128) << SHIFT_TAIL_NODE_ID) as u128)) | + // Mask in new bits. + ((node_id as u128) << SHIFT_TAIL_NODE_ID) + } + + #[test_only] + /// Return a `u128` corresponding to provided byte string `s`. The + /// byte should only contain only "0"s and "1"s, up to 128 + /// characters max (e.g. `b"100101...10101010"`). + /// + /// # Testing + /// + /// * `test_u_128_64()` + /// * `test_u_128_failure()` + public fun u_128( + s: vector + ): u128 { + let n = vector::length(&s); // Get number of bits. + let r = 0; // Initialize result to 0. + let i = 0; // Start loop at least significant bit. + while (i < n) { // While there are bits left to review. + // Get bit under review. + let b = *vector::borrow(&s, n - 1 - i); + if (b == 0x31) { // If the bit is 1 (0x31 in ASCII): + // OR result with the correspondingly leftshifted bit. + r = r | (1 << (i as u8)); + // Otherwise, assert bit is marked 0 (0x30 in ASCII). + } else assert!(b == 0x30, E_BIT_NOT_0_OR_1); + i = i + 1; // Proceed to next-least-significant bit. + }; + r // Return result. + } + + #[test_only] + /// Return `u128` corresponding to concatenated result of `a`, `b`, + /// `c`, and `d`. Useful for line-wrapping long byte strings, and + /// inspection via 32-bit sections. + /// + /// # Testing + /// + /// * `test_u_128_64()` + public fun u_128_by_32( + a: vector, + b: vector, + c: vector, + d: vector, + ): u128 { + vector::append(&mut c, d); // Append d onto c. + vector::append(&mut b, c); // Append c onto b. + vector::append(&mut a, b); // Append b onto a. + u_128(a) // Return u128 equivalent of concatenated bytestring. + } + + #[test_only] + /// Wrapper for `u_128()`, casting return to `u64`. + /// + /// # Testing + /// + /// * `test_u_128_64()` + public fun u_64(s: vector): u64 {(u_128(s) as u64)} + + #[test_only] + /// Wrapper for `u_128_by_32()`, accepting only two inputs, with + /// casted return to `u64`. + public fun u_64_by_32( + a: vector, + b: vector + ): u64 { + // Get u128 for given inputs, cast to u64. + (u_128_by_32(a, b, b"", b"") as u64) + } + + // Test-only functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Tests >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test] + /// Verify successful returns. + fun test_access_key_getters() { + // Assert access key not marked ascending if no bits set. + assert!(!is_ascending_access_key((NIL as u64)), 0); + // Declare encoded information for access key. + let tree_node_id = u_64(b"10000000000001"); + let list_node_id = u_64(b"11000000000011"); + let insertion_key = u_64(b"10000000000000000000000000000001"); + let access_key = u_64_by_32( + b"00010000000000001110000000000111", + // ^ bits 47-60 ^^ bits 33-46 ^^ bit 32 + b"10000000000000000000000000000001"); + // Assert access key getter returns. + assert!(get_access_key_tree_node_id_test(access_key) + == tree_node_id, 0); + assert!(get_access_key_list_node_id_test(access_key) + == list_node_id, 0); + assert!(is_ascending_access_key(access_key), 0); + assert!(get_access_key_insertion_key(access_key) + == insertion_key, 0); + } + + #[test] + /// Verify successful state updates. + fun test_borrow_borrow_mut() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + let access_key_0 = insert(&mut avlq, 0, 123); // Insert key. + // Assert borrow. + assert!(*borrow(&avlq, access_key_0) == 123, 0); + *borrow_mut(&mut avlq, access_key_0) = 456; // Mutate directly. + assert!(*borrow_head(&avlq) == 456, 0); // Assert head borrow. + *borrow_head_mut(&mut avlq) = 789; // Mutate via head lookup. + assert!(*borrow_tail(&avlq) == 789, 0); // Assert tail borrow. + *borrow_tail_mut(&mut avlq) = 321; // Mutate via tail lookup. + insert(&mut avlq, 1, 654); // Insert new tail. + assert!(*borrow_head(&avlq) == 321, 0); // Assert head borrow. + assert!(*borrow_tail(&avlq) == 654, 0); // Assert tail borrow. + *borrow_tail_mut(&mut avlq) = 987; // Mutate via tail lookup. + assert!(*borrow_tail(&avlq) == 987, 0); // Assert tail borrow. + *borrow_head_mut(&mut avlq) = 123; // Mutate via head lookup. + // Assert borrow. + assert!(*borrow(&avlq, access_key_0) == 123, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify successful extraction. + fun test_get_child_left_test() { + let tree_node = TreeNode{bits: u_128_by_32( // Create tree node. + b"11111111111111111111111111111111", + b"11111111111111111111111111100000", + // ^ bit 69 + b"00000001111111111111111111111111", + // ^ bit 56 + b"11111111111111111111111111111111")}; + // Assert left child node ID. + assert!(get_child_left_test(&tree_node) == u_64(b"10000000000001"), 0); + let TreeNode{bits: _} = tree_node; // Unpack tree node. + } + + #[test] + /// Verify successful extraction. + fun test_get_child_right_test() { + let tree_node = TreeNode{bits: u_128_by_32( // Create tree node. + b"11111111111111111111111111111111", + b"11111111111111111111111111111111", + b"11111111100000000000011111111111", + // ^ bit 55 ^ bit 42 + b"11111111111111111111111111111111")}; + assert!( // Assert right child node ID. + get_child_right_test(&tree_node) == u_64(b"10000000000001"), 0); + let TreeNode{bits: _} = tree_node; // Unpack tree node. + } + + #[test] + /// Verify successful returns. + fun test_get_head_tail_key() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Assert head and tail keys indicated as none. + assert!(option::is_none(&get_head_key(&avlq)), 0); + assert!(option::is_none(&get_tail_key(&avlq)), 0); + // Insert minimum insertion key. + let access_key_lo = insert(&mut avlq, 0, 0); + // Assert head and tail keys indicate the same. + assert!(*option::borrow(&get_head_key(&avlq)) == 0, 0); + assert!(*option::borrow(&get_tail_key(&avlq)) == 0, 0); + // Insert maximum insertion key. + let access_key_hi = insert(&mut avlq, HI_INSERTION_KEY, HI_64); + // Assert head and tail keys indicate differently. + assert!(*option::borrow(&get_head_key(&avlq)) == 0, 0); + assert!(*option::borrow(&get_tail_key(&avlq)) == HI_INSERTION_KEY, 0); + // Remove minimum insertion key. + remove(&mut avlq, access_key_lo); + // Assert head and tail keys indicate the same. + assert!(*option::borrow(&get_head_key(&avlq)) == HI_INSERTION_KEY, 0); + assert!(*option::borrow(&get_tail_key(&avlq)) == HI_INSERTION_KEY, 0); + // Remove maximum insertion key. + remove(&mut avlq, access_key_hi); + // Assert head and tail keys indicated as none. + assert!(option::is_none(&get_head_key(&avlq)), 0); + assert!(option::is_none(&get_tail_key(&avlq)), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify successful returns for `get_height()` reference diagram. + fun test_get_height() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Assert height indicated as none. + assert!(option::is_none(&get_height(&avlq)), 0); + insert(&mut avlq, 4, 0); // Insert key 4. + // Assert height indicated as 0. + assert!(*option::borrow(&get_height(&avlq)) == 0, 0); + insert(&mut avlq, 5, 0); // Insert key 5. + // Assert height indicated as 1. + assert!(*option::borrow(&get_height(&avlq)) == 1, 0); + insert(&mut avlq, 3, 0); // Insert key 3. + // Assert height still indicated as 1. + assert!(*option::borrow(&get_height(&avlq)) == 1, 0); + insert(&mut avlq, 1, 0); // Insert key 1. + // Assert height indicated as 2. + assert!(*option::borrow(&get_height(&avlq)) == 2, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify successful extraction. + fun test_get_height_left_test() { + let tree_node = TreeNode{bits: u_128_by_32( // Create tree node. + b"11111111111111111111111111111111", + b"11100011111111111111111111111111", + // ^ ^ bits 89-93 + b"11111111111111111111111111111111", + b"11111111111111111111111111111111")}; + // Assert left height. + assert!(get_height_left_test(&tree_node) == (u_64(b"10001") as u8), 0); + let TreeNode{bits: _} = tree_node; // Unpack tree node. + } + + #[test] + /// Verify successful extraction. + fun test_get_height_right_test() { + let tree_node = TreeNode{bits: u_128_by_32( // Create tree node. + b"11111111111111111111111111111111", + b"11111111000111111111111111111111", + // ^ ^ bits 84-88 + b"11111111111111111111111111111111", + b"11111111111111111111111111111111")}; + assert!( // Assert right height. + get_height_right_test(&tree_node) == (u_64(b"10001") as u8), 0); + let TreeNode{bits: _} = tree_node; // Unpack tree node. + } + + #[test] + /// Verify successful extraction. + fun test_get_insertion_key_test() { + let tree_node = TreeNode{bits: u_128_by_32( // Create tree node. + b"11100000000000000000000000000000", + // ^ bit 125 + b"01111111111111111111111111111111", + // ^ bit 94 + b"11111111111111111111111111111111", + b"11111111111111111111111111111111")}; + // Assert insertion key + assert!(get_insertion_key_test(&tree_node) == + u_64(b"10000000000000000000000000000001"), 0); + let TreeNode{bits: _} = tree_node; // Unpack tree node. + } + + #[test] + /// Verify successful extraction. + fun test_get_list_head_test() { + let tree_node = TreeNode{bits: u_128_by_32( // Create tree node. + b"11111111111111111111111111111111", + b"11111111111111111111111111111111", + b"11111111111111111111111000000000", + // ^ bit 41 + b"00011111111111111111111111111111")}; + // ^ bit 28 + // Assert list head node ID. + assert!(get_list_head_test(&tree_node) == u_64(b"10000000000001"), 0); + let TreeNode{bits: _} = tree_node; // Unpack tree node. + } + + #[test] + /// Verify successful extraction. + fun test_get_list_last_test() { + // Declare list node. + let list_node = ListNode{ + last_msbs: (u_64(b"00100000") as u8), + last_lsbs: (u_64(b"00000001") as u8), + next_msbs: 0, + next_lsbs: 0}; + // Get last node info. + let (node_id, is_tree_node) = get_list_last_test(&list_node); + // Assert last node ID. + assert!(node_id == u_64(b"10000000000001"), 0); + // Assert not marked as tree node. + assert!(!is_tree_node, 0); + // Flag as tree node. + list_node.last_msbs = (u_64(b"01100000") as u8); + // Get last node info. + (node_id, is_tree_node) = get_list_last_test(&list_node); + // Assert last node ID unchanged. + assert!(node_id == u_64(b"10000000000001"), 0); + // Assert marked as tree node. + assert!(is_tree_node, 0); + ListNode{last_msbs: _, last_lsbs: _, next_msbs: _, next_lsbs: _} = + list_node; // Unpack list node. + } + + #[test] + /// Verify successful extraction. + fun test_get_list_next_test() { + // Declare list node. + let list_node = ListNode{ + last_msbs: 0, + last_lsbs: 0, + next_msbs: (u_64(b"00100000") as u8), + next_lsbs: (u_64(b"00000001") as u8)}; + // Get next node info. + let (node_id, is_tree_node) = get_list_next_test(&list_node); + // Assert next node ID. + assert!(node_id == u_64(b"10000000000001"), 0); + // Assert not marked as tree node. + assert!(!is_tree_node, 0); + // Flag as tree node. + list_node.next_msbs = (u_64(b"01100000") as u8); + // Get next node info. + (node_id, is_tree_node) = get_list_next_test(&list_node); + // Assert next node ID unchanged. + assert!(node_id == u_64(b"10000000000001"), 0); + // Assert marked as tree node. + assert!(is_tree_node, 0); + ListNode{last_msbs: _, last_lsbs: _, next_msbs: _, next_lsbs: _} = + list_node; // Unpack list node. + } + + #[test] + /// Verify successful extraction. + fun test_get_list_tail_test() { + let tree_node = TreeNode{bits: u_128_by_32( // Create tree node. + b"11111111111111111111111111111111", + b"11111111111111111111111111111111", + b"11111111111111111111111111111111", + b"11111000000000000111111111111111")}; + // ^ bit 27 ^ bit 14 + // Assert list tail node ID. + assert!(get_list_tail_test(&tree_node) == u_64(b"10000000000001"), 0); + let TreeNode{bits: _} = tree_node; // Unpack tree node. + } + + #[test] + /// Verify successful extraction. + fun test_get_list_top_test() { + let avlq = AVLqueue{ // Create empty AVL queue. + bits: u_128_by_32( + b"11111111111111111000000000000111", + // ^ bit 111 ^ bit 98 + b"11111111111111111111111111111111", + b"11111111111111111111111111111111", + b"11111111111111111111111111111111"), + root_lsbs: (NIL as u8), + tree_nodes: table_with_length::new(), + list_nodes: table_with_length::new(), + values: table::new(), + }; + // Assert list top. + assert!(get_list_top_test(&avlq) == u_64(b"10000000000001"), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify successful extraction. + fun test_get_parent_test() { + let tree_node = TreeNode{bits: u_128_by_32( // Create tree node. + b"11111111111111111111111111111111", + b"11111111111110000000000001111111", + // ^ bit 83 ^ bit 70 + b"11111111111111111111111111111111", + b"11111111111111111111111111111111")}; + // Assert parent node ID. + assert!(get_parent_test(&tree_node) == u_64(b"10000000000001"), 0); + let TreeNode{bits: _} = tree_node; // Unpack tree node. + } + + #[test] + /// Verify successful extraction. + fun test_get_tree_next_test() { + // Declare tree node. + let tree_node = TreeNode{bits: u_128_by_32( + b"11111111111111111111111111111111", + b"11111111111111111111111111111111", + b"11111111111111111111111111111111", + b"11111111111111111110000000000001")}; + assert!( // Assert next node ID. + get_tree_next_test(&tree_node) == u_64(b"10000000000001"), 0); + TreeNode{bits: _} = tree_node; // Unpack tree node. + } + + #[test] + /// Verify successful extraction. + fun test_get_tree_top_test() { + let avlq = AVLqueue{ // Create empty AVL queue. + bits: u_128_by_32( + b"11100000000000011111111111111111", + // ^ bit 125 ^ bit 112 + b"11111111111111111111111111111111", + b"11111111111111111111111111111111", + b"11111111111111111111111111111111"), + root_lsbs: (NIL as u8), + tree_nodes: table_with_length::new(), + list_nodes: table_with_length::new(), + values: table::new(), + }; + // Assert tree top. + assert!(get_tree_top_test(&avlq) == u_64(b"10000000000001"), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify expected returns. + fun test_contains_active_list_node_id() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + let access_key = HI_64; // Declare bogus access key. + // Assert marked as not having active list node ID encoded. + assert!(!contains_active_list_node_id(&avlq, access_key), 0); + // Insert arbitrary key, reassigning access key. + access_key = insert(&mut avlq, 1, 0); + // Assert marked as having active list node ID encoded. + assert!(contains_active_list_node_id(&avlq, access_key), 0); + remove(&mut avlq, access_key); // Remove key-value pair. + // Assert marked as not having active list node ID encoded. + assert!(!contains_active_list_node_id(&avlq, access_key), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify expected returns. + fun test_has_key() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Assert not marked as having arbitrary key. + assert!(!has_key(&avlq, 1), 0); + insert(&mut avlq, 1, 0); // Insert arbitrary key. + // Assert marked as having key. + assert!(has_key(&avlq, 1), 0); + // Assert not marked as having different key. + assert!(!has_key(&avlq, 2), 0); + assert!(!has_key(&avlq, HI_INSERTION_KEY), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + #[expected_failure(abort_code = E_INSERTION_KEY_TOO_LARGE)] + /// Verify failure for insertion key too big. + fun test_has_key_too_big() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Attempt invalid invocation. + has_key(&avlq, HI_INSERTION_KEY + 1); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify insertion sequence from `insert()`. + fun test_insert() { + // Init ascending AVL queue with allocated nodes. + let avlq = new(ASCENDING, 7, 3); + // Insert per reference diagram, storing access keys and testing + // local tail check updates. + let access_key_3_9 = insert(&mut avlq, 3, 9); + assert!(is_local_tail(&avlq, access_key_3_9), 0); + let access_key_4_8 = insert(&mut avlq, 4, 8); + assert!(is_local_tail(&avlq, access_key_4_8), 0); + let access_key_5_7 = insert(&mut avlq, 5, 7); + assert!(is_local_tail(&avlq, access_key_5_7), 0); + let access_key_3_6 = insert(&mut avlq, 3, 6); + assert!(is_local_tail(&avlq, access_key_3_6), 0); + assert!(!is_local_tail(&avlq, access_key_3_9), 0); + let access_key_5_5 = insert(&mut avlq, 5, 5); + assert!(!is_local_tail(&avlq, access_key_5_7), 0); + assert!(is_local_tail(&avlq, access_key_5_5), 0); + // Declare expected node IDs per initial node allocations. + let tree_node_id_3_9 = 7; + let tree_node_id_4_8 = 6; + let tree_node_id_5_7 = 5; + let tree_node_id_3_6 = 7; + let tree_node_id_5_5 = 5; + let list_node_id_3_9 = 3; + let list_node_id_4_8 = 2; + let list_node_id_5_7 = 1; + let list_node_id_3_6 = 4; + let list_node_id_5_5 = 5; + let tree_node_id_3 = tree_node_id_3_9; + let tree_node_id_4 = tree_node_id_4_8; + let tree_node_id_5 = tree_node_id_5_7; + // Assert access key insertion keys. + assert!(get_access_key_insertion_key(access_key_3_9) == 3, 0); + assert!(get_access_key_insertion_key(access_key_4_8) == 4, 0); + assert!(get_access_key_insertion_key(access_key_5_7) == 5, 0); + assert!(get_access_key_insertion_key(access_key_3_6) == 3, 0); + assert!(get_access_key_insertion_key(access_key_5_5) == 5, 0); + // Assert access key sort order. + assert!(is_ascending_access_key(access_key_3_9), 0); + assert!(is_ascending_access_key(access_key_4_8), 0); + assert!(is_ascending_access_key(access_key_5_7), 0); + assert!(is_ascending_access_key(access_key_3_6), 0); + assert!(is_ascending_access_key(access_key_5_5), 0); + // Assert access key tree node IDs. + assert!(get_access_key_tree_node_id_test(access_key_3_9) + == tree_node_id_3_9, 0); + assert!(get_access_key_tree_node_id_test(access_key_4_8) + == tree_node_id_4_8, 0); + assert!(get_access_key_tree_node_id_test(access_key_5_7) + == tree_node_id_5_7, 0); + assert!(get_access_key_tree_node_id_test(access_key_3_6) + == tree_node_id_3_6, 0); + assert!(get_access_key_tree_node_id_test(access_key_5_5) + == tree_node_id_5_5, 0); + // Assert access key list node IDs. + assert!(get_access_key_list_node_id_test(access_key_3_9) + == list_node_id_3_9, 0); + assert!(get_access_key_list_node_id_test(access_key_4_8) + == list_node_id_4_8, 0); + assert!(get_access_key_list_node_id_test(access_key_5_7) + == list_node_id_5_7, 0); + assert!(get_access_key_list_node_id_test(access_key_3_6) + == list_node_id_3_6, 0); + assert!(get_access_key_list_node_id_test(access_key_5_5) + == list_node_id_5_5, 0); + // Assert root tree node ID. + assert!(get_root_test(&avlq) == tree_node_id_4, 0); + // Assert inactive tree node stack top + assert!(get_tree_top_test(&avlq) == 4, 0); + // Assert empty inactive list node stack. + assert!(get_list_top_test(&avlq) == (NIL as u64), 0); + // Assert AVL queue head and tail. + assert!(get_head_node_id_test(&avlq) == list_node_id_3_9, 0); + assert!(get_tail_node_id_test(&avlq) == list_node_id_5_5, 0); + assert!(get_head_key_test(&avlq) == 3, 0); + assert!(get_tail_key_test(&avlq) == 5, 0); + // Assert all tree node state. + assert!(get_insertion_key_by_id_test(&avlq, tree_node_id_3) == 3, 0); + assert!(get_height_left_by_id_test( &avlq, tree_node_id_3) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, tree_node_id_3) == 0, 0); + assert!(get_parent_by_id_test( &avlq, tree_node_id_3) + == tree_node_id_4, 0); + assert!(get_child_left_by_id_test( &avlq, tree_node_id_3) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, tree_node_id_3) + == (NIL as u64), 0); + assert!(get_list_head_by_id_test( &avlq, tree_node_id_3) + == list_node_id_3_9, 0); + assert!(get_list_tail_by_id_test( &avlq, tree_node_id_3) + == list_node_id_3_6, 0); + assert!(get_tree_next_by_id_test( &avlq, tree_node_id_3) + == (NIL as u64), 0); + assert!(get_insertion_key_by_id_test(&avlq, tree_node_id_4) == 4, 0); + assert!(get_height_left_by_id_test( &avlq, tree_node_id_4) == 1, 0); + assert!(get_height_right_by_id_test( &avlq, tree_node_id_4) == 1, 0); + assert!(get_parent_by_id_test( &avlq, tree_node_id_4) + == (NIL as u64), 0); + assert!(get_child_left_by_id_test( &avlq, tree_node_id_4) + == tree_node_id_3, 0); + assert!(get_child_right_by_id_test( &avlq, tree_node_id_4) + == tree_node_id_5, 0); + assert!(get_list_head_by_id_test( &avlq, tree_node_id_4) + == list_node_id_4_8, 0); + assert!(get_list_tail_by_id_test( &avlq, tree_node_id_4) + == list_node_id_4_8, 0); + assert!(get_tree_next_by_id_test( &avlq, tree_node_id_4) + == (NIL as u64), 0); + assert!(get_insertion_key_by_id_test(&avlq, tree_node_id_5) == 5, 0); + assert!(get_height_left_by_id_test( &avlq, tree_node_id_5) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, tree_node_id_5) == 0, 0); + assert!(get_parent_by_id_test( &avlq, tree_node_id_5) + == tree_node_id_4, 0); + assert!(get_child_left_by_id_test( &avlq, tree_node_id_5) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, tree_node_id_5) + == (NIL as u64), 0); + assert!(get_list_head_by_id_test( &avlq, tree_node_id_5) + == list_node_id_5_7, 0); + assert!(get_list_tail_by_id_test( &avlq, tree_node_id_5) + == list_node_id_5_5, 0); + assert!(get_tree_next_by_id_test( &avlq, tree_node_id_5) + == (NIL as u64), 0); + // Assert all list node state. + assert!(get_list_last_node_id_by_id_test( &avlq, list_node_id_3_9) + == tree_node_id_3, 0); + assert!( is_tree_node_list_last_by_id_test(&avlq, list_node_id_3_9), + 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_node_id_3_9) + == list_node_id_3_6, 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, list_node_id_3_9), + 0); + assert!(get_list_last_node_id_by_id_test( &avlq, list_node_id_3_6) + == list_node_id_3_9, 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, list_node_id_3_6), + 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_node_id_3_6) + == tree_node_id_3, 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, list_node_id_3_6), + 0); + assert!(get_list_last_node_id_by_id_test( &avlq, list_node_id_4_8) + == tree_node_id_4, 0); + assert!( is_tree_node_list_last_by_id_test(&avlq, list_node_id_4_8), + 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_node_id_4_8) + == tree_node_id_4, 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, list_node_id_4_8), + 0); + assert!(get_list_last_node_id_by_id_test( &avlq, list_node_id_5_7) + == tree_node_id_5, 0); + assert!( is_tree_node_list_last_by_id_test(&avlq, list_node_id_5_7), + 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_node_id_5_7) + == list_node_id_5_5, 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, list_node_id_5_7), + 0); + assert!(get_list_last_node_id_by_id_test( &avlq, list_node_id_5_5) + == list_node_id_5_7, 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, list_node_id_5_5), + 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_node_id_5_5) + == tree_node_id_5, 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, list_node_id_5_5), + 0); + // Assert all insertion values. + assert!(get_value_test(&avlq, list_node_id_3_9) == 9, 0); + assert!(get_value_test(&avlq, list_node_id_3_6) == 6, 0); + assert!(get_value_test(&avlq, list_node_id_4_8) == 8, 0); + assert!(get_value_test(&avlq, list_node_id_5_7) == 7, 0); + assert!(get_value_test(&avlq, list_node_id_5_5) == 5, 0); + drop_avlq_test(avlq); // Drop AVL queue. + // Assert access keys flagged as such for descending AVL queue. + avlq = new(DESCENDING, 0, 0); + assert!(!is_ascending_access_key(insert(&mut avlq, 1, 0)), 0); + assert!(!is_ascending_access_key(insert(&mut avlq, 2, 0)), 0); + assert!(!is_ascending_access_key(insert(&mut avlq, 3, 0)), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify returns, state updates for `insert_check_eviction()` + /// reference diagram case 1. + fun test_insert_check_eviction_case_1() { + let avlq = new(ASCENDING, 0, 0); // Initialize AVL queue. + // Insert root tree node and node with insertion key 1. + insert(&mut avlq, 1, 2); + insert(&mut avlq, 1, 2); + let v = 3; // Initialize insertion value counter. + while (v <= N_NODES_MAX) { // Activate max list nodes. + insert(&mut avlq, 3, v); // Insert with insertion value 3. + v = v + 1; // Increment value counter. + }; + // Attempt invalid insertion. + let (access_key, evictee_access_key, evictee_value) = + insert_check_eviction(&mut avlq, 3, 123, 2); + // Assert flagged invalid. + assert!(access_key == (NIL as u64), 0); + assert!(evictee_access_key == (NIL as u64), 0); + assert!(*option::borrow(&evictee_value) == 123, 0); + // Attempt valid insertion. + (access_key, evictee_access_key, evictee_value) = + insert_check_eviction(&mut avlq, 2, 123, 2); + // Assert access key lookup on insertion value. + assert!(*borrow(&avlq, access_key) == 123, 0); + // Assert encoded insertion key. + assert!(get_access_key_insertion_key(access_key) == 2, 0); + // Assert evictee insertion key. + assert!(get_access_key_insertion_key(evictee_access_key) == 3, 0); + // Assert evictee insertion value. + assert!(*option::borrow(&evictee_value) == N_NODES_MAX, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify returns, state updates for `insert_check_eviction()` + /// reference diagram case 2. + fun test_insert_check_eviction_case_2() { + let avlq = new(DESCENDING, 0, 0); // Initialize AVL queue. + // Insert nodes top to bottom, left to right. + insert(&mut avlq, 2, 123); + insert(&mut avlq, 1, 456); + insert(&mut avlq, 3, 789); + insert(&mut avlq, 4, 321); + // Attempt invalid insertion. + let (access_key, evictee_access_key, evictee_value) = + insert_check_eviction(&mut avlq, 1, 987, 1); + // Assert flagged invalid. + assert!(access_key == (NIL as u64), 0); + assert!(evictee_access_key == (NIL as u64), 0); + assert!(*option::borrow(&evictee_value) == 987, 0); + // Attempt valid insertion. + (access_key, evictee_access_key, evictee_value) = + insert_check_eviction(&mut avlq, 2, 987, 1); + // Assert access key lookup on insertion value. + assert!(*borrow(&avlq, access_key) == 987, 0); + // Assert encoded insertion key. + assert!(get_access_key_insertion_key(access_key) == 2, 0); + // Assert evictee insertion key. + assert!(get_access_key_insertion_key(evictee_access_key) == 1, 0); + // Assert evictee insertion value. + assert!(*option::borrow(&evictee_value) == 456, 0); + // Attempt valid insertion. + (access_key, evictee_access_key, evictee_value) = + insert_check_eviction(&mut avlq, 1, 654, 10); + // Assert access key lookup on insertion value. + assert!(*borrow(&avlq, access_key) == 654, 0); + // Assert encoded insertion key. + assert!(get_access_key_insertion_key(access_key) == 1, 0); + // Assert no evictee. + assert!(evictee_access_key == (NIL as u64), 0); + assert!(option::is_none(&evictee_value), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify returns, state updates for inserting to empty AVL queue. + fun test_insert_check_eviction_empty() { + let avlq = new(ASCENDING, 0, 0); // Initialize AVL queue. + let (access_key, evictee_access_key, evictee_value) = + insert_check_eviction(&mut avlq, 123, 456, 0); + // Assert access key lookup on insertion value. + assert!(*borrow(&avlq, access_key) == 456, 0); + // Assert encoded insertion key. + assert!(get_access_key_insertion_key(access_key) == 123, 0); + // Assert no evictee. + assert!(evictee_access_key == (NIL as u64), 0); + assert!(option::is_none(&evictee_value), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + #[expected_failure(abort_code = E_INVALID_HEIGHT)] + /// Verify failure for invalid height. + fun test_insert_check_eviction_invalid_height() { + let avlq = new(ASCENDING, 0, 0); // Initialize AVL queue. + // Attempt invalid insertion. + insert_check_eviction(&mut avlq, 1, 1, MAX_HEIGHT + 1); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify successful state manipulation. + fun test_insert_check_head_tail_ascending() { + // Init ascending AVL queue. + let avlq = new(ASCENDING, 0, 0); + // Assert head and tail fields. + assert!(get_head_key_test(&avlq) == (NIL as u64), 0); + assert!(get_tail_key_test(&avlq) == (NIL as u64), 0); + assert!(get_head_node_id_test(&avlq) == (NIL as u64), 0); + assert!(get_tail_node_id_test(&avlq) == (NIL as u64), 0); + // Declare insertion key and list node ID. + let key_0 = HI_INSERTION_KEY - 1; + let list_node_id_0 = HI_NODE_ID; + // Check head and tail accordingly. + insert_check_head_tail(&mut avlq, key_0, list_node_id_0); + // Assert head and tail fields both updated. + assert!(get_head_key_test(&avlq) == key_0, 0); + assert!(get_tail_key_test(&avlq) == key_0, 0); + assert!(get_head_node_id_test(&avlq) == list_node_id_0, 0); + assert!(get_tail_node_id_test(&avlq) == list_node_id_0, 0); + // Declare same insertion key with new node ID. + let key_1 = key_0; + let list_node_id_1 = list_node_id_0 - 1; + // Check head and tail accordingly. + insert_check_head_tail(&mut avlq, key_1, list_node_id_1); + // Assert head not updated, but tail updated. + assert!(get_head_key_test(&avlq) == key_0, 0); + assert!(get_tail_key_test(&avlq) == key_0, 0); + assert!(get_tail_key_test(&avlq) == key_1, 0); + assert!(get_head_node_id_test(&avlq) == list_node_id_0, 0); + assert!(get_tail_node_id_test(&avlq) == list_node_id_1, 0); + // Declare insertion key smaller than first, new node ID. + let key_2 = key_1 - 1; + let list_node_id_2 = list_node_id_1 - 1; + // Check head and tail accordingly. + insert_check_head_tail(&mut avlq, key_2, list_node_id_2); + // Assert head updated, but tail not updated. + assert!(get_head_key_test(&avlq) == key_2, 0); + assert!(get_tail_key_test(&avlq) == key_0, 0); + assert!(get_head_node_id_test(&avlq) == list_node_id_2, 0); + assert!(get_tail_node_id_test(&avlq) == list_node_id_1, 0); + // Declare insertion key larger than first, new node ID. + let key_3 = key_0 + 1; + let list_node_id_3 = list_node_id_1 - 1; + // Check head and tail accordingly. + insert_check_head_tail(&mut avlq, key_3, list_node_id_3); + // Assert head not updated, but tail updated. + assert!(get_head_key_test(&avlq) == key_2, 0); + assert!(get_tail_key_test(&avlq) == key_3, 0); + assert!(get_head_node_id_test(&avlq) == list_node_id_2, 0); + assert!(get_tail_node_id_test(&avlq) == list_node_id_3, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify successful state manipulation. + fun test_insert_check_head_tail_descending() { + // Init descending AVL queue. + let avlq = new(DESCENDING, 0, 0); + // Assert head and tail fields. + assert!(get_head_key_test(&avlq) == (NIL as u64), 0); + assert!(get_tail_key_test(&avlq) == (NIL as u64), 0); + assert!(get_head_node_id_test(&avlq) == (NIL as u64), 0); + assert!(get_tail_node_id_test(&avlq) == (NIL as u64), 0); + // Declare insertion key and list node ID. + let key_0 = HI_INSERTION_KEY - 1; + let list_node_id_0 = HI_NODE_ID; + // Check head and tail accordingly. + insert_check_head_tail(&mut avlq, key_0, list_node_id_0); + // Assert head and tail fields both updated. + assert!(get_head_key_test(&avlq) == key_0, 0); + assert!(get_tail_key_test(&avlq) == key_0, 0); + assert!(get_head_node_id_test(&avlq) == list_node_id_0, 0); + assert!(get_tail_node_id_test(&avlq) == list_node_id_0, 0); + // Declare same insertion key with new node ID. + let key_1 = key_0; + let list_node_id_1 = list_node_id_0 - 1; + // Check head and tail accordingly. + insert_check_head_tail(&mut avlq, key_1, list_node_id_1); + // Assert head not updated, but tail updated. + assert!(get_head_key_test(&avlq) == key_0, 0); + assert!(get_tail_key_test(&avlq) == key_0, 0); + assert!(get_tail_key_test(&avlq) == key_1, 0); + assert!(get_head_node_id_test(&avlq) == list_node_id_0, 0); + assert!(get_tail_node_id_test(&avlq) == list_node_id_1, 0); + // Declare insertion key larger than first, new node ID. + let key_2 = key_1 + 1; + let list_node_id_2 = list_node_id_1 - 1; + // Check head and tail accordingly. + insert_check_head_tail(&mut avlq, key_2, list_node_id_2); + // Assert head updated, but tail not updated. + assert!(get_head_key_test(&avlq) == key_2, 0); + assert!(get_tail_key_test(&avlq) == key_0, 0); + assert!(get_head_node_id_test(&avlq) == list_node_id_2, 0); + assert!(get_tail_node_id_test(&avlq) == list_node_id_1, 0); + // Declare insertion key smaller than first, new node ID. + let key_3 = key_0 - 1; + let list_node_id_3 = list_node_id_1 - 1; + // Check head and tail accordingly. + insert_check_head_tail(&mut avlq, key_3, list_node_id_3); + // Assert head not updated, but tail updated. + assert!(get_head_key_test(&avlq) == key_2, 0); + assert!(get_tail_key_test(&avlq) == key_3, 0); + assert!(get_head_node_id_test(&avlq) == list_node_id_2, 0); + assert!(get_tail_node_id_test(&avlq) == list_node_id_3, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify successful returns for ascending and descending cases. + fun test_insert_evict_tail() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Insert two elements in same doubly linked list, storing + // access keys. + let (access_key_2_123, access_key_2_456) = ( + insert(&mut avlq, 2, 123), insert(&mut avlq, 2, 456)); + // Insert and evict tail, storing returns. + let (access_key_1_789, access_key_return, insertion_value_return) = + insert_evict_tail(&mut avlq, 1, 789); + // Assert returns. + assert!(access_key_return == access_key_2_456, 0); + assert!(insertion_value_return == 456, 0); + // Insert and evict tail, storing returns. + let (access_key_1_321, access_key_return, insertion_value_return) = + insert_evict_tail(&mut avlq, 1, 321); + // Assert returns. + assert!(access_key_return == access_key_2_123, 0); + assert!(insertion_value_return == 123, 0); + // Insert and evict tail, storing returns. + (_, access_key_return, insertion_value_return) = + insert_evict_tail(&mut avlq, 0, 0); + // Assert returns. + assert!(access_key_return == access_key_1_321, 0); + assert!(insertion_value_return == 321, 0); + // Insert and evict tail, storing returns. + (_, access_key_return, insertion_value_return) = + insert_evict_tail(&mut avlq, 0, 0); + // Assert returns. + assert!(access_key_return == access_key_1_789, 0); + assert!(insertion_value_return == 789, 0); + drop_avlq_test(avlq); // Drop AVL queue. + avlq = new(DESCENDING, 0, 0); // Init AVL queue. + // Insert two elements in same doubly linked list, storing + // access keys. + (access_key_2_123, access_key_2_456) = ( + insert(&mut avlq, 2, 123), insert(&mut avlq, 2, 456)); + // Insert and evict tail, storing returns. + let (access_key_3_789, access_key_return, insertion_value_return) = + insert_evict_tail(&mut avlq, 3, 789); + // Assert returns. + assert!(access_key_return == access_key_2_456, 0); + assert!(insertion_value_return == 456, 0); + // Insert and evict tail, storing returns. + let (access_key_3_321, access_key_return, insertion_value_return) = + insert_evict_tail(&mut avlq, 3, 321); + // Assert returns. + assert!(access_key_return == access_key_2_123, 0); + assert!(insertion_value_return == 123, 0); + // Insert and evict tail, storing returns. + (_, access_key_return, insertion_value_return) = + insert_evict_tail(&mut avlq, 4, 0); + // Assert returns. + assert!(access_key_return == access_key_3_321, 0); + assert!(insertion_value_return == 321, 0); + // Insert and evict tail, storing returns. + (_, access_key_return, insertion_value_return) = + insert_evict_tail(&mut avlq, 4, 0); + // Assert returns. + assert!(access_key_return == access_key_3_789, 0); + assert!(insertion_value_return == 789, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + #[expected_failure(abort_code = E_EVICT_EMPTY)] + /// Verify failure for insertion with eviction from empty AVL queue. + fun test_insert_evict_tail_empty() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Attempt invalid insertion with eviction. + insert_evict_tail(&mut avlq, 0, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + #[expected_failure(abort_code = E_EVICT_NEW_TAIL)] + /// Verify failure for insertion with eviction for key-value + /// insertion pair that would become tail. + fun test_insert_evict_tail_new_tail_ascending() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + insert(&mut avlq, 0, 0); // Insert tail. + insert(&mut avlq, 1, 0); // Insert new tail. + // Attempt invalid insertion with eviction. + insert_evict_tail(&mut avlq, 1, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + #[expected_failure(abort_code = E_EVICT_NEW_TAIL)] + /// Verify failure for insertion with eviction for key-value + /// insertion pair that would become tail. + fun test_insert_evict_tail_new_tail_descending() { + let avlq = new(DESCENDING, 0, 0); // Init AVL queue. + insert(&mut avlq, 1, 0); // Insert tail. + insert(&mut avlq, 0, 0); // Insert new tail. + // Attempt invalid insertion with eviction. + insert_evict_tail(&mut avlq, 0, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + #[expected_failure(abort_code = E_INSERTION_KEY_TOO_LARGE)] + /// Verify failure for insertion key too large. + fun test_insert_insertion_key_too_large() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Attempt invalid insertion + insert(&mut avlq, HI_INSERTION_KEY + 1, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify return and state updates for allocating new list node. + fun test_insert_list_node_assign_fields_allocate() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Declare inputs. + let value = 123; + let last = 456; + let next = 789; + // Assign fields to inserted list node, store its ID. + let list_node_id = insert_list_node_assign_fields( + &mut avlq, last, next, value); + assert!(list_node_id == 1, 0); // Assert list node ID. + // Assert field assignments. + let list_node_ref = borrow_list_node_test(&avlq, list_node_id); + let (last_assigned, _) = get_list_last_test(list_node_ref); + assert!(last_assigned == last, 0); + let (next_assigned, _) = get_list_next_test(list_node_ref); + assert!(next_assigned == next, 0); + assert!(get_value_test(&avlq, list_node_id) == value, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify return and state updates for inserting stack top. + fun test_insert_list_node_assign_fields_stacked() { + let stack_top_id = 321; + let avlq = new(ASCENDING, 0, stack_top_id); // Init AVL queue. + // Declare inputs. + let value = 123; + let last = 456; + let next = 789; + // Assign fields to inserted list node, store its ID. + let list_node_id = insert_list_node_assign_fields( + &mut avlq, last, next, value); + // Assert list node ID. + assert!(list_node_id == stack_top_id, 0); + // Assert field assignments. + let list_node_ref = borrow_list_node_test(&avlq, list_node_id); + let (last_assigned, _) = get_list_last_test(list_node_ref); + assert!(last_assigned == last, 0); + let (next_assigned, _) = get_list_next_test(list_node_ref); + assert!(next_assigned == next, 0); + assert!(get_value_test(&avlq, list_node_id) == value, 0); + // Assert stack top update. + assert!(get_list_top_test(&avlq) == stack_top_id - 1, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify returns for list node becoming new tail. + fun test_insert_list_node_get_last_next_new_tail() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + let anchor_tree_node_id = 15; // Declare anchor tree node ID. + let old_list_tail = 31; // Declare old list tail node ID. + // Manually add anchor tree node to tree nodes table. + table_with_length::add(&mut avlq.tree_nodes, anchor_tree_node_id, + TreeNode{bits: (old_list_tail as u128) << SHIFT_LIST_TAIL}); + let (last, next) = // Get virtual last and next fields. + insert_list_node_get_last_next(&avlq, anchor_tree_node_id); + // Assert last and next fields. + assert!(last == u_64(b"11111"), 0); + assert!(next == u_64(b"100000000001111"), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify returns for solo list node and allocated tree node. + fun test_insert_list_node_get_last_next_solo_allocate() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + let (last, next) = // Get virtual last and next fields. + insert_list_node_get_last_next(&avlq, (NIL as u64)); + // Assert last and next fields. + assert!(last == u_64(b"100000000000001"), 0); + assert!(next == u_64(b"100000000000001"), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify returns for solo list node and tree node on stack. + fun test_insert_list_node_get_last_next_solo_stacked() { + let avlq = new(ASCENDING, 7, 0); // Init AVL queue. + let (last, next) = // Get virtual last and next fields. + insert_list_node_get_last_next(&avlq, (NIL as u64)); + // Assert last and next fields. + assert!(last == u_64(b"100000000000111"), 0); + assert!(next == u_64(b"100000000000111"), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify return, state updates for list node that is not solo. + fun test_insert_list_node_not_solo() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Declare old list tail state. + let old_list_tail = 1; + let anchor_tree_node_id = 321; + let list_node_id = 2; // Declare list node ID post-allocation. + // Manually add anchor tree node to tree nodes table. + table_with_length::add(&mut avlq.tree_nodes, anchor_tree_node_id, + TreeNode{bits: (old_list_tail as u128) << SHIFT_LIST_TAIL}); + // Manually add old list tail to list nodes table. + table_with_length::add(&mut avlq.list_nodes, old_list_tail, + ListNode{last_msbs: 0, last_lsbs: 0, next_msbs: 0, next_lsbs: 0}); + let value = 100; // Declare insertion value. + let list_node_id_return = // Insert node, storing resultant ID. + insert_list_node(&mut avlq, anchor_tree_node_id, value); + // Assert return. + assert!(list_node_id_return == list_node_id, 0); + // Assert state updates. + let list_node_ref = borrow_list_node_test(&avlq, list_node_id); + let (last_assigned, is_tree_node) = get_list_last_test(list_node_ref); + assert!(last_assigned == old_list_tail, 0); + assert!(!is_tree_node, 0); + let (next_assigned, is_tree_node) = get_list_next_test(list_node_ref); + assert!(next_assigned == anchor_tree_node_id, 0); + assert!(is_tree_node, 0); + let old_tail_ref = borrow_list_node_test(&avlq, old_list_tail); + (next_assigned, is_tree_node) = get_list_next_test(old_tail_ref); + assert!(next_assigned == list_node_id, 0); + assert!(!is_tree_node, 0); + assert!(get_value_test(&avlq, list_node_id) == value, 0); + let anchor_node_ref = + borrow_tree_node_test(&avlq, anchor_tree_node_id); + assert!(get_list_tail_test(anchor_node_ref) == list_node_id, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify return, state updates for solo list node. + fun test_insert_list_node_solo() { + // Declare tree node ID and list node IDs at top of inactive + // stacks. + let tree_node_id = 123; + let list_node_id = 456; + // Init AVL queue. + let avlq = new(ASCENDING, tree_node_id, list_node_id); + let value = 100; // Declare insertion value. + let list_node_id_return = // Insert node, storing resultant ID. + insert_list_node(&mut avlq, (NIL as u64), value); + // Assert return. + assert!(list_node_id_return == list_node_id, 0); + // Assert state updates. + let list_node_ref = borrow_list_node_test(&avlq, list_node_id); + let (last_assigned, is_tree_node) = get_list_last_test(list_node_ref); + assert!(last_assigned == tree_node_id, 0); + assert!(is_tree_node, 0); + let (next_assigned, is_tree_node) = get_list_next_test(list_node_ref); + assert!(next_assigned == tree_node_id, 0); + assert!(is_tree_node, 0); + assert!(get_value_test(&avlq, list_node_id) == value, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + #[expected_failure(abort_code = E_TOO_MANY_LIST_NODES)] + /// Assert failure for too many list nodes. + fun test_insert_too_many_list_nodes() { + // Init AVL queue with max list nodes allocated. + let avlq = new(ASCENDING, 0, N_NODES_MAX); + // Reassign inactive list nodes stack top to null: + avlq.bits = avlq.bits & + (HI_128 ^ // Clear out field via mask unset at field bits. + (((HI_NODE_ID as u128) << SHIFT_LIST_STACK_TOP) as u128)); + // Attempt invalid insertion. + insert(&mut avlq, 0, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state update for inserting tree node with empty stack. + fun test_insert_tree_node_empty() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + let tree_node_id = 1; // Declare inserted tree node ID. + let solo_node_id = 789; // Declare solo list node ID. + let key = 321; // Declare insertion key. + // Insert new tree node, storing its tree node ID. + let tree_node_id_return = insert_tree_node( + &mut avlq, key, (NIL as u64), solo_node_id, option::none()); + // Assert inserted tree node ID. + assert!(tree_node_id_return == tree_node_id, 0); + // Assert new tree node state. + assert!(get_insertion_key_by_id_test(&avlq, tree_node_id) == key, 0); + assert!(get_parent_by_id_test(&avlq, tree_node_id) == (NIL as u64), 0); + assert!(get_list_head_by_id_test(&avlq, tree_node_id) + == solo_node_id, 0); + assert!(get_list_tail_by_id_test(&avlq, tree_node_id) + == solo_node_id, 0); + // Assert stack top. + assert!(get_tree_top_test(&avlq) == (NIL as u64), 0); + // Assert root update. + assert!(get_root_test(&avlq) == tree_node_id, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state update for inserting tree node with stack. + fun test_insert_tree_node_stacked() { + let tree_node_id = 123; // Declare inserted tree node ID. + // Init AVL queue. + let avlq = new(ASCENDING, tree_node_id, 0); + let solo_node_id = 789; // Declare solo list node ID. + let key = 321; // Declare insertion key. + // Insert tree node, storing its tree node ID. + let tree_node_id_return = insert_tree_node( + &mut avlq, key, (NIL as u64), solo_node_id, option::none()); + // Assert inserted tree node ID. + assert!(tree_node_id_return == tree_node_id, 0); + // Assert tree node state. + assert!(get_insertion_key_by_id_test(&avlq, tree_node_id) == key, 0); + assert!(get_parent_by_id_test(&avlq, tree_node_id) == (NIL as u64), 0); + assert!(get_list_head_by_id_test(&avlq, tree_node_id) + == solo_node_id, 0); + assert!(get_list_tail_by_id_test(&avlq, tree_node_id) + == solo_node_id, 0); + // Assert stack top. + assert!(get_tree_top_test(&avlq) == tree_node_id - 1, 0); + // Assert root update. + assert!(get_root_test(&avlq) == tree_node_id, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state update for inserting left child. + fun test_insert_tree_node_update_parent_edge_left() { + let tree_node_id = 1234; // Declare inserted tree node ID. + let parent = 321; + let avlq = new(ASCENDING, parent, 0); // Init AVL queue. + // Declare empty new leaf side. + let new_leaf_side = option::some(LEFT); + // Update parent to inserted node. + insert_tree_node_update_parent_edge( + &mut avlq, tree_node_id, parent, new_leaf_side); + // Assert update to parent's child field. + assert!(get_child_left_by_id_test(&avlq, parent) == tree_node_id, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state update for inserting right child. + fun test_insert_tree_node_update_parent_edge_right() { + let tree_node_id = 1234; // Declare inserted tree node ID. + let parent = 321; + let avlq = new(ASCENDING, parent, 0); // Init AVL queue. + // Declare empty new leaf side. + let new_leaf_side = option::some(RIGHT); + // Update parent to inserted node. + insert_tree_node_update_parent_edge( + &mut avlq, tree_node_id, parent, new_leaf_side); + // Assert update to parent's child field. + assert!(get_child_right_by_id_test(&avlq, parent) == tree_node_id, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state update for inserting root. + fun test_insert_tree_node_update_parent_edge_root() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + let tree_node_id = 1234; // Declare inserted tree node ID. + let parent = (NIL as u64); // Declare parent as root flag. + // Declare empty new leaf side. + let new_leaf_side = option::none(); + // Assert null root. + assert!(get_root_test(&avlq) == (NIL as u64), 0); + // Update parent for inserted root node. + insert_tree_node_update_parent_edge( + &mut avlq, tree_node_id, parent, new_leaf_side); + // Assert root update. + assert!(get_root_test(&avlq) == tree_node_id, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify successful checks. + fun test_is_ascending() { + let avlq = AVLqueue{ // Create empty AVL queue. + bits: (NIL as u128), + root_lsbs: NIL, + tree_nodes: table_with_length::new(), + list_nodes: table_with_length::new(), + values: table::new(), + }; + // Assert flagged descending. + assert!(!is_ascending(&avlq), 0); + // Flag as ascending. + avlq.bits = u_128_by_32( + b"01000000000000000000000000000000", + // ^ bit 126 + b"00000000000000000000000000000000", + b"00000000000000000000000000000000", + b"00000000000000000000000000000000" + ); + // Assert flagged descending. + assert!(is_ascending(&avlq), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify successful returns. + fun test_is_empty() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + assert!(is_empty(&avlq), 0); // Assert marked empty. + let access_key = insert(&mut avlq, 0, 0); // Insert. + assert!(!is_empty(&avlq), 0); // Assert marked not empty. + remove(&mut avlq, access_key); // Remove sole entry. + assert!(is_empty(&avlq), 0); // Assert marked empty. + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify successful initialization for no node allocations. + fun test_new_no_nodes() { + // Init ascending AVL queue. + let avlq = new(ASCENDING, 0, 0); + // Assert flagged ascending. + assert!(is_ascending(&avlq), 0); + // Assert null stack tops. + assert!(get_list_top_test(&avlq) == (NIL as u64), 0); + assert!(get_tree_top_test(&avlq) == (NIL as u64), 0); + drop_avlq_test(avlq); // Drop AVL queue. + // Init descending AVL queue. + avlq = new(DESCENDING, 0, 0); + // Assert flagged descending. + assert!(!is_ascending(&avlq), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify successful initialization for allocating tree nodes. + fun test_new_some_nodes() { + // Init ascending AVL queue with two nodes each. + let avlq = new(ASCENDING, 3, 2); + // Assert table lengths. + assert!(table_with_length::length(&avlq.tree_nodes) == 3, 0); + assert!(table_with_length::length(&avlq.list_nodes) == 2, 0); + // Assert stack tops. + assert!(get_tree_top_test(&avlq) == 3, 0); + assert!(get_list_top_test(&avlq) == 2, 0); + // Assert inactive tree node stack next chain. + assert!(get_tree_next_test(borrow_tree_node_test(&avlq, 3)) == 2, 0); + assert!(get_tree_next_test(borrow_tree_node_test(&avlq, 2)) == 1, 0); + assert!(get_tree_next_test(borrow_tree_node_test(&avlq, 1)) == + (NIL as u64), 0); + // Assert inactive list node stack next chain. + let (node_id, is_tree_node) = + get_list_next_test(borrow_list_node_test(&avlq, 2)); + assert!(node_id == 1, 0); + assert!(!is_tree_node, 0); + (node_id, is_tree_node) = + get_list_next_test(borrow_list_node_test(&avlq, 1)); + assert!(node_id == (NIL as u64), 0); + assert!(!is_tree_node, 0); + // Assert value options initialize to none. + assert!(option::is_none(borrow_value_option_test(&avlq, 2)), 0); + assert!(option::is_none(borrow_value_option_test(&avlq, 1)), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify successful initialization for allocating tree nodes. + fun test_new_some_nodes_loop() { + // Declare number of tree and list nodes to allocate. + let (n_tree_nodes, n_list_nodes) = (1234, 321); + // Init ascending AVL queue accordingly. + let avlq = new(ASCENDING, n_tree_nodes, n_list_nodes); + // Assert table lengths. + assert!(table_with_length::length(&avlq.tree_nodes) == + n_tree_nodes, 0); + assert!(table_with_length::length(&avlq.list_nodes) == + n_list_nodes, 0); + // Assert stack tops. + assert!(get_tree_top_test(&avlq) == n_tree_nodes, 0); + assert!(get_list_top_test(&avlq) == n_list_nodes, 0); + let i = n_tree_nodes; // Declare loop counter. + while (i > (NIL as u64)) { // Loop over all tree nodes in stack: + // Assert next indicated tree node in stack. + assert!(get_tree_next_test(borrow_tree_node_test(&avlq, i)) == + i - 1, 0); + i = i - 1; // Decrement loop counter. + }; + i = n_list_nodes; // Re-declare loop counter. + while (i > (NIL as u64)) { // Loop over all list nodes in stack: + // Assert next indicated list node in stack. + let (node_id, is_tree_node) = + get_list_next_test(borrow_list_node_test(&avlq, i)); + assert!(node_id == i - 1, 0); + assert!(!is_tree_node, 0); + // Assert value option initializes to none. + assert!(option::is_none(borrow_value_option_test(&avlq, i)), 0); + i = i - 1; // Decrement loop counter. + }; + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + #[expected_failure(abort_code = E_TOO_MANY_LIST_NODES)] + /// Verify failure for attempting to allocate too many list nodes. + fun test_new_too_many_list_nodes() { + // Attempt invalid invocation. + let avlq = new(ASCENDING, 0, N_NODES_MAX + 1); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + #[expected_failure(abort_code = E_TOO_MANY_TREE_NODES)] + /// Verify failure for attempting to allocate too many tree nodes. + fun test_new_too_many_tree_nodes() { + // Attempt invalid invocation. + let avlq = new(ASCENDING, N_NODES_MAX + 1, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify returns for ascending and descending AVL queue. + fun test_next_list_node_id_in_access_key() { + let n_list_nodes = 15; + let list_nodes_per_tree_node = 3; + let avlq = new(ASCENDING, 0, 0); + let access_keys = vector[]; + let i = 0; + // Insert multiple list nodes per tree node. + while (i < n_list_nodes) { + vector::push_back( + &mut access_keys, + insert(&mut avlq, i / list_nodes_per_tree_node, i) + ); + i = i + 1; + }; + let list_node_id_in_access_key = *vector::borrow(&access_keys, 0); + i = 0; + // Assert next operation for all nodes except last. + while (i < (n_list_nodes - 1)) { + list_node_id_in_access_key = next_list_node_id_in_access_key( + &avlq, + list_node_id_in_access_key + ); + assert!(*borrow(&avlq, list_node_id_in_access_key) == i + 1, 0); + i = i + 1; + }; + // Assert operation for last node. + assert!( + (NIL as u64) == next_list_node_id_in_access_key( + &avlq, + list_node_id_in_access_key + ), + 0 + ); + drop_avlq_test(avlq); // Drop AVL queue. + // Repeat for descending AVL queue. + avlq = new(DESCENDING, 0, 0); + i = 0; + // Insert multiple list nodes per tree node, descending queue. + while (i < n_list_nodes) { + vector::push_back( + &mut access_keys, + insert( + &mut avlq, + HI_INSERTION_KEY - i / list_nodes_per_tree_node, i + ) + ); + i = i + 1; + }; + list_node_id_in_access_key = *vector::borrow(&access_keys, 0); + i = 0; + // Assert next operation for all nodes except last. + while (i < (n_list_nodes - 1)) { + list_node_id_in_access_key = next_list_node_id_in_access_key( + &avlq, + list_node_id_in_access_key + ); + assert!(*borrow(&avlq, list_node_id_in_access_key) == i + 1, 0); + i = i + 1; + }; + // Assert operation for last node. + assert!( + (NIL as u64) == next_list_node_id_in_access_key( + &avlq, + list_node_id_in_access_key + ), + 0 + ); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Insert 5 key-value pairs for each of 80 insertion keys, pop + /// first 200 from head, then final 200 from tail. + fun test_pop_head_tail() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + let i = 0; // Declare loop counter. + while (i < 400) { // Insert all key-value insertion pairs. + // {0, 0}, {0, 1}, ... {0, 4}, {1, 5}, ... {79, 399} + insert(&mut avlq, i / 5, i); // Insert all keys. + i = i + 1; // Increment loop counter. + }; + i = 0; // Reset loop counter. + while (i < 200) { // Pop first 200 from head. + // Assert popped insertion value. + assert!(pop_head(&mut avlq) == i, 0); + i = i + 1; // Increment loop counter. + }; + i = 0; // Reset loop counter. + while (i < 200) { // Pop final 200 from tail. + // Assert popped insertion value. + assert!(pop_tail(&mut avlq) == 399 - i, 0); + i = i + 1; // Increment loop counter. + }; + assert!(is_empty(&avlq), 0); // Assert AVL queue empty. + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates for `remove()` reference diagram case 1. + fun test_remove_1() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Insert tree nodes top to bottom, list nodes head to tail. + let (access_key_2_3, access_key_2_4, access_key_1_5, access_key_1_6) = + (insert(&mut avlq, 2, 3), insert(&mut avlq, 2, 4), + insert(&mut avlq, 1, 5), insert(&mut avlq, 1, 6)); + // Get node IDs. + let node_id_1 = get_access_key_tree_node_id_test(access_key_1_5); + let node_id_2 = get_access_key_tree_node_id_test(access_key_2_3); + let node_id_3 = get_access_key_list_node_id_test(access_key_2_3); + let node_id_4 = get_access_key_list_node_id_test(access_key_2_4); + let node_id_5 = get_access_key_list_node_id_test(access_key_1_5); + let node_id_6 = get_access_key_list_node_id_test(access_key_1_6); + // Execute first removal, asserting returned insertion value. + assert!(remove(&mut avlq, access_key_1_5) == 5, 0); + // Assert AVL queue state. + assert!(get_tree_top_test( &avlq) == (NIL as u64), 0); + assert!(get_list_top_test( &avlq) == node_id_5, 0); + assert!(get_head_node_id_test(&avlq) == node_id_6, 0); + assert!(get_head_key_test( &avlq) == 1, 0); + assert!(get_tail_node_id_test(&avlq) == node_id_4, 0); + assert!(get_tail_key_test( &avlq) == 2, 0); + assert!(get_root_test( &avlq) == node_id_2, 0); + // Assert active tree node state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_1) == 1, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_1) + == node_id_2 , 0); + assert!(get_child_left_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + assert!(get_list_head_by_id_test( &avlq, node_id_1) + == node_id_6 , 0); + assert!(get_list_tail_by_id_test( &avlq, node_id_1) + == node_id_6 , 0); + assert!(get_insertion_key_by_id_test(&avlq, node_id_2) == 2, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_2) == 1, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_2) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(get_child_left_by_id_test( &avlq, node_id_2) + == node_id_1 , 0); + assert!(get_child_right_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(get_list_head_by_id_test( &avlq, node_id_2) + == node_id_3 , 0); + assert!(get_list_tail_by_id_test( &avlq, node_id_2) + == node_id_4 , 0); + // Assert inactive list node state. + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_5), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_5) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_5), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_5) + == (NIL as u64), 0); + // Assert active list node state. + assert!( is_tree_node_list_last_by_id_test(&avlq, node_id_6), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_6) + == node_id_1 , 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, node_id_6), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_6) + == node_id_1 , 0); + assert!( is_tree_node_list_last_by_id_test(&avlq, node_id_3), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_3) + == node_id_2 , 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_3), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_3) + == node_id_4 , 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_4), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_4) + == node_id_3 , 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, node_id_4), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_4) + == node_id_2 , 0); + // Execute second removal, asserting returned insertion value. + assert!(remove(&mut avlq, access_key_1_6) == 6, 0); + // Assert AVL queue state. + assert!(get_tree_top_test( &avlq) == node_id_1, 0); + assert!(get_list_top_test( &avlq) == node_id_6, 0); + assert!(get_head_node_id_test(&avlq) == node_id_3, 0); + assert!(get_head_key_test( &avlq) == 2, 0); + assert!(get_tail_node_id_test(&avlq) == node_id_4, 0); + assert!(get_tail_key_test( &avlq) == 2, 0); + assert!(get_root_test( &avlq) == node_id_2, 0); + // Assert inactive tree node state indicates stack bottom. + let node_1_ref = borrow_tree_node_test(&avlq, node_id_1); + assert!(node_1_ref.bits == (NIL as u128), 0); + // Assert active tree node state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_2) == 2, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_2) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_2) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(get_child_left_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(get_list_head_by_id_test( &avlq, node_id_2) + == node_id_3 , 0); + assert!(get_list_tail_by_id_test( &avlq, node_id_2) + == node_id_4 , 0); + // Assert inactive list node state. + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_5), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_5) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_5), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_5) + == (NIL as u64), 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_6), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_6) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_6), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_6) + == node_id_5 , 0); + // Assert active list node state. + assert!( is_tree_node_list_last_by_id_test(&avlq, node_id_3), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_3) + == node_id_2 , 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_3), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_3) + == node_id_4 , 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_4), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_4) + == node_id_3 , 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, node_id_4), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_4) + == node_id_2 , 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates for `remove()` reference diagram case 2. + fun test_remove_2() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Insert tree nodes top to bottom, list nodes head to tail. + let (access_key_2_3, access_key_2_4, access_key_1_5, access_key_1_6) = + (insert(&mut avlq, 2, 3), insert(&mut avlq, 2, 4), + insert(&mut avlq, 1, 5), insert(&mut avlq, 1, 6)); + // Get node IDs. + let node_id_1 = get_access_key_tree_node_id_test(access_key_1_5); + let node_id_2 = get_access_key_tree_node_id_test(access_key_2_3); + let node_id_3 = get_access_key_list_node_id_test(access_key_2_3); + let node_id_4 = get_access_key_list_node_id_test(access_key_2_4); + let node_id_5 = get_access_key_list_node_id_test(access_key_1_5); + let node_id_6 = get_access_key_list_node_id_test(access_key_1_6); + // Assert local tail state for tree node with insertion key 2. + assert!(!is_local_tail(&avlq, access_key_2_3), 0); + assert!(is_local_tail(&avlq, access_key_2_4), 0); + // Execute first removal, asserting returned insertion value. + assert!(remove(&mut avlq, access_key_2_4) == 4, 0); + // Assert local tail state for tree node with insertion key 2. + assert!(is_local_tail(&avlq, access_key_2_3), 0); + assert!(!is_local_tail(&avlq, access_key_2_4), 0); + // Assert AVL queue state. + assert!(get_tree_top_test( &avlq) == (NIL as u64), 0); + assert!(get_list_top_test( &avlq) == node_id_4, 0); + assert!(get_head_node_id_test(&avlq) == node_id_5, 0); + assert!(get_head_key_test( &avlq) == 1, 0); + assert!(get_tail_node_id_test(&avlq) == node_id_3, 0); + assert!(get_tail_key_test( &avlq) == 2, 0); + assert!(get_root_test( &avlq) == node_id_2, 0); + // Assert active tree node state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_1) == 1, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_1) + == node_id_2 , 0); + assert!(get_child_left_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + assert!(get_list_head_by_id_test( &avlq, node_id_1) + == node_id_5 , 0); + assert!(get_list_tail_by_id_test( &avlq, node_id_1) + == node_id_6 , 0); + assert!(get_insertion_key_by_id_test(&avlq, node_id_2) == 2, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_2) == 1, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_2) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(get_child_left_by_id_test( &avlq, node_id_2) + == node_id_1 , 0); + assert!(get_child_right_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(get_list_head_by_id_test( &avlq, node_id_2) + == node_id_3 , 0); + assert!(get_list_tail_by_id_test( &avlq, node_id_2) + == node_id_3 , 0); + // Assert inactive list node state. + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_4), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_4) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_4), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_4) + == (NIL as u64), 0); + // Assert active list node state. + assert!( is_tree_node_list_last_by_id_test(&avlq, node_id_5), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_5) + == node_id_1 , 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_5), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_5) + == node_id_6 , 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_6), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_6) + == node_id_5 , 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, node_id_6), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_6) + == node_id_1 , 0); + assert!( is_tree_node_list_last_by_id_test(&avlq, node_id_3), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_3) + == node_id_2 , 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, node_id_3), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_3) + == node_id_2 , 0); + // Execute second removal, asserting returned insertion value. + assert!(remove(&mut avlq, access_key_2_3) == 3, 0); + // Assert AVL queue state. + assert!(get_tree_top_test( &avlq) == node_id_2, 0); + assert!(get_list_top_test( &avlq) == node_id_3, 0); + assert!(get_head_node_id_test(&avlq) == node_id_5, 0); + assert!(get_head_key_test( &avlq) == 1, 0); + assert!(get_tail_node_id_test(&avlq) == node_id_6, 0); + assert!(get_tail_key_test( &avlq) == 1, 0); + assert!(get_root_test( &avlq) == node_id_1, 0); + // Assert inactive tree node state indicates stack bottom. + let node_2_ref = borrow_tree_node_test(&avlq, node_id_2); + assert!(node_2_ref.bits == (NIL as u128), 0); + // Assert active tree node state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_1) == 1, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + assert!(get_child_left_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + assert!(get_list_head_by_id_test( &avlq, node_id_1) + == node_id_5 , 0); + assert!(get_list_tail_by_id_test( &avlq, node_id_1) + == node_id_6 , 0); + // Assert inactive list node state. + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_3), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_3) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_3), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_3) + == node_id_4 , 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_4), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_4) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_4), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_4) + == (NIL as u64), 0); + // Assert active list node state. + assert!( is_tree_node_list_last_by_id_test(&avlq, node_id_5), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_5) + == node_id_1 , 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_5), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_5) + == node_id_6 , 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_6), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_6) + == node_id_5 , 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, node_id_6), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_6) + == node_id_1 , 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates for `remove()` reference diagram case 3. + fun test_remove_3() { + let avlq = new(DESCENDING, 0, 0); // Init AVL queue. + // Insert tree nodes top to bottom, list nodes head to tail. + let (access_key_2_3, access_key_2_4, access_key_1_5, access_key_1_6) = + (insert(&mut avlq, 2, 3), insert(&mut avlq, 2, 4), + insert(&mut avlq, 1, 5), insert(&mut avlq, 1, 6)); + // Get node IDs. + let node_id_1 = get_access_key_tree_node_id_test(access_key_1_5); + let node_id_2 = get_access_key_tree_node_id_test(access_key_2_3); + let node_id_3 = get_access_key_list_node_id_test(access_key_2_3); + let node_id_4 = get_access_key_list_node_id_test(access_key_2_4); + let node_id_5 = get_access_key_list_node_id_test(access_key_1_5); + let node_id_6 = get_access_key_list_node_id_test(access_key_1_6); + // Execute first removal, asserting returned insertion value. + assert!(remove(&mut avlq, access_key_2_3) == 3, 0); + // Assert AVL queue state. + assert!(get_tree_top_test( &avlq) == (NIL as u64), 0); + assert!(get_list_top_test( &avlq) == node_id_3, 0); + assert!(get_head_node_id_test(&avlq) == node_id_4, 0); + assert!(get_head_key_test( &avlq) == 2, 0); + assert!(get_tail_node_id_test(&avlq) == node_id_6, 0); + assert!(get_tail_key_test( &avlq) == 1, 0); + assert!(get_root_test( &avlq) == node_id_2, 0); + // Assert active tree node state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_1) == 1, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_1) + == node_id_2 , 0); + assert!(get_child_left_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + assert!(get_list_head_by_id_test( &avlq, node_id_1) + == node_id_5 , 0); + assert!(get_list_tail_by_id_test( &avlq, node_id_1) + == node_id_6 , 0); + assert!(get_insertion_key_by_id_test(&avlq, node_id_2) == 2, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_2) == 1, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_2) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(get_child_left_by_id_test( &avlq, node_id_2) + == node_id_1 , 0); + assert!(get_child_right_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(get_list_head_by_id_test( &avlq, node_id_2) + == node_id_4 , 0); + assert!(get_list_tail_by_id_test( &avlq, node_id_2) + == node_id_4 , 0); + // Assert inactive list node state. + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_3), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_3) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_3), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_3) + == (NIL as u64), 0); + // Assert active list node state. + assert!( is_tree_node_list_last_by_id_test(&avlq, node_id_5), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_5) + == node_id_1 , 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_5), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_5) + == node_id_6 , 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_6), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_6) + == node_id_5 , 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, node_id_6), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_6) + == node_id_1 , 0); + assert!( is_tree_node_list_last_by_id_test(&avlq, node_id_4), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_4) + == node_id_2 , 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, node_id_4), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_4) + == node_id_2 , 0); + // Execute second removal, asserting returned insertion value. + assert!(remove(&mut avlq, access_key_2_4) == 4, 0); + // Assert AVL queue state. + assert!(get_tree_top_test( &avlq) == node_id_2, 0); + assert!(get_list_top_test( &avlq) == node_id_4, 0); + assert!(get_head_node_id_test(&avlq) == node_id_5, 0); + assert!(get_head_key_test( &avlq) == 1, 0); + assert!(get_tail_node_id_test(&avlq) == node_id_6, 0); + assert!(get_tail_key_test( &avlq) == 1, 0); + assert!(get_root_test( &avlq) == node_id_1, 0); + // Assert inactive tree node state indicates stack bottom. + let node_2_ref = borrow_tree_node_test(&avlq, node_id_2); + assert!(node_2_ref.bits == (NIL as u128), 0); + // Assert active tree node state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_1) == 1, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + assert!(get_child_left_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + assert!(get_list_head_by_id_test( &avlq, node_id_1) + == node_id_5 , 0); + assert!(get_list_tail_by_id_test( &avlq, node_id_1) + == node_id_6 , 0); + // Assert inactive list node state. + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_3), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_3) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_3), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_3) + == (NIL as u64), 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_4), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_4) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_4), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_4) + == node_id_3 , 0); + // Assert active list node state. + assert!( is_tree_node_list_last_by_id_test(&avlq, node_id_5), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_5) + == node_id_1 , 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_5), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_5) + == node_id_6 , 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_6), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_6) + == node_id_5 , 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, node_id_6), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_6) + == node_id_1 , 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates for `remove()` reference diagram case 4. + fun test_remove_4() { + let avlq = new(DESCENDING, 0, 0); // Init AVL queue. + // Insert tree nodes top to bottom, list nodes head to tail. + let (access_key_2_3, access_key_2_4, access_key_1_5, access_key_1_6) = + (insert(&mut avlq, 2, 3), insert(&mut avlq, 2, 4), + insert(&mut avlq, 1, 5), insert(&mut avlq, 1, 6)); + // Get node IDs. + let node_id_1 = get_access_key_tree_node_id_test(access_key_1_5); + let node_id_2 = get_access_key_tree_node_id_test(access_key_2_3); + let node_id_3 = get_access_key_list_node_id_test(access_key_2_3); + let node_id_4 = get_access_key_list_node_id_test(access_key_2_4); + let node_id_5 = get_access_key_list_node_id_test(access_key_1_5); + let node_id_6 = get_access_key_list_node_id_test(access_key_1_6); + // Execute first removal, asserting returned insertion value. + assert!(remove(&mut avlq, access_key_1_6) == 6, 0); + // Assert AVL queue state. + assert!(get_tree_top_test( &avlq) == (NIL as u64), 0); + assert!(get_list_top_test( &avlq) == node_id_6, 0); + assert!(get_head_node_id_test(&avlq) == node_id_3, 0); + assert!(get_head_key_test( &avlq) == 2, 0); + assert!(get_tail_node_id_test(&avlq) == node_id_5, 0); + assert!(get_tail_key_test( &avlq) == 1, 0); + assert!(get_root_test( &avlq) == node_id_2, 0); + // Assert active tree node state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_1) == 1, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_1) + == node_id_2 , 0); + assert!(get_child_left_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + assert!(get_list_head_by_id_test( &avlq, node_id_1) + == node_id_5 , 0); + assert!(get_list_tail_by_id_test( &avlq, node_id_1) + == node_id_5 , 0); + assert!(get_insertion_key_by_id_test(&avlq, node_id_2) == 2, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_2) == 1, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_2) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(get_child_left_by_id_test( &avlq, node_id_2) + == node_id_1 , 0); + assert!(get_child_right_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(get_list_head_by_id_test( &avlq, node_id_2) + == node_id_3 , 0); + assert!(get_list_tail_by_id_test( &avlq, node_id_2) + == node_id_4 , 0); + // Assert inactive list node state. + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_6), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_6) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_6), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_6) + == (NIL as u64), 0); + // Assert active list node state. + assert!( is_tree_node_list_last_by_id_test(&avlq, node_id_5), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_5) + == node_id_1 , 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, node_id_5), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_5) + == node_id_1 , 0); + assert!( is_tree_node_list_last_by_id_test(&avlq, node_id_3), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_3) + == node_id_2 , 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_3), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_3) + == node_id_4 , 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_4), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_4) + == node_id_3 , 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, node_id_4), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_4) + == node_id_2 , 0); + // Execute second removal, asserting returned insertion value. + assert!(remove(&mut avlq, access_key_1_5) == 5, 0); + // Assert AVL queue state. + assert!(get_tree_top_test( &avlq) == node_id_1, 0); + assert!(get_list_top_test( &avlq) == node_id_5, 0); + assert!(get_head_node_id_test(&avlq) == node_id_3, 0); + assert!(get_head_key_test( &avlq) == 2, 0); + assert!(get_tail_node_id_test(&avlq) == node_id_4, 0); + assert!(get_tail_key_test( &avlq) == 2, 0); + assert!(get_root_test( &avlq) == node_id_2, 0); + // Assert inactive tree node state indicates stack bottom. + let node_1_ref = borrow_tree_node_test(&avlq, node_id_1); + assert!(node_1_ref.bits == (NIL as u128), 0); + // Assert active tree node state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_2) == 2, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_2) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_2) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(get_child_left_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(get_list_head_by_id_test( &avlq, node_id_2) + == node_id_3 , 0); + assert!(get_list_tail_by_id_test( &avlq, node_id_2) + == node_id_4 , 0); + // Assert inactive list node state. + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_6), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_6) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_6), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_6) + == (NIL as u64), 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_5), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_5) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_5), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_5) + == node_id_6 , 0); + // Assert active list node state. + assert!( is_tree_node_list_last_by_id_test(&avlq, node_id_3), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_3) + == node_id_2 , 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_3), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_3) + == node_id_4 , 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_4), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_4) + == node_id_3 , 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, node_id_4), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_4) + == node_id_2 , 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates for `remove_tree_node_with_children()` + /// reference diagram 1. + fun test_remove_children_1() { + // Declare number of allocated tree nodes. + let n_allocated_tree_nodes = 8; + // Init AVL queue with 4 allocated tree nodes. + let avlq = new(ASCENDING, n_allocated_tree_nodes, 0); + // Insert nodes from top to bottom, left to right. + let (node_id_2, node_id_1, node_id_4, node_id_3, node_id_5) = + (get_access_key_tree_node_id_test(insert(&mut avlq, 2, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 1, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 4, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 3, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 5, 0))); + // Declare number of active tree nodes. + let n_active_tree_nodes = 5; + remove_tree_node(&mut avlq, node_id_4); // Remove node x. + assert!(get_root_test(&avlq) == node_id_2, 0); // Assert root. + // Assert inactive tree nodes stack top. + assert!(get_tree_top_test(&avlq) == node_id_4, 0); + // Assert node x state contains only bits for node ID of next + // tree node ID in inactive tree node stack. + let node_4_ref = borrow_tree_node_test(&avlq, node_id_4); + assert!(node_4_ref.bits == + ((n_allocated_tree_nodes - n_active_tree_nodes) as u128), 0); + // Assert node l state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_3) == 3, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_3) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_3) == 1, 0); + assert!(get_parent_by_id_test( &avlq, node_id_3) + == node_id_2, 0); + assert!(get_child_left_by_id_test( &avlq, node_id_3) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_3) + == node_id_5, 0); + // Assert node r state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_5) == 5, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_5) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_5) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_5) + == node_id_3, 0); + assert!(get_child_left_by_id_test( &avlq, node_id_5) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_5) + == (NIL as u64), 0); + // Assert node 1 state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_1) == 1, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_1) + == node_id_2, 0); + assert!(get_child_left_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + // Assert node 2 state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_2) == 2, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_2) == 1, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_2) == 2, 0); + assert!(get_parent_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(get_child_left_by_id_test( &avlq, node_id_2) + == node_id_1, 0); + assert!(get_child_right_by_id_test( &avlq, node_id_2) + == node_id_3, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates for `remove_tree_node_with_children()` + /// reference diagram 2. + fun test_remove_children_2() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Insert nodes from top to bottom, left to right. + let (node_id_5, node_id_2, node_id_6, node_id_1, node_id_4, node_id_7, + node_id_3) = + (get_access_key_tree_node_id_test(insert(&mut avlq, 5, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 2, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 6, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 1, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 4, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 7, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 3, 0))); + remove_tree_node(&mut avlq, node_id_5); // Remove node x. + assert!(get_root_test(&avlq) == node_id_4, 0); // Assert root. + // Assert inactive tree nodes stack top. + assert!(get_tree_top_test(&avlq) == node_id_5, 0); + // Assert node x state contains only bits for node ID of next + // tree node ID in inactive tree node stack. + let node_5_ref = borrow_tree_node_test(&avlq, node_id_5); + assert!(node_5_ref.bits == (NIL as u128), 0); + // Assert node r state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_6) == 6, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_6) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_6) == 1, 0); + assert!(get_parent_by_id_test( &avlq, node_id_6) + == node_id_4, 0); + assert!(get_child_left_by_id_test( &avlq, node_id_6) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_6) + == node_id_7, 0); + // Assert node y state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_4) == 4, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_4) == 2, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_4) == 2, 0); + assert!(get_parent_by_id_test( &avlq, node_id_4) + == (NIL as u64), 0); + assert!(get_child_left_by_id_test( &avlq, node_id_4) + == node_id_2, 0); + assert!(get_child_right_by_id_test( &avlq, node_id_4) + == node_id_6, 0); + // Assert state for parent to node y. + assert!(get_insertion_key_by_id_test(&avlq, node_id_2) == 2, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_2) == 1, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_2) == 1, 0); + assert!(get_parent_by_id_test( &avlq, node_id_2) + == node_id_4, 0); + assert!(get_child_left_by_id_test( &avlq, node_id_2) + == node_id_1, 0); + assert!(get_child_right_by_id_test( &avlq, node_id_2) + == node_id_3, 0); + // Assert tree y state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_3) == 3, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_3) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_3) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_3) + == node_id_2, 0); + assert!(get_child_left_by_id_test( &avlq, node_id_3) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_3) + == (NIL as u64), 0); + // Assert tree l state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_1) == 1, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_1) + == node_id_2, 0); + assert!(get_child_left_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + // Assert state for child to node r. + assert!(get_insertion_key_by_id_test(&avlq, node_id_7) == 7, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_7) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_7) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_7) + == node_id_6, 0); + assert!(get_child_left_by_id_test( &avlq, node_id_7) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates for `remove_tree_node_with_children()` + /// reference diagram 3. + fun test_remove_children_3() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Insert nodes from top to bottom, left to right. + let (node_id_5, node_id_2, node_id_6, node_id_1, node_id_3, node_id_7, + node_id_4) = + (get_access_key_tree_node_id_test(insert(&mut avlq, 5, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 2, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 6, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 1, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 3, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 7, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 4, 0))); + remove_tree_node(&mut avlq, node_id_5); // Remove node x. + assert!(get_root_test(&avlq) == node_id_4, 0); // Assert root. + // Assert inactive tree nodes stack top. + assert!(get_tree_top_test(&avlq) == node_id_5, 0); + // Assert node x state contains only bits for node ID of next + // tree node ID in inactive tree node stack. + let node_5_ref = borrow_tree_node_test(&avlq, node_id_5); + assert!(node_5_ref.bits == (NIL as u128), 0); + // Assert node r state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_6) == 6, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_6) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_6) == 1, 0); + assert!(get_parent_by_id_test( &avlq, node_id_6) + == node_id_4, 0); + assert!(get_child_left_by_id_test( &avlq, node_id_6) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_6) + == node_id_7, 0); + // Assert node y state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_4) == 4, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_4) == 2, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_4) == 2, 0); + assert!(get_parent_by_id_test( &avlq, node_id_4) + == (NIL as u64), 0); + assert!(get_child_left_by_id_test( &avlq, node_id_4) + == node_id_2, 0); + assert!(get_child_right_by_id_test( &avlq, node_id_4) + == node_id_6, 0); + // Assert state for node l. + assert!(get_insertion_key_by_id_test(&avlq, node_id_2) == 2, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_2) == 1, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_2) == 1, 0); + assert!(get_parent_by_id_test( &avlq, node_id_2) + == node_id_4, 0); + assert!(get_child_left_by_id_test( &avlq, node_id_2) + == node_id_1, 0); + assert!(get_child_right_by_id_test( &avlq, node_id_2) + == node_id_3, 0); + // Assert state for parent to node y. + assert!(get_insertion_key_by_id_test(&avlq, node_id_3) == 3, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_3) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_3) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_3) + == node_id_2, 0); + assert!(get_child_left_by_id_test( &avlq, node_id_3) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_3) + == (NIL as u64), 0); + // Assert tree l state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_1) == 1, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_1) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_1) + == node_id_2, 0); + assert!(get_child_left_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + // Assert state for child to node r. + assert!(get_insertion_key_by_id_test(&avlq, node_id_7) == 7, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_7) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_7) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_7) + == node_id_6, 0); + assert!(get_child_left_by_id_test( &avlq, node_id_7) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_1) + == (NIL as u64), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates for removing head, tail, and sole list + /// node. + fun test_remove_list_node() { + // Declare tree node ID for sole activated tree node. + let tree_id_1 = 10; + let avlq = new(ASCENDING, tree_id_1, 0); // Init AVL queue. + // Insert, storing list node IDs. + let list_id_1 = get_access_key_list_node_id_test( + insert(&mut avlq, HI_INSERTION_KEY, 1)); + let list_id_2 = get_access_key_list_node_id_test( + insert(&mut avlq, HI_INSERTION_KEY, 2)); + let list_id_3 = get_access_key_list_node_id_test( + insert(&mut avlq, HI_INSERTION_KEY, 3)); + // Assert inactive list node stack top. + assert!(get_list_top_test(&avlq) == (NIL as u64), 0); + // Assert tree node state. + assert!(get_list_head_by_id_test(&avlq, tree_id_1) + == list_id_1, 0); + assert!(get_list_tail_by_id_test(&avlq, tree_id_1) + == list_id_3, 0); + // Assert list node state. + assert!( is_tree_node_list_last_by_id_test(&avlq, list_id_1), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, list_id_1) + == tree_id_1, 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, list_id_1), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_id_1) + == list_id_2, 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, list_id_2), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, list_id_2) + == list_id_1, 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, list_id_2), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_id_2) + == list_id_3, 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, list_id_3), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, list_id_3) + == list_id_2, 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, list_id_3), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_id_3) + == tree_id_1, 0); + let (value, new_head, new_tail) = // Remove list head. + remove_list_node(&mut avlq, list_id_1); + // Assert returns. + assert!(value == 1, 0); + assert!(*option::borrow(&new_head) == list_id_2, 0); + assert!(option::is_none(&new_tail), 0); + // Assert inactive list node stack top. + assert!(get_list_top_test(&avlq) == list_id_1, 0); + // Assert tree node state. + assert!(get_list_head_by_id_test(&avlq, tree_id_1) + == list_id_2, 0); + assert!(get_list_tail_by_id_test(&avlq, tree_id_1) + == list_id_3, 0); + // Assert list node state. + assert!(!is_tree_node_list_last_by_id_test(&avlq, list_id_1), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, list_id_1) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, list_id_1), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_id_1) + == (NIL as u64), 0); + assert!( is_tree_node_list_last_by_id_test(&avlq, list_id_2), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, list_id_2) + == tree_id_1 , 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, list_id_2), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_id_2) + == list_id_3 , 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, list_id_3), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, list_id_3) + == list_id_2 , 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, list_id_3), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_id_3) + == tree_id_1 , 0); + // Remove list tail + (value, new_head, new_tail) = remove_list_node(&mut avlq, list_id_3); + // Assert returns. + assert!(value == 3, 0); + assert!(option::is_none(&new_head), 0); + assert!(*option::borrow(&new_tail) == list_id_2, 0); + // Assert inactive list node stack top. + assert!(get_list_top_test(&avlq) == list_id_3, 0); + // Assert tree node state. + assert!(get_list_head_by_id_test(&avlq, tree_id_1) + == list_id_2, 0); + assert!(get_list_tail_by_id_test(&avlq, tree_id_1) + == list_id_2, 0); + // Assert list node state. + assert!(!is_tree_node_list_last_by_id_test(&avlq, list_id_1), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, list_id_1) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, list_id_1), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_id_1) + == (NIL as u64), 0); + assert!( is_tree_node_list_last_by_id_test(&avlq, list_id_2), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, list_id_2) + == tree_id_1 , 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, list_id_2), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_id_2) + == tree_id_1 , 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, list_id_3), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, list_id_3) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, list_id_3), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_id_3) + == list_id_1 , 0); + // Remove sole node in list + (value, new_head, new_tail) = remove_list_node(&mut avlq, list_id_2); + // Assert returns. + assert!(value == 2, 0); + assert!(*option::borrow(&new_head) == (NIL as u64), 0); + assert!(*option::borrow(&new_tail) == (NIL as u64), 0); + // Assert inactive list node stack top. + assert!(get_list_top_test(&avlq) == list_id_2, 0); + // Assert tree node state unmodified. + assert!(get_list_head_by_id_test(&avlq, tree_id_1) + == list_id_2, 0); + assert!(get_list_tail_by_id_test(&avlq, tree_id_1) + == list_id_2, 0); + // Assert list node state. + assert!(!is_tree_node_list_last_by_id_test(&avlq, list_id_1), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, list_id_1) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, list_id_1), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_id_1) + == (NIL as u64), 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, list_id_2), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, list_id_2) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, list_id_2), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_id_2) + == list_id_3 , 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, list_id_3), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, list_id_3) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, list_id_3), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_id_3) + == list_id_1 , 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates for removing a list node that is neither + /// head nor tail of corresponding doubly linked list. + fun test_remove_mid_list() { + let (n_allocated_tree_nodes, n_allocated_list_nodes) = (7, 4); + let avlq = new( // Init AVL queue. + ASCENDING, n_allocated_tree_nodes, n_allocated_list_nodes); + // Declare three list nodes all having insertion key 1. + let access_key_head = insert(&mut avlq, 1, 1); + let access_key_middle = insert(&mut avlq, 1, 2); + let access_key_tail = insert(&mut avlq, 1, 3); + // Remove node from middle of list, asserting insertion value. + assert!(remove(&mut avlq, access_key_middle) == 2, 0); + let head_list_node_id = // Get head list node ID. + get_access_key_list_node_id_test(access_key_head); + let tail_list_node_id = // Get tail list node ID. + get_access_key_list_node_id_test(access_key_tail); + let list_node_id = // Get removed list node ID. + get_access_key_list_node_id_test(access_key_middle); + let tree_node_id = // Get active tree node ID. + get_access_key_tree_node_id_test(access_key_head); + // Assert AVL queue state. + assert!(get_tree_top_test(&avlq) == n_allocated_tree_nodes - 1, 0); + assert!(get_list_top_test(&avlq) == list_node_id, 0); + assert!(get_head_node_id_test(&avlq) == head_list_node_id, 0); + assert!(get_head_key_test(&avlq) == 1, 0); + assert!(get_tail_node_id_test(&avlq) == tail_list_node_id, 0); + assert!(get_tail_key_test(&avlq) == 1, 0); + assert!(get_root_test(&avlq) == tree_node_id, 0); + // Assert tree node state. + assert!(get_insertion_key_by_id_test(&avlq, tree_node_id) == 1, 0); + assert!(get_height_left_by_id_test( &avlq, tree_node_id) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, tree_node_id) == 0, 0); + assert!(get_parent_by_id_test( &avlq, tree_node_id) + == (NIL as u64), 0); + assert!(get_child_left_by_id_test( &avlq, tree_node_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, tree_node_id) + == (NIL as u64), 0); + assert!(get_list_head_by_id_test( &avlq, tree_node_id) + == head_list_node_id, 0); + assert!(get_list_tail_by_id_test( &avlq, tree_node_id) + == tail_list_node_id, 0); + // Assert inactive list node state. + assert!(!is_tree_node_list_last_by_id_test(&avlq, list_node_id), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, list_node_id) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, list_node_id), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, list_node_id) + == n_allocated_list_nodes - 3, 0); + // Assert active list node state. + assert!( is_tree_node_list_last_by_id_test(&avlq, head_list_node_id), + 0); + assert!(get_list_last_node_id_by_id_test( &avlq, head_list_node_id) + == tree_node_id, 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, head_list_node_id), + 0); + assert!(get_list_next_node_id_by_id_test( &avlq, head_list_node_id) + == tail_list_node_id, 0); + assert!(!is_tree_node_list_last_by_id_test(&avlq, tail_list_node_id), + 0); + assert!(get_list_last_node_id_by_id_test( &avlq, tail_list_node_id) + == head_list_node_id, 0); + assert!( is_tree_node_list_next_by_id_test(&avlq, tail_list_node_id), + 0); + assert!(get_list_next_node_id_by_id_test( &avlq, tail_list_node_id) + == tree_node_id, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates for removing key-value insertion pair + /// (1, 2) as sole entry in ascending as descending AVL queue. + fun test_remove_root() { + // Init ascending AVL queue. + let avlq = new(ASCENDING, 0, 0); + // Insert sole entry. + let access_key_1_2 = insert(&mut avlq, 1, 2); + // Get node IDs. + // Get node IDs. + let node_id_1 = get_access_key_tree_node_id_test(access_key_1_2); + let node_id_2 = get_access_key_list_node_id_test(access_key_1_2); + // Remove sole entry, asserting returned insertion value. + assert!(remove(&mut avlq, access_key_1_2) == 2, 0); + // Assert AVL queue state. + assert!(get_tree_top_test( &avlq) == node_id_1, 0); + assert!(get_list_top_test( &avlq) == node_id_2, 0); + assert!(get_head_node_id_test(&avlq) == (NIL as u64), 0); + assert!(get_head_key_test( &avlq) == (NIL as u64), 0); + assert!(get_tail_node_id_test(&avlq) == (NIL as u64), 0); + assert!(get_tail_key_test( &avlq) == (NIL as u64), 0); + assert!(get_root_test( &avlq) == (NIL as u64), 0); + // Assert inactive tree node state indicates stack bottom. + let node_1_ref = borrow_tree_node_test(&avlq, node_id_1); + assert!(node_1_ref.bits == (NIL as u128), 0); + // Assert inactive list node state indicates stack bottom. + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_2), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_2), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + drop_avlq_test(avlq); // Drop AVL queue. + // Init descending AVL queue. + let avlq = new(DESCENDING, 0, 0); + insert(&mut avlq, 1, 2); // Insert sole entry. + // Remove sole entry, asserting returned insertion value. + assert!(remove(&mut avlq, access_key_1_2) == 2, 0); + // Assert AVL queue state. + assert!(get_tree_top_test( &avlq) == node_id_1, 0); + assert!(get_list_top_test( &avlq) == node_id_2, 0); + assert!(get_head_node_id_test(&avlq) == (NIL as u64), 0); + assert!(get_head_key_test( &avlq) == (NIL as u64), 0); + assert!(get_tail_node_id_test(&avlq) == (NIL as u64), 0); + assert!(get_tail_key_test( &avlq) == (NIL as u64), 0); + assert!(get_root_test( &avlq) == (NIL as u64), 0); + // Assert inactive tree node state indicates stack bottom. + let node_1_ref = borrow_tree_node_test(&avlq, node_id_1); + assert!(node_1_ref.bits == (NIL as u128), 0); + // Assert inactive list node state indicates stack bottom. + assert!(!is_tree_node_list_last_by_id_test(&avlq, node_id_2), 0); + assert!(get_list_last_node_id_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(!is_tree_node_list_next_by_id_test(&avlq, node_id_2), 0); + assert!(get_list_next_node_id_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates for tree having 1 at root and 2 as right + /// child, removing the root twice. + fun test_remove_root_twice() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + let (node_id_1, node_id_2) = // Insert nodes. + (get_access_key_tree_node_id_test(insert(&mut avlq, 1, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 2, 0))); + remove_tree_node(&mut avlq, node_id_1); // Remove root 1. + assert!(get_root_test(&avlq) == node_id_2, 0); // Assert root. + // Assert inactive tree nodes stack top. + assert!(get_tree_top_test(&avlq) == node_id_1, 0); + // Assert node 1 state contains only bits for node ID of next + // tree node ID in inactive tree node stack. + let node_1_ref = borrow_tree_node_test(&avlq, node_id_1); + assert!(node_1_ref.bits == (NIL as u128), 0); + // Assert node 2 state. + assert!(get_insertion_key_by_id_test(&avlq, node_id_2) == 2, 0); + assert!(get_height_left_by_id_test( &avlq, node_id_2) == 0, 0); + assert!(get_height_right_by_id_test( &avlq, node_id_2) == 0, 0); + assert!(get_parent_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(get_child_left_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test( &avlq, node_id_2) + == (NIL as u64), 0); + remove_tree_node(&mut avlq, node_id_2); // Remove root 2. + // Assert root for empty tree. + assert!(get_root_test(&avlq) == (NIL as u64), 0); + // Assert inactive tree nodes stack top. + assert!(get_tree_top_test(&avlq) == node_id_2, 0); + // Assert node 2 state contains only bits for node ID of next + // tree node ID in inactive tree node stack. + let node_2_ref = borrow_tree_node_test(&avlq, node_id_2); + assert!(node_2_ref.bits == 1, 0); // Node 1 allocated first. + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates for reference operations in `retrace()`. + fun test_retrace_insert_remove() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Declare node IDs. + let node_a_id = HI_NODE_ID; + let node_b_id = node_a_id - 1; + let node_c_id = node_b_id - 1; + let node_d_id = node_c_id - 1; + // Mutably borrow tree nodes table. + let tree_nodes_ref_mut = &mut avlq.tree_nodes; + // Manually insert nodes from reference diagram, with heights + // not yet updated via insertion retrace. + table_with_length::add(tree_nodes_ref_mut, node_a_id, TreeNode{bits: + ( 3 as u128) << SHIFT_INSERTION_KEY | + (node_b_id as u128) << SHIFT_PARENT }); + table_with_length::add(tree_nodes_ref_mut, node_b_id, TreeNode{bits: + ( 4 as u128) << SHIFT_INSERTION_KEY | + ( 1 as u128) << SHIFT_HEIGHT_LEFT | + ( 1 as u128) << SHIFT_HEIGHT_RIGHT | + (node_a_id as u128) << SHIFT_CHILD_LEFT | + (node_c_id as u128) << SHIFT_CHILD_RIGHT }); + table_with_length::add(tree_nodes_ref_mut, node_c_id, TreeNode{bits: + ( 5 as u128) << SHIFT_INSERTION_KEY | + (node_b_id as u128) << SHIFT_PARENT | + (node_d_id as u128) << SHIFT_CHILD_RIGHT }); + table_with_length::add(tree_nodes_ref_mut, node_d_id, TreeNode{bits: + ( 6 as u128) << SHIFT_INSERTION_KEY | + (node_c_id as u128) << SHIFT_PARENT }); + // Set root node ID. + set_root_test(&mut avlq, node_b_id); + // Retrace from node c. + retrace(&mut avlq, node_c_id, INCREMENT, RIGHT); + // Assert state for node a. + assert!(get_insertion_key_by_id_test(&avlq, node_a_id) == 3, 0); + assert!(get_height_left_by_id_test(&avlq, node_a_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, node_a_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, node_a_id) == node_b_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_a_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, node_a_id) + == (NIL as u64), 0); + // Assert state for node b. + assert!(get_insertion_key_by_id_test(&avlq, node_b_id) == 4, 0); + assert!(get_height_left_by_id_test(&avlq, node_b_id) == 1, 0); + assert!(get_height_right_by_id_test(&avlq, node_b_id) == 2, 0); + assert!(get_parent_by_id_test(&avlq, node_b_id) == (NIL as u64), 0); + assert!(get_child_left_by_id_test(&avlq, node_b_id) == node_a_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_b_id) == node_c_id, 0); + // Assert state for node c. + assert!(get_insertion_key_by_id_test(&avlq, node_c_id) == 5, 0); + assert!(get_height_left_by_id_test(&avlq, node_c_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, node_c_id) == 1, 0); + assert!(get_parent_by_id_test(&avlq, node_c_id) == node_b_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_c_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, node_c_id) == node_d_id, 0); + // Assert state for node d. + assert!(get_insertion_key_by_id_test(&avlq, node_d_id) == 6, 0); + assert!(get_height_left_by_id_test(&avlq, node_d_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, node_d_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, node_d_id) == node_c_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_d_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, node_d_id) + == (NIL as u64), 0); + // Assert root. + assert!(get_root_test(&avlq) == node_b_id, 0); + // Mutably borrow tree nodes table. + let tree_nodes_ref_mut = &mut avlq.tree_nodes; + // Simulate removing node d by clearing out node c's right child + // field: remove and unpack node, then add new one with + // corresponding state. + let TreeNode{bits: _} = + table_with_length::remove(tree_nodes_ref_mut, node_c_id); + table_with_length::add(tree_nodes_ref_mut, node_c_id, TreeNode{bits: + ( 5 as u128) << SHIFT_INSERTION_KEY | + ( 1 as u128) << SHIFT_HEIGHT_RIGHT | + (node_b_id as u128) << SHIFT_PARENT }); + // Retrace from node c. + retrace(&mut avlq, node_c_id, DECREMENT, RIGHT); + // Assert state for node a. + assert!(get_insertion_key_by_id_test(&avlq, node_a_id) == 3, 0); + assert!(get_height_left_by_id_test(&avlq, node_a_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, node_a_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, node_a_id) == node_b_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_a_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, node_a_id) + == (NIL as u64), 0); + // Assert state for node b. + assert!(get_insertion_key_by_id_test(&avlq, node_b_id) == 4, 0); + assert!(get_height_left_by_id_test(&avlq, node_b_id) == 1, 0); + assert!(get_height_right_by_id_test(&avlq, node_b_id) == 1, 0); + assert!(get_parent_by_id_test(&avlq, node_b_id) == (NIL as u64), 0); + assert!(get_child_left_by_id_test(&avlq, node_b_id) == node_a_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_b_id) == node_c_id, 0); + // Assert state for node c. + assert!(get_insertion_key_by_id_test(&avlq, node_c_id) == 5, 0); + assert!(get_height_left_by_id_test(&avlq, node_c_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, node_c_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, node_c_id) == node_b_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_c_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, node_c_id) + == (NIL as u64), 0); + // Assert root. + assert!(get_root_test(&avlq) == node_b_id, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates/returns for `retrace_prep_iterate()` case + /// 1. + fun test_retrace_prep_iterate_1() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Declare arguments. + let insertion_key = HI_INSERTION_KEY; + let parent_id = HI_NODE_ID; + let node_id = parent_id - 1; + let new_subtree_root = node_id - 1; + let sibling_id = new_subtree_root - 1; + let height = 2; + let height_old = 3; + // Mutably borrow tree nodes table. + let tree_nodes_ref_mut = &mut avlq.tree_nodes; + // Manually insert parent node. + table_with_length::add(tree_nodes_ref_mut, parent_id, TreeNode{bits: + (insertion_key as u128) << SHIFT_INSERTION_KEY | + (node_id as u128) << SHIFT_CHILD_LEFT | + (sibling_id as u128) << SHIFT_CHILD_RIGHT}); + // Prepare for next iteration, storing returns. + let (node_ref_mut, operation, side, delta) = retrace_prep_iterate( + &mut avlq, parent_id, node_id, new_subtree_root, height, + height_old); + // Assert insertion key accessed by mutable reference return. + assert!(get_insertion_key_test(node_ref_mut) == insertion_key, 0); + // Assert other returns. + assert!(operation == DECREMENT, 0); + assert!(side == LEFT, 0); + assert!(delta == 1, 0); + // Assert child fields of parent. + assert!(get_child_left_by_id_test(&avlq, parent_id) + == new_subtree_root, 0); + assert!(get_child_right_by_id_test(&avlq, parent_id) == sibling_id, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates/returns for `retrace_prep_iterate()` case + /// 2. + fun test_retrace_prep_iterate_2() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Declare arguments. + let insertion_key = HI_INSERTION_KEY; + let parent_id = HI_NODE_ID; + let node_id = parent_id - 1; + let new_subtree_root = node_id - 1; + let sibling_id = new_subtree_root - 1; + let height = 3; + let height_old = 3; + // Mutably borrow tree nodes table. + let tree_nodes_ref_mut = &mut avlq.tree_nodes; + // Manually insert parent node. + table_with_length::add(tree_nodes_ref_mut, parent_id, TreeNode{bits: + (insertion_key as u128) << SHIFT_INSERTION_KEY | + (sibling_id as u128) << SHIFT_CHILD_LEFT | + (node_id as u128) << SHIFT_CHILD_RIGHT}); + // Prepare for next iteration, storing returns. + let (node_ref_mut, operation, side, delta) = retrace_prep_iterate( + &mut avlq, parent_id, node_id, new_subtree_root, height, + height_old); + // Assert insertion key accessed by mutable reference return. + assert!(get_insertion_key_test(node_ref_mut) == insertion_key, 0); + // Assert other returns. + assert!(operation == DECREMENT, 0); + assert!(side == RIGHT, 0); + assert!(delta == 0, 0); + // Assert child fields of parent. + assert!(get_child_left_by_id_test(&avlq, parent_id) == sibling_id, 0); + assert!(get_child_right_by_id_test(&avlq, parent_id) + == new_subtree_root, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates/returns for `retrace_prep_iterate()` case + /// 3. + fun test_retrace_prep_iterate_3() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Declare arguments. + let insertion_key = HI_INSERTION_KEY; + let parent_id = HI_NODE_ID; + let node_id = parent_id - 1; + let new_subtree_root = (NIL as u64); + let height = 1; + let height_old = 0; + // Mutably borrow tree nodes table. + let tree_nodes_ref_mut = &mut avlq.tree_nodes; + // Manually insert parent node. + table_with_length::add(tree_nodes_ref_mut, parent_id, TreeNode{bits: + (insertion_key as u128) << SHIFT_INSERTION_KEY | + (node_id as u128) << SHIFT_CHILD_RIGHT}); + // Prepare for next iteration, storing returns. + let (node_ref_mut, operation, side, delta) = retrace_prep_iterate( + &mut avlq, parent_id, node_id, new_subtree_root, height, + height_old); + // Assert insertion key accessed by mutable reference return. + assert!(get_insertion_key_test(node_ref_mut) == insertion_key, 0); + // Assert other returns. + assert!(operation == INCREMENT, 0); + assert!(side == RIGHT, 0); + assert!(delta == 1, 0); + // Assert child fields of parent. + assert!(get_child_left_by_id_test(&avlq, parent_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, parent_id) == node_id, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates/returns for `retrace_update_heights()` + /// case 1. + fun test_retrace_update_heights_1() { + // Declare arguments. + let tree_node = TreeNode{bits: + ( 3 as u128) << SHIFT_HEIGHT_LEFT | + ( 1 as u128) << SHIFT_HEIGHT_RIGHT }; + let side = LEFT; + let operation = DECREMENT; + let delta = 1; + // Update heights, storing returns. + let (height_left, height_right, height, height_old) = + retrace_update_heights(&mut tree_node, side, operation, delta); + // Assert returns. + assert!(height_left == 2, 0); + assert!(height_right == 1, 0); + assert!(height == 2, 0); + assert!(height_old == 3, 0); + // Assert node state. + assert!(get_height_left_test(&tree_node) == 2, 0); + assert!(get_height_right_test(&tree_node) == 1, 0); + // Unpack tree node, dropping bits. + let TreeNode{bits: _} = tree_node; + } + + #[test] + /// Verify state updates/returns for `retrace_update_heights()` + /// case 2. + fun test_retrace_update_heights_2() { + // Declare arguments. + let tree_node = TreeNode{bits: + ( 3 as u128) << SHIFT_HEIGHT_LEFT | + ( 4 as u128) << SHIFT_HEIGHT_RIGHT }; + let side = RIGHT; + let operation = INCREMENT; + let delta = 1; + // Update heights, storing returns. + let (height_left, height_right, height, height_old) = + retrace_update_heights(&mut tree_node, side, operation, delta); + // Assert returns. + assert!(height_left == 3, 0); + assert!(height_right == 5, 0); + assert!(height == 5, 0); + assert!(height_old == 4, 0); + // Assert node state. + assert!(get_height_left_test(&tree_node) == 3, 0); + assert!(get_height_right_test(&tree_node) == 5, 0); + // Unpack tree node, dropping bits. + let TreeNode{bits: _} = tree_node; + } + + #[test] + /// Verify returns/state updates for + /// `retrace_rebalance_rotate_left()` reference rotation 1. + fun test_rotate_left_1() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Declare node/tree IDs. + let node_x_id = HI_NODE_ID; + let node_z_id = node_x_id - 1; + let tree_3_id = node_z_id - 1; + // Mutably borrow tree nodes table. + let tree_nodes_ref_mut = &mut avlq.tree_nodes; + // Manually insert nodes from reference diagram. + table_with_length::add(tree_nodes_ref_mut, node_x_id, TreeNode{bits: + ( 4 as u128) << SHIFT_INSERTION_KEY | + ( 2 as u128) << SHIFT_HEIGHT_RIGHT | + (node_z_id as u128) << SHIFT_CHILD_RIGHT }); + table_with_length::add(tree_nodes_ref_mut, node_z_id, TreeNode{bits: + ( 6 as u128) << SHIFT_INSERTION_KEY | + ( 1 as u128) << SHIFT_HEIGHT_RIGHT | + (node_x_id as u128) << SHIFT_PARENT | + (tree_3_id as u128) << SHIFT_CHILD_RIGHT }); + table_with_length::add(tree_nodes_ref_mut, tree_3_id, TreeNode{bits: + ( 8 as u128) << SHIFT_INSERTION_KEY | + (node_z_id as u128) << SHIFT_PARENT }); + // Rebalance via left rotation, storing new subtree root node ID + // and height. + let (node_z_id_return, node_z_height_return) = + retrace_rebalance(&mut avlq, node_x_id, node_z_id, false); + // Assert returns. + assert!(node_z_id_return == node_z_id, 0); + assert!(node_z_height_return == 1, 0); + // Assert state for node x. + assert!(get_insertion_key_by_id_test(&avlq, node_x_id) == 4, 0); + assert!(get_height_left_by_id_test(&avlq, node_x_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, node_x_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, node_x_id) == node_z_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_x_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, node_x_id) + == (NIL as u64), 0); + // Assert state for node z. + assert!(get_insertion_key_by_id_test(&avlq, node_z_id) == 6, 0); + assert!(get_height_left_by_id_test(&avlq, node_z_id) == 1, 0); + assert!(get_height_right_by_id_test(&avlq, node_z_id) == 1, 0); + assert!(get_parent_by_id_test(&avlq, node_z_id) == (NIL as u64), 0); + assert!(get_child_left_by_id_test(&avlq, node_z_id) == node_x_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_z_id) == tree_3_id, 0); + // Assert state for tree 3. + assert!(get_insertion_key_by_id_test(&avlq, tree_3_id) == 8, 0); + assert!(get_height_left_by_id_test(&avlq, tree_3_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_3_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_3_id) == node_z_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_3_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_3_id) + == (NIL as u64), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates for `retrace_rebalance_rotate_left()` + /// reference rotation 2. + fun test_rotate_left_2() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Insert nodes from top to bottom, left to right. + let (node_a_id, node_b_id, node_x_id, node_c_id, node_d_id, node_z_id, + tree_2_id, tree_3_id) = + (get_access_key_tree_node_id_test(insert(&mut avlq, 3, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 2, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 5, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 1, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 4, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 7, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 6, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 8, 0))); + // Remove node d, rebalancing via left rotation. + remove_tree_node(&mut avlq, node_d_id); + assert!(get_root_test(&avlq) == node_a_id, 0); // Assert root. + // Assert inactive tree nodes stack top. + assert!(get_tree_top_test(&avlq) == node_d_id, 0); + // Assert node d state contains only bits for node ID of next + // tree node ID in inactive tree node stack. + let node_d_ref = borrow_tree_node_test(&avlq, node_d_id); + assert!(node_d_ref.bits == (NIL as u128), 0); + // Assert state for node x. + assert!(get_insertion_key_by_id_test(&avlq, node_x_id) == 5, 0); + assert!(get_height_left_by_id_test(&avlq, node_x_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, node_x_id) == 1, 0); + assert!(get_parent_by_id_test(&avlq, node_x_id) == node_z_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_x_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, node_x_id) == tree_2_id, 0); + // Assert state for node z. + assert!(get_insertion_key_by_id_test(&avlq, node_z_id) == 7, 0); + assert!(get_height_left_by_id_test(&avlq, node_z_id) == 2, 0); + assert!(get_height_right_by_id_test(&avlq, node_z_id) == 1, 0); + assert!(get_parent_by_id_test(&avlq, node_z_id) == node_a_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_z_id) == node_x_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_z_id) == tree_3_id, 0); + // Assert state for tree 3. + assert!(get_insertion_key_by_id_test(&avlq, tree_3_id) == 8, 0); + assert!(get_height_left_by_id_test(&avlq, tree_3_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_3_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_3_id) == node_z_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_3_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_3_id) + == (NIL as u64), 0); + // Assert state for tree 2. + assert!(get_insertion_key_by_id_test(&avlq, tree_2_id) == 6, 0); + assert!(get_height_left_by_id_test(&avlq, tree_2_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_2_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_2_id) == node_x_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_2_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_2_id) + == (NIL as u64), 0); + // Assert state for node a. + assert!(get_insertion_key_by_id_test(&avlq, node_a_id) == 3, 0); + assert!(get_height_left_by_id_test(&avlq, node_a_id) == 2, 0); + assert!(get_height_right_by_id_test(&avlq, node_a_id) == 3, 0); + assert!(get_parent_by_id_test(&avlq, node_a_id) == (NIL as u64), 0); + assert!(get_child_left_by_id_test(&avlq, node_a_id) == node_b_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_a_id) == node_z_id, 0); + // Assert state for node b. + assert!(get_insertion_key_by_id_test(&avlq, node_b_id) == 2, 0); + assert!(get_height_left_by_id_test(&avlq, node_b_id) == 1, 0); + assert!(get_height_right_by_id_test(&avlq, node_b_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, node_b_id) == node_a_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_b_id) == node_c_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_b_id) + == (NIL as u64), 0); + // Assert state for node c. + assert!(get_insertion_key_by_id_test(&avlq, node_c_id) == 1, 0); + assert!(get_height_left_by_id_test(&avlq, node_c_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, node_c_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, node_c_id) == node_b_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_c_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, node_c_id) + == (NIL as u64), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates for `retrace_rebalance_rotate_left_right()` + /// reference rotation 1. + fun test_rotate_left_right_1() { + // Declare number of allocated tree nodes. + let n_allocated_tree_nodes = 12; + // Init AVL queue. + let avlq = new(ASCENDING, n_allocated_tree_nodes, 0); + // Insert nodes from top to bottom, left to right. + let (node_x_id, node_z_id, node_r_id, tree_1_id, node_y_id, tree_4_id, + tree_3_id) = + (get_access_key_tree_node_id_test(insert(&mut avlq, 5, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 2, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 6, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 1, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 3, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 7, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 4, 0))); + // Declare number of active tree nodes. + let n_active_tree_nodes = 7; + // Remove node r, rebalancing via left-right rotation. + remove_tree_node(&mut avlq, node_r_id); + assert!(get_root_test(&avlq) == node_y_id, 0); // Assert root. + // Assert inactive tree nodes stack top. + assert!(get_tree_top_test(&avlq) == node_r_id, 0); + // Assert node r state contains only bits for node ID of next + // tree node ID in inactive tree node stack. + let node_r_ref = borrow_tree_node_test(&avlq, node_r_id); + assert!(node_r_ref.bits == + ((n_allocated_tree_nodes - n_active_tree_nodes) as u128), 0); + // Assert state for node x. + assert!(get_insertion_key_by_id_test(&avlq, node_x_id) == 5, 0); + assert!(get_height_left_by_id_test(&avlq, node_x_id) == 1, 0); + assert!(get_height_right_by_id_test(&avlq, node_x_id) == 1, 0); + assert!(get_parent_by_id_test(&avlq, node_x_id) == node_y_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_x_id) == tree_3_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_x_id) == tree_4_id, 0); + // Assert state for node y. + assert!(get_insertion_key_by_id_test(&avlq, node_y_id) == 3, 0); + assert!(get_height_left_by_id_test(&avlq, node_y_id) == 2, 0); + assert!(get_height_right_by_id_test(&avlq, node_y_id) == 2, 0); + assert!(get_parent_by_id_test(&avlq, node_y_id) == (NIL as u64), 0); + assert!(get_child_left_by_id_test(&avlq, node_y_id) == node_z_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_y_id) == node_x_id, 0); + // Assert state for node z. + assert!(get_insertion_key_by_id_test(&avlq, node_z_id) == 2, 0); + assert!(get_height_left_by_id_test(&avlq, node_z_id) == 1, 0); + assert!(get_height_right_by_id_test(&avlq, node_z_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, node_z_id) == node_y_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_z_id) == tree_1_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_z_id) + == (NIL as u64), 0); + // Assert state for tree 1. + assert!(get_insertion_key_by_id_test(&avlq, tree_1_id) == 1, 0); + assert!(get_height_left_by_id_test(&avlq, tree_1_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_1_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_1_id) == node_z_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_1_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_1_id) + == (NIL as u64), 0); + // Assert state for tree 3. + assert!(get_insertion_key_by_id_test(&avlq, tree_3_id) == 4, 0); + assert!(get_height_left_by_id_test(&avlq, tree_3_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_3_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_3_id) == node_x_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_3_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_3_id) + == (NIL as u64), 0); + // Assert state for tree 4. + assert!(get_insertion_key_by_id_test(&avlq, tree_4_id) == 7, 0); + assert!(get_height_left_by_id_test(&avlq, tree_4_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_4_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_4_id) == node_x_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_4_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_4_id) + == (NIL as u64), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify returns/state updates for + /// `retrace_rebalance_rotate_left_right()` reference rotation 2. + fun test_rotate_left_right_2() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Declare node/tree IDs. + let node_x_id = HI_NODE_ID; + let node_z_id = node_x_id - 1; + let node_y_id = node_z_id - 1; + let tree_1_id = node_y_id - 1; + let tree_2_id = tree_1_id - 1; + let tree_4_id = tree_2_id - 1; + // Mutably borrow tree nodes table. + let tree_nodes_ref_mut = &mut avlq.tree_nodes; + // Manually insert nodes from reference diagram. + table_with_length::add(tree_nodes_ref_mut, node_x_id, TreeNode{bits: + ( 8 as u128) << SHIFT_INSERTION_KEY | + ( 3 as u128) << SHIFT_HEIGHT_LEFT | + ( 1 as u128) << SHIFT_HEIGHT_RIGHT | + (node_z_id as u128) << SHIFT_CHILD_LEFT | + (tree_4_id as u128) << SHIFT_CHILD_RIGHT }); + table_with_length::add(tree_nodes_ref_mut, node_z_id, TreeNode{bits: + ( 2 as u128) << SHIFT_INSERTION_KEY | + ( 1 as u128) << SHIFT_HEIGHT_LEFT | + ( 2 as u128) << SHIFT_HEIGHT_RIGHT | + (node_x_id as u128) << SHIFT_PARENT | + (tree_1_id as u128) << SHIFT_CHILD_LEFT | + (node_y_id as u128) << SHIFT_CHILD_RIGHT }); + table_with_length::add(tree_nodes_ref_mut, node_y_id, TreeNode{bits: + ( 6 as u128) << SHIFT_INSERTION_KEY | + ( 1 as u128) << SHIFT_HEIGHT_LEFT | + (node_z_id as u128) << SHIFT_PARENT | + (tree_2_id as u128) << SHIFT_CHILD_LEFT }); + table_with_length::add(tree_nodes_ref_mut, tree_1_id, TreeNode{bits: + ( 1 as u128) << SHIFT_INSERTION_KEY | + (node_z_id as u128) << SHIFT_PARENT }); + table_with_length::add(tree_nodes_ref_mut, tree_2_id, TreeNode{bits: + ( 5 as u128) << SHIFT_INSERTION_KEY | + (node_y_id as u128) << SHIFT_PARENT }); + table_with_length::add(tree_nodes_ref_mut, tree_4_id, TreeNode{bits: + ( 9 as u128) << SHIFT_INSERTION_KEY | + (node_x_id as u128) << SHIFT_PARENT }); + // Rebalance via left-right rotation, storing new subtree root + // node ID and height. + let (node_y_id_return, node_y_height_return) = + retrace_rebalance(&mut avlq, node_x_id, node_z_id, true); + // Assert returns. + assert!(node_y_id_return == node_y_id, 0); + assert!(node_y_height_return == 2, 0); + // Assert state for node x. + assert!(get_insertion_key_by_id_test(&avlq, node_x_id) == 8, 0); + assert!(get_height_left_by_id_test(&avlq, node_x_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, node_x_id) == 1, 0); + assert!(get_parent_by_id_test(&avlq, node_x_id) == node_y_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_x_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, node_x_id) == tree_4_id, 0); + // Assert state for node y. + assert!(get_insertion_key_by_id_test(&avlq, node_y_id) == 6, 0); + assert!(get_height_left_by_id_test(&avlq, node_y_id) == 2, 0); + assert!(get_height_right_by_id_test(&avlq, node_y_id) == 2, 0); + assert!(get_parent_by_id_test(&avlq, node_y_id) == (NIL as u64), 0); + assert!(get_child_left_by_id_test(&avlq, node_y_id) == node_z_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_y_id) == node_x_id, 0); + // Assert state for node z. + assert!(get_insertion_key_by_id_test(&avlq, node_z_id) == 2, 0); + assert!(get_height_left_by_id_test(&avlq, node_z_id) == 1, 0); + assert!(get_height_right_by_id_test(&avlq, node_z_id) == 1, 0); + assert!(get_parent_by_id_test(&avlq, node_z_id) == node_y_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_z_id) == tree_1_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_z_id) == tree_2_id, 0); + // Assert state for tree 1. + assert!(get_insertion_key_by_id_test(&avlq, tree_1_id) == 1, 0); + assert!(get_height_left_by_id_test(&avlq, tree_1_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_1_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_1_id) == node_z_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_1_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_1_id) + == (NIL as u64), 0); + // Assert state for tree 2. + assert!(get_insertion_key_by_id_test(&avlq, tree_2_id) == 5, 0); + assert!(get_height_left_by_id_test(&avlq, tree_2_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_2_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_2_id) == node_z_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_2_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_2_id) + == (NIL as u64), 0); + // Assert state for tree 4. + assert!(get_insertion_key_by_id_test(&avlq, tree_4_id) == 9, 0); + assert!(get_height_left_by_id_test(&avlq, tree_4_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_4_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_4_id) == node_x_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_4_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_4_id) + == (NIL as u64), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify returns/state updates for + /// `retrace_rebalance_rotate_right()` reference rotation 1. + fun test_rotate_right_1() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Declare node/tree IDs. + let node_x_id = HI_NODE_ID; + let node_z_id = node_x_id - 1; + let tree_1_id = node_z_id - 1; + // Mutably borrow tree nodes table. + let tree_nodes_ref_mut = &mut avlq.tree_nodes; + // Manually insert nodes from reference diagram, with heights + // not yet updated via retrace. + table_with_length::add(tree_nodes_ref_mut, node_x_id, TreeNode{bits: + ( 8 as u128) << SHIFT_INSERTION_KEY | + ( 1 as u128) << SHIFT_HEIGHT_LEFT | + (node_z_id as u128) << SHIFT_CHILD_LEFT }); + table_with_length::add(tree_nodes_ref_mut, node_z_id, TreeNode{bits: + ( 6 as u128) << SHIFT_INSERTION_KEY | + (node_x_id as u128) << SHIFT_PARENT | + (tree_1_id as u128) << SHIFT_CHILD_LEFT }); + table_with_length::add(tree_nodes_ref_mut, tree_1_id, TreeNode{bits: + ( 4 as u128) << SHIFT_INSERTION_KEY | + (node_z_id as u128) << SHIFT_PARENT }); + // Set root node ID. + set_root_test(&mut avlq, node_z_id); + // Retrace from node z, rebalancing via right rotation. + retrace(&mut avlq, node_z_id, INCREMENT, LEFT); + // Assert state for node x. + assert!(get_insertion_key_by_id_test(&avlq, node_x_id) == 8, 0); + assert!(get_height_left_by_id_test(&avlq, node_x_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, node_x_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, node_x_id) == node_z_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_x_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, node_x_id) + == (NIL as u64), 0); + // Assert state for node z. + assert!(get_insertion_key_by_id_test(&avlq, node_z_id) == 6, 0); + assert!(get_height_left_by_id_test(&avlq, node_z_id) == 1, 0); + assert!(get_height_right_by_id_test(&avlq, node_z_id) == 1, 0); + assert!(get_parent_by_id_test(&avlq, node_z_id) == (NIL as u64), 0); + assert!(get_child_left_by_id_test(&avlq, node_z_id) == tree_1_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_z_id) == node_x_id, 0); + // Assert state for tree 1. + assert!(get_insertion_key_by_id_test(&avlq, tree_1_id) == 4, 0); + assert!(get_height_left_by_id_test(&avlq, tree_1_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_1_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_1_id) == node_z_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_1_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_1_id) + == (NIL as u64), 0); + // Assert root. + assert!(get_root_test(&avlq) == node_z_id, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify returns/state updates for + /// `retrace_rebalance_rotate_right()` reference rotation 2. + fun test_rotate_right_2() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Declare node/tree IDs. + let node_x_id = HI_NODE_ID; + let node_z_id = node_x_id - 1; + let tree_1_id = node_z_id - 1; + let tree_2_id = tree_1_id - 2; + // Mutably borrow tree nodes table. + let tree_nodes_ref_mut = &mut avlq.tree_nodes; + // Manually insert nodes from reference diagram. + table_with_length::add(tree_nodes_ref_mut, node_x_id, TreeNode{bits: + ( 7 as u128) << SHIFT_INSERTION_KEY | + ( 2 as u128) << SHIFT_HEIGHT_LEFT | + (NIL as u128) << SHIFT_PARENT | + (node_z_id as u128) << SHIFT_CHILD_LEFT }); + table_with_length::add(tree_nodes_ref_mut, node_z_id, TreeNode{bits: + ( 4 as u128) << SHIFT_INSERTION_KEY | + ( 1 as u128) << SHIFT_HEIGHT_LEFT | + ( 1 as u128) << SHIFT_HEIGHT_RIGHT | + (node_x_id as u128) << SHIFT_PARENT | + (tree_1_id as u128) << SHIFT_CHILD_LEFT | + (tree_2_id as u128) << SHIFT_CHILD_RIGHT }); + table_with_length::add(tree_nodes_ref_mut, tree_1_id, TreeNode{bits: + ( 3 as u128) << SHIFT_INSERTION_KEY | + (node_z_id as u128) << SHIFT_PARENT }); + table_with_length::add(tree_nodes_ref_mut, tree_2_id, TreeNode{bits: + ( 5 as u128) << SHIFT_INSERTION_KEY | + (node_z_id as u128) << SHIFT_PARENT }); + // Rebalance via right rotation, storing new subtree root node + // ID and height. + let (node_z_id_return, node_z_height_return) = + retrace_rebalance(&mut avlq, node_x_id, node_z_id, true); + // Assert returns. + assert!(node_z_id_return == node_z_id, 0); + assert!(node_z_height_return == 2, 0); + // Assert state for node x. + assert!(get_insertion_key_by_id_test(&avlq, node_x_id) == 7, 0); + assert!(get_height_left_by_id_test(&avlq, node_x_id) == 1, 0); + assert!(get_height_right_by_id_test(&avlq, node_x_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, node_x_id) == node_z_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_x_id) == tree_2_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_x_id) + == (NIL as u64), 0); + // Assert state for node z. + assert!(get_insertion_key_by_id_test(&avlq, node_z_id) == 4, 0); + assert!(get_height_left_by_id_test(&avlq, node_z_id) == 1, 0); + assert!(get_height_right_by_id_test(&avlq, node_z_id) == 2, 0); + assert!(get_parent_by_id_test(&avlq, node_z_id) == (NIL as u64), 0); + assert!(get_child_left_by_id_test(&avlq, node_z_id) == tree_1_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_z_id) == node_x_id, 0); + // Assert state for tree 1. + assert!(get_insertion_key_by_id_test(&avlq, tree_1_id) == 3, 0); + assert!(get_height_left_by_id_test(&avlq, tree_1_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_1_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_1_id) == node_z_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_1_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_1_id) + == (NIL as u64), 0); + // Assert state for tree 2. + assert!(get_insertion_key_by_id_test(&avlq, tree_2_id) == 5, 0); + assert!(get_height_left_by_id_test(&avlq, tree_2_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_2_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_2_id) == node_x_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_2_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_2_id) + == (NIL as u64), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify returns/state updates for + /// `retrace_rebalance_rotate_right_left()` reference rotation 1. + fun test_rotate_right_left_1() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Declare node/tree IDs. + let node_x_id = HI_NODE_ID; + let node_z_id = node_x_id - 1; + let node_y_id = node_z_id - 1; + let tree_1_id = node_y_id - 1; + let tree_2_id = tree_1_id - 1; + let tree_4_id = tree_2_id - 1; + // Mutably borrow tree nodes table. + let tree_nodes_ref_mut = &mut avlq.tree_nodes; + // Manually insert nodes from reference diagram. + table_with_length::add(tree_nodes_ref_mut, node_x_id, TreeNode{bits: + ( 2 as u128) << SHIFT_INSERTION_KEY | + ( 1 as u128) << SHIFT_HEIGHT_LEFT | + ( 3 as u128) << SHIFT_HEIGHT_RIGHT | + (tree_1_id as u128) << SHIFT_CHILD_LEFT | + (node_z_id as u128) << SHIFT_CHILD_RIGHT }); + table_with_length::add(tree_nodes_ref_mut, node_z_id, TreeNode{bits: + ( 8 as u128) << SHIFT_INSERTION_KEY | + ( 2 as u128) << SHIFT_HEIGHT_LEFT | + ( 1 as u128) << SHIFT_HEIGHT_RIGHT | + (node_x_id as u128) << SHIFT_PARENT | + (node_y_id as u128) << SHIFT_CHILD_LEFT | + (tree_4_id as u128) << SHIFT_CHILD_RIGHT }); + table_with_length::add(tree_nodes_ref_mut, node_y_id, TreeNode{bits: + ( 4 as u128) << SHIFT_INSERTION_KEY | + ( 1 as u128) << SHIFT_HEIGHT_LEFT | + (node_z_id as u128) << SHIFT_PARENT | + (tree_2_id as u128) << SHIFT_CHILD_LEFT }); + table_with_length::add(tree_nodes_ref_mut, tree_1_id, TreeNode{bits: + ( 1 as u128) << SHIFT_INSERTION_KEY | + (node_x_id as u128) << SHIFT_PARENT }); + table_with_length::add(tree_nodes_ref_mut, tree_2_id, TreeNode{bits: + ( 3 as u128) << SHIFT_INSERTION_KEY | + (node_y_id as u128) << SHIFT_PARENT }); + table_with_length::add(tree_nodes_ref_mut, tree_4_id, TreeNode{bits: + ( 9 as u128) << SHIFT_INSERTION_KEY | + (node_z_id as u128) << SHIFT_PARENT }); + // Rebalance via right-left rotation, storing new subtree root + // node ID and height. + let (node_y_id_return, node_y_height_return) = + retrace_rebalance(&mut avlq, node_x_id, node_z_id, false); + // Assert returns. + assert!(node_y_id_return == node_y_id, 0); + assert!(node_y_height_return == 2, 0); + // Assert state for node x. + assert!(get_insertion_key_by_id_test(&avlq, node_x_id) == 2, 0); + assert!(get_height_left_by_id_test(&avlq, node_x_id) == 1, 0); + assert!(get_height_right_by_id_test(&avlq, node_x_id) == 1, 0); + assert!(get_parent_by_id_test(&avlq, node_x_id) == node_y_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_x_id) == tree_1_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_x_id) == tree_2_id, 0); + // Assert state for node y. + assert!(get_insertion_key_by_id_test(&avlq, node_y_id) == 4, 0); + assert!(get_height_left_by_id_test(&avlq, node_y_id) == 2, 0); + assert!(get_height_right_by_id_test(&avlq, node_y_id) == 2, 0); + assert!(get_parent_by_id_test(&avlq, node_y_id) == (NIL as u64), 0); + assert!(get_child_left_by_id_test(&avlq, node_y_id) == node_x_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_y_id) == node_z_id, 0); + // Assert state for node z. + assert!(get_insertion_key_by_id_test(&avlq, node_z_id) == 8, 0); + assert!(get_height_left_by_id_test(&avlq, node_z_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, node_z_id) == 1, 0); + assert!(get_parent_by_id_test(&avlq, node_z_id) == node_y_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_z_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, node_z_id) == tree_4_id, 0); + // Assert state for tree 1. + assert!(get_insertion_key_by_id_test(&avlq, tree_1_id) == 1, 0); + assert!(get_height_left_by_id_test(&avlq, tree_1_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_1_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_1_id) == node_x_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_1_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_1_id) + == (NIL as u64), 0); + // Assert state for tree 2. + assert!(get_insertion_key_by_id_test(&avlq, tree_2_id) == 3, 0); + assert!(get_height_left_by_id_test(&avlq, tree_2_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_2_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_2_id) == node_x_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_2_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_2_id) + == (NIL as u64), 0); + // Assert state for tree 4. + assert!(get_insertion_key_by_id_test(&avlq, tree_4_id) == 9, 0); + assert!(get_height_left_by_id_test(&avlq, tree_4_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_4_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_4_id) == node_z_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_4_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_4_id) + == (NIL as u64), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify state updates for `retrace_rebalance_rotate_right_left()` + /// reference rotation 2. + fun test_rotate_right_left_2() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Insert nodes from top to bottom, left to right. + let (node_x_id, node_r_id, node_z_id, tree_1_id, node_y_id, tree_4_id, + tree_3_id) = + (get_access_key_tree_node_id_test(insert(&mut avlq, 3, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 2, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 6, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 1, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 4, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 7, 0)), + get_access_key_tree_node_id_test(insert(&mut avlq, 5, 0))); + // Remove node r, rebalancing via right-left rotation. + remove_tree_node(&mut avlq, node_r_id); + assert!(get_root_test(&avlq) == node_y_id, 0); // Assert root. + // Assert inactive tree nodes stack top. + assert!(get_tree_top_test(&avlq) == node_r_id, 0); + // Assert node r state contains only bits for node ID of next + // tree node ID in inactive tree node stack. + let node_r_ref = borrow_tree_node_test(&avlq, node_r_id); + assert!(node_r_ref.bits == (NIL as u128), 0); + // Assert state for node x. + assert!(get_insertion_key_by_id_test(&avlq, node_x_id) == 3, 0); + assert!(get_height_left_by_id_test(&avlq, node_x_id) == 1, 0); + assert!(get_height_right_by_id_test(&avlq, node_x_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, node_x_id) == node_y_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_x_id) == tree_1_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_x_id) + == (NIL as u64), 0); + // Assert state for node y. + assert!(get_insertion_key_by_id_test(&avlq, node_y_id) == 4, 0); + assert!(get_height_left_by_id_test(&avlq, node_y_id) == 2, 0); + assert!(get_height_right_by_id_test(&avlq, node_y_id) == 2, 0); + assert!(get_parent_by_id_test(&avlq, node_y_id) == (NIL as u64), 0); + assert!(get_child_left_by_id_test(&avlq, node_y_id) == node_x_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_y_id) == node_z_id, 0); + // Assert state for node z. + assert!(get_insertion_key_by_id_test(&avlq, node_z_id) == 6, 0); + assert!(get_height_left_by_id_test(&avlq, node_z_id) == 1, 0); + assert!(get_height_right_by_id_test(&avlq, node_z_id) == 1, 0); + assert!(get_parent_by_id_test(&avlq, node_z_id) == node_y_id, 0); + assert!(get_child_left_by_id_test(&avlq, node_z_id) == tree_3_id, 0); + assert!(get_child_right_by_id_test(&avlq, node_z_id) == tree_4_id, 0); + // Assert state for tree 1. + assert!(get_insertion_key_by_id_test(&avlq, tree_1_id) == 1, 0); + assert!(get_height_left_by_id_test(&avlq, tree_1_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_1_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_1_id) == node_x_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_1_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_1_id) + == (NIL as u64), 0); + // Assert state for tree 3. + assert!(get_insertion_key_by_id_test(&avlq, tree_3_id) == 5, 0); + assert!(get_height_left_by_id_test(&avlq, tree_3_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_3_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_3_id) == node_z_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_3_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_3_id) + == (NIL as u64), 0); + // Assert state for tree 4. + assert!(get_insertion_key_by_id_test(&avlq, tree_4_id) == 7, 0); + assert!(get_height_left_by_id_test(&avlq, tree_4_id) == 0, 0); + assert!(get_height_right_by_id_test(&avlq, tree_4_id) == 0, 0); + assert!(get_parent_by_id_test(&avlq, tree_4_id) == node_z_id, 0); + assert!(get_child_left_by_id_test(&avlq, tree_4_id) + == (NIL as u64), 0); + assert!(get_child_right_by_id_test(&avlq, tree_4_id) + == (NIL as u64), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify returns for reference diagram in `search()`. + fun test_search() { + // Init ascending AVL queue. + let avlq = new(ASCENDING, 0, 0); + // Assert returns for when empty. + let (node_id, side_option) = search(&mut avlq, 12345); + assert!(node_id == (NIL as u64), 0); + assert!(option::is_none(&side_option), 0); + // Manually set root. + set_root_test(&mut avlq, 1); + // Mutably borrow tree nodes table. + let tree_nodes_ref_mut = &mut avlq.tree_nodes; + // Manually insert nodes from reference diagram. + table_with_length::add(tree_nodes_ref_mut, 1, TreeNode{bits: + (4 as u128) << SHIFT_INSERTION_KEY | + (5 as u128) << SHIFT_CHILD_LEFT | + (2 as u128) << SHIFT_CHILD_RIGHT}); + table_with_length::add(tree_nodes_ref_mut, 2, TreeNode{bits: + (8 as u128) << SHIFT_INSERTION_KEY | + (1 as u128) << SHIFT_PARENT | + (4 as u128) << SHIFT_CHILD_LEFT | + (3 as u128) << SHIFT_CHILD_RIGHT}); + table_with_length::add(tree_nodes_ref_mut, 3, TreeNode{bits: + (10 as u128) << SHIFT_INSERTION_KEY | + (2 as u128) << SHIFT_PARENT}); + table_with_length::add(tree_nodes_ref_mut, 4, TreeNode{bits: + (6 as u128) << SHIFT_INSERTION_KEY | + (2 as u128) << SHIFT_PARENT}); + table_with_length::add(tree_nodes_ref_mut, 5, TreeNode{bits: + (2 as u128) << SHIFT_INSERTION_KEY | + (1 as u128) << SHIFT_PARENT}); + // Assert returns in order from reference table. + (node_id, side_option) = search(&mut avlq, 2); + let node_ref = borrow_tree_node_test(&avlq, node_id); + assert!(get_insertion_key_test(node_ref) == 2, 0); + assert!(node_id == 5, 0); + assert!(option::is_none(&side_option), 0); + (node_id, side_option) = search(&mut avlq, 7); + node_ref = borrow_tree_node_test(&avlq, node_id); + assert!(get_insertion_key_test(node_ref) == 6, 0); + assert!(node_id == 4, 0); + assert!(*option::borrow(&side_option) == RIGHT, 0); + (node_id, side_option) = search(&mut avlq, 9); + node_ref = borrow_tree_node_test(&avlq, node_id); + assert!(get_insertion_key_test(node_ref) == 10, 0); + assert!(node_id == 3, 0); + assert!(*option::borrow(&side_option) == LEFT, 0); + (node_id, side_option) = search(&mut avlq, 4); + node_ref = borrow_tree_node_test(&avlq, node_id); + assert!(get_insertion_key_test(node_ref) == 4, 0); + assert!(node_id == 1, 0); + assert!(option::is_none(&side_option), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify successful state operations. + fun test_set_get_head_tail_test() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + avlq.bits = 0; // Clear out all bits. + // Declare head and tail keys, node IDs. + let head_key = u_64(b"10000000000000000000000000000001"); + let tail_key = u_64(b"11000000000000000000000000000011"); + let head_node_id = u_64(b"10000000000001"); + let tail_node_id = u_64(b"11000000000011"); + // Set head and tail keys, node IDs. + set_head_key_test(&mut avlq, head_key); + set_tail_key_test(&mut avlq, tail_key); + set_head_node_id_test(&mut avlq, head_node_id); + set_tail_node_id_test(&mut avlq, tail_node_id); + // Assert bit fields. + assert!(avlq.bits == u_128_by_32( + b"00000000000000000000000000000010", + // ^ bit 97 + b"00000000000110000000000000000000", + // bit 84 ^^ bit 83 + b"00000000000111000000000011110000", + // bit 52 ^^ bits 38-51 ^^ bit 37 + b"00000000000000000000000011000000"), 0); + // ^ bit 6 + // Assert getter returns. + assert!(get_head_key_test(&avlq) == head_key, 0); + assert!(get_tail_key_test(&avlq) == tail_key, 0); + assert!(get_head_node_id_test(&avlq) == head_node_id, 0); + assert!(get_tail_node_id_test(&avlq) == tail_node_id, 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify successful state operations. + fun test_set_get_root_test() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + avlq.bits = u_128_by_32( // Set all bits. + b"11111111111111111111111111111111", + b"11111111111111111111111111111111", + b"11111111111111111111111111111111", + b"11111111111111111111111111111111"); + avlq.root_lsbs = (u_64(b"11111111") as u8); // Set all bits. + // Assert getter return. + assert!(get_root_test(&avlq) == HI_NODE_ID, 0); + let new_root = u_64(b"10000000000001"); // Declare new root. + set_root_test(&mut avlq, new_root); // Set new root. + // Assert getter return. + assert!(get_root_test(&avlq) == new_root, 0); + // Assert fields. + assert!(avlq.bits == u_128_by_32( + b"11111111111111111111111111111111", + b"11111111111111111111111111111111", + b"11111111111111111111111111111111", + b"11111111111111111111111111100000"), 0); + assert!(avlq.root_lsbs == (u_64(b"00000001") as u8), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify successful returns for reference diagram in `traverse()`, + /// with two list nodes in each tree node. + fun test_traverse() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Insert root from reference diagram. + let access_key_5_1 = (insert(&mut avlq, 5, 1)); + let access_key_5_2 = (insert(&mut avlq, 5, 2)); + let tree_id_5 = get_access_key_tree_node_id_test(access_key_5_1); + let list_id_5_1 = get_access_key_list_node_id_test(access_key_5_1); + let list_id_5_2 = get_access_key_list_node_id_test(access_key_5_2); + // Assert returns for traversal at root. + let (key, head, tail) = traverse(&avlq, tree_id_5, PREDECESSOR); + assert!(key == (NIL as u64), 0); + assert!(head == (NIL as u64), 0); + assert!(tail == (NIL as u64), 0); + (key, head, tail) = traverse(&avlq, tree_id_5, SUCCESSOR); + assert!(key == (NIL as u64), 0); + assert!(head == (NIL as u64), 0); + assert!(tail == (NIL as u64), 0); + // Insert in remaining sequence from reference diagram. + let access_key_8_1 = insert(&mut avlq, 8, 1); + let access_key_8_2 = insert(&mut avlq, 8, 2); + let tree_id_8 = get_access_key_tree_node_id_test(access_key_8_1); + let list_id_8_1 = get_access_key_list_node_id_test(access_key_8_1); + let list_id_8_2 = get_access_key_list_node_id_test(access_key_8_2); + let access_key_2_1 = insert(&mut avlq, 2, 1); + let access_key_2_2 = insert(&mut avlq, 2, 2); + let tree_id_2 = get_access_key_tree_node_id_test(access_key_2_1); + let list_id_2_1 = get_access_key_list_node_id_test(access_key_2_1); + let list_id_2_2 = get_access_key_list_node_id_test(access_key_2_2); + let access_key_1_1 = insert(&mut avlq, 1, 1); + let access_key_1_2 = insert(&mut avlq, 1, 2); + let tree_id_1 = get_access_key_tree_node_id_test(access_key_1_1); + let list_id_1_1 = get_access_key_list_node_id_test(access_key_1_1); + let list_id_1_2 = get_access_key_list_node_id_test(access_key_1_2); + let access_key_3_1 = insert(&mut avlq, 3, 1); + let access_key_3_2 = insert(&mut avlq, 3, 2); + let tree_id_3 = get_access_key_tree_node_id_test(access_key_3_1); + let list_id_3_1 = get_access_key_list_node_id_test(access_key_3_1); + let list_id_3_2 = get_access_key_list_node_id_test(access_key_3_2); + let access_key_7_1 = insert(&mut avlq, 7, 1); + let access_key_7_2 = insert(&mut avlq, 7, 2); + let tree_id_7 = get_access_key_tree_node_id_test(access_key_7_1); + let list_id_7_1 = get_access_key_list_node_id_test(access_key_7_1); + let list_id_7_2 = get_access_key_list_node_id_test(access_key_7_2); + let access_key_9_1 = insert(&mut avlq, 9, 1); + let access_key_9_2 = insert(&mut avlq, 9, 2); + let tree_id_9 = get_access_key_tree_node_id_test(access_key_9_1); + let list_id_9_1 = get_access_key_list_node_id_test(access_key_9_1); + let list_id_9_2 = get_access_key_list_node_id_test(access_key_9_2); + let access_key_4_1 = insert(&mut avlq, 4, 1); + let access_key_4_2 = insert(&mut avlq, 4, 2); + let tree_id_4 = get_access_key_tree_node_id_test(access_key_4_1); + let list_id_4_1 = get_access_key_list_node_id_test(access_key_4_1); + let list_id_4_2 = get_access_key_list_node_id_test(access_key_4_2); + let access_key_6_1 = insert(&mut avlq, 6, 1); + let access_key_6_2 = insert(&mut avlq, 6, 2); + let tree_id_6 = get_access_key_tree_node_id_test(access_key_6_1); + let list_id_6_1 = get_access_key_list_node_id_test(access_key_6_1); + let list_id_6_2 = get_access_key_list_node_id_test(access_key_6_2); + // Assert predecessor returns. + (key, head, tail) = traverse(&avlq, tree_id_1, PREDECESSOR); + assert!(key == (NIL as u64), 0); + assert!(head == (NIL as u64), 0); + assert!(tail == (NIL as u64), 0); + (key, head, tail) = traverse(&avlq, tree_id_2, PREDECESSOR); + assert!(key == 1, 0); + assert!(head == list_id_1_1, 0); + assert!(tail == list_id_1_2, 0); + (key, head, tail) = traverse(&avlq, tree_id_3, PREDECESSOR); + assert!(key == 2, 0); + assert!(head == list_id_2_1, 0); + assert!(tail == list_id_2_2, 0); + (key, head, tail) = traverse(&avlq, tree_id_4, PREDECESSOR); + assert!(key == 3, 0); + assert!(head == list_id_3_1, 0); + assert!(tail == list_id_3_2, 0); + (key, head, tail) = traverse(&avlq, tree_id_5, PREDECESSOR); + assert!(key == 4, 0); + assert!(head == list_id_4_1, 0); + assert!(tail == list_id_4_2, 0); + (key, head, tail) = traverse(&avlq, tree_id_6, PREDECESSOR); + assert!(key == 5, 0); + assert!(head == list_id_5_1, 0); + assert!(tail == list_id_5_2, 0); + (key, head, tail) = traverse(&avlq, tree_id_7, PREDECESSOR); + assert!(key == 6, 0); + assert!(head == list_id_6_1, 0); + assert!(tail == list_id_6_2, 0); + (key, head, tail) = traverse(&avlq, tree_id_8, PREDECESSOR); + assert!(key == 7, 0); + assert!(head == list_id_7_1, 0); + assert!(tail == list_id_7_2, 0); + (key, head, tail) = traverse(&avlq, tree_id_9, PREDECESSOR); + assert!(key == 8, 0); + assert!(head == list_id_8_1, 0); + assert!(tail == list_id_8_2, 0); + // Assert successor returns. + (key, head, tail) = traverse(&avlq, tree_id_1, SUCCESSOR); + assert!(key == 2, 0); + assert!(head == list_id_2_1, 0); + assert!(tail == list_id_2_2, 0); + (key, head, tail) = traverse(&avlq, tree_id_2, SUCCESSOR); + assert!(key == 3, 0); + assert!(head == list_id_3_1, 0); + assert!(tail == list_id_3_2, 0); + (key, head, tail) = traverse(&avlq, tree_id_3, SUCCESSOR); + assert!(key == 4, 0); + assert!(head == list_id_4_1, 0); + assert!(tail == list_id_4_2, 0); + (key, head, tail) = traverse(&avlq, tree_id_4, SUCCESSOR); + assert!(key == 5, 0); + assert!(head == list_id_5_1, 0); + assert!(tail == list_id_5_2, 0); + (key, head, tail) = traverse(&avlq, tree_id_5, SUCCESSOR); + assert!(key == 6, 0); + assert!(head == list_id_6_1, 0); + assert!(tail == list_id_6_2, 0); + (key, head, tail) = traverse(&avlq, tree_id_6, SUCCESSOR); + assert!(key == 7, 0); + assert!(head == list_id_7_1, 0); + assert!(tail == list_id_7_2, 0); + (key, head, tail) = traverse(&avlq, tree_id_7, SUCCESSOR); + assert!(key == 8, 0); + assert!(head == list_id_8_1, 0); + assert!(tail == list_id_8_2, 0); + (key, head, tail) = traverse(&avlq, tree_id_8, SUCCESSOR); + assert!(key == 9, 0); + assert!(head == list_id_9_1, 0); + assert!(tail == list_id_9_2, 0); + (key, head, tail) = traverse(&avlq, tree_id_9, SUCCESSOR); + assert!(key == (NIL as u64), 0); + assert!(head == (NIL as u64), 0); + assert!(tail == (NIL as u64), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + /// Verify successful return values. + fun test_u_128_64() { + assert!(u_128(b"0") == 0, 0); + assert!(u_128(b"1") == 1, 0); + assert!(u_128(b"00") == 0, 0); + assert!(u_128(b"01") == 1, 0); + assert!(u_128(b"10") == 2, 0); + assert!(u_128(b"11") == 3, 0); + assert!(u_128(b"10101010") == 170, 0); + assert!(u_128(b"00000001") == 1, 0); + assert!(u_128(b"11111111") == 255, 0); + assert!(u_128_by_32( + b"11111111111111111111111111111111", + b"11111111111111111111111111111111", + b"11111111111111111111111111111111", + b"11111111111111111111111111111111" + ) == HI_128, 0); + assert!(u_128_by_32( + b"11111111111111111111111111111111", + b"11111111111111111111111111111111", + b"11111111111111111111111111111111", + b"11111111111111111111111111111110" + ) == HI_128 - 1, 0); + assert!(u_64(b"0") == 0, 0); + assert!(u_64(b"0") == 0, 0); + assert!(u_64(b"1") == 1, 0); + assert!(u_64(b"00") == 0, 0); + assert!(u_64(b"01") == 1, 0); + assert!(u_64(b"10") == 2, 0); + assert!(u_64(b"11") == 3, 0); + assert!(u_64(b"10101010") == 170, 0); + assert!(u_64(b"00000001") == 1, 0); + assert!(u_64(b"11111111") == 255, 0); + assert!(u_64_by_32( + b"11111111111111111111111111111111", + b"11111111111111111111111111111111" + ) == HI_64, 0); + assert!(u_64_by_32( + b"11111111111111111111111111111111", + b"11111111111111111111111111111110" + ) == HI_64 - 1, 0); + } + + #[test] + #[expected_failure(abort_code = E_BIT_NOT_0_OR_1)] + /// Verify failure for non-binary-representative byte string. + fun test_u_128_failure() {u_128(b"2");} + + #[test] + /// Verify expected returns. + fun test_would_update_head_tail() { + // Init ascending AVL queue. + let avlq = new(ASCENDING, 0, 0); + // Assert returns for empty AVL queue. + assert!( would_update_head(&avlq, 0 ), 0); + assert!( would_update_tail(&avlq, 0 ), 0); + assert!( would_update_head(&avlq, HI_INSERTION_KEY ), 0); + assert!( would_update_tail(&avlq, HI_INSERTION_KEY ), 0); + insert(&mut avlq, 1, 0); // Insert key 1 above min. + // Assert returns 1 less than, equal to, and 1 greater than key. + assert!( would_update_head(&avlq, 0 ), 0); + assert!(!would_update_head(&avlq, 1 ), 0); + assert!(!would_update_head(&avlq, 2 ), 0); + assert!(!would_update_tail(&avlq, 0 ), 0); + assert!( would_update_tail(&avlq, 1 ), 0); + assert!( would_update_tail(&avlq, 2 ), 0); + // Insert key 1 below max. + insert(&mut avlq, HI_INSERTION_KEY - 1, 0); + // Assert returns 1 less than, equal to, and 1 greater than key. + assert!(!would_update_head(&avlq, HI_INSERTION_KEY - 2), 0); + assert!(!would_update_head(&avlq, HI_INSERTION_KEY - 1), 0); + assert!(!would_update_head(&avlq, HI_INSERTION_KEY ), 0); + assert!(!would_update_tail(&avlq, HI_INSERTION_KEY - 2), 0); + assert!( would_update_tail(&avlq, HI_INSERTION_KEY - 1), 0); + assert!( would_update_tail(&avlq, HI_INSERTION_KEY ), 0); + drop_avlq_test(avlq); // Drop AVL queue. + // Init descending AVL queue. + let avlq = new(DESCENDING, 0, 0); + // Assert returns for empty AVL queue. + assert!( would_update_head(&avlq, 0 ), 0); + assert!( would_update_tail(&avlq, 0 ), 0); + assert!( would_update_head(&avlq, HI_INSERTION_KEY ), 0); + assert!( would_update_tail(&avlq, HI_INSERTION_KEY ), 0); + insert(&mut avlq, 1, 0); // Insert key 1 above min. + // Assert returns 1 less than, equal to, and 1 greater than key. + assert!(!would_update_head(&avlq, 0 ), 0); + assert!(!would_update_head(&avlq, 1 ), 0); + assert!( would_update_head(&avlq, 2 ), 0); + assert!( would_update_tail(&avlq, 0 ), 0); + assert!( would_update_tail(&avlq, 1 ), 0); + assert!(!would_update_tail(&avlq, 2 ), 0); + // Insert key 1 below max. + insert(&mut avlq, HI_INSERTION_KEY - 1, 0); + // Assert returns 1 less than, equal to, and 1 greater than key. + assert!(!would_update_head(&avlq, HI_INSERTION_KEY - 2), 0); + assert!(!would_update_head(&avlq, HI_INSERTION_KEY - 1), 0); + assert!( would_update_head(&avlq, HI_INSERTION_KEY ), 0); + assert!(!would_update_tail(&avlq, HI_INSERTION_KEY - 2), 0); + assert!(!would_update_tail(&avlq, HI_INSERTION_KEY - 1), 0); + assert!(!would_update_tail(&avlq, HI_INSERTION_KEY ), 0); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + #[expected_failure(abort_code = E_INSERTION_KEY_TOO_LARGE)] + /// Verify failure for insertion key too large. + fun test_would_update_head_too_big() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Attempt invalid invocation. + would_update_head(&avlq, HI_INSERTION_KEY + 1); + drop_avlq_test(avlq); // Drop AVL queue. + } + + #[test] + #[expected_failure(abort_code = E_INSERTION_KEY_TOO_LARGE)] + /// Verify failure for insertion key too large. + fun test_would_update_tail_too_big() { + let avlq = new(ASCENDING, 0, 0); // Init AVL queue. + // Attempt invalid invocation. + would_update_tail(&avlq, HI_INSERTION_KEY + 1); + drop_avlq_test(avlq); // Drop AVL queue. + } + + // Tests <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +} \ No newline at end of file diff --git a/testsuite/module-publish/src/packages/econia/sources/faucet.move b/testsuite/module-publish/src/packages/econia/sources/faucet.move new file mode 100644 index 0000000000000..66678831020d5 --- /dev/null +++ b/testsuite/module-publish/src/packages/econia/sources/faucet.move @@ -0,0 +1,75 @@ +module econia::faucet { + use aptos_std::coin::{ + Self, + BurnCapability, + FreezeCapability, + MintCapability, + }; + use aptos_std::string; + use aptos_std::type_info; + use std::signer::{address_of}; + + /// No capability store at coin type publisher's account. + const E_NO_CAP_STORE: u64 = 0; + + /// A wrapper for coin type capabilities. + struct CapabilityStore has key { + burn_cap: BurnCapability, + freeze_cap: FreezeCapability, + mint_cap: MintCapability, + } + + /// Init and store coin capabilities, at type publisher's account. + public entry fun initialize( + account: &signer, + name: string::String, + symbol: string::String, + decimals: u8, + monitor_supply: bool, + ) { + // Initialize coin info at coin type publisher's account, + // returning coin capabilities (this fails is the calling + // account is not the coin type publisher). + let (burn_cap, freeze_cap, mint_cap) = coin::initialize( + account, + name, + symbol, + decimals, + monitor_supply + ); + // Store capabilities under the publisher's account. + move_to(account, CapabilityStore { + burn_cap, + freeze_cap, + mint_cap, + }); + } + + /// Permissionlessly mint a coin type to a caller's coin store. + public entry fun mint( + account: &signer, + amount: u64, + ) acquires CapabilityStore { + // Get caller's account address. + let account_addr = address_of(account); + // If caller does not have a coin store for coin type: + if (!coin::is_account_registered(account_addr)) { + // Regiser one. + coin::register(account) + }; + // Get the coin type publisher's address. + let coin_type_publisher_address = + type_info::account_address(&type_info::type_of()); + // Assert a coin cap store exists at the publisher's address. + assert!( + exists>(coin_type_publisher_address), + E_NO_CAP_STORE + ); + // Immutably borrow the mint capability for the coin type. + let mint_cap_ref = &borrow_global>( + coin_type_publisher_address + ).mint_cap; + // Deposit to caller's coin store the minted coins. + coin::deposit(account_addr, coin::mint(amount, mint_cap_ref)); + } +} \ No newline at end of file diff --git a/testsuite/module-publish/src/packages/econia/sources/incentives.move b/testsuite/module-publish/src/packages/econia/sources/incentives.move new file mode 100644 index 0000000000000..b2491d7041718 --- /dev/null +++ b/testsuite/module-publish/src/packages/econia/sources/incentives.move @@ -0,0 +1,3274 @@ +/// Incentive-associated parameters and data structures. +/// +/// Contains hard-coded "genesis parameters" that are are set +/// upon module publication per `init_module()`, and which can be +/// updated later per `set_incentive_parameters()`. +/// +/// # General overview sections +/// +/// [Incentive model](#incentive-model) +/// +/// [Functions](#functions) +/// +/// * [View functions](#view-functions) +/// * [Public getters](#public-getters) +/// * [Other public functions](#other-public-functions) +/// * [Public entry functions](#public-entry-functions) +/// * [Public friend functions](#public-friend-functions) +/// +/// [Dependency charts](#dependency-charts) +/// +/// * [Incentive parameters setters](#incentive-parameter-setters) +/// * [Econia fee account operations](#econia-fee-account-operations) +/// * [Registrant operations](#registrant-operations) +/// * [Integrator operations](#integrator-operations) +/// * [Match operations](#match-operations) +/// +/// [Complete DocGen index](#complete-docgen-index) +/// +/// # Incentive model +/// +/// As a permissionless system, Econia mitigates denial-of-service (DoS) +/// attacks by charging utility coins for assorted operations. Econia +/// also charges taker fees, denominated in the quote coin for a given +/// market, which are distributed between integrators and Econia. The +/// share of taker fees distributed between an integrator and Econia, +/// for a given market, is determined by the "tier" to which the +/// integrator has "activated" their fee store: when the matching engine +/// fills a taker order, the integrator who facilitated the transaction +/// has a portion of taker fees deposited to their fee store, and Econia +/// gets the rest, with the split thereof determined by the integrator's +/// fee store tier for the given market. Econia does not charge maker +/// fees. +/// +/// Hence Econia involves 5 major incentive parameters, defined at +/// `IncentiveParameters`: +/// +/// 1. The utility coin type. +/// 2. The fee, denominated in the utility coin, to register a market. +/// 3. The fee, denominated in the utility coin, to register as an +/// underwriter for a generic market. +/// 4. The fee, denominated in the utility coin, to register as +/// custodian. +/// 5. The taker fee divisor, denoting the portion of quote coins for a +/// particular trade, paid by the taker, to be split between the +/// integrator who facilitated the trade, and Econia. +/// +/// `IncentiveParameters` also includes a vector of +/// `IntegratorFeeStoreTierParameters`, which define 3 parameters per +/// tier: +/// +/// 1. The taker fee divisor, denoting the portion of quote coins for a +/// particular trade, paid by the taker, to be collected by an +/// integrator whose fee store is activated to the given tier. +/// 2. The cumulative fee, denominated in the utility coin, to activate +/// to the given tier. +/// 3. The fee, denominated in the utility coin, to withdraw quote coins +/// collected as fees, from an integrator's fee store. +/// +/// Upon module publication, the Econia "genesis parameters" are +/// set according to hard-coded values via `init_module()`. Later, the +/// parameters can be updated via `set_incentive_parameters()`, so long +/// as the number of tiers is not reduced and other minor restrictions +/// are met. For an implementation-exact description of restrictions and +/// corresponding abort codes, see: +/// +/// * `set_incentive_parameters()` +/// * `set_incentive_parameters_range_check_inputs()` +/// * `set_incentive_parameters_parse_tiers_vector()` +/// +/// # Functions +/// +/// ## View functions +/// +/// * `get_cost_to_upgrade_integrator_fee_store_view()` +/// * `get_custodian_registration_fee()` +/// * `get_fee_share_divisor()` +/// * `get_integrator_withdrawal_fee_view()` +/// * `get_market_registration_fee()` +/// * `get_n_fee_store_tiers()` +/// * `get_taker_fee_divisor()` +/// * `get_tier_activation_fee()` +/// * `get_tier_withdrawal_fee()` +/// * `get_underwriter_registration_fee()` +/// * `is_utility_coin_type()` +/// +/// ## Public getters +/// +/// * `get_cost_to_upgrade_integrator_fee_store()` +/// * `get_integrator_withdrawal_fee()` +/// * `verify_utility_coin_type()` +/// +/// ## Other public functions +/// +/// * `upgrade_integrator_fee_store()` +/// * `withdraw_econia_fees()` +/// * `withdraw_econia_fees_all()` +/// * `withdraw_integrator_fees()` +/// * `withdraw_utility_coins()` +/// * `withdraw_utility_coins_all()` +/// +/// ## Public entry functions +/// +/// * `update_incentives()` +/// * `upgrade_integrator_fee_store_via_coinstore()` +/// * `withdraw_econia_fees_all_to_coin_store()` +/// * `withdraw_econia_fees_to_coin_store()` +/// * `withdraw_integrator_fees_via_coinstores()` +/// * `withdraw_utility_coins_all_to_coin_store()` +/// * `withdraw_utility_coins_to_coin_store()` +/// +/// ## Public friend functions +/// +/// * `assess_taker_fees()` +/// * `calculate_max_quote_match()` +/// * `deposit_custodian_registration_utility_coins()` +/// * `deposit_market_registration_utility_coins()` +/// * `deposit_underwriter_registration_utility_coins()` +/// * `register_econia_fee_store_entry()` +/// * `register_integrator_fee_store()` +/// +/// # Dependency charts +/// +/// The below dependency charts use `mermaid.js` syntax, which can be +/// automatically rendered into a diagram (depending on the browser) +/// when viewing the documentation file generated from source code. If +/// a browser renders the diagrams with coloring that makes it difficult +/// to read, try a different browser. +/// +/// ## Incentive parameter setters +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// update_incentives --> set_incentive_parameters +/// init_module --> set_incentive_parameters +/// set_incentive_parameters --> +/// set_incentive_parameters_parse_tiers_vector +/// set_incentive_parameters --> resource_account::get_signer +/// set_incentive_parameters --> +/// set_incentive_parameters_range_check_inputs +/// set_incentive_parameters --> init_utility_coin_store +/// set_incentive_parameters --> get_n_fee_store_tiers +/// +/// ``` +/// +/// ## Econia fee account operations +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// deposit_utility_coins --> resource_account::get_address +/// deposit_utility_coins --> range_check_coin_merge +/// deposit_utility_coins_verified --> verify_utility_coin_type +/// deposit_utility_coins_verified --> deposit_utility_coins +/// withdraw_utility_coins --> withdraw_utility_coins_internal +/// withdraw_utility_coins_all --> withdraw_utility_coins_internal +/// withdraw_utility_coins_all_to_coin_store --> +/// withdraw_utility_coins_to_coin_store_internal +/// withdraw_utility_coins_to_coin_store --> +/// withdraw_utility_coins_to_coin_store_internal +/// withdraw_utility_coins_to_coin_store_internal --> +/// withdraw_utility_coins_internal +/// withdraw_utility_coins_internal --> resource_account::get_address +/// withdraw_econia_fees --> withdraw_econia_fees_internal +/// withdraw_econia_fees_all --> withdraw_econia_fees_internal +/// withdraw_econia_fees_internal --> resource_account::get_address +/// withdraw_econia_fees_all_to_coin_store --> +/// withdraw_econia_fees_to_coin_store_internal +/// withdraw_econia_fees_to_coin_store --> +/// withdraw_econia_fees_to_coin_store_internal +/// withdraw_econia_fees_to_coin_store_internal --> +/// withdraw_econia_fees_internal +/// register_econia_fee_store_entry --> resource_account::get_signer +/// +/// ``` +/// +/// ## Registrant operations +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// deposit_custodian_registration_utility_coins --> +/// get_custodian_registration_fee +/// deposit_custodian_registration_utility_coins --> +/// deposit_utility_coins_verified +/// deposit_underwriter_registration_utility_coins --> +/// get_underwriter_registration_fee +/// deposit_underwriter_registration_utility_coins ---> +/// deposit_utility_coins_verified +/// deposit_market_registration_utility_coins --> +/// deposit_utility_coins_verified +/// deposit_market_registration_utility_coins --> +/// get_market_registration_fee +/// +/// ``` +/// +/// ## Integrator operations +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// withdraw_integrator_fees_via_coinstores --> +/// get_integrator_withdrawal_fee +/// get_integrator_withdrawal_fee --> get_tier_withdrawal_fee +/// withdraw_integrator_fees_via_coinstores --> withdraw_integrator_fees +/// withdraw_integrator_fees --> get_tier_withdrawal_fee +/// withdraw_integrator_fees --> deposit_utility_coins_verified +/// register_integrator_fee_store ---> deposit_utility_coins_verified +/// register_integrator_fee_store --> get_tier_activation_fee +/// upgrade_integrator_fee_store_via_coinstore --> +/// upgrade_integrator_fee_store +/// upgrade_integrator_fee_store_via_coinstore --> +/// get_cost_to_upgrade_integrator_fee_store +/// upgrade_integrator_fee_store --> deposit_utility_coins_verified +/// upgrade_integrator_fee_store --> +/// get_cost_to_upgrade_integrator_fee_store +/// get_cost_to_upgrade_integrator_fee_store --> +/// get_cost_to_upgrade_integrator_fee_store_view +/// get_integrator_withdrawal_fee --> get_integrator_withdrawal_fee_view +/// +/// ``` +/// +/// ## Match operations +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// assess_taker_fees --> get_fee_share_divisor +/// assess_taker_fees --> get_taker_fee_divisor +/// assess_taker_fees --> resource_account::get_address +/// assess_taker_fees --> range_check_coin_merge +/// +/// ``` +/// +/// # Complete DocGen index +/// +/// The below index is automatically generated from source code: +module econia::incentives { + + // Uses >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + use aptos_framework::coin::{Self, Coin}; + use aptos_std::type_info::{Self, TypeInfo}; + use econia::resource_account; + use econia::tablist::{Self, Tablist}; + use std::signer::address_of; + use std::vector; + + // Uses <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Friends >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + friend econia::registry; + friend econia::market; + + // Friends <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Test-only uses >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test_only] + use aptos_framework::account; + + use econia::assets::UC; + + // Test-only uses <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Structs >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Portion of taker fees not claimed by an integrator, which are + /// reserved for Econia. + struct EconiaFeeStore has key { + /// Map from market ID to fees collected for given market, + /// enabling duplicate checks and iterable indexing. + map: Tablist> + } + + /// Incentive parameters for assorted operations. + struct IncentiveParameters has drop, key { + /// Utility coin type info. Corresponds to the phantom + /// `CoinType` (`address:module::MyCoin` rather than + /// `aptos_framework::coin::Coin`) of + /// the coin required for utility purposes. Set to `APT` at + /// mainnet launch, later the Econia coin. + utility_coin_type_info: TypeInfo, + /// `Coin.value` required to register a market. + market_registration_fee: u64, + /// `Coin.value` required to register as an underwriter. + underwriter_registration_fee: u64, + /// `Coin.value` required to register as a custodian. + custodian_registration_fee: u64, + /// Nominal amount divisor for quote coin fee charged to takers. + /// For example, if a transaction involves a quote coin fill of + /// 1000000 units and the taker fee divisor is 2000, takers pay + /// 1/2000th (0.05%) of the nominal amount (500 quote coin + /// units) in fees. Instituted as a divisor for optimized + /// calculations. + taker_fee_divisor: u64, + /// 0-indexed list from tier number to corresponding parameters. + integrator_fee_store_tiers: vector + } + + /// Fee store for a given integrator, on a given market. + struct IntegratorFeeStore has store { + /// Activation tier, incremented by paying utility coins. + tier: u8, + /// Collected fees, in quote coins for given market. + coins: Coin + } + + /// All of an integrator's `IntegratorFeeStore`s for given + /// `QuoteCoinType`. + struct IntegratorFeeStores has key { + /// Map from market ID to `IntegratorFeeStore`, enabling + /// duplicate checks and iterable indexing. + map: Tablist> + } + + /// Integrator fee store tier parameters for a given tier. + struct IntegratorFeeStoreTierParameters has drop, store { + /// Nominal amount divisor for taker quote coin fee reserved for + /// integrators having activated their fee store to the given + /// tier. For example, if a transaction involves a quote coin + /// fill of 1000000 units and the fee share divisor at the given + /// tier is 4000, integrators get 1/4000th (0.025%) of the + /// nominal amount (250 quote coin units) in fees at the given + /// tier. Instituted as a divisor for optimized calculations. + /// May not be larger than the + /// `IncentiveParameters.taker_fee_divisor`, since the + /// integrator fee share is deducted from the taker fee (with + /// the remaining proceeds going to an `EconiaFeeStore` for the + /// given market). + fee_share_divisor: u64, + /// Cumulative cost, in utility coin units, to activate to the + /// current tier. For example, if an integrator has already + /// activated to tier 3, which has a tier activation fee of 1000 + /// units, and tier 4 has a tier activation fee of 10000 units, + /// the integrator only has to pay 9000 units to activate to + /// tier 4. + tier_activation_fee: u64, + /// Cost, in utility coin units, to withdraw from an integrator + /// fee store. Shall never be nonzero, since a disincentive is + /// required to prevent excessively-frequent withdrawals and + /// thus transaction collisions with the matching engine. + withdrawal_fee: u64 + } + + /// Container for utility coin fees collected by Econia. + struct UtilityCoinStore has key { + /// Coins collected as utility fees. + coins: Coin + } + + // Structs <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Genesis parameters >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Genesis parameter. + const MARKET_REGISTRATION_FEE: u64 = 204918032; + /// Genesis parameter. + const UNDERWRITER_REGISTRATION_FEE: u64 = 81967; + /// Genesis parameter. + const CUSTODIAN_REGISTRATION_FEE: u64 = 81967; + /// Genesis parameter. + const TAKER_FEE_DIVISOR: u64 = 2000; + /// Genesis parameter. + const FEE_SHARE_DIVISOR_0: u64 = 10000; + /// Genesis parameter. + const FEE_SHARE_DIVISOR_1: u64 = 8333; + /// Genesis parameter. + const FEE_SHARE_DIVISOR_2: u64 = 7692; + /// Genesis parameter. + const FEE_SHARE_DIVISOR_3: u64 = 7143; + /// Genesis parameter. + const FEE_SHARE_DIVISOR_4: u64 = 6667; + /// Genesis parameter. + const FEE_SHARE_DIVISOR_5: u64 = 6250; + /// Genesis parameter. + const FEE_SHARE_DIVISOR_6: u64 = 5882; + /// Genesis parameter. + const TIER_ACTIVATION_FEE_0: u64 = 0; + /// Genesis parameter. + const TIER_ACTIVATION_FEE_1: u64 = 1639344; + /// Genesis parameter. + const TIER_ACTIVATION_FEE_2: u64 = 24590163; + /// Genesis parameter. + const TIER_ACTIVATION_FEE_3: u64 = 327868852; + /// Genesis parameter. + const TIER_ACTIVATION_FEE_4: u64 = 4098360655; + /// Genesis parameter. + const TIER_ACTIVATION_FEE_5: u64 = 49180327868; + /// Genesis parameter. + const TIER_ACTIVATION_FEE_6: u64 = 573770491803; + /// Genesis parameter. + const WITHDRAWAL_FEE_0: u64 = 1639344; + /// Genesis parameter. + const WITHDRAWAL_FEE_1: u64 = 1557377; + /// Genesis parameter. + const WITHDRAWAL_FEE_2: u64 = 1475409; + /// Genesis parameter. + const WITHDRAWAL_FEE_3: u64 = 1393442; + /// Genesis parameter. + const WITHDRAWAL_FEE_4: u64 = 1311475; + /// Genesis parameter. + const WITHDRAWAL_FEE_5: u64 = 1229508; + /// Genesis parameter. + const WITHDRAWAL_FEE_6: u64 = 1147540; + + // Genesis parameters <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Error codes >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Caller is not Econia, but should be. + const E_NOT_ECONIA: u64 = 0; + /// Type does not correspond to an initialized coin. + const E_NOT_COIN: u64 = 1; + /// Passed fee store tiers vector is empty. + const E_EMPTY_FEE_STORE_TIERS: u64 = 2; + /// Indicated fee share divisor for given tier is too big. + const E_FEE_SHARE_DIVISOR_TOO_BIG: u64 = 3; + /// The indicated fee share divisor for a given tier is less than + /// the indicated taker fee divisor. + const E_FEE_SHARE_DIVISOR_TOO_SMALL: u64 = 4; + /// Market registration fee is less than the minimum. + const E_MARKET_REGISTRATION_FEE_LESS_THAN_MIN: u64 = 5; + /// Custodian registration fee is less than the minimum. + const E_CUSTODIAN_REGISTRATION_FEE_LESS_THAN_MIN: u64 = 6; + /// Taker fee divisor is less than the minimum. + const E_TAKER_DIVISOR_LESS_THAN_MIN: u64 = 7; + /// The wrong number of fields are passed for a given tier. + const E_TIER_FIELDS_WRONG_LENGTH: u64 = 8; + /// The indicated tier activation fee is too small. + const E_ACTIVATION_FEE_TOO_SMALL: u64 = 9; + /// The indicated withdrawal fee is too big. + const E_WITHDRAWAL_FEE_TOO_BIG: u64 = 10; + /// The indicated withdrawal fee is too small. + const E_WITHDRAWAL_FEE_TOO_SMALL: u64 = 11; + /// Type is not the utility coin type. + const E_INVALID_UTILITY_COIN_TYPE: u64 = 12; + /// Not enough utility coins provided. + const E_NOT_ENOUGH_UTILITY_COINS: u64 = 13; + /// Too many integrator fee store tiers indicated. + const E_TOO_MANY_TIERS: u64 = 14; + /// Indicated tier is not higher than existing tier. + const E_NOT_AN_UPGRADE: u64 = 15; + /// An update to the incentive parameters set indicates a reduction + /// in fee store tiers. + const E_FEWER_TIERS: u64 = 16; + /// The cost to activate to tier 0 is nonzero. + const E_FIRST_TIER_ACTIVATION_FEE_NONZERO: u64 = 17; + /// Custodian registration fee is less than the minimum. + const E_UNDERWRITER_REGISTRATION_FEE_LESS_THAN_MIN: u64 = 18; + /// Depositing to an integrator fee store would result in an + /// overflow. + const E_INTEGRATOR_FEE_STORE_OVERFLOW: u64 = 19; + /// Depositing to an Econia fee store would result in an overflow. + const E_ECONIA_FEE_STORE_OVERFLOW: u64 = 20; + /// Depositing to a utility coin store would result in an overflow. + const E_UTILITY_COIN_STORE_OVERFLOW: u64 = 21; + /// There is no tier with given number. + const E_INVALID_TIER: u64 = 22; + /// Cumulative activation fee for new tier is not greater than that + /// of current tier. + const E_TIER_COST_NOT_INCREASE: u64 = 23; + + // Error codes <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Constants >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Buy direction flag, as defined in `market.move`. + const BUY: bool = false; + /// Index of fee share in vectorized representation of an + /// `IntegratorFeeStoreTierParameters`. + const FEE_SHARE_DIVISOR_INDEX: u64 = 0; + /// `u64` bitmask with all bits set, generated in Python via + /// `hex(int('1' * 64, 2))`. + const HI_64: u64 = 0xffffffffffffffff; + /// Maximum number of integrator fee store tiers is largest number + /// that can fit in a `u8`. + const MAX_INTEGRATOR_FEE_STORE_TIERS: u64 = 0xff; + /// Minimum possible divisor for avoiding divide-by-zero error, + /// including during denominator calculation for a `SELL` in + /// `calculate_max_quote_match()`. + const MIN_DIVISOR: u64 = 2; + /// Minimum possible flat fee, required to disincentivize excessive + /// bogus transactions. + const MIN_FEE: u64 = 1; + /// Number of fields in an `IntegratorFeeStoreTierParameters`. + const N_TIER_FIELDS: u64 = 3; + /// Sell direction flag, as defined in `market.move`. + const SELL: bool = true; + /// Index of tier activation fee in vectorized representation of an + /// `IntegratorFeeStoreTierParameters`. + const TIER_ACTIVATION_FEE_INDEX: u64 = 1; + /// Index of withdrawal fee in vectorized representation of an + /// `IntegratorFeeStoreTierParameters`. + const WITHDRAWAL_FEE_INDEX: u64 = 2; + + // Constants <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // View functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[view] + /// Calculate cost to upgrade `IntegratorFeeStore` to higher tier. + /// + /// # Type parameters + /// + /// * `QuoteCoinType`: The quote coin type for market. + /// * `UtilityCoinType`: The utility coin type. + /// + /// # Parameters + /// + /// * `integrator_address`: Integrator address. + /// * `market_id`: Market ID for corresponding market. + /// * `new_tier`: Tier to upgrade to. + /// + /// # Returns + /// + /// * `u64`: Cost, in utility coins, to upgrade to given tier, + /// calculated as the difference between the cumulative activation + /// cost for each tier. For example, if it costs 1000 to activate + /// to tier 3 and 100 to activate to tier 1, it costs 900 to + /// upgrade from tier 1 to tier 3. + /// + /// # Aborts + /// + /// * `E_NOT_AN_UPGRADE`: `new_tier` is not higher than the one + /// that the `IntegratorFeeStore` is already activated to. + /// * `E_TIER_COST_NOT_INCREASE`: Cumulative activation fee for new + /// tier is not greater than that of current tier. + /// + /// # Restrictions + /// + /// * Restricted to private view function to prevent excessive + /// public queries on an `IntegratorFeeStore` and thus transaction + /// collisions with the matching engine. + /// + /// # Testing + /// + /// * `test_get_cost_to_upgrade_integrator_fee_store_not_increase()` + /// * `test_get_cost_to_upgrade_integrator_fee_store_not_upgrade()` + fun get_cost_to_upgrade_integrator_fee_store_view< + QuoteCoinType, + UtilityCoinType + >( + integrator_address: address, + market_id: u64, + new_tier: u8 + ): u64 + acquires + IncentiveParameters, + IntegratorFeeStores + { + // Immutably borrow integrator fee stores map for given quote + // coin type. + let integrator_fee_stores_map_ref = + &borrow_global>( + integrator_address).map; + // Immutably borrow corresponding integrator fee store for + // given market ID. + let integrator_fee_store_ref = tablist::borrow( + integrator_fee_stores_map_ref, market_id); + // Get current tier number. + let current_tier = integrator_fee_store_ref.tier; + // Assert actually attempting to upgrade to new tier. + assert!(new_tier > current_tier, E_NOT_AN_UPGRADE); + // Get cumulative activation fee for current tier. + let current_tier_fee = get_tier_activation_fee(current_tier); + // Get cumulative activation fee for new tier. + let new_tier_fee = get_tier_activation_fee(new_tier); + // Assert new tier fee is greater than current tier fee. + assert!(new_tier_fee > current_tier_fee, E_TIER_COST_NOT_INCREASE); + // Return difference in cumulative cost to upgrade. + new_tier_fee - current_tier_fee + } + + #[view] + /// Return custodian registration fee. + /// + /// # Testing + /// + /// * `test_init_update_get_incentives()` + public fun get_custodian_registration_fee(): + u64 + acquires IncentiveParameters { + borrow_global(@econia).custodian_registration_fee + } + + #[view] + /// Return integrator fee share divisor for `tier`. + /// + /// # Testing + /// + /// * `test_get_fee_share_divisor_invalid_tier()` + /// * `test_init_update_get_incentives()` + public fun get_fee_share_divisor( + tier: u8 + ): u64 + acquires IncentiveParameters { + // Borrow immutable reference to integrator fee store tiers + // vector. + let integrator_fee_store_tiers_ref = + &borrow_global(@econia). + integrator_fee_store_tiers; + // Assert provided 0-indexed tier number is within range. + assert!((tier as u64) < vector::length(integrator_fee_store_tiers_ref), + E_INVALID_TIER); + // Borrow immutable reference to indicated tier parameters. + let integrator_fee_store_tier_ref = vector::borrow( + integrator_fee_store_tiers_ref, (tier as u64)); + // Return corresponding fee share divisor. + integrator_fee_store_tier_ref.fee_share_divisor + } + + #[view] + /// Return withdrawal fee for given `integrator_address` and + /// `market_id`. + /// + /// # Restrictions + /// + /// * Restricted to private view function to prevent excessive + /// public queries on an `IntegratorFeeStore` and thus transaction + /// collisions with the matching engine. + fun get_integrator_withdrawal_fee_view( + integrator_address: address, + market_id: u64, + ): u64 + acquires + IncentiveParameters, + IntegratorFeeStores + { + // Borrow mutable reference to integrator fee stores map for + // quote coin type. + let integrator_fee_stores_map_ref = &borrow_global< + IntegratorFeeStores>(integrator_address).map; + // Borrow mutable reference to integrator fee store for given + // market ID. + let integrator_fee_store_ref = tablist::borrow( + integrator_fee_stores_map_ref, market_id); + // Return withdrawal fee for given tier. + get_tier_withdrawal_fee(integrator_fee_store_ref.tier) + } + + #[view] + /// Return market registration fee. + /// + /// # Testing + /// + /// * `test_init_update_get_incentives()` + public fun get_market_registration_fee(): + u64 + acquires IncentiveParameters { + borrow_global(@econia).market_registration_fee + } + + #[view] + /// Return number of fee store tiers. + /// + /// # Testing + /// + /// * `test_init_update_get_incentives()` + public fun get_n_fee_store_tiers(): + u64 + acquires IncentiveParameters { + // Borrow immutable reference to integrator fee store tiers + // vector. + let integrator_fee_store_tiers_ref = + &borrow_global(@econia). + integrator_fee_store_tiers; + // Return its vector length + vector::length(integrator_fee_store_tiers_ref) + } + + #[view] + /// Return taker fee divisor. + /// + /// # Testing + /// + /// * `test_init_update_get_incentives()` + public fun get_taker_fee_divisor(): + u64 + acquires IncentiveParameters { + borrow_global(@econia).taker_fee_divisor + } + + #[view] + /// Return fee to activate an `IntegratorFeeStore` to given `tier`. + /// + /// # Testing + /// + /// * `test_get_tier_activation_fee_invalid_tier()` + /// * `test_init_update_get_incentives()` + public fun get_tier_activation_fee( + tier: u8 + ): u64 + acquires IncentiveParameters { + // Borrow immutable reference to integrator fee store tiers + // vector. + let integrator_fee_store_tiers_ref = + &borrow_global(@econia). + integrator_fee_store_tiers; + // Assert provided 0-indexed tier number is within range. + assert!((tier as u64) < vector::length(integrator_fee_store_tiers_ref), + E_INVALID_TIER); + // Borrow immutable reference to given tier. + let integrator_fee_store_tier_ref = vector::borrow( + integrator_fee_store_tiers_ref, (tier as u64)); + // Return its activation fee. + integrator_fee_store_tier_ref.tier_activation_fee + } + + #[view] + /// Return fee to withdraw from `IntegratorFeeStore` activated to + /// given `tier`. + /// + /// # Testing + /// + /// * `test_get_tier_withdrawal_fee_invalid_tier()` + /// * `test_init_update_get_incentives()` + public fun get_tier_withdrawal_fee( + tier: u8 + ): u64 + acquires IncentiveParameters { + // Borrow immutable reference to integrator fee store tiers + // vector. + let integrator_fee_store_tiers_ref = + &borrow_global(@econia). + integrator_fee_store_tiers; + // Assert provided 0-indexed tier number is within range. + assert!((tier as u64) < vector::length(integrator_fee_store_tiers_ref), + E_INVALID_TIER); + // Borrow immutable reference to given tier. + let integrator_fee_store_tier_ref = vector::borrow( + integrator_fee_store_tiers_ref, (tier as u64)); + // Return its withdrawal fee. + integrator_fee_store_tier_ref.withdrawal_fee + } + + #[view] + /// Return underwriter registration fee. + /// + /// # Testing + /// + /// * `test_init_update_get_incentives()` + public fun get_underwriter_registration_fee(): + u64 + acquires IncentiveParameters { + borrow_global(@econia). + underwriter_registration_fee + } + + #[view] + /// Return `true` if `T` is the utility coin type. + /// + /// # Testing + /// + /// * `test_init_update_get_incentives()` + public fun is_utility_coin_type(): + bool + acquires IncentiveParameters { + // Return if provided type info is that of the utility coin. + type_info::type_of() == + borrow_global(@econia).utility_coin_type_info + } + + // View functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Public functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Public function wrapper for + /// `get_cost_to_upgrade_integrator_fee_store_view()`, requiring + /// integrator signature to prevent runtime transaction collisions. + /// + /// # Testing + /// + /// * `test_get_cost_to_upgrade_integrator_fee_store_not_increase()` + /// * `test_get_cost_to_upgrade_integrator_fee_store_not_upgrade()` + public fun get_cost_to_upgrade_integrator_fee_store< + QuoteCoinType, + UtilityCoinType + >( + integrator: &signer, + market_id: u64, + new_tier: u8, + ): u64 + acquires + IncentiveParameters, + IntegratorFeeStores + { + get_cost_to_upgrade_integrator_fee_store_view< + QuoteCoinType, UtilityCoinType>( + address_of(integrator), market_id, new_tier) + } + + /// Public function wrapper for + /// `get_integrator_withdrawal_fee_view()`, requiring integrator + /// signature to prevent runtime transaction collisions. + public fun get_integrator_withdrawal_fee( + integrator: &signer, + market_id: u64, + ): u64 + acquires + IncentiveParameters, + IntegratorFeeStores + { + get_integrator_withdrawal_fee_view( + address_of(integrator), market_id) + } + + /// Upgrade `IntegratorFeeStore` to a higher tier. + /// + /// # Type parameters + /// + /// * `QuoteCoinType`: The quote coin type for market. + /// * `UtilityCoinType`: The utility coin type. + /// + /// # Parameters + /// + /// * `integrator`: Integrator account. + /// * `market_id`: Market ID for corresponding market. + /// * `new_tier`: Tier to upgrade to. + /// * `utility_coins`: Utility coins paid for upgrade. + public fun upgrade_integrator_fee_store< + QuoteCoinType, + UtilityCoinType + >( + integrator: &signer, + market_id: u64, + new_tier: u8, + utility_coins: coin::Coin + ) acquires + IncentiveParameters, + IntegratorFeeStores, + UtilityCoinStore + { + // Get cost to upgrade to new tier. + let cost = get_cost_to_upgrade_integrator_fee_store(integrator, market_id, new_tier); + // Deposit verified amount and type of utility coins. + deposit_utility_coins_verified(utility_coins, cost); + // Get integrator address. + let integrator_address = address_of(integrator); + // Borrow mutable reference to integrator fee stores map for + // quote coin type. + let integrator_fee_stores_map_ref_mut = + &mut borrow_global_mut>( + integrator_address).map; + // Borrow mutable reference to integrator fee store for given + // market ID. + let integrator_fee_store_ref_mut = tablist::borrow_mut( + integrator_fee_stores_map_ref_mut, market_id); + // Set the new tier. + integrator_fee_store_ref_mut.tier = new_tier; + } + + /// Assert `T` is utility coin type. + /// + /// # Aborts + /// + /// * `E_INVALID_UTILITY_COIN_TYPE`: `T` is not utility coin type. + /// + /// # Testing + /// + /// * `test_verify_utility_coin_type()` + public fun verify_utility_coin_type() + acquires IncentiveParameters { + assert!(is_utility_coin_type(), E_INVALID_UTILITY_COIN_TYPE); + } + + /// Withdraw `amount` of fee coins from an `EconiaFeeStore` of given + /// `QuoteCoinType` and having `market_id`, under authority of + /// `econia`. + /// + /// See inner function `withdraw_econia_fees_internal()`. + /// + /// Testing + /// + /// * `test_register_assess_withdraw()` + /// * `test_withdraw_econia_fees_not_econia()` + public fun withdraw_econia_fees( + econia: &signer, + market_id: u64, + amount: u64 + ): coin::Coin + acquires + EconiaFeeStore + { + withdraw_econia_fees_internal( + econia, market_id, false, amount) + } + + /// Withdraw all fee coins from an `EconiaFeeStore` of given + /// `QuoteCoinType` and having `market_id`, under authority of + /// `econia`. + /// + /// See inner function `withdraw_econia_fees_internal()`. + /// + /// Testing + /// + /// * `test_register_assess_withdraw()` + /// * `test_withdraw_econia_fees_all_not_econia()` + public fun withdraw_econia_fees_all( + econia: &signer, + market_id: u64, + ): coin::Coin + acquires + EconiaFeeStore + { + withdraw_econia_fees_internal( + econia, market_id, true, 0) + } + + /// Withdraw all fees from an `IntegratorFeeStore`. + /// + /// # Type parameters + /// + /// * `QuoteCoinType`: The quote coin type for market. + /// * `UtilityCoinType`: The utility coin type. + /// + /// # Parameters + /// + /// * `integrator`: Integrator account. + /// * `market_id`: Market ID for corresponding market. + /// * `utility_coins`: Utility coins paid in order to make + /// withdrawal, required to disincentivize excessively frequent + /// withdrawals and thus transaction collisions with the matching + /// engine. + /// + /// # Returns + /// + /// * `coin::Coin`: Quote coin fees for given market. + public fun withdraw_integrator_fees< + QuoteCoinType, + UtilityCoinType + >( + integrator: &signer, + market_id: u64, + utility_coins: coin::Coin + ): coin::Coin + acquires + IncentiveParameters, + IntegratorFeeStores, + UtilityCoinStore + { + // Borrow mutable reference to integrator fee stores map for + // quote coin type. + let integrator_fee_stores_map_ref_mut = &mut borrow_global_mut< + IntegratorFeeStores>(address_of(integrator)).map; + // Borrow mutable reference to integrator fee store for given + // market ID. + let integrator_fee_store_ref_mut = tablist::borrow_mut( + integrator_fee_stores_map_ref_mut, market_id); + // Get fee to withdraw from fee store at given tier. + let withdrawal_fee = get_tier_withdrawal_fee( + integrator_fee_store_ref_mut.tier); + // Deposit verified amount and type of utility coins. + deposit_utility_coins_verified(utility_coins, withdrawal_fee); + // Extract and return all coins in integrator fee store. + coin::extract_all(&mut integrator_fee_store_ref_mut.coins) + } + + /// Withdraw `amount` of utility coins from the `UtilityCoinStore`, + /// under authority of `econia`. + /// + /// See inner function `withdraw_utility_coins_internal()`. + /// + /// # Testing + /// + /// * `test_deposit_withdraw_utility_coins()` + /// * `test_register_assess_withdraw()` + /// * `test_withdraw_utility_coins_not_econia()` + public fun withdraw_utility_coins( + econia: &signer, + amount: u64 + ): coin::Coin + acquires + UtilityCoinStore + { + withdraw_utility_coins_internal(econia, false, amount) + } + + /// Withdraw all utility coins from the `UtilityCoinStore`, under + /// authority of `econia`. + /// + /// See inner function `withdraw_utility_coins_internal()`. + /// + /// # Testing + /// + /// * `test_deposit_withdraw_utility_coins()` + /// * `test_register_assess_withdraw()` + /// * `test_withdraw_utility_coins_all_not_econia()` + public fun withdraw_utility_coins_all( + econia: &signer + ): coin::Coin + acquires + UtilityCoinStore + { + withdraw_utility_coins_internal(econia, true, 0) + } + + // Public functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Public entry functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Wrapped call to `set_incentive_parameters()`, when calling after + /// initialization. + /// + /// Accepts same arguments as `set_incentive_parameters()`. + /// + /// # Testing + /// + /// * `test_init_update_get_incentives()` + /// * `test_update_incentives_fewer_tiers()` + public entry fun update_incentives( + econia: &signer, + market_registration_fee: u64, + underwriter_registration_fee: u64, + custodian_registration_fee: u64, + taker_fee_divisor: u64, + integrator_fee_store_tiers: vector> + ) acquires + IncentiveParameters + { + set_incentive_parameters(econia, + market_registration_fee, underwriter_registration_fee, + custodian_registration_fee, taker_fee_divisor, + &integrator_fee_store_tiers, true); + } + + /// Wrapped call to `upgrade_integrator_fee_store()`, for paying + /// utility coins from an `aptos_framework::Coin::CoinStore`. + /// + /// See wrapped function `upgrade_integrator_fee_store()`. + /// + /// # Testing + /// + /// * `upgrade_integrator_fee_store_via_coinstore()` + public entry fun upgrade_integrator_fee_store_via_coinstore< + QuoteCoinType, + UtilityCoinType + >( + integrator: &signer, + market_id: u64, + new_tier: u8, + ) acquires + IncentiveParameters, + IntegratorFeeStores, + UtilityCoinStore + { + // Get cost to upgrade to new tier. + let cost = get_cost_to_upgrade_integrator_fee_store(integrator, market_id, new_tier); + // Upgrade integrator fee store, paying cost from coin store. + upgrade_integrator_fee_store( + integrator, market_id, new_tier, coin::withdraw( + integrator, cost)); + } + + /// Wrapped call to `withdraw_econia_fees_to_coin_store_internal()`, + /// similar to `withdraw_econia_fees_all()`. + /// + /// # Testing + /// + /// * `test_withdraw_to_coin_store_econia()` + public entry fun withdraw_econia_fees_all_to_coin_store( + econia: &signer, + market_id: u64, + ) acquires + EconiaFeeStore + { + withdraw_econia_fees_to_coin_store_internal( + econia, market_id, true, 0); + } + + /// Wrapped call to `withdraw_econia_fees_to_coin_store_internal()`, + /// similar to `withdraw_econia_fees()`. + /// + /// # Testing + /// + /// * `test_withdraw_to_coin_store_econia()` + public entry fun withdraw_econia_fees_to_coin_store( + econia: &signer, + market_id: u64, + amount: u64 + ) acquires + EconiaFeeStore + { + withdraw_econia_fees_to_coin_store_internal( + econia, market_id, false, amount); + } + + /// Wrapped call to `get_withdraw_integrator_fees()`, for paying + /// utility coins from an `aptos_framework::Coin::CoinStore` and + /// depositing quote coins to one too. + /// + /// See wrapped function `withdraw_integrator_fees()`. + /// + /// # Type parameters + /// + /// * `QuoteCoinType`: The quote coin type for market. + /// * `UtilityCoinType`: The utility coin type. + /// + /// # Parameters + /// + /// * `integrator`: Integrator account. + /// * `market_id`: Market ID of corresponding market. + /// + /// Testing + /// + /// * `test_register_assess_withdraw()` + public entry fun withdraw_integrator_fees_via_coinstores< + QuoteCoinType, + UtilityCoinType + >( + integrator: &signer, + market_id: u64 + ) acquires + IncentiveParameters, + IntegratorFeeStores, + UtilityCoinStore + { + // Get fee to withdraw from integrator fee coin store. + let withdrawal_fee = get_integrator_withdrawal_fee( + integrator, market_id); + // Withdraw enough utility coins to pay fee. + let utility_coins = coin::withdraw( + integrator, withdrawal_fee); + let quote_coins = // Withdraw integrator fees (quote coins). + withdraw_integrator_fees( + integrator, market_id, utility_coins); + // Get integrator address. + let integrator_address = address_of(integrator); + // If integrator does not have quote coin store, register one. + if (!coin::is_account_registered(integrator_address)) + coin::register(integrator); + // Deposit quote coins to integrator quote coin store. + coin::deposit(address_of(integrator), quote_coins); + } + + /// Wrapped `withdraw_utility_coins_to_coin_store_internal()` call, + /// similar to `withdraw_utility_coins_all()`. + /// + /// # Testing + /// + /// * `test_withdraw_to_coin_store_econia()` + public entry fun withdraw_utility_coins_all_to_coin_store( + econia: &signer, + ) acquires + UtilityCoinStore + { + withdraw_utility_coins_to_coin_store_internal( + econia, true, 0); + } + + /// Wrapped `withdraw_utility_coins_to_coin_store_internal()` call, + /// similar to `withdraw_utility_coins()`. + /// + /// # Testing + /// + /// * `test_withdraw_to_coin_store_econia()` + public entry fun withdraw_utility_coins_to_coin_store( + econia: &signer, + amount: u64 + ) acquires + UtilityCoinStore + { + withdraw_utility_coins_to_coin_store_internal( + econia, false, amount); + } + + // Public entry functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Public friend functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Assess fees after a taker fill. + /// + /// First attempts to assess an integrator's share of taker fees, + /// then provides Econia with the remaining share. If the + /// `integrator_address` does not have an `IntegratorFeeStore` for + /// the given `market_id` and `QuoteCoinType`, all taker fees are + /// passed on to Econia. Otherwise the integrator's fee share is + /// determined based on their tier for the given market. + /// + /// # Type parameters + /// + /// * `QuoteCoinType`: Quote coin type for market. + /// + /// # Parameters + /// + /// * `market_id`: Market ID for corresponding market. + /// * `integrator_address`: Integrator's address. May be + /// intentionally marked an address known not to be an integrator, + /// for example `@0x0` or `@econia`, in the service of diverting + /// all fees to Econia. + /// * `taker_fee_divisor`: Taker fee divisor. + /// * `quote_fill`: Amount of quote coins filled during taker match. + /// * `quote_coins`: Quote coins to withdraw fees from. + /// + /// # Returns + /// + /// * `coin::Coin`: Remaining quote coins after fees + /// assessed. + /// * `u64`: Amount of fees assessed. + /// + /// # Aborts + /// + /// * `E_INTEGRATOR_FEE_STORE_OVERFLOW`: Depositing to integrator + /// fee store would result in an overflow. Rather than relying on + /// the underlying coin operation to abort, this check is + /// performed to provide additional feedback in the unlikely event + /// that a coin with a supply far in excess of `HI_64` is the + /// quote coin for a market. + /// * `E_ECONIA_FEE_STORE_OVERFLOW`: Depositing to Econia fee store + /// would result in an overflow per above. + /// + /// # Assumptions + /// + /// * `taker_fee_divisor` is nonzero. + /// + /// Testing + /// + /// * `test_register_assess_withdraw()` + public(friend) fun assess_taker_fees( + market_id: u64, + integrator_address: address, + taker_fee_divisor: u64, + quote_fill: u64, + quote_coins: coin::Coin, + ): ( + coin::Coin, + u64 + ) acquires + EconiaFeeStore, + IncentiveParameters, + IntegratorFeeStores + { + // Declare tracker for amount of fees collected by integrator. + let integrator_fee_share = 0; + // Calculate total taker fee. + let total_fee = quote_fill / taker_fee_divisor; + // If integrator fee stores map for quote coin type exists at + // indicated integrator address: + if (exists>(integrator_address)) { + // Borrow mutable reference to integrator fee stores map. + let integrator_fee_stores_map_ref_mut = + &mut borrow_global_mut>( + integrator_address).map; + // Determine if the fee stores map contains an entry for the + // given market ID. + let contains_market_id_entry = tablist::contains( + integrator_fee_stores_map_ref_mut, market_id); + // If fee stores map contains an entry for given market ID: + if (contains_market_id_entry) { + // Borrow mutable reference to corresponding fee store. + let integrator_fee_store_ref_mut = tablist::borrow_mut( + integrator_fee_stores_map_ref_mut, market_id); + // Get fee share divisor for given tier. + let fee_share_divisor = get_fee_share_divisor( + integrator_fee_store_ref_mut.tier); + // Calculate resultant integrator fee share. + integrator_fee_share = quote_fill / fee_share_divisor; + // Verify merge will not overflow integrator fee store. + range_check_coin_merge( + integrator_fee_share, &integrator_fee_store_ref_mut.coins, + E_INTEGRATOR_FEE_STORE_OVERFLOW); + // Extract resultant amount from supplied quote coins. + let integrator_fees = + coin::extract(&mut quote_coins, integrator_fee_share); + // Merge the fees into the corresponding fee store. + coin::merge(&mut integrator_fee_store_ref_mut.coins, + integrator_fees); + } + }; // Integrator fee share has been assessed. + // Fee share remaining for Econia is the total taker fee amount + // less the integrator fee share. + let econia_fee_share = total_fee - integrator_fee_share; + // Extract resultant amount from supplied quote coins. + let econia_fees = coin::extract(&mut quote_coins, econia_fee_share); + // Get fee account address. + let fee_account_address = resource_account::get_address(); + // Borrow mutable reference to Econia fee store map for given + // quote coin type. + let econia_fee_store_map_ref_mut = + &mut borrow_global_mut>( + fee_account_address).map; + // Borrow mutable reference to fees for given market ID. + let econia_fee_store_coins_ref_mut = tablist::borrow_mut( + econia_fee_store_map_ref_mut, market_id); + // Verify merge will not overflow Econia fee store. + range_check_coin_merge( + econia_fee_share, econia_fee_store_coins_ref_mut, + E_ECONIA_FEE_STORE_OVERFLOW); + // Merge the Econia fees into the fee store. + coin::merge(econia_fee_store_coins_ref_mut, econia_fees); + (quote_coins, total_fee) // Return coins, fee paid. + } + + /// Get max quote coin match amount, per user input and fee divisor. + /// + /// Whether a taker buy or sell, users specify a maximum quote coin + /// amount when initiating the transaction. This amount indicates + /// the maximum amount of quote coins they are willing to spend in + /// the case of a taker buy, and the maximum amount of quote coins + /// they are willing to receive in the case of a taker sell. The + /// user-specified amount refers to the net change in taker's quote + /// coin holdings due to matching and fees, which are assessed after + /// matching concludes. Hence it is necessary to calculate a maximum + /// quote match amount prior to matching. + /// + /// # Example buy + /// + /// * Taker is willing to spend 105 quote coins. + /// * Fee is 5% (divisor of 20). + /// * Max match is thus 100 quote coins. + /// * Matching engine halts after 100 quote coins filled. + /// * 5% fee then assessed, withdrawn from takers's quote coins. + /// * Taker has spent 105 quote coins. + /// + /// # Example sell + /// + /// * Taker is willing to receive 100 quote coins. + /// * Fee is 4% (divisor of 25). + /// * Max match is thus 104 quote coins. + /// * Matching engine halts after 104 quote coins filled. + /// * 4% fee then assessed, withdrawn from quote coins received. + /// * Taker has received 100 quote coins. + /// + /// # Variables + /// + /// The relationship between user-indicated maximum quote coin trade + /// amount, taker fee divisor, and the amount of quote coins matched + /// can be described with the following variables: + /// + /// * $\Delta_t$: Change in quote coins seen by taker. + /// * $d_t$: Taker fee divisor. + /// * $q_m$: Quote coins matched. + /// * $f = \frac{q_m}{d_t}$: Fees assessed. + /// + /// # Equations + /// + /// ## Buy + /// + /// $$q_m = \Delta_t - f = \Delta_t - \frac{q_m}{d_t}$$ + /// + /// $$\Delta_t = q_m + \frac{q_m}{d_t} = q_m(1 + \frac{1}{d_t})$$ + /// + /// $$ q_m = \frac{\Delta_t}{1 + \frac{1}{d_t}} $$ + /// + /// $$ q_m = \frac{d_t \Delta_t}{d_t + 1}$$ + /// + /// ## Sell + /// + /// $$q_m = \Delta_t + f = \Delta_t + \frac{q_m}{d_t}$$ + /// + /// $$\Delta_t = q_m - \frac{q_m}{d_t} = q_m(1 - \frac{1}{d_t})$$ + /// + /// $$ q_m = \frac{\Delta_t}{1 - \frac{1}{d_t}} $$ + /// + /// $$ q_m = \frac{d_t \Delta_t}{d_t - 1}$$ + /// + /// # Overflow correction + /// + /// Per above, if a taker specifies that they are willing to receive + /// `HI_64` coins during a sell, the corresponding max quote match + /// amount will overflow a `u64`, since more than `HI_64` quote + /// coins will need to be matched before a fee is assessed. Hence if + /// the maximum quote match amount for a sell is calculated to be + /// in excess of `HI_64`, the maximum quote match amount is simply + /// corrected to `HI_64`. Here, the maximum user-specified amount + /// that will not require such correction, $\Delta_{t, m}$ , is + /// defined in terms of the maximum possible quote match amount + /// $q_{m, m} = 2^{63} - 1$ (`HI_64`), and the taker fee divisor: + /// + /// $$ \Delta_{t, m} + \frac{q_{m, m}}{d_t} = q_{m, m} $$ + /// + /// $$ \Delta_{t, m} = q_{m, m} - \frac{q_{m, m}}{d_t} $$ + /// + /// Such an overflow correction does not apply in the case of a + /// taker buy because the maximum quote match amount is strictly + /// smaller than the user-specified change in quote coins (the + /// amount of quote coins the taker is willing to spend). + /// + /// # Parameters + /// + /// * `direction`: `BUY` or `SELL`. + /// * `taker_fee_divisor`: Taker fee divisor. + /// * `max_quote_delta_user`: Maximum change in quote coins seen by + /// user: spent if a `BUY` and received if a `SELL`. + /// + /// # Returns + /// + /// * `u64`: Maximum amount of quote coins to match. + /// + /// # Assumptions + /// + /// * Taker fee divisor is greater than 1. + /// + /// # Testing + /// + /// * `test_calculate_max_quote_match()` + /// * `test_calculate_max_quote_match_overflow()` + public(friend) fun calculate_max_quote_match( + direction: bool, + taker_fee_divisor: u64, + max_quote_delta_user: u64 + ): u64 { + // Calculate numerator for both buy and sell equations. + let numerator = (taker_fee_divisor as u128) * + (max_quote_delta_user as u128); + // Calculate denominator based on direction. + let denominator = if (direction == BUY) + (taker_fee_divisor + 1 as u128) else + (taker_fee_divisor - 1 as u128); + // Calculate maximum quote coins to match. + let max_quote_match = numerator / denominator; + // Return corrected sell overflow match amount if needed, + if (max_quote_match > (HI_64 as u128)) HI_64 else + (max_quote_match as u64) // Else max quote match amount. + } + + /// Deposit `coins` of `UtilityCoinType`, verifying that the proper + /// amount is supplied for custodian registration. + /// + /// # Testing + /// + /// * `test_deposit_registration_fees_mixed()` + public(friend) fun deposit_custodian_registration_utility_coins< + UtilityCoinType + >( + coins: coin::Coin + ) acquires + IncentiveParameters, + UtilityCoinStore + { + deposit_utility_coins_verified(coins, + get_custodian_registration_fee()); + } + + /// Deposit `coins` of `UtilityCoinType`, verifying that the proper + /// amount is supplied for market registration. + /// + /// # Testing + /// + /// * `test_deposit_registration_fees_mixed()` + public(friend) fun deposit_market_registration_utility_coins< + UtilityCoinType + >( + coins: coin::Coin + ) acquires + IncentiveParameters, + UtilityCoinStore + { + deposit_utility_coins_verified(coins, + get_market_registration_fee()); + } + + /// Deposit `coins` of `UtilityCoinType`, verifying that the proper + /// amount is supplied for underwriter registration. + /// + /// # Testing + /// + /// * `test_deposit_registration_fees_mixed()` + public(friend) fun deposit_underwriter_registration_utility_coins< + UtilityCoinType + >( + coins: coin::Coin + ) acquires + IncentiveParameters, + UtilityCoinStore + { + deposit_utility_coins_verified(coins, + get_underwriter_registration_fee()); + } + + /// Register an `EconiaFeeStore` entry for given `market_id` and + /// `QuoteCoinType`. + /// + /// Testing + /// + /// * `test_register_assess_withdraw()` + public(friend) fun register_econia_fee_store_entry( + market_id: u64 + ) acquires + EconiaFeeStore, + { + // Get fee account signer. + let fee_account = resource_account::get_signer(); + // Get fee account address. + let fee_account_address = address_of(&fee_account); + // If an Econia fee store for the quote coin type has not + // already been initialized at the fee account: + if (!exists>(fee_account_address)) + // Move to the Econia fee account an empty one. + move_to>(&fee_account, + EconiaFeeStore{map: tablist::new()}); + // Borrow mutable reference to Econia fee store map for + // given quote coin type. + let econia_fee_store_map_ref_mut = + &mut borrow_global_mut>( + fee_account_address).map; + // Declare zero coins of quote coin type + let zero_coins = coin::zero(); + // Add to fee store map an entry given market ID and no coins. + tablist::add(econia_fee_store_map_ref_mut, market_id, zero_coins); + } + + /// Register an `IntegratorFeeStore` entry for given `integrator`. + /// + /// # Type parameters + /// + /// * `QuoteCoinType`: The quote coin type for market. + /// * `UtilityCoinType`: The utility coin type. + /// + /// # Parameters + /// + /// * `integrator`: Integrator account. + /// * `market_id`: Market ID for corresponding market. + /// * `tier`: `IntegratorFeeStore` tier to activate to. + /// * `utility_coins`: Utility coins paid to activate to given tier. + /// + /// Testing + /// + /// * `test_register_assess_withdraw()` + /// * `test_upgrade_integrator_fee_store_via_coinstore()` + public(friend) fun register_integrator_fee_store< + QuoteCoinType, + UtilityCoinType + >( + integrator: &signer, + market_id: u64, + tier: u8, + utility_coins: coin::Coin + ) acquires + IncentiveParameters, + IntegratorFeeStores, + UtilityCoinStore + { + // Get tier activation fee for given tier. + let tier_activation_fee = get_tier_activation_fee(tier); + // Deposit utility coins, verifying sufficient amount provided. + // Deposit verified amount and type of utility coins. + deposit_utility_coins_verified(utility_coins, tier_activation_fee); + // Get integrator address. + let integrator_address = address_of(integrator); + // If an integrator fee store for the quote coin type has not + // already been initialized at the integrator account: + if (!exists>(integrator_address)) + // Move to the integrator account an empty one. + move_to>(integrator, + IntegratorFeeStores{map: tablist::new()}); + // Declare integrator fee store for given tier, with no coins. + let integrator_fee_store = + IntegratorFeeStore{tier, coins: coin::zero()}; + // Borrow mutable reference to integrator fee stores map for + // given quote coin type. + let integrator_fee_stores_map_ref_mut = + &mut borrow_global_mut>( + integrator_address).map; + // Add to the map an entry having with given market ID and + // generated integrator fee store. + tablist::add(integrator_fee_stores_map_ref_mut, market_id, + integrator_fee_store); + } + + // Public friend functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Private functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Deposit `coins` to the Econia `UtilityCoinStore`. + /// + /// # Aborts + /// + /// * `E_UTILITY_COIN_STORE_OVERFLOW`: Depositing to utility coin + /// store would result in an overflow. Rather than relying on the + /// underlying coin operation to abort, this check is performed to + /// provide additional feedback in the unlikely event that a coin + /// with a supply far in excess of `HI_64` is used as a utility + /// coin. + /// + /// # Testing + /// + /// * `test_deposit_withdraw_utility_coins()` + fun deposit_utility_coins( + coins: coin::Coin + ) acquires + UtilityCoinStore + { + // Get fee account address. + let fee_account_address = resource_account::get_address(); + // Borrow mutable reference to coins in utility coin store. + let utility_coins_ref_mut = + &mut borrow_global_mut>( + fee_account_address).coins; + // Verify merge will not overflow utility coin store. + range_check_coin_merge(coin::value(&coins), + utility_coins_ref_mut, E_UTILITY_COIN_STORE_OVERFLOW); + // Merge in deposited coins. + coin::merge(utility_coins_ref_mut, coins); + } + + /// Verify that `UtilityCoinType` is the utility coin type and that + /// `coins` has at least the `min_amount`, then deposit all utility + /// coins to `UtilityCoinStore`. + /// + /// # Aborts + /// + /// * `E_NOT_ENOUGH_UTILITY_COINS`: Insufficient utility coins + /// provided. + /// + /// # Testing + /// + /// * `test_deposit_utility_coins_verified_not_enough()` + fun deposit_utility_coins_verified( + coins: coin::Coin, + min_amount: u64 + ) acquires + IncentiveParameters, + UtilityCoinStore + { + // Verify utility coin type. + verify_utility_coin_type(); + // Assert sufficient utility coins provided. + assert!(coin::value(&coins) >= min_amount, E_NOT_ENOUGH_UTILITY_COINS); + // Deposit all utility coins to utility coin store. + deposit_utility_coins(coins); + } + + /// Initialize incentives during first-time publication. + /// + /// Uses hard-coded genesis parameters that can be updated later. + /// + /// # Testing + /// + /// * `test_init_update_get_incentives()` + fun init_module( + econia: &signer + ) acquires + IncentiveParameters + { + // Vectorize fee store tier parameters. + let integrator_fee_store_tiers = vector[ + vector[FEE_SHARE_DIVISOR_0, + TIER_ACTIVATION_FEE_0, + WITHDRAWAL_FEE_0], + vector[FEE_SHARE_DIVISOR_1, + TIER_ACTIVATION_FEE_1, + WITHDRAWAL_FEE_1], + vector[FEE_SHARE_DIVISOR_2, + TIER_ACTIVATION_FEE_2, + WITHDRAWAL_FEE_2], + vector[FEE_SHARE_DIVISOR_3, + TIER_ACTIVATION_FEE_3, + WITHDRAWAL_FEE_3], + vector[FEE_SHARE_DIVISOR_4, + TIER_ACTIVATION_FEE_4, + WITHDRAWAL_FEE_4], + vector[FEE_SHARE_DIVISOR_5, + TIER_ACTIVATION_FEE_5, + WITHDRAWAL_FEE_5], + vector[FEE_SHARE_DIVISOR_6, + TIER_ACTIVATION_FEE_6, + WITHDRAWAL_FEE_6]]; + // // Set incentive parameters for the first time. + // set_incentive_parameters(econia, + // MARKET_REGISTRATION_FEE, UNDERWRITER_REGISTRATION_FEE, + // CUSTODIAN_REGISTRATION_FEE, TAKER_FEE_DIVISOR, + // &integrator_fee_store_tiers, false); + // Set incentive parameters for the first time. + set_incentive_parameters(econia, + MARKET_REGISTRATION_FEE, UNDERWRITER_REGISTRATION_FEE, + CUSTODIAN_REGISTRATION_FEE, TAKER_FEE_DIVISOR, + &integrator_fee_store_tiers, false); + } + + /// Initialize a `UtilityCoinStore` under the Econia fee account. + /// + /// Returns without initializing if a `UtilityCoinStore` already + /// exists for given `CoinType`, which may happen in the case of + /// switching back to a utility coin type after having abandoned it. + /// + /// # Type Parameters + /// + /// * `CoinType`: Utility coin phantom type. + /// + /// # Parameters + /// + /// * `fee_account`: Econia fee account `signer`. + /// + /// # Aborts + /// + /// * `E_NOT_COIN`: `CoinType` does not correspond to an initialized + /// `aptos_framework::coin::Coin`. + /// + /// # Testing + /// + /// * `test_init_utility_coin_store()` + /// * `test_init_utility_coin_store_not_coin()` + fun init_utility_coin_store( + fee_account: &signer + ) { + // Assert coin type corresponds to initialized coin. + assert!(coin::is_coin_initialized(), E_NOT_COIN); + // If a utility coin store does not already exist at account, + if(!exists>(address_of(fee_account))) + // Move to the fee account an initialized one. + move_to>(fee_account, + UtilityCoinStore{coins: coin::zero()}); + } + + /// Verify that attempting to merge `amount` into `target_coins` + /// does not overflow a `u64`, aborting with `error_code` if it + /// does. + /// + /// Since coins can be minted in excess of a `HI_64` supply, this + /// is an unlikely but potentially catastrophic event, especially + /// if the overflowed account blocks other transactions from + /// proceeding. Hence the extra feedback in this module, in the + /// form of a custom error code for the given operation, which + /// allows for diagnosis in extreme cases. + /// + /// # Aborts + /// + /// * `error_code`: Proposed coin merge overflows a `u64`. + /// + /// # Testing + /// + /// * `test_range_check_coin_merge()` + fun range_check_coin_merge( + amount: u64, + target_coins: &coin::Coin, + error_code: u64 + ) { + // Get value of target coins. + let target_value = coin::value(target_coins); + // Assert merge does not overflow a u64. + assert!((amount as u128) + (target_value as u128) <= (HI_64 as u128), + error_code); + } + + /// Set all fields for `IncentiveParameters` under Econia account. + /// + /// Rather than pass-by-value a + /// `vector`, mutably reassigns + /// the values of `IncentiveParameters.integrator_fee_store_tiers` + /// via `set_incentive_parameters_parse_tiers_vector()`. + /// + /// # Type Parameters + /// + /// * `UtilityCoinType`: Utility coin phantom type. + /// + /// # Parameters + /// + /// * `econia`: Econia account `signer`. + /// * `market_registration_fee`: Market registration fee to set. + /// * `underwriter_registration_fee`: Underwriter registration fee + /// to set. + /// * `custodian_registration_fee`: Custodian registration fee to + /// set. + /// * `taker_fee_divisor`: Taker fee divisor to set. + /// * `integrator_fee_store_tiers_ref`: Immutable reference to + /// 0-indexed vector of 3-element vectors, with each 3-element + /// vector containing fields for a corresponding + /// `IntegratorFeeStoreTierParameters`. + /// * `updating`: `true` if updating incentive parameters that have + /// already been set, `false` if setting parameters for the first + /// time. + /// + /// # Assumptions + /// + /// * If `updating` is `true`, an `IncentiveParameters` already + /// exists at the Econia account. + /// * If `updating` is `false`, an `IncentiveParameters` does not + /// exist at the Econia account. + /// + /// # Aborts + /// + /// * `E_FEWER_TIERS`: `updating` is `true` and the new parameter + /// set indicates a reduction in the number of fee store + /// activation tiers, which would mean that integrators who had + /// previously upgraded to the highest tier would become subject + /// to undefined behavior. + fun set_incentive_parameters( + econia: &signer, + market_registration_fee: u64, + underwriter_registration_fee: u64, + custodian_registration_fee: u64, + taker_fee_divisor: u64, + integrator_fee_store_tiers_ref: &vector>, + updating: bool + ) acquires + IncentiveParameters + { + // Range check inputs. + set_incentive_parameters_range_check_inputs(econia, + market_registration_fee, underwriter_registration_fee, + custodian_registration_fee, taker_fee_divisor, + integrator_fee_store_tiers_ref); + // Get fee account signer. + let fee_account = resource_account::get_signer(); + // Initialize a utility coin store under the fee account (aborts + // if not an initialized coin type). + init_utility_coin_store(&fee_account); + if (updating) { // If updating previously-set values: + // Get number of tiers before upgrade. + let n_old_tiers = get_n_fee_store_tiers(); + // Get number of tiers in new parameter set. + let n_new_tiers = vector::length(integrator_fee_store_tiers_ref); + // Assert new parameter set indicates at least as many fee + // store tiers as the set from before the upgrade. + assert!(n_new_tiers >= n_old_tiers, E_FEWER_TIERS); + // Borrow a mutable reference to the incentive parameters + // resource at the Econia account. + let incentive_parameters_ref_mut = + borrow_global_mut(address_of(econia)); + // Set integrator fee stores to empty vector before + // moving from. + incentive_parameters_ref_mut.integrator_fee_store_tiers = + vector::empty(); + // Move from and drop the existing incentive parameters + // resource at the Econia account. + move_from(address_of(econia)); + }; + // Get utility coin type info. + let utility_coin_type_info = type_info::type_of(); + // Declare integrator fee store tiers vector as empty. + let integrator_fee_store_tiers = vector::empty(); + // Initialize an incentive parameters resource with + // range-checked inputs and empty tiers vector. + move_to(econia, IncentiveParameters{ + utility_coin_type_info, market_registration_fee, + underwriter_registration_fee, custodian_registration_fee, + taker_fee_divisor, integrator_fee_store_tiers}); + // Borrow a mutable reference to the incentive parameters + // resource at the Econia account. + let incentive_parameters_ref_mut = + borrow_global_mut(@econia); + // Parse in integrator fee store tier parameters. + set_incentive_parameters_parse_tiers_vector( + taker_fee_divisor, integrator_fee_store_tiers_ref, + &mut incentive_parameters_ref_mut.integrator_fee_store_tiers); + } + + /// Parse vectorized fee store tier parameters passed to + /// `set_incentive_parameters()`. + /// + /// * `taker_fee_divisor`: Taker fee divisor just set. + /// * `integrator_fee_store_tiers_ref`: Immutable reference to + /// 0-indexed vector of 3-element vectors, with each 3-element + /// vector containing fields for a corresponding + /// `IntegratorFeeStoreTierParameters`. + /// * `integrator_fee_store_tiers_target_ref_mut`: Mutable reference + /// to the `IncentiveParameters.integrator_fee_store_tiers` field + /// to parse into. + /// + /// # Aborts + /// + /// * `E_TIER_FIELDS_WRONG_LENGTH`: An indicated inner vector from + /// `integrator_fee_store_tiers_ref` is the wrong length. + /// * `E_FEE_SHARE_DIVISOR_TOO_BIG`: Fee share divisor does not + /// decrease with tier number. + /// * `E_FEE_SHARE_DIVISOR_TOO_SMALL`: A fee share divisor is less + /// than taker fee divisor. + /// * `E_FIRST_TIER_ACTIVATION_FEE_NONZERO`: Tier activation fee for + /// first tier is nonzero. + /// * `E_ACTIVATION_FEE_TOO_SMALL`: Tier activation fee does not + /// increase with tier number. + /// * `E_WITHDRAWAL_FEE_TOO_BIG`: Withdrawal fee does not decrease + /// with tier number. + /// * `E_WITHDRAWAL_FEE_TOO_SMALL`: The withdrawal fee for a given + /// tier does not meet minimum threshold. + /// + /// # Assumptions + /// + /// * `taker_fee_divisor` has been range-checked via + /// `set_incentive_parameters_range_check_inputs()`. + /// * An `IncentiveParameters` exists at the Econia account. + /// * `integrator_fee_store_tiers_ref` does not indicate an empty + /// vector. + /// * `integrator_fee_store_tiers_target_ref_mut` indicates an empty + /// vector. + /// + /// # Testing + /// + /// * `test_set_incentive_params_parse_tiers_vec_activate_0()` + /// * `test_set_incentive_params_parse_tiers_vec_activate_1()` + /// * `test_set_incentive_params_parse_tiers_vec_divisor_big_0()` + /// * `test_set_incentive_params_parse_tiers_vec_divisor_big_1()` + /// * `test_set_incentive_params_parse_tiers_vec_divisor_small()` + /// * `test_set_incentive_params_parse_tiers_vec_withdraw_big_0()` + /// * `test_set_incentive_params_parse_tiers_vec_withdraw_big_1()` + /// * `test_set_incentive_params_parse_tiers_vec_withdraw_small()` + /// * `test_set_incentive_params_parse_tiers_vec_wrong_length()` + fun set_incentive_parameters_parse_tiers_vector( + taker_fee_divisor: u64, + integrator_fee_store_tiers_ref: &vector>, + integrator_fee_store_tiers_target_ref_mut: + &mut vector + ) { + // Initialize tracker variables for the fee store parameters of + // the last parsed tier. + let (divisor_last, activation_fee_last, withdrawal_fee_last) = ( + HI_64, 0, HI_64); + // Get number of specified integrator fee store tiers. + let n_tiers = vector::length(integrator_fee_store_tiers_ref); + let i = 0; // Declare counter for loop variable. + while (i < n_tiers) { // Loop over all specified tiers + // Borrow immutable reference to fields for given tier. + let tier_fields_ref = + vector::borrow(integrator_fee_store_tiers_ref, i); + // Assert containing vector is correct length. + assert!(vector::length(tier_fields_ref) == N_TIER_FIELDS, + E_TIER_FIELDS_WRONG_LENGTH); + // Borrow immutable reference to fee share divisor. + let fee_share_divisor_ref = + vector::borrow(tier_fields_ref, FEE_SHARE_DIVISOR_INDEX); + // Assert indicated fee share divisor is less than divisor + // from last tier. + assert!(*fee_share_divisor_ref < divisor_last, + E_FEE_SHARE_DIVISOR_TOO_BIG); + // Assert indicated fee share divisor is greater than or + // equal to taker fee divisor. + assert!(*fee_share_divisor_ref >= taker_fee_divisor, + E_FEE_SHARE_DIVISOR_TOO_SMALL); + // Borrow immutable reference to tier activation fee. + let tier_activation_fee_ref = + vector::borrow(tier_fields_ref, TIER_ACTIVATION_FEE_INDEX); + if (i == 0) { // If parsing parameters for first tier: + // Assert activation fee is 0. + assert!(*tier_activation_fee_ref == 0, + E_FIRST_TIER_ACTIVATION_FEE_NONZERO); + } else { // If parameters for tier that is not first: + // Assert activation fee greater than that of last tier. + assert!(*tier_activation_fee_ref > activation_fee_last, + E_ACTIVATION_FEE_TOO_SMALL); + }; + // Borrow immutable reference to withdrawal fee. + let withdrawal_fee_ref = + vector::borrow(tier_fields_ref, WITHDRAWAL_FEE_INDEX); + // Assert withdrawal fee is less than that of last tier. + assert!(*withdrawal_fee_ref < withdrawal_fee_last, + E_WITHDRAWAL_FEE_TOO_BIG); + // Assert withdrawal fee meets minimum threshold. + assert!(*withdrawal_fee_ref >= MIN_FEE, + E_WITHDRAWAL_FEE_TOO_SMALL); + // Mark indicated tier in target tiers vector. + vector::push_back(integrator_fee_store_tiers_target_ref_mut, + IntegratorFeeStoreTierParameters{ + fee_share_divisor: *fee_share_divisor_ref, + tier_activation_fee: *tier_activation_fee_ref, + withdrawal_fee: *withdrawal_fee_ref}); + // Store divisor for comparison during next iteration. + divisor_last = *fee_share_divisor_ref; + // Store activation fee to compare during next iteration. + activation_fee_last = *tier_activation_fee_ref; + // Store withdrawal fee to compare during next iteration. + withdrawal_fee_last = *withdrawal_fee_ref; + i = i + 1; // Increment loop counter + }; + } + + /// Range check inputs for `set_incentive_parameters()`. + /// + /// # Parameters + /// + /// * `econia`: Econia account `signer`. + /// * `market_registration_fee`: Market registration fee to set. + /// * `underwriter_registration_fee`: Underwriter registration fee + /// to set. + /// * `custodian_registration_fee`: Custodian registration fee to + /// set. + /// * `taker_fee_divisor`: Taker fee divisor to set. + /// * `integrator_fee_store_tiers_ref`: Immutable reference to + /// 0-indexed vector of 3-element vectors, with each 3-element + /// vector containing fields for a corresponding + /// `IntegratorFeeStoreTierParameters`. + /// + /// # Aborts + /// + /// * `E_NOT_ECONIA`: `econia` is not Econia account. + /// * `E_MARKET_REGISTRATION_FEE_LESS_THAN_MIN`: + /// `market_registration_fee` does not meet minimum threshold. + /// * `E_UNDERWRITER_REGISTRATION_FEE_LESS_THAN_MIN`: + /// `underwriter_registration_fee` does not meet minimum + /// threshold. + /// * `E_CUSTODIAN_REGISTRATION_FEE_LESS_THAN_MIN`: + /// `custodian_registration_fee` does not meet minimum threshold. + /// * `E_TAKER_DIVISOR_LESS_THAN_MIN`: `taker_fee_divisor` does not + /// meet minimum threshold. + /// * `E_EMPTY_FEE_STORE_TIERS`: `integrator_fee_store_tiers_ref` + /// indicates an empty vector. + /// * `E_TOO_MANY_TIERS`: `integrator_fee_store_tiers_ref` indicates + /// a vector that is too long. + /// + /// # Testing + /// + /// * `test_set_incentive_params_range_check_inputs_custodian_fee()` + /// * `test_set_incentive_params_range_check_inputs_divisor()` + /// * `test_set_incentive_params_range_check_inputs_market_fee()` + /// * `test_set_incentive_params_range_check_inputs_not_econia()` + /// * `test_set_incentive_params_range_check_inputs_underwriter()` + /// * `test_set_incentive_params_range_check_inputs_vector_empty()` + /// * `test_set_incentive_params_range_check_inputs_vector_long()` + fun set_incentive_parameters_range_check_inputs( + econia: &signer, + market_registration_fee: u64, + underwriter_registration_fee: u64, + custodian_registration_fee: u64, + taker_fee_divisor: u64, + integrator_fee_store_tiers_ref: &vector> + ) { + // Assert signer is from Econia account. + assert!(address_of(econia) == @econia, E_NOT_ECONIA); + // Assert market registration fee meets minimum threshold. + assert!(market_registration_fee >= MIN_FEE, + E_MARKET_REGISTRATION_FEE_LESS_THAN_MIN); + // Assert underwriter registration fee meets minimum threshold. + assert!(underwriter_registration_fee >= MIN_FEE, + E_UNDERWRITER_REGISTRATION_FEE_LESS_THAN_MIN); + // Assert custodian registration fee meets minimum threshold. + assert!(custodian_registration_fee >= MIN_FEE, + E_CUSTODIAN_REGISTRATION_FEE_LESS_THAN_MIN); + // Assert taker fee divisor is meets minimum threshold. + assert!(taker_fee_divisor >= MIN_DIVISOR, + E_TAKER_DIVISOR_LESS_THAN_MIN); + // Assert integrator fee store parameters vector not empty. + assert!(!vector::is_empty(integrator_fee_store_tiers_ref), + E_EMPTY_FEE_STORE_TIERS); + // Assert integrator fee store parameters vector not too long. + assert!(vector::length(integrator_fee_store_tiers_ref) <= + MAX_INTEGRATOR_FEE_STORE_TIERS, E_TOO_MANY_TIERS); + } + + /// Withdraw all fee coins from an `EconiaFeeStore` for given + /// `QuoteCoinType` and `market_id` if `all` is `true`, otherwise + /// withdraw `amount` (which may correspond to all coins), aborting + /// if `account` is not Econia. + /// + /// # Aborts + /// + /// * `E_NOT_ECONIA`: `account` is not Econia account. + fun withdraw_econia_fees_internal( + account: &signer, + market_id: u64, + all: bool, + amount: u64 + ): coin::Coin + acquires + EconiaFeeStore + { + // Assert account is Econia. + assert!(address_of(account) == @econia, E_NOT_ECONIA); + // Get fee account address. + let fee_account_address = resource_account::get_address(); + // Borrow mutable reference to Econia fee store map for given + // quote coin type. + let econia_fee_store_map_ref_mut = + &mut borrow_global_mut>( + fee_account_address).map; + // Borrow mutable reference to fees for given market ID. + let fee_coins_ref_mut = tablist::borrow_mut( + econia_fee_store_map_ref_mut, market_id); + // If flagged to extract all, extract all and return. + if (all) coin::extract_all(fee_coins_ref_mut) else + // Else extract specified amount and return. + coin::extract(fee_coins_ref_mut, amount) + } + + /// Wrapped call to `withdraw_econia_fees_internal()`, for + /// depositing withdrawn coins to an + /// `aptos_framework::coin::CoinStore`. + /// + /// # Testing + /// + /// * `test_withdraw_to_coin_store_econia()` + fun withdraw_econia_fees_to_coin_store_internal( + econia: &signer, + market_id: u64, + all: bool, + amount: u64 + ) acquires EconiaFeeStore { + // Withdraw coins from fee store, verifying Econia signer. + let coins = withdraw_econia_fees_internal( + econia, market_id, all, amount); + // If Econia does not have coin store for coin type: + if (!coin::is_account_registered(@econia)) + // Register one. + coin::register(econia); + // Deposit quote coins to coin store under Econia account. + coin::deposit(@econia, coins); + } + + /// Withdraw all utility coins from the `UtilityCoinStore` if `all` + /// is `true`, otherwise withdraw `amount` (which may correspond to + /// all coins), aborting if `account` is not Econia. + /// + /// # Aborts + /// + /// * `E_NOT_ECONIA`: `account` is not Econia account. + fun withdraw_utility_coins_internal( + account: &signer, + all: bool, + amount: u64 + ): coin::Coin + acquires + UtilityCoinStore + { + // Assert account is Econia. + assert!(address_of(account) == @econia, E_NOT_ECONIA); + // Get fee account address. + let fee_account_address = resource_account::get_address(); + // Borrow mutable reference to coins in utility coin store. + let utility_coins_ref_mut = + &mut borrow_global_mut>( + fee_account_address).coins; + // If flagged to extract all, extract all and return. + if (all) coin::extract_all(utility_coins_ref_mut) else + // Else extract specified amount and return. + coin::extract(utility_coins_ref_mut, amount) + } + + /// Wrapped call to `withdraw_utility_coins_internal()`, for + /// depositing withdrawn coins to an + /// `aptos_framework::coin::CoinStore`. + /// + /// # Testing + /// + /// * `test_withdraw_to_coin_store_econia()` + fun withdraw_utility_coins_to_coin_store_internal( + econia: &signer, + all: bool, + amount: u64 + ) acquires UtilityCoinStore { + // Withdraw coins from fee store, verifying Econia signer. + let coins = withdraw_utility_coins_internal( + econia, all, amount); + // If Econia does not have coin store for coin type: + if (!coin::is_account_registered(@econia)) + // Register one. + coin::register(econia); + // Deposit utility coins to coin store under Econia account. + coin::deposit(@econia, coins); + } + + // Private functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Test-only functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test_only] + /// Return `BUY`, for testing synchronization with `market.move`. + public fun get_BUY_test(): bool {BUY} + + #[test_only] + /// Return amount of quote coins in `EconiaFeeStore` for given + /// `QuoteCoinType` and `market_id`. + /// + /// # Restrictions + /// + /// * Restricted to test-only to prevent excessive public queries + /// and thus transaction collisions. + public fun get_econia_fee_store_balance_test( + market_id: u64 + ): u64 + acquires + EconiaFeeStore + { + coin::value(tablist::borrow( + &borrow_global>( + resource_account::get_address()).map, market_id)) + } + + #[test_only] + /// Return amount of quote coins in `IntegratorFeeStore` for given + /// `QuoteCoinType` and `market_id`. + /// + /// # Restrictions + /// + /// * Restricted to test-only to prevent excessive public queries + /// and thus transaction collisions. + public fun get_integrator_fee_store_balance_test( + integrator: address, + market_id: u64 + ): u64 + acquires + IntegratorFeeStores + { + coin::value(&tablist::borrow( + &borrow_global>(integrator).map, + market_id).coins) + } + + #[test_only] + /// Return activation tier of `IntegratorFeeStore` for given + /// `QuoteCoinType` and `market_id`. + /// + /// # Restrictions + /// + /// * Restricted to test-only to prevent excessive public queries + /// and thus transaction collisions. + public fun get_integrator_fee_store_tier_test( + integrator: address, + market_id: u64 + ): u8 + acquires + IntegratorFeeStores + { + tablist::borrow(&borrow_global>( + integrator).map, market_id).tier + } + + #[test_only] + /// Return `SELL`, for testing synchronization with `market.move`. + public fun get_SELL_test(): bool {SELL} + + #[test_only] + /// Return amount of utility coins in `UtilityCoinStore` for utility + /// coin type `UC`. + /// + /// # Restrictions + /// + /// * Restricted to test-only to prevent excessive public queries + /// and thus transaction collisions. + public fun get_utility_coin_store_balance_test(): + u64 + acquires + UtilityCoinStore + { + coin::value(&borrow_global>( + resource_account::get_address()).coins) + } + + #[test_only] + /// Initialize incentives with `UC` utility coin type. + public fun init_test() + acquires + IncentiveParameters + { + assets::init_coin_types_test(); // Initialize coin types. + // Get signer for Econia account. + let econia = account::create_signer_with_capability( + &account::create_test_signer_cap(@econia)); + resource_account::init_test(); // Init fee account. + // Vectorize fee store tier parameters. + let integrator_fee_store_tiers = vector[ + vector[ + FEE_SHARE_DIVISOR_0, + TIER_ACTIVATION_FEE_0, + WITHDRAWAL_FEE_0 + ], + vector[ + FEE_SHARE_DIVISOR_1, + TIER_ACTIVATION_FEE_1, + WITHDRAWAL_FEE_1 + ] + ]; + // Initialize incentives with mock utility coin. + set_incentive_parameters(&econia, MARKET_REGISTRATION_FEE, + UNDERWRITER_REGISTRATION_FEE, CUSTODIAN_REGISTRATION_FEE, + TAKER_FEE_DIVISOR, &integrator_fee_store_tiers, false); + } + + #[test_only] + /// Return `true` if `integrator` has an `IntegratorFeeStore` for + /// given `QuoteCoinType` and `market_id`. + /// + /// # Restrictions + /// + /// * Restricted to test-only to prevent excessive public queries + /// and thus transaction collisions. + public fun has_integrator_fee_store_test( + integrator: address, + market_id: u64 + ): bool + acquires + IntegratorFeeStores + { + // Return false if integrator does not have integrator fee + // stores map for given quote coin type. + if (!exists>(integrator)) + return false; + // Immutably borrow integrator fee stores map. + let integrator_fee_stores_map_ref = + &borrow_global>(integrator).map; + // Return true if integrator fee stores map has entry for given + // market ID. + tablist::contains(integrator_fee_stores_map_ref, market_id) + } + + // Test-only functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Tests >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test] + /// Verify max quote match amounts. + fun test_calculate_max_quote_match() { + // Declare matching parameters. + let direction = BUY; + let taker_fee_divisor = 20; + let max_quote_delta_user = 105; + let max_quote_match_expected = 100; + // Calculate max quote match value. + let max_quote_match = calculate_max_quote_match( + direction, taker_fee_divisor, max_quote_delta_user); + // Assert calculated amount. + assert!(max_quote_match == max_quote_match_expected, 0); + // Repeat for a sell. + direction = SELL; + taker_fee_divisor = 25; + max_quote_delta_user = 100; + max_quote_match_expected = 104; + // Calculate max quote match value. + max_quote_match = calculate_max_quote_match( + direction, taker_fee_divisor, max_quote_delta_user); + // Assert calculated amount. + assert!(max_quote_match == max_quote_match_expected, 0); + } + + #[test] + /// Verify correction for overflowing quote match amount. + fun test_calculate_max_quote_match_overflow() { + // Declare matching parameters. + let direction = SELL; + // Define taker fee divisor as a power of two to avoid + // truncation. + let taker_fee_divisor = 16; + let max_quote_delta_user = HI_64 - HI_64 / taker_fee_divisor; + // Calculate max quote match value for critical amount. + let max_quote_match = calculate_max_quote_match( + direction, taker_fee_divisor, max_quote_delta_user); + // Assert calculated amount. + assert!(max_quote_match == HI_64, 0); + // Calculate max quote match value for one more than critical + // amount. + max_quote_match = calculate_max_quote_match( + direction, taker_fee_divisor, max_quote_delta_user + 1); + // Assert corrected amount. + assert!(max_quote_match == HI_64, 0); + // Calculate max quote match value for highest possible input. + max_quote_match = calculate_max_quote_match( + direction, taker_fee_divisor, HI_64); + // Assert corrected amount. + assert!(max_quote_match == HI_64, 0); + // Calculate max quote match value for one less than critical + // amount. + max_quote_match = calculate_max_quote_match( + direction, taker_fee_divisor, max_quote_delta_user - 1); + // Calculate expected return. + let max_quote_match_expected = ((taker_fee_divisor as u128) * + ((max_quote_delta_user - 1) as u128)) / + ((taker_fee_divisor - 1) as u128); + // Assert expected return below max possible u64. + assert!(max_quote_match_expected < (HI_64 as u128), 0); + // Assert expected return. + assert!(max_quote_match == (max_quote_match_expected as u64), 0); + } + + #[test] + /// Verify deposits for mixed registration fees. + fun test_deposit_registration_fees_mixed() + acquires + IncentiveParameters, + UtilityCoinStore + { + init_test(); // Initialize incentives. + // Get registration fees. + let market_registration_fee = get_market_registration_fee(); + let underwriter_registration_fee = get_underwriter_registration_fee(); + let custodian_registration_fee = get_custodian_registration_fee(); + // Deposit fees. + deposit_market_registration_utility_coins(assets::mint_test( + market_registration_fee)); + deposit_underwriter_registration_utility_coins(assets::mint_test( + underwriter_registration_fee)); + deposit_custodian_registration_utility_coins(assets::mint_test( + custodian_registration_fee)); + // Assert total amount. + assert!(get_utility_coin_store_balance_test() == + MARKET_REGISTRATION_FEE + UNDERWRITER_REGISTRATION_FEE + + CUSTODIAN_REGISTRATION_FEE, 0); + } + + #[test] + #[expected_failure(abort_code = E_NOT_ENOUGH_UTILITY_COINS)] + /// Verify failure for not enough utility coins. + fun test_deposit_utility_coins_verified_not_enough() + acquires + IncentiveParameters, + UtilityCoinStore + { + init_test(); // Init incentives. + // Attempt invalid invocation. + deposit_utility_coins_verified(coin::zero(), 1); + } + + #[test(econia = @econia)] + /// Verify deposit and withdrawal of utility coins. + fun test_deposit_withdraw_utility_coins( + econia: &signer + ) acquires + IncentiveParameters, + UtilityCoinStore + { + init_test(); // Initialize incentives. + // Deposit utility coins. + deposit_utility_coins(assets::mint_test(100)); + // Withdraw some utility coins. + let coins = withdraw_utility_coins(econia, 40); + assert!(coin::value(&coins) == 40, 0); // Assert value. + assets::burn(coins); // Burn coins + // Withdraw all utility coins + coins = withdraw_utility_coins_all(econia); + assert!(coin::value(&coins) == 60, 0); // Assert value. + assets::burn(coins); // Burn coins + } + + #[test(integrator = @user)] + #[expected_failure(abort_code = E_TIER_COST_NOT_INCREASE)] + /// Verify expected failure for not an increase in tier cost. + fun test_get_cost_to_upgrade_integrator_fee_store_not_increase( + integrator: &signer + ) acquires + IncentiveParameters, + IntegratorFeeStores, + UtilityCoinStore + { + init_test(); // Init incentives. + // Declare market ID, tier numbers. + let (market_id, tier_0, tier_1) = (0, 0, 1); + // Register to tier 0. + register_integrator_fee_store(integrator, market_id, tier_0, + assets::mint_test(get_tier_activation_fee(tier_0))); + // Get cumulative fee to activate to tier 0. + let tier_0_fee = get_tier_activation_fee(tier_0); + // Mutably borrow incentive parameters. + let incentive_parameters_ref_mut = + borrow_global_mut(@econia); + // Mutably borrow integrator fee store tiers. + let integrator_fee_store_tiers_ref_mut = + &mut incentive_parameters_ref_mut.integrator_fee_store_tiers; + // Mutably borrow tier 1. + let tier_1_ref_mut = vector::borrow_mut( + integrator_fee_store_tiers_ref_mut, (tier_1 as u64)); + // Manually set fee to that of previous tier. + tier_1_ref_mut.tier_activation_fee = tier_0_fee; + // Attempt invalid query against modified tier 1. + get_cost_to_upgrade_integrator_fee_store( + integrator, market_id, tier_1); + } + + #[test(integrator = @user)] + #[expected_failure(abort_code = E_NOT_AN_UPGRADE)] + /// Verify expected failure for not an upgrade. + fun test_get_cost_to_upgrade_integrator_fee_store_not_upgrade( + integrator: &signer + ) acquires + IncentiveParameters, + IntegratorFeeStores, + UtilityCoinStore + { + init_test(); // Init incentives. + let (market_id, tier) = (0, 0); // Declare market ID, tier. + // Register to given tier. + register_integrator_fee_store(integrator, market_id, tier, + assets::mint_test(get_tier_activation_fee(tier))); + // Attempt invalid query. + get_cost_to_upgrade_integrator_fee_store( + integrator, market_id, tier); + } + + #[test] + #[expected_failure(abort_code = E_INVALID_TIER)] + /// Verify failure for invalid tier number. + fun test_get_fee_share_divisor_invalid_tier() + acquires IncentiveParameters { + init_test(); // Init for testing. + // Get maximum 0-indexed tier number. + let max_tier_number = (get_n_fee_store_tiers() as u8) - 1; + // Attempt invalid invocation. + get_fee_share_divisor(max_tier_number + 1); + } + + #[test] + #[expected_failure(abort_code = E_INVALID_TIER)] + fun test_get_tier_activation_fee_invalid_tier() + acquires IncentiveParameters { + init_test(); // Init for testing. + // Get maximum 0-indexed tier number. + let max_tier_number = (get_n_fee_store_tiers() as u8) - 1; + // Attempt invalid invocation. + get_tier_activation_fee(max_tier_number + 1); + } + + #[test] + #[expected_failure(abort_code = E_INVALID_TIER)] + fun test_get_tier_withdrawal_fee_invalid_tier() + acquires IncentiveParameters { + init_test(); // Init for testing. + // Get maximum 0-indexed tier number. + let max_tier_number = (get_n_fee_store_tiers() as u8) - 1; + // Attempt invalid invocation. + get_tier_withdrawal_fee(max_tier_number + 1); + } + + #[test(econia = @econia)] + /// Verify initializing, updating, and getting incentive parameters. + fun test_init_update_get_incentives( + econia: &signer + ) acquires + IncentiveParameters + { + assets::init_coin_types_test(); // Init coin types. + resource_account::init_test(); // Init fee account. + init_module(econia); // Initialize incentives. + // Assert state. + verify_utility_coin_type(); + assert!(!is_utility_coin_type(), 0); + assert!(get_market_registration_fee() == MARKET_REGISTRATION_FEE, 0); + assert!(get_underwriter_registration_fee() == + UNDERWRITER_REGISTRATION_FEE, 0); + assert!(get_custodian_registration_fee() == + CUSTODIAN_REGISTRATION_FEE, 0); + assert!(get_taker_fee_divisor() == TAKER_FEE_DIVISOR, 0); + assert!(get_n_fee_store_tiers() == 7, 0); + assert!(get_fee_share_divisor((0 as u8)) == FEE_SHARE_DIVISOR_0, 0); + assert!(get_fee_share_divisor((1 as u8)) == FEE_SHARE_DIVISOR_1, 0); + assert!(get_fee_share_divisor((2 as u8)) == FEE_SHARE_DIVISOR_2, 0); + assert!(get_fee_share_divisor((3 as u8)) == FEE_SHARE_DIVISOR_3, 0); + assert!(get_fee_share_divisor((4 as u8)) == FEE_SHARE_DIVISOR_4, 0); + assert!(get_fee_share_divisor((5 as u8)) == FEE_SHARE_DIVISOR_5, 0); + assert!(get_fee_share_divisor((6 as u8)) == FEE_SHARE_DIVISOR_6, 0); + assert!(get_tier_activation_fee((0 as u8)) == + TIER_ACTIVATION_FEE_0, 0); + assert!(get_tier_activation_fee((1 as u8)) == + TIER_ACTIVATION_FEE_1, 0); + assert!(get_tier_activation_fee((2 as u8)) == + TIER_ACTIVATION_FEE_2, 0); + assert!(get_tier_activation_fee((3 as u8)) == + TIER_ACTIVATION_FEE_3, 0); + assert!(get_tier_activation_fee((4 as u8)) == + TIER_ACTIVATION_FEE_4, 0); + assert!(get_tier_activation_fee((5 as u8)) == + TIER_ACTIVATION_FEE_5, 0); + assert!(get_tier_activation_fee((6 as u8)) == + TIER_ACTIVATION_FEE_6, 0); + assert!(get_tier_withdrawal_fee((0 as u8)) == WITHDRAWAL_FEE_0, 0); + assert!(get_tier_withdrawal_fee((1 as u8)) == WITHDRAWAL_FEE_1, 0); + assert!(get_tier_withdrawal_fee((2 as u8)) == WITHDRAWAL_FEE_2, 0); + assert!(get_tier_withdrawal_fee((3 as u8)) == WITHDRAWAL_FEE_3, 0); + assert!(get_tier_withdrawal_fee((4 as u8)) == WITHDRAWAL_FEE_4, 0); + assert!(get_tier_withdrawal_fee((5 as u8)) == WITHDRAWAL_FEE_5, 0); + assert!(get_tier_withdrawal_fee((6 as u8)) == WITHDRAWAL_FEE_6, 0); + assert!(exists>( + resource_account::get_address()), 0); + // Update incentive parameters. + let market_registration_fee = MARKET_REGISTRATION_FEE + 5; + let underwriter_registration_fee = UNDERWRITER_REGISTRATION_FEE + 5; + let custodian_registration_fee = CUSTODIAN_REGISTRATION_FEE + 5; + let taker_fee_divisor = TAKER_FEE_DIVISOR + 5; + let fee_share_divisor_0 = FEE_SHARE_DIVISOR_0 + 5; + let tier_activation_fee_0 = TIER_ACTIVATION_FEE_0; + let withdrawal_fee_0 = WITHDRAWAL_FEE_0 + 5; + let fee_share_divisor_1 = fee_share_divisor_0 - 1; + let tier_activation_fee_1 = tier_activation_fee_0 + 1; + let withdrawal_fee_1 = withdrawal_fee_0 - 1; + let fee_share_divisor_2 = fee_share_divisor_1 - 1; + let tier_activation_fee_2 = tier_activation_fee_1 + 1; + let withdrawal_fee_2 = withdrawal_fee_1 - 1; + let fee_share_divisor_3 = fee_share_divisor_2 - 1; + let tier_activation_fee_3 = tier_activation_fee_2 + 1; + let withdrawal_fee_3 = withdrawal_fee_2 - 1; + let fee_share_divisor_4 = fee_share_divisor_3 - 1; + let tier_activation_fee_4 = tier_activation_fee_3 + 1; + let withdrawal_fee_4 = withdrawal_fee_3 - 1; + let fee_share_divisor_5 = fee_share_divisor_4 - 1; + let tier_activation_fee_5 = tier_activation_fee_4 + 1; + let withdrawal_fee_5 = withdrawal_fee_4 - 1; + let fee_share_divisor_6 = fee_share_divisor_5 - 1; + let tier_activation_fee_6 = tier_activation_fee_5 + 1; + let withdrawal_fee_6 = withdrawal_fee_5 - 1; + // Vectorize fee store tier parameters. + let integrator_fee_store_tiers = vector[ + vector[fee_share_divisor_0, + tier_activation_fee_0, + withdrawal_fee_0], + vector[fee_share_divisor_1, + tier_activation_fee_1, + withdrawal_fee_1], + vector[fee_share_divisor_2, + tier_activation_fee_2, + withdrawal_fee_2], + vector[fee_share_divisor_3, + tier_activation_fee_3, + withdrawal_fee_3], + vector[fee_share_divisor_4, + tier_activation_fee_4, + withdrawal_fee_4], + vector[fee_share_divisor_5, + tier_activation_fee_5, + withdrawal_fee_5], + vector[fee_share_divisor_6, + tier_activation_fee_6, + withdrawal_fee_6]]; + // Update incentives. + update_incentives(econia, market_registration_fee, + underwriter_registration_fee, custodian_registration_fee, + taker_fee_divisor, integrator_fee_store_tiers); + // Assert state. + verify_utility_coin_type(); + assert!(!is_utility_coin_type(), 0); + assert!(get_market_registration_fee() == market_registration_fee, 0); + assert!(get_underwriter_registration_fee() == + underwriter_registration_fee, 0); + assert!(get_custodian_registration_fee() == + custodian_registration_fee, 0); + assert!(get_taker_fee_divisor() == taker_fee_divisor, 0); + assert!(get_fee_share_divisor((0 as u8)) == fee_share_divisor_0, 0); + assert!(get_fee_share_divisor((1 as u8)) == fee_share_divisor_1, 0); + assert!(get_fee_share_divisor((2 as u8)) == fee_share_divisor_2, 0); + assert!(get_fee_share_divisor((3 as u8)) == fee_share_divisor_3, 0); + assert!(get_fee_share_divisor((4 as u8)) == fee_share_divisor_4, 0); + assert!(get_fee_share_divisor((5 as u8)) == fee_share_divisor_5, 0); + assert!(get_fee_share_divisor((6 as u8)) == fee_share_divisor_6, 0); + assert!(get_tier_activation_fee((0 as u8)) == + tier_activation_fee_0, 0); + assert!(get_tier_activation_fee((1 as u8)) == + tier_activation_fee_1, 0); + assert!(get_tier_activation_fee((2 as u8)) == + tier_activation_fee_2, 0); + assert!(get_tier_activation_fee((3 as u8)) == + tier_activation_fee_3, 0); + assert!(get_tier_activation_fee((4 as u8)) == + tier_activation_fee_4, 0); + assert!(get_tier_activation_fee((5 as u8)) == + tier_activation_fee_5, 0); + assert!(get_tier_activation_fee((6 as u8)) == + tier_activation_fee_6, 0); + assert!(get_tier_withdrawal_fee((0 as u8)) == withdrawal_fee_0, 0); + assert!(get_tier_withdrawal_fee((1 as u8)) == withdrawal_fee_1, 0); + assert!(get_tier_withdrawal_fee((2 as u8)) == withdrawal_fee_2, 0); + assert!(get_tier_withdrawal_fee((3 as u8)) == withdrawal_fee_3, 0); + assert!(get_tier_withdrawal_fee((4 as u8)) == withdrawal_fee_4, 0); + assert!(get_tier_withdrawal_fee((5 as u8)) == withdrawal_fee_5, 0); + assert!(get_tier_withdrawal_fee((6 as u8)) == withdrawal_fee_6, 0); + assert!( + exists>(resource_account::get_address()), 0); + } + + #[test] + /// Verify successful `UtilityCoinStore` initialization. + fun test_init_utility_coin_store() { + assets::init_coin_types_test(); // Init coin types. + resource_account::init_test(); // Init fee account. + // Get fee account signer. + let fee_account = resource_account::get_signer(); + // Init utility coin store under fee account. + init_utility_coin_store(&fee_account); + // Verify can call re-init for when already initialized. + init_utility_coin_store(&fee_account); + // Assert a utility coin store exists under fee account. + assert!(exists>(address_of(&fee_account)), 0); + } + + #[test(account = @user)] + #[expected_failure(abort_code = E_NOT_COIN)] + /// Verify failure for attempting to initialize with non-coin type. + fun test_init_utility_coin_store_not_coin( + account: &signer + ) { + // Attempt invalid invocation. + init_utility_coin_store(account); + } + + #[test] + #[expected_failure(abort_code = 12345, location = Self)] + /// Verify failure for overflow. + fun test_range_check_coin_merge() { + let target_coins = assets::mint_test(HI_64); // Mint coins. + // Attempt invalid invocation. + range_check_coin_merge(1, &target_coins, 12345); + assets::burn(target_coins); // Burn target coins. + } + + #[test( + econia = @econia, + integrator = @user + )] + /// Verify registration of assorted coin stores, fee assessment, and + /// withdrawal scenarios. + fun test_register_assess_withdraw( + econia: &signer, + integrator: &signer + ) acquires + EconiaFeeStore, + IncentiveParameters, + IntegratorFeeStores, + UtilityCoinStore + { + init_test(); // Init incentives. + // Declare market IDs. + let (market_id_0, market_id_1, market_id_2) = (0, 1, 2); + // Declare integrator fee store tiers. + let (tier_0, tier_1) = (0, 1); + // Get taker fee divisor. + let taker_fee_divisor = get_taker_fee_divisor(); + // Declare utility coin balance after integrator registration. + let utility_coin_balance_0 = get_tier_activation_fee(tier_0) + + get_tier_activation_fee(tier_1); + // Declare utility coin balance after integrator fee withdrawal. + let utility_coin_balance_1 = utility_coin_balance_0 + + get_tier_withdrawal_fee(tier_0); + let quote_fill_0 = 12345; // Declare quote fill amount, fill 0. + // Calculate integrator fee share for fill 0. + let integrator_fees_0 = quote_fill_0 / get_fee_share_divisor(tier_0); + // Calculate taker fees assessed on fill 0. + let taker_fees_0 = quote_fill_0 / taker_fee_divisor; + // Calculate Econia fees assessed on fill 0. + let econia_fees_0 = taker_fees_0 - integrator_fees_0; + let quote_fill_1 = 54321; // Declare quote fill amount, fill 1. + // Declare Econia fees for fill 1, where integrator does not + // have a fee stores map for given quote coin types + let econia_fees_1 = quote_fill_1 / taker_fee_divisor; + let quote_fill_2 = 23456; // Declare quote fill amount, fill 2. + // Declare Econia fees for fill 2, where integrator does not + // have a fee store for given market ID. + let econia_fees_2 = quote_fill_2 / taker_fee_divisor; + // Register an Econia fee store for all markets. + register_econia_fee_store_entry(market_id_0); + register_econia_fee_store_entry(market_id_1); + register_econia_fee_store_entry(market_id_2); + // Register an integrator fee store for first two markets. + register_integrator_fee_store(integrator, market_id_0, + tier_0, assets::mint_test(get_tier_activation_fee(tier_0))); + register_integrator_fee_store(integrator, market_id_1, + tier_1, assets::mint_test(get_tier_activation_fee(tier_1))); + // Assert tiers. + assert!(get_integrator_fee_store_tier_test(@user, market_id_0) == + tier_0, 0); + assert!(get_integrator_fee_store_tier_test(@user, market_id_1) == + tier_1, 0); + // Assert utility coins deposited. + assert!(get_utility_coin_store_balance_test() == + utility_coin_balance_0, 0); + // Mint enough quote coins to cover taker fees for fill 0. + let quote_coins = assets::mint_test(taker_fees_0); + // Assess fees on fill 0. + let (quote_coins, taker_fees) = assess_taker_fees( + market_id_0, @user, taker_fee_divisor, quote_fill_0, quote_coins); + // Assert fee amount. + assert!(taker_fees == taker_fees_0, 0); + // Destroy empty coins, asserting that all taker fees assessed. + coin::destroy_zero(quote_coins); + assert!(get_econia_fee_store_balance_test(market_id_0) == + econia_fees_0, 0); // Assert Econia fee share. + assert!(get_integrator_fee_store_balance_test(@user, market_id_0) + == integrator_fees_0, 0); // Assert integrator fee share. + // Mint enough quote coins to cover taker fees for fill 1. + quote_coins = assets::mint_test(econia_fees_1); + // Assess fees on fill 1. + (quote_coins, taker_fees) = assess_taker_fees( + market_id_1, @econia, taker_fee_divisor, quote_fill_1, + quote_coins); + // Assert fee amount. + assert!(taker_fees == econia_fees_1, 0); + // Destroy empty coins, asserting that all taker fees assessed. + coin::destroy_zero(quote_coins); + assert!(get_econia_fee_store_balance_test(market_id_1) == + econia_fees_1, 0); // Assert Econia fee share. + // Mint enough quote coins to cover taker fees for fill 2. + quote_coins = assets::mint_test(econia_fees_2); + // Assess fees on fill 2. + (quote_coins, taker_fees) = assess_taker_fees( + market_id_2, @user, taker_fee_divisor, quote_fill_2, quote_coins); + // Assert fee amount. + assert!(taker_fees == econia_fees_2, 0); + // Destroy empty coins, asserting that all taker fees assessed. + coin::destroy_zero(quote_coins); + assert!(get_econia_fee_store_balance_test(market_id_2) == + econia_fees_2, 0); // Assert Econia fee share. + // Register account for integrator. + account::create_account_for_test(@user); + // Register utility coin store for integrator. + coin::register(integrator); + // Deposit sufficient utility coins to pay fees + coin::deposit(@user, + assets::mint_test(get_tier_withdrawal_fee(tier_0))); + // Have integrator withdraw all fees for market ID 0. + withdraw_integrator_fees_via_coinstores(integrator, + market_id_0); + // Assert integrator got all coins. + assert!(coin::balance(@user) == integrator_fees_0, 0); + // Assert utility coins deposited. + assert!(get_utility_coin_store_balance_test() == + utility_coin_balance_1, 0); + // Have Econia withdraw 1 coin for market ID 0. + quote_coins = withdraw_econia_fees(econia, market_id_0, 1); + // Assert 1 coin withdrawn. + assert!(coin::value("e_coins) == 1, 0); + assets::burn(quote_coins); // Burn coins. + // Have Econia withdraw all coins for market ID 0. + quote_coins = withdraw_econia_fees_all(econia, market_id_0); + // Assert remaining coins withdrawn. + assert!(coin::value("e_coins) == econia_fees_0 - 1, 0); + assets::burn(quote_coins); // Burn coins. + // Have Econia withdraw 1 utility coin. + let utility_coins = withdraw_utility_coins(econia, 1); + // Assert 1 coin withdrawn. + assert!(coin::value(&utility_coins) == 1, 0); + assets::burn(utility_coins); // Burn coins. + // Have Econia withdraw all utility coins. + utility_coins = withdraw_utility_coins_all(econia); + // Assert remaining coins withdrawn. + assert!(coin::value(&utility_coins) == utility_coin_balance_1 - 1, 0); + assets::burn(utility_coins); // Burn coins. + // Deposit sufficient utility coins to integrator to pay + // withdrawal fees a second time. + coin::deposit(@user, + assets::mint_test(get_tier_withdrawal_fee(tier_0))); + // Have integrator withdraw fees for market ID 0 + withdraw_integrator_fees_via_coinstores(integrator, + market_id_0); + // Assert integrator quote coin balance unchanged. + assert!(coin::balance(@user) == integrator_fees_0, 0); + } + + #[test] + #[expected_failure(abort_code = E_FIRST_TIER_ACTIVATION_FEE_NONZERO)] + /// Verify failure for nonzero activation fee on first tier. + fun test_set_incentive_params_parse_tiers_vec_activate_0() { + // Declare mock inputs. + let taker_fee_divisor = 2345; + // Divisor. + let tier_0 = vector::singleton(taker_fee_divisor + 1); + vector::push_back(&mut tier_0, 1); // Activation fee. + vector::push_back(&mut tier_0, HI_64 - 1); // Withdrawal fee. + let integrator_fee_store_tiers = vector::singleton(tier_0); + let integrator_fee_store_tiers_target = vector::empty(); + set_incentive_parameters_parse_tiers_vector( + taker_fee_divisor, &integrator_fee_store_tiers, + &mut integrator_fee_store_tiers_target); + } + + #[test] + #[expected_failure(abort_code = E_ACTIVATION_FEE_TOO_SMALL)] + /// Verify failure for activation fee too small on 1st tier. + fun test_set_incentive_params_parse_tiers_vec_activate_1() { + // Declare mock inputs. + let taker_fee_divisor = 2345; + // Divisor. + let tier_0 = vector::singleton(taker_fee_divisor + 2); + // Activation fee. + vector::push_back(&mut tier_0, TIER_ACTIVATION_FEE_0); + vector::push_back(&mut tier_0, HI_64 - 1); // Withdrawal fee. + // Divisor. + let tier_1 = vector::singleton(taker_fee_divisor + 1); + // Activation fee. + vector::push_back(&mut tier_1, TIER_ACTIVATION_FEE_0); + vector::push_back(&mut tier_1, HI_64 - 2); // Withdrawal fee. + let integrator_fee_store_tiers = vector::singleton(tier_0); + vector::push_back(&mut integrator_fee_store_tiers, tier_1); + let integrator_fee_store_tiers_target = vector::empty(); + set_incentive_parameters_parse_tiers_vector( + taker_fee_divisor, &integrator_fee_store_tiers, + &mut integrator_fee_store_tiers_target); + } + + #[test] + #[expected_failure(abort_code = E_FEE_SHARE_DIVISOR_TOO_BIG)] + /// Verify failure for fee share divisor too big on 0th tier. + fun test_set_incentive_params_parse_tiers_vec_divisor_big_0() { + // Declare mock inputs. + let taker_fee_divisor = 2345; + let tier_0 = vector::singleton(HI_64); // Divisor. + vector::push_back(&mut tier_0, 0); // Activation fee. + vector::push_back(&mut tier_0, 0); // Withdrawal fee. + let integrator_fee_store_tiers = vector::singleton(tier_0); + let integrator_fee_store_tiers_target = vector::empty(); + set_incentive_parameters_parse_tiers_vector( + taker_fee_divisor, &integrator_fee_store_tiers, + &mut integrator_fee_store_tiers_target); + } + + #[test] + #[expected_failure(abort_code = E_FEE_SHARE_DIVISOR_TOO_BIG)] + /// Verify failure for fee share divisor too big on 1st tier. + fun test_set_incentive_params_parse_tiers_vec_divisor_big_1() { + // Declare mock inputs. + let taker_fee_divisor = 2345; + // Divisor. + let tier_0 = vector::singleton(taker_fee_divisor + 1); + // Activation fee. + vector::push_back(&mut tier_0, TIER_ACTIVATION_FEE_0); + vector::push_back(&mut tier_0, HI_64 - 1); // Withdrawal fee. + // Divisor. + let tier_1 = vector::singleton(taker_fee_divisor + 1); + vector::push_back(&mut tier_1, 2); // Activation fee. + vector::push_back(&mut tier_1, HI_64 - 2); // Withdrawal fee. + let integrator_fee_store_tiers = vector::singleton(tier_0); + vector::push_back(&mut integrator_fee_store_tiers, tier_1); + let integrator_fee_store_tiers_target = vector::empty(); + set_incentive_parameters_parse_tiers_vector( + taker_fee_divisor, &integrator_fee_store_tiers, + &mut integrator_fee_store_tiers_target); + } + + #[test] + #[expected_failure(abort_code = E_FEE_SHARE_DIVISOR_TOO_SMALL)] + /// Verify failure for fee share divisor too small. + fun test_set_incentive_params_parse_tiers_vec_divisor_small() { + // Declare mock inputs. + let taker_fee_divisor = 2345; + // Divisor. + let tier_0 = vector::singleton(taker_fee_divisor - 1); + // Activation fee. + vector::push_back(&mut tier_0, TIER_ACTIVATION_FEE_0); + vector::push_back(&mut tier_0, 0); // Withdrawal fee. + let integrator_fee_store_tiers = vector::singleton(tier_0); + let integrator_fee_store_tiers_target = vector::empty(); + set_incentive_parameters_parse_tiers_vector( + taker_fee_divisor, &integrator_fee_store_tiers, + &mut integrator_fee_store_tiers_target); + } + + #[test] + #[expected_failure(abort_code = E_WITHDRAWAL_FEE_TOO_BIG)] + /// Verify failure for withdrawal fee too big on 0th tier. + fun test_set_incentive_params_parse_tiers_vec_withdraw_big_0() { + // Declare mock inputs. + let taker_fee_divisor = 2345; + // Divisor. + let tier_0 = vector::singleton(taker_fee_divisor + 2); + // Activation fee. + vector::push_back(&mut tier_0, TIER_ACTIVATION_FEE_0); + vector::push_back(&mut tier_0, HI_64); // Withdrawal fee. + let integrator_fee_store_tiers = vector::singleton(tier_0); + let integrator_fee_store_tiers_target = vector::empty(); + set_incentive_parameters_parse_tiers_vector( + taker_fee_divisor, &integrator_fee_store_tiers, + &mut integrator_fee_store_tiers_target); + } + + #[test] + #[expected_failure(abort_code = E_WITHDRAWAL_FEE_TOO_BIG)] + /// Verify failure for withdrawal fee too big on 1st tier. + fun test_set_incentive_params_parse_tiers_vec_withdraw_big_1() { + // Declare mock inputs. + let taker_fee_divisor = 2345; + // Divisor. + let tier_0 = vector::singleton(taker_fee_divisor + 2); + // Activation fee. + vector::push_back(&mut tier_0, TIER_ACTIVATION_FEE_0); + vector::push_back(&mut tier_0, HI_64 - 1); // Withdrawal fee. + // Divisor. + let tier_1 = vector::singleton(taker_fee_divisor + 1); + vector::push_back(&mut tier_1, 2); // Activation fee. + vector::push_back(&mut tier_1, HI_64 - 1); // Withdrawal fee. + let integrator_fee_store_tiers = vector::singleton(tier_0); + vector::push_back(&mut integrator_fee_store_tiers, tier_1); + let integrator_fee_store_tiers_target = vector::empty(); + set_incentive_parameters_parse_tiers_vector( + taker_fee_divisor, &integrator_fee_store_tiers, + &mut integrator_fee_store_tiers_target); + } + + #[test] + #[expected_failure(abort_code = E_WITHDRAWAL_FEE_TOO_SMALL)] + /// Verify failure for withdrawal fee too small. + fun test_set_incentive_params_parse_tiers_vec_withdraw_small() { + // Declare mock inputs. + let taker_fee_divisor = 2345; + // Divisor. + let tier_0 = vector::singleton(taker_fee_divisor + 1); + // Activation fee. + vector::push_back(&mut tier_0, TIER_ACTIVATION_FEE_0); + vector::push_back(&mut tier_0, 0); // Withdrawal fee. + let integrator_fee_store_tiers = vector::singleton(tier_0); + let integrator_fee_store_tiers_target = vector::empty(); + set_incentive_parameters_parse_tiers_vector( + taker_fee_divisor, &integrator_fee_store_tiers, + &mut integrator_fee_store_tiers_target); + } + + #[test] + #[expected_failure(abort_code = E_TIER_FIELDS_WRONG_LENGTH)] + /// Verify failure for wrong length of inner vector. + fun test_set_incentive_params_parse_tiers_vec_wrong_length() { + // Declare mock inputs. + let taker_fee_divisor = 2345; + let integrator_fee_store_tiers = vector::singleton(vector::empty()); + let integrator_fee_store_tiers_target = vector::empty(); + set_incentive_parameters_parse_tiers_vector( + taker_fee_divisor, &integrator_fee_store_tiers, + &mut integrator_fee_store_tiers_target); + } + + #[test(econia = @econia)] + #[expected_failure( + abort_code = E_CUSTODIAN_REGISTRATION_FEE_LESS_THAN_MIN + )] + /// Verify failure for custodian registration fee too low. + fun test_set_incentive_params_range_check_inputs_custodian_fee( + econia: &signer + ) { + // Attempt invalid invocation. + set_incentive_parameters_range_check_inputs(econia, 1, 1, 0, 0, + &vector::empty()); + } + + #[test(econia = @econia)] + #[expected_failure(abort_code = E_TAKER_DIVISOR_LESS_THAN_MIN)] + /// Verify failure for divisor too low. + fun test_set_incentive_params_range_check_inputs_divisor( + econia: &signer + ) { + // Attempt invalid invocation. + set_incentive_parameters_range_check_inputs(econia, 1, 1, 1, 0, + &vector::empty()); + } + + #[test(econia = @econia)] + #[expected_failure(abort_code = E_MARKET_REGISTRATION_FEE_LESS_THAN_MIN)] + /// Verify failure for market registration fee too low. + fun test_set_incentive_params_range_check_inputs_market_fee( + econia: &signer + ) { + // Attempt invalid invocation. + set_incentive_parameters_range_check_inputs(econia, 0, 0, 0, 0, + &vector::empty()); + } + + #[test(account = @user)] + #[expected_failure(abort_code = E_NOT_ECONIA)] + /// Verify failure for not Econia account. + fun test_set_incentive_params_range_check_inputs_not_econia( + account: &signer + ) { + // Attempt invalid invocation. + set_incentive_parameters_range_check_inputs(account, 0, 0, 0, 0, + &vector::empty()); + } + + #[test(econia = @econia)] + #[expected_failure( + abort_code = E_UNDERWRITER_REGISTRATION_FEE_LESS_THAN_MIN + )] + /// Verify failure for underwriter registration fee too low. + fun test_set_incentive_params_range_check_inputs_underwriter( + econia: &signer + ) { + // Attempt invalid invocation. + set_incentive_parameters_range_check_inputs(econia, 1, 0, 0, 0, + &vector::empty()); + } + + #[test(econia = @econia)] + #[expected_failure(abort_code = E_EMPTY_FEE_STORE_TIERS)] + /// Verify failure for empty fee store tiers. + fun test_set_incentive_params_range_check_inputs_vector_empty( + econia: &signer + ) { + // Attempt invalid invocation. + set_incentive_parameters_range_check_inputs(econia, 1, 1, 1, 2, + &vector::empty()); + } + + #[test(econia = @econia)] + #[expected_failure(abort_code = E_TOO_MANY_TIERS)] + /// Verify failure for too many elements in fee store tiers vector. + fun test_set_incentive_params_range_check_inputs_vector_long( + econia: &signer + ) { + // Declare empty integrator fee store tiers vector. + let integrator_fee_store_tiers = vector::empty(); + let i = 0; // Declare loop counter. + // For one iteration more than the number of max tiers: + while (i < MAX_INTEGRATOR_FEE_STORE_TIERS + 1) { + // Push back an empty vector onto fee store tiers vector. + vector::push_back(&mut integrator_fee_store_tiers, + vector::empty()); + i = i + 1; // Increment loop counter. + }; + // Attempt invalid invocation. + set_incentive_parameters_range_check_inputs(econia, 1, 1, 1, 2, + &integrator_fee_store_tiers); + } + + #[test(econia = @econia)] + #[expected_failure(abort_code = E_FEWER_TIERS)] + /// Verify failure for attempting to update incentive parameters + /// with fewer integrator fee store tiers than before. + fun test_update_incentives_fewer_tiers( + econia: &signer + ) acquires + IncentiveParameters + { + assets::init_coin_types_test(); // Init coin types. + resource_account::init_test(); // Init fee account. + init_module(econia); // Initialize incentives. + // Vectorize fee store tier parameters. + let tier_0 = vector::singleton(FEE_SHARE_DIVISOR_0); + vector::push_back(&mut tier_0, TIER_ACTIVATION_FEE_0); + vector::push_back(&mut tier_0, WITHDRAWAL_FEE_0); + let integrator_fee_store_tiers = vector::singleton(tier_0); + // Attempt invalid update to incentive parameter set. + update_incentives(econia, MARKET_REGISTRATION_FEE, + UNDERWRITER_REGISTRATION_FEE, CUSTODIAN_REGISTRATION_FEE, + TAKER_FEE_DIVISOR, integrator_fee_store_tiers); + } + + #[test(integrator = @user)] + /// Verify upgrade and fee assessment. + fun test_upgrade_integrator_fee_store_via_coinstore( + integrator: &signer + ) acquires + IncentiveParameters, + IntegratorFeeStores, + UtilityCoinStore + { + init_test(); // Init incentives. + // Declare market ID, tier. + let (market_id, tier_start, tier_upgrade) = (0, 0, 1); + // Declare activation fee for start and upgrade tiers. + let (fee_start, fee_upgrade) = (get_tier_activation_fee(tier_start), + get_tier_activation_fee(tier_upgrade)); + // Register to start tier. + register_integrator_fee_store(integrator, market_id, + tier_start, assets::mint_test(fee_start)); + // Assert start tier. + assert!(get_integrator_fee_store_tier_test(@user, market_id) == + tier_start, 0); + // Register account for given integrator. + account::create_account_for_test(@user); + // Register integrator with coinstore for utility coin. + coin::register(integrator); + // Deposit enough utility coins to pay for upgrade. + coin::deposit(@user, assets::mint_test(fee_upgrade)); + // Upgrade to upgrade tier. + upgrade_integrator_fee_store_via_coinstore(integrator, + market_id, tier_upgrade); + // Assert fees assessed for cumulative amount required to + // activate to upgrade tier. + assert!(get_utility_coin_store_balance_test() == fee_upgrade, 0); + // Assert upgrade tier. + assert!(get_integrator_fee_store_tier_test(@user, market_id) == + tier_upgrade, 0); + } + + #[test] + #[expected_failure(abort_code = E_INVALID_UTILITY_COIN_TYPE)] + /// Verify failure for wrong type. + fun test_verify_utility_coin_type() + acquires + IncentiveParameters + { + init_test(); // Initialize incentives for testing. + verify_utility_coin_type(); // Attempt invalid invocation. + } + + #[test(account = @user)] + #[expected_failure(abort_code = E_NOT_ECONIA)] + /// Verify failure for account is not Econia. + fun test_withdraw_econia_fees_all_not_econia( + account: &signer + ) acquires + EconiaFeeStore + { + // Attempt invalid invocation. + let fees = withdraw_econia_fees_all(account, 0); + assets::burn(fees); // Burn fees. + } + + #[test(account = @user)] + #[expected_failure(abort_code = E_NOT_ECONIA)] + /// Verify failure for account is not Econia. + fun test_withdraw_econia_fees_not_econia( + account: &signer + ) acquires + EconiaFeeStore + { + // Attempt invalid invocation. + let fees = withdraw_econia_fees(account, 0, 0); + assets::burn(fees); // Burn fees. + } + + #[test] + /// Verify state updates for withdrawing to a standard coin store, + /// from Econia's fee and utility coin stores. + fun test_withdraw_to_coin_store_econia() + acquires + EconiaFeeStore, + IncentiveParameters, + UtilityCoinStore + { + init_test(); // Init incentives. + // Declare coin amounts, mock market ID. + let utility_coin_amount = 123; + let fee_coin_amount = 321; + let market_id = 456; + // Deposit utility coins. + deposit_utility_coins( + assets::mint_test(utility_coin_amount)); + // Register Econia account. + let econia = account::create_account_for_test(@econia); + // Withdraw 1 coin, registering coin store. + withdraw_utility_coins_to_coin_store(&econia, 1); + // Assert coin store balance. + assert!(coin::balance(@econia) == 1, 0); + // Withdraw remaining coins. + withdraw_utility_coins_all_to_coin_store(&econia,); + // Assert coin store balance. + assert!(coin::balance(@econia) == utility_coin_amount, 0); + // Register Econia fee store. + register_econia_fee_store_entry(market_id); + // Mutably borrow Econia fee store map for quote coin type. + let econia_fee_store_map_ref_mut = + &mut borrow_global_mut>( + resource_account::get_address()).map; + // Borrow mutable reference to fees for given market ID. + let econia_fee_store_coins_ref_mut = tablist::borrow_mut( + econia_fee_store_map_ref_mut, market_id); + // Merge simulated fees into the fee store. + coin::merge(econia_fee_store_coins_ref_mut, + assets::mint_test(fee_coin_amount)); + // Withdraw 1 coin, registering coin store. + withdraw_econia_fees_to_coin_store(&econia, market_id, 1); + // Assert coin store balance. + assert!(coin::balance(@econia) == 1, 0); + // Withdraw remaining coins. + withdraw_econia_fees_all_to_coin_store(&econia, market_id); + // Assert coin store balance. + assert!(coin::balance(@econia) == fee_coin_amount, 0); + } + + #[test(account = @user)] + #[expected_failure(abort_code = E_NOT_ECONIA)] + /// Verify failure for account is not Econia. + fun test_withdraw_utility_coins_all_not_econia( + account: &signer + ): coin::Coin + acquires + UtilityCoinStore + { + // Attempt invalid invocation. + withdraw_utility_coins_all(account) + } + + #[test(account = @user)] + #[expected_failure(abort_code = E_NOT_ECONIA)] + /// Verify failure for account is not Econia. + fun test_withdraw_utility_coins_not_econia( + account: &signer + ): coin::Coin + acquires + UtilityCoinStore + { + // Attempt invalid invocation. + withdraw_utility_coins(account, 1234) + } + + // Tests <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +} \ No newline at end of file diff --git a/testsuite/module-publish/src/packages/econia/sources/market.move b/testsuite/module-publish/src/packages/econia/sources/market.move new file mode 100644 index 0000000000000..e856e33b6f223 --- /dev/null +++ b/testsuite/module-publish/src/packages/econia/sources/market.move @@ -0,0 +1,4231 @@ +/// Market functionality for order book operations. +/// +/// For each registered market, Econia has an order book stored under a +/// global resource account. When someone registers a market, a new +/// order book entry is added under the resource account at a new market +/// ID. +/// +/// Once a market is registered, signing users and delegated custodians +/// can place limit orders and market orders, and cancel or change the +/// size of any open orders. Swaps can be placed permissionlessly +/// without a market account. +/// +/// Econia implements an atomic matching engine, and emits events in +/// response to changes in order book state as well as assorted market +/// operations. Notably, Econia evicts the ask or bid with the lowest +/// price-time priority when inserting a limit order to a binary search +/// tree that exceeds a critical height. +/// +/// Multiple API variants are supported for market registration and +/// order management function, to enable diagnostic function returns, +/// public entry calls, etc. +/// +/// All orders are issued an order ID upon placement, which is unique to +/// the given market. The order ID encodes a counter fo the number of +/// orders that have been placed on the corresponding market. For orders +/// that result in a post to the book, the market order ID additionally +/// encodes an "AVL queue access key" (essentially a pointer into +/// order book memory), which is required for order lookup during order +/// size change and/or order cancellation operations. +/// +/// Note that the terms "order ID" and "market order ID" are used +/// interchangeably. +/// +/// # General overview sections +/// +/// [View functions](#view-functions) +/// +/// * [Constant getters](#constant-getters) +/// * [Market order ID decoders](#market-order-id-decoders) +/// * [Order lookup](#order-lookup) +/// +/// [Public function index](#public-function-index) +/// +/// * [Market registration](#market-registration) +/// * [Limit orders](#limit-orders) +/// * [Passive advance limit orders](#passive-advance-limit-orders) +/// * [Market orders](#market-orders) +/// * [Swaps](#swaps) +/// * [Change order size](#change-order-size) +/// * [Cancel orders](#cancel-orders) +/// +/// [Dependency charts](#dependency-charts) +/// +/// * [Internal dependencies](#internal-dependencies) +/// * [External module dependencies](#external-module-dependencies) +/// +/// [Order management testing](#order-management-testing) +/// +/// * [Functions with aborts](#functions-with-aborts) +/// * [Return proxies](#return-proxies) +/// * [Invocation proxies](#invocation-proxies) +/// * [Branching functions](#branching-functions) +/// +/// [Complete DocGen index](#complete-docgen-index) +/// +/// # View functions +/// +/// ## Constant getters +/// +/// * `get_ABORT()` +/// * `get_ASK()` +/// * `get_BID()` +/// * `get_BUY()` +/// * `get_CANCEL_BOTH()` +/// * `get_CANCEL_MAKER()` +/// * `get_CANCEL_TAKER()` +/// * `get_FILL_OR_ABORT()` +/// * `get_HI_PRICE()` +/// * `get_IMMEDIATE_OR_CANCEL()` +/// * `get_MAX_POSSIBLE()` +/// * `get_NO_CUSTODIAN()` +/// * `get_NO_RESTRICTION()` +/// * `get_NO_UNDERWRITER()` +/// * `get_POST_OR_ABORT()` +/// * `get_PERCENT()` +/// * `get_SELL()` +/// * `get_TICKS()` +/// +/// ## Market order ID decoders +/// +/// * `did_order_post()` +/// * `get_market_order_id_counter()` +/// * `get_market_order_id_price()` +/// * `get_posted_order_id_side()` +/// +/// ## Event handle lookup +/// +/// * `get_market_event_handle_creation_info()` +/// * `get_swapper_event_handle_creation_numbers()` +/// +/// ## Order lookup +/// +/// * `get_open_order()` +/// * `get_open_orders()` +/// * `get_open_orders_all()` +/// * `get_open_orders_paginated()` +/// * `get_price_levels()` +/// * `get_price_levels_all()` +/// * `get_price_levels_paginated()` +/// * `has_open_order()` +/// +/// # Public function index +/// +/// See the [dependency charts](#dependency-charts) for a visual map of +/// associated function wrappers. +/// +/// ## Market registration +/// +/// * `register_market_base_coin()` +/// * `register_market_base_coin_from_coinstore()` +/// * `register_market_base_generic()` +/// +/// ## Limit orders +/// +/// * `place_limit_order_custodian()` +/// * `place_limit_order_user()` +/// * `place_limit_order_user_entry()` +/// +/// ## Passive advance limit orders +/// +/// * `place_limit_order_passive_advance_custodian()` +/// * `place_limit_order_passive_advance_user()` +/// * `place_limit_order_passive_advance_user_entry()` +/// +/// ## Market orders +/// +/// * `place_market_order_custodian()` +/// * `place_market_order_user()` +/// * `place_market_order_user_entry()` +/// +/// ## Swaps +/// +/// * `swap_between_coinstores()` +/// * `swap_between_coinstores_entry()` +/// * `swap_coins()` +/// * `swap_generic()` +/// +/// ## Change order size +/// +/// * `change_order_size_custodian()` +/// * `change_order_size_user()` +/// +/// ## Cancel orders +/// +/// * `cancel_order_custodian()` +/// * `cancel_order_user()` +/// * `cancel_all_orders_custodian()` +/// * `cancel_all_orders_user()` +/// +/// # Dependency charts +/// +/// The below dependency charts use `mermaid.js` syntax, which can be +/// automatically rendered into a diagram (depending on the browser) +/// when viewing the documentation file generated from source code. If +/// a browser renders the diagrams with coloring that makes it difficult +/// to read, try a different browser. +/// +/// ## Internal dependencies +/// +/// These charts describe dependencies between `market` functions. +/// +/// Market registration: +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// register_market_base_coin --> register_market +/// +/// register_market_base_generic --> register_market +/// +/// register_market_base_coin_from_coinstore --> +/// register_market_base_coin +/// +/// ``` +/// +/// Placing orders: +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// place_limit_order ---> match +/// +/// place_limit_order --> range_check_trade +/// +/// place_market_order ---> match +/// +/// place_market_order --> range_check_trade +/// +/// swap ---> match +/// +/// swap_between_coinstores ---> range_check_trade +/// +/// subgraph Swaps +/// +/// swap_between_coinstores_entry --> swap_between_coinstores +/// +/// swap_between_coinstores --> swap +/// +/// swap_coins --> swap +/// +/// swap_generic --> swap +/// +/// end +/// +/// swap_coins ---> range_check_trade +/// +/// swap_generic ---> range_check_trade +/// +/// place_limit_order_passive_advance --> place_limit_order +/// +/// subgraph Market orders +/// +/// place_market_order_user_entry --> place_market_order_user +/// +/// place_market_order_user --> place_market_order +/// +/// place_market_order_custodian --> place_market_order +/// +/// end +/// +/// subgraph Limit orders +/// +/// place_limit_order_user_entry --> place_limit_order_user +/// +/// place_limit_order_user --> place_limit_order +/// +/// place_limit_order_custodian --> place_limit_order +/// +/// end +/// +/// subgraph Passive advance limit orders +/// +/// place_limit_order_passive_advance_user_entry --> +/// place_limit_order_passive_advance_user +/// +/// place_limit_order_passive_advance_user --> +/// place_limit_order_passive_advance +/// +/// place_limit_order_passive_advance_custodian --> +/// place_limit_order_passive_advance +/// +/// end +/// +/// ``` +/// +/// Cancel reasons: +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// place_market_order --> +/// get_cancel_reason_option_for_market_order_or_swap +/// swap --> get_cancel_reason_option_for_market_order_or_swap +/// +/// ``` +/// +/// Changing order size: +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// change_order_size_custodian --> change_order_size +/// +/// change_order_size_user --> change_order_size +/// +/// ``` +/// +/// Cancelling orders: +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// cancel_all_orders_custodian --> cancel_all_orders +/// +/// cancel_order_custodian --> cancel_order +/// +/// cancel_all_orders_user --> cancel_all_orders +/// +/// cancel_order_user --> cancel_order +/// +/// cancel_all_orders --> cancel_order +/// +/// ``` +/// +/// View functions: +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// get_open_orders --> get_open_orders_for_side +/// get_open_orders_all --> get_open_orders +/// get_price_levels --> get_open_orders +/// get_price_levels --> get_price_levels_for_side +/// get_market_order_id_price --> did_order_post +/// get_price_levels_all --> get_price_levels +/// get_open_order --> has_open_order +/// get_open_order --> get_posted_order_id_side +/// get_open_order --> get_order_id_avl_queue_access_key +/// get_posted_order_id_side --> did_order_post +/// get_posted_order_id_side --> get_order_id_avl_queue_access_key +/// has_open_order --> get_posted_order_id_side +/// has_open_order --> get_order_id_avl_queue_access_key +/// get_open_orders_paginated --> get_open_orders_for_side_paginated +/// get_open_orders_paginated --> verify_pagination_order_ids +/// get_open_orders_for_side_paginated --> +/// get_order_id_avl_queue_access_key +/// get_price_levels_paginated --> get_price_levels_for_side_paginated +/// get_price_levels_paginated --> verify_pagination_order_ids +/// get_price_levels_for_side_paginated --> +/// get_order_id_avl_queue_access_key +/// verify_pagination_order_ids --> has_open_order +/// verify_pagination_order_ids --> get_posted_order_id_side +/// +/// ``` +/// +/// ## External module dependencies +/// +/// These charts describe `market` function dependencies on functions +/// from other Econia modules, other than `avl_queue` and `tablist`, +/// which are essentially data structure libraries. +/// +/// `incentives`: +/// +/// ``` mermaid +/// +/// flowchart LR +/// +/// register_market_base_coin_from_coinstore --> +/// incentives::get_market_registration_fee +/// +/// register_market --> incentives::register_econia_fee_store_entry +/// +/// match --> incentives::get_taker_fee_divisor +/// match --> incentives::calculate_max_quote_match +/// match --> incentives::assess_taker_fees +/// +/// ``` +/// +/// `registry`: +/// +/// ``` mermaid +/// +/// flowchart LR +/// +/// register_market_base_coin --> +/// registry::register_market_base_coin_internal +/// +/// register_market_base_generic --> +/// registry::register_market_base_generic_internal +/// register_market_base_generic --> +/// registry::get_underwriter_id +/// +/// place_limit_order_custodian --> registry::get_custodian_id +/// +/// place_market_order_custodian --> registry::get_custodian_id +/// +/// swap_generic --> registry::get_underwriter_id +/// +/// change_order_size_custodian --> registry::get_custodian_id +/// +/// cancel_order_custodian --> registry::get_custodian_id +/// +/// cancel_all_orders_custodian --> registry::get_custodian_id +/// +/// ``` +/// +/// `resource_account`: +/// +/// ``` mermaid +/// +/// flowchart LR +/// +/// init_module --> resource_account::get_signer +/// +/// register_market --> resource_account::get_signer +/// +/// place_limit_order --> resource_account::get_address +/// +/// place_market_order --> resource_account::get_address +/// +/// swap --> resource_account::get_address +/// swap --> resource_account::get_signer +/// +/// change_order_size --> resource_account::get_address +/// +/// cancel_order --> resource_account::get_address +/// +/// get_open_order --> resource_account::get_address +/// +/// get_open_orders --> resource_account::get_address +/// +/// has_open_order --> resource_account::get_address +/// +/// get_price_levels --> resource_account::get_address +/// +/// get_market_event_handle_creation_info --> +/// resource_account::get_address +/// +/// get_open_orders_paginated --> resource_account::get_address +/// +/// get_price_levels_paginated --> resource_account::get_address +/// +/// ``` +/// +/// `user`: +/// +/// ``` mermaid +/// +/// flowchart LR +/// +/// place_limit_order --> user::get_asset_counts_internal +/// place_limit_order --> user::withdraw_assets_internal +/// place_limit_order --> user::deposit_assets_internal +/// place_limit_order --> user::get_next_order_access_key_internal +/// place_limit_order --> user::place_order_internal +/// place_limit_order --> user::cancel_order_internal +/// place_limit_order --> user::emit_limit_order_events_internal +/// +/// place_market_order --> user::get_asset_counts_internal +/// place_market_order --> user::withdraw_assets_internal +/// place_market_order --> user::deposit_assets_internal +/// place_market_order --> user::emit_market_order_events_internal +/// +/// match --> user::fill_order_internal +/// match --> user::create_fill_event_internal +/// +/// change_order_size --> user::change_order_size_internal +/// +/// cancel_order --> user::cancel_order_internal +/// +/// cancel_all_orders --> user::get_active_market_order_ids_internal +/// +/// has_open_order --> user::get_open_order_id_internal +/// +/// get_open_orders_for_side --> user::get_open_order_id_internal +/// +/// swap --> user::create_cancel_order_event_internal +/// swap --> user::emit_swap_maker_fill_events_internal +/// +/// get_open_orders_for_side_paginated --> +/// user::get_open_order_id_internal +/// +/// get_price_levels_for_side_paginated --> +/// user::get_open_order_id_internal +/// +/// ``` +/// +/// # Order management testing +/// +/// While market registration functions can be simply verified with +/// straightforward tests, order management functions are more +/// comprehensively tested through integrated tests that verify multiple +/// logical branches, returns, and state updates. Aborts are tested +/// individually for each function. +/// +/// ## Functions with aborts +/// +/// Function aborts to test: +/// +/// * [x] `cancel_order()` +/// * [x] `change_order_size()` +/// * [x] `match()` +/// * [x] `place_limit_order()` +/// * [x] `place_limit_order_passive_advance()` +/// * [x] `place_market_order()` +/// * [x] `range_check_trade()` +/// * [x] `swap()` +/// +/// ## Return proxies +/// +/// Various order management functions have returns, and verifying the +/// returns of some functions verifies the returns of associated inner +/// functions. For example, the collective verification of the returns +/// of `swap_coins()` and `swap_generic()` verifies the returns of both +/// `swap()` and `match()`, such that the combination of `swap_coins()` +/// and `swap_generic()` can be considered a "return proxy" of both +/// `swap()` and of `match()`. Hence the most efficient test suite +/// involves return verification for the minimal return proxy set: +/// +/// | Function | Return proxy | +/// |----------------------------------|-----------------------------| +/// | `match()` | `swap_coins()`, `swap_generic()` | +/// | `place_limit_order()` | `place_limit_order_user()` | +/// | `place_limit_order_custodian()` | None | +/// | `place_limit_order_user()` | None | +/// | `place_market_order()` | `place_market_order_user()` | +/// | `place_market_order_custodian()` | None | +/// | `place_market_order_user()` | None | +/// | `swap()` | `swap_coins()`, `swap_generic()` | +/// | `swap_between_coinstores()` | None | +/// | `swap_coins()` | None | +/// | `swap_generic()` | None | +/// +/// Passive advance limit order functions do not fit in the above table +/// without excessive line length, and are thus presented here: +/// +/// * Function `place_limit_order_passive_advance()` has return proxy +/// `place_limit_order_passive_advance_user()`. +/// * Function `place_limit_order_passive_advance_user()` has no return +/// proxy. +/// * Function `place_limit_order_passive_advance_custodian()` has no +/// return proxy. +/// +/// Function returns to test: +/// +/// * [x] `place_limit_order_custodian()` +/// * [x] `place_limit_order_passive_advance_custodian()` +/// * [x] `place_limit_order_passive_advance_user()` +/// * [x] `place_limit_order_user()` +/// * [x] `place_market_order_custodian()` +/// * [x] `place_market_order_user()` +/// * [x] `swap_between_coinstores()` +/// * [x] `swap_coins()` +/// * [x] `swap_generic()` +/// +/// ## Invocation proxies +/// +/// Similarly, verifying the invocation of some functions verifies the +/// invocation of associated inner functions. For example, +/// `cancel_all_orders_user()` can be considered an invocation proxy +/// of `cancel_all_orders()` and of `cancel_order()`. Here, to provide +/// 100% invocation coverage, only functions at the top of the +/// dependency stack must be verified. +/// +/// Function invocations to test: +/// +/// * [x] `cancel_all_orders_custodian()` +/// * [x] `cancel_all_orders_user()` +/// * [x] `cancel_order_custodian()` +/// * [x] `cancel_order_user()` +/// * [x] `change_order_size_custodian()` +/// * [x] `change_order_size_user()` +/// * [x] `place_limit_order_user_entry()` +/// * [x] `place_limit_order_custodian()` +/// * [x] `place_limit_order_passive_advance_custodian()` +/// * [x] `place_limit_order_passive_advance_user_entry()` +/// * [x] `place_market_order_user_entry()` +/// * [x] `place_market_order_custodian()` +/// * [x] `swap_between_coinstores_entry()` +/// * [x] `swap_coins()` +/// * [x] `swap_generic()` +/// +/// ## Branching functions +/// +/// Functions with logical branches to test: +/// +/// * [x] `cancel_all_orders()` +/// * [x] `cancel_order()` +/// * [x] `change_order_size()` +/// * [x] `match()` +/// * [x] `place_limit_order()` +/// * [x] `place_limit_order_passive_advance()` +/// * [x] `place_market_order()` +/// * [x] `range_check_trade()` +/// * [x] `swap_between_coinstores()` +/// * [x] `swap_coins()` +/// * [x] `swap_generic()` +/// * [x] `swap()` +/// +/// See each function for its logical branches. +/// +/// # Complete DocGen index +/// +/// The below index is automatically generated from source code: +module econia::market { + + // Uses >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + use aptos_framework::account; + use aptos_framework::coin::{Self, Coin}; + use aptos_framework::event::{Self, EventHandle}; + use aptos_framework::guid; + use aptos_framework::table::{Self, Table}; + use aptos_framework::type_info::{Self, TypeInfo}; + use econia::avl_queue::{Self, AVLqueue}; + use econia::incentives; + use econia::registry::{ + Self, CustodianCapability, GenericAsset, UnderwriterCapability}; + use econia::resource_account; + use econia::tablist::{Self, Tablist}; + use econia::user::{Self, CancelOrderEvent, FillEvent}; + use std::option::{Self, Option}; + use std::signer::address_of; + use std::string::{Self, String}; + use std::vector; + + // Uses <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Test-only uses >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test_only] + use econia::assets::{Self, BC, QC, UC}; + + // Test-only uses <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Structs >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// View function return for getting event handle creation info of a + /// particular `MarketEventHandlesForMarket`. + struct MarketEventHandleCreationInfo has copy, drop { + /// Econia resource account address, corresponding to event + /// handle creator address. + resource_account_address: address, + /// Creation number of `cancel_order_events` handle in a + /// `MarketEventHandlesForMarket`. + cancel_order_events_handle_creation_num: u64, + /// Creation number of `place_swap_order_events` handle in a + /// `MarketEventHandlesForMarket`. + place_swap_order_events_handle_creation_num: u64 + } + + /// All of the Econia resource account's + /// `MarketEventHandlesForMarket`. + struct MarketEventHandles has key { + /// Map from market ID to `MarketEventHandlesForMarket`. + map: Table + } + + /// Within a given market, event handles for market events that are + /// not emitted elsewhere when associated with a swap order placed + /// by a non-signing swapper. + struct MarketEventHandlesForMarket has store { + /// Event handle for `user::CancelOrderEvent`s. + cancel_order_events: EventHandle, + /// Event handle for `PlaceSwapOrderEvent`s. + place_swap_order_events: EventHandle + } + + /// An order on the order book. + struct Order has store { + /// Number of lots to be filled. + size: u64, + /// Order price, in ticks per lot. + price: u64, + /// Address of user holding order. + user: address, + /// For given user, ID of custodian required to approve order + /// operations and withdrawals on given market account. + custodian_id: u64, + /// User-side access key for storage-optimized lookup. + order_access_key: u64 + } + + /// An order book for a given market. Contains + /// `registry::MarketInfo` field duplicates to reduce global storage + /// item queries against the registry. + struct OrderBook has store { + /// `registry::MarketInfo.base_type`. + base_type: TypeInfo, + /// `registry::MarketInfo.base_name_generic`. + base_name_generic: String, + /// `registry::MarketInfo.quote_type`. + quote_type: TypeInfo, + /// `registry::MarketInfo.lot_size`. + lot_size: u64, + /// `registry::MarketInfo.tick_size`. + tick_size: u64, + /// `registry::MarketInfo.min_size`. + min_size: u64, + /// `registry::MarketInfo.underwriter_id`. + underwriter_id: u64, + /// Asks AVL queue. + asks: AVLqueue, + /// Bids AVL queue. + bids: AVLqueue, + /// Cumulative number of orders placed. + counter: u64, + /// Deprecated field retained for compatible upgrade policy. + maker_events: EventHandle, + /// Deprecated field retained for compatible upgrade policy. + taker_events: EventHandle + } + + /// Order book map for all Econia order books. + struct OrderBooks has key { + /// Map from market ID to corresponding order book. Enables + /// off-chain iterated indexing by market ID. + map: Tablist + } + + /// User-friendly representation of an open order on the order book. + struct OrderView has copy, drop { + /// Market ID for open order. + market_id: u64, + /// `ASK` or `BID`. + side: bool, + /// The order ID for the posted order. + order_id: u128, + /// Remaining number of lots to be filled. + remaining_size: u64, + /// Order price, in ticks per lot. + price: u64, + /// Address of user holding order. + user: address, + /// For given user, ID of custodian required to approve order + /// operations and withdrawals on given market account. + custodian_id: u64 + } + + /// `OrderView` instances from an `OrderBook`, indexed by side and + /// sorted by price-time priority. + struct OrdersView has copy, drop { + /// Asks sorted by price-time priority: oldest order at lowest + /// price first in vector. + asks: vector, + /// Bids sorted by price-time priority: oldest order at highest + /// price first in vector. + bids: vector + } + + /// Emitted when a swap order is placed. + struct PlaceSwapOrderEvent has copy, drop, store { + /// Market ID for order. + market_id: u64, + /// Signing account if swap is placed by a signing swapper, else + /// `NO_TAKER_ADDRESS`. + signing_account: address, + /// Integrator address passed during swap order placement, + /// eligible for a portion of any generated taker fees. + integrator: address, + /// Either `BUY` or `SELL`. + direction: bool, + /// Indicated minimum base subunits to trade. + min_base: u64, + /// Indicated maximum base subunits to trade. + max_base: u64, + /// Indicated minimum quote subunits to trade. + min_quote: u64, + /// Indicated maximum quote subunits to trade. + max_quote: u64, + /// Indicated limit price. + limit_price: u64, + /// Unique ID for order within market. + order_id: u128 + } + + /// A price level from an `OrderBook`. + struct PriceLevel has copy, drop { + /// Price, in ticks per lot. + price: u64, + /// Cumulative size of open orders at price level, in lots. + size: u128 + } + + /// `PriceLevel` instances from an `OrderBook`, indexed by side and + /// sorted by price-time priority. + struct PriceLevels has copy, drop { + /// Market ID of corresponding market. + market_id: u64, + /// Ask price levels sorted by price-time priority: lowest price + /// level first in vector. + asks: vector, + /// Ask price levels sorted by price-time priority: highest + /// price level first in vector. + bids: vector + } + + /// View function return for getting event handle creation numbers + /// for a signing swapper's `SwapperEventHandlesForMarket`. + struct SwapperEventHandleCreationNumbers has copy, drop { + /// Creation number of `cancel_order_events` handle in a + /// `SwapperEventHandlesForMarket`. + cancel_order_events_handle_creation_num: u64, + /// Creation number of `fill_events` handle in a + /// `SwapperEventHandlesForMarket`. + fill_events_handle_creation_num: u64, + /// Creation number of `place_swap_order_events` handle in a + /// `SwapperEventHandlesForMarket`. + place_swap_order_events_handle_creation_num: u64 + } + + /// All of a signing swapper's `SwapperEventHandlesForMarket`. + struct SwapperEventHandles has key { + /// Map from market ID to `SwapperEventHandlesForMarket`. + map: Table + } + + /// Event handles for market events associated with a signing + /// swapper on a particular market. Stored under a signing swapper's + /// account (not market account), since swaps are processed outside + /// of an Econia-style market account. + struct SwapperEventHandlesForMarket has store { + /// Event handle for `user::CancelOrderEvent`s. + cancel_order_events: EventHandle, + /// Event handle for `user::FillEvent`s. + fill_events: EventHandle, + /// Event handle for `PlaceSwapOrderEvent`s. + place_swap_order_events: EventHandle + } + + // Structs <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Error codes >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Maximum base trade amount specified as 0. + const E_MAX_BASE_0: u64 = 0; + /// Maximum quote trade amount specified as 0. + const E_MAX_QUOTE_0: u64 = 1; + /// Minimum base trade amount exceeds maximum base trade amount. + const E_MIN_BASE_EXCEEDS_MAX: u64 = 2; + /// Minimum quote trade amount exceeds maximum quote trade amount. + const E_MIN_QUOTE_EXCEEDS_MAX: u64 = 3; + /// Filling order would overflow asset received from trade. + const E_OVERFLOW_ASSET_IN: u64 = 4; + /// Not enough asset to trade away. + const E_NOT_ENOUGH_ASSET_OUT: u64 = 5; + /// No market with given ID. + const E_INVALID_MARKET_ID: u64 = 6; + /// Base asset type is invalid. + const E_INVALID_BASE: u64 = 7; + /// Quote asset type is invalid. + const E_INVALID_QUOTE: u64 = 8; + /// Minimum base asset trade amount requirement not met. + const E_MIN_BASE_NOT_TRADED: u64 = 9; + /// Minimum quote coin trade amount requirement not met. + const E_MIN_QUOTE_NOT_TRADED: u64 = 10; + /// Order price specified as 0. + const E_PRICE_0: u64 = 11; + /// Order price exceeds maximum allowable price. + const E_PRICE_TOO_HIGH: u64 = 12; + /// Post-or-abort limit order price crosses spread. + const E_POST_OR_ABORT_CROSSES_SPREAD: u64 = 13; + /// Order size does not meet minimum size for market. + const E_SIZE_TOO_SMALL: u64 = 14; + /// Limit order size results in base asset amount overflow. + const E_SIZE_BASE_OVERFLOW: u64 = 15; + /// Limit order size and price results in ticks amount overflow. + const E_SIZE_PRICE_TICKS_OVERFLOW: u64 = 16; + /// Limit order size and price results in quote amount overflow. + const E_SIZE_PRICE_QUOTE_OVERFLOW: u64 = 17; + /// Invalid restriction flag. + const E_INVALID_RESTRICTION: u64 = 18; + /// A self match occurs when self match behavior is abort. + const E_SELF_MATCH: u64 = 19; + /// No room to insert order with such low price-time priority. + const E_PRICE_TIME_PRIORITY_TOO_LOW: u64 = 20; + /// Underwriter invalid for given market. + const E_INVALID_UNDERWRITER: u64 = 21; + /// Market order ID invalid. + const E_INVALID_MARKET_ORDER_ID: u64 = 22; + /// Custodian not authorized for operation. + const E_INVALID_CUSTODIAN: u64 = 23; + /// Invalid user indicated for operation. + const E_INVALID_USER: u64 = 24; + /// Fill-or-abort price does not cross the spread. + const E_FILL_OR_ABORT_NOT_CROSS_SPREAD: u64 = 25; + /// AVL queue head price does not match head order price. + const E_HEAD_KEY_PRICE_MISMATCH: u64 = 26; + /// Simulation query called by invalid account. + const E_NOT_SIMULATION_ACCOUNT: u64 = 27; + /// Invalid self match behavior flag. + const E_INVALID_SELF_MATCH_BEHAVIOR: u64 = 28; + /// Passive advance percent is not less than or equal to 100. + const E_INVALID_PERCENT: u64 = 29; + /// Order size change requiring insertion resulted in an AVL queue + /// access key mismatch. + const E_SIZE_CHANGE_INSERTION_ERROR: u64 = 30; + /// Order ID corresponds to an order that did not post. + const E_ORDER_DID_NOT_POST: u64 = 31; + /// Order price field does not match AVL queue insertion key price. + const E_ORDER_PRICE_MISMATCH: u64 = 32; + /// New order size is less than the minimum order size for market. + const E_SIZE_CHANGE_BELOW_MIN_SIZE: u64 = 33; + + // Error codes <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Constants >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Ascending AVL queue flag, for asks AVL queue. + const ASCENDING: bool = true; + /// Flag to abort during a self match. + const ABORT: u8 = 0; + /// Flag for ask side. + const ASK: bool = true; + /// Flag for bid side. + const BID: bool = false; + /// Flag for buy direction. + const BUY: bool = false; + /// Flag to cancel maker and taker order during a self match. + const CANCEL_BOTH: u8 = 1; + /// Flag to cancel maker order only during a self match. + const CANCEL_MAKER: u8 = 2; + /// Order cancelled because it was evicted from the price-time + /// priority queue. + const CANCEL_REASON_EVICTION: u8 = 1; + /// Order cancelled because it was an immediate-or-cancel order + /// that did not immediately fill. + const CANCEL_REASON_IMMEDIATE_OR_CANCEL: u8 = 2; + /// Order cancelled because it was manually cancelled by either + /// signing user or custodian. + const CANCEL_REASON_MANUAL_CANCEL: u8 = 3; + /// Order cancelled because no more quote asset could be traded. + const CANCEL_REASON_MAX_QUOTE_TRADED: u8 = 4; + /// Order cancelled because there was not enough liquidity to take + /// from. + const CANCEL_REASON_NOT_ENOUGH_LIQUIDITY: u8 = 5; + /// Order cancelled because it was on the maker side of an fill + /// where self match behavior indicated cancelling the maker order. + const CANCEL_REASON_SELF_MATCH_MAKER: u8 = 6; + /// Order cancelled because it was on the taker side of an fill + /// where self match behavior indicated cancelling the taker order. + const CANCEL_REASON_SELF_MATCH_TAKER: u8 = 7; + /// Swap order cancelled because the remaining base asset amount to + /// match was too small to fill a single lot. + const CANCEL_REASON_TOO_SMALL_TO_FILL_LOT: u8 = 8; + /// Swap order cancelled because the next order on the book to match + /// against violated the swap order limit price. + const CANCEL_REASON_VIOLATED_LIMIT_PRICE: u8 = 9; + /// Flag to cancel taker order only during a self match. + const CANCEL_TAKER: u8 = 3; + /// Critical tree height above which evictions may take place. + const CRITICAL_HEIGHT: u8 = 18; + /// Descending AVL queue flag, for bids AVL queue. + const DESCENDING: bool = false; + /// Flag for fill-or-abort order restriction. + const FILL_OR_ABORT: u8 = 1; + /// `u64` bitmask with all bits set, generated in Python via + /// `hex(int('1' * 64, 2))`. + const HI_64: u64 = 0xffffffffffffffff; + /// All bits set in integer of width required to encode price. + /// Generated in Python via `hex(int('1' * 32, 2))`. + const HI_PRICE: u64 = 0xffffffff; + /// Flag for immediate-or-cancel order restriction. + const IMMEDIATE_OR_CANCEL: u8 = 2; + /// Flag to trade max possible asset amount: `u64` bitmask with all + /// bits set, generated in Python via `hex(int('1' * 64, 2))`. + const MAX_POSSIBLE: u64 = 0xffffffffffffffff; + /// Number of restriction flags. + const N_RESTRICTIONS: u8 = 3; + /// Flag for null value when null defined as 0. + const NIL: u64 = 0; + /// Custodian ID flag for no custodian. + const NO_CUSTODIAN: u64 = 0; + /// Flag for no order restriction. + const NO_RESTRICTION: u8 = 0; + /// Taker address flag for when taker order does not originate from + /// a market account or a signing swapper. + const NO_TAKER_ADDRESS: address = @0x0; + /// Underwriter ID flag for no underwriter. + const NO_UNDERWRITER: u64 = 0; + /// Flag for passive order specified by percent advance. + const PERCENT: bool = true; + /// Maximum percentage passive advance. + const PERCENT_100: u64 = 100; + /// Flag for post-or-abort order restriction. + const POST_OR_ABORT: u8 = 3; + /// Flag for sell direction. + const SELL: bool = true; + /// Number of bits order counter is shifted in an order ID. + const SHIFT_COUNTER: u8 = 64; + /// Flag for passive order specified by advance in ticks. + const TICKS: bool = false; + + // Constants <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // View functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + + #[view] + /// Public constant getter for `ABORT`. + /// + /// # Testing + /// + /// * `test_get_ABORT()` + public fun get_ABORT(): u8 {ABORT} + + #[view] + /// Public constant getter for `ASK`. + /// + /// # Testing + /// + /// * `test_direction_side_polarities()` + /// * `test_get_ASK()` + public fun get_ASK(): bool {ASK} + + #[view] + /// Public constant getter for `BID`. + /// + /// # Testing + /// + /// * `test_direction_side_polarities()` + /// * `test_get_BID()` + public fun get_BID(): bool {BID} + + #[view] + /// Public constant getter for `BUY`. + /// + /// # Testing + /// + /// * `test_direction_side_polarities()` + /// * `test_get_BUY()` + public fun get_BUY(): bool {BUY} + + #[view] + /// Public constant getter for `CANCEL_BOTH`. + /// + /// # Testing + /// + /// * `test_get_CANCEL_BOTH()` + public fun get_CANCEL_BOTH(): u8 {CANCEL_BOTH} + + #[view] + /// Public constant getter for `CANCEL_MAKER`. + /// + /// # Testing + /// + /// * `test_get_CANCEL_MAKER()` + public fun get_CANCEL_MAKER(): u8 {CANCEL_MAKER} + + #[view] + /// Public constant getter for `CANCEL_TAKER`. + /// + /// # Testing + /// + /// * `test_get_CANCEL_TAKER()` + public fun get_CANCEL_TAKER(): u8 {CANCEL_TAKER} + + #[view] + /// Public constant getter for `FILL_OR_ABORT`. + /// + /// # Testing + /// + /// * `test_get_FILL_OR_ABORT()` + public fun get_FILL_OR_ABORT(): u8 {FILL_OR_ABORT} + + #[view] + /// Public constant getter for `HI_PRICE`. + /// + /// # Testing + /// + /// * `test_get_HI_PRICE()` + public fun get_HI_PRICE(): u64 {HI_PRICE} + + #[view] + /// Public constant getter for `IMMEDIATE_OR_CANCEL`. + /// + /// # Testing + /// + /// * `test_get_IMMEDIATE_OR_CANCEL()` + public fun get_IMMEDIATE_OR_CANCEL(): u8 {IMMEDIATE_OR_CANCEL} + + #[view] + /// Public constant getter for `MAX_POSSIBLE`. + /// + /// # Testing + /// + /// * `test_get_MAX_POSSIBLE()` + public fun get_MAX_POSSIBLE(): u64 {MAX_POSSIBLE} + + #[view] + /// Public constant getter for `NO_CUSTODIAN`. + /// + /// # Testing + /// + /// * `test_get_NO_CUSTODIAN()` + public fun get_NO_CUSTODIAN(): u64 {NO_CUSTODIAN} + + #[view] + /// Public constant getter for `NO_RESTRICTION`. + /// + /// # Testing + /// + /// * `test_get_NO_RESTRICTION()` + public fun get_NO_RESTRICTION(): u8 {NO_RESTRICTION} + + #[view] + /// Public constant getter for `NO_UNDERWRITER`. + /// + /// # Testing + /// + /// * `test_get_NO_UNDERWRITER()` + public fun get_NO_UNDERWRITER(): u64 {NO_UNDERWRITER} + + #[view] + /// Public constant getter for `POST_OR_ABORT`. + /// + /// # Testing + /// + /// * `test_get_POST_OR_ABORT()` + public fun get_POST_OR_ABORT(): u8 {POST_OR_ABORT} + + #[view] + /// Public constant getter for `PERCENT`. + /// + /// # Testing + /// + /// * `test_get_PERCENT()` + public fun get_PERCENT(): bool {PERCENT} + + #[view] + /// Public constant getter for `SELL`. + /// + /// # Testing + /// + /// * `test_direction_side_polarities()` + /// * `test_get_SELL()` + public fun get_SELL(): bool {SELL} + + #[view] + /// Public constant getter for `TICKS`. + /// + /// # Testing + /// + /// * `test_get_TICKS()` + public fun get_TICKS(): bool {TICKS} + + #[view] + /// Return a `MarketEventHandleCreationInfo` for `market_id`, if + /// Econia resource account has event handles for indicated market. + /// + /// Restricted to private view function to prevent runtime handle + /// contention. + /// + /// # Testing + /// + /// * `test_swap_between_coinstores_register_base_store()` + fun get_market_event_handle_creation_info( + market_id: u64 + ): Option + acquires MarketEventHandles { + // Return none if Econia resource account does not have market + // event handles map. + let resource_account_address = resource_account::get_address(); + if (!exists(resource_account_address)) + return option::none(); + // Return none if no handles exist for market. + let market_event_handles_map_ref = + &borrow_global(resource_account_address).map; + let has_handles = table::contains( + market_event_handles_map_ref, market_id); + if (!has_handles) return option::none(); + let market_handles_ref = table::borrow( + market_event_handles_map_ref, market_id); + // Return option-packed creation info for market. + option::some(MarketEventHandleCreationInfo{ + resource_account_address: resource_account_address, + cancel_order_events_handle_creation_num: + guid::creation_num(event::guid( + &market_handles_ref.cancel_order_events)), + place_swap_order_events_handle_creation_num: + guid::creation_num(event::guid( + &market_handles_ref.place_swap_order_events)) + }) + } + + // View functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Public functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Public function wrapper for `cancel_all_orders()` for cancelling + /// orders under authority of delegated custodian. + /// + /// # Invocation testing + /// + /// * `test_cancel_all_orders_ask_custodian()` + public fun cancel_all_orders_custodian( + user_address: address, + market_id: u64, + side: bool, + custodian_capability_ref: &CustodianCapability + ) acquires OrderBooks { + cancel_all_orders( + user_address, + market_id, + registry::get_custodian_id(custodian_capability_ref), + side); + } + + /// Public function wrapper for `cancel_order()` for cancelling + /// order under authority of delegated custodian. + /// + /// # Invocation testing + /// + /// * `test_cancel_order_ask_custodian()` + public fun cancel_order_custodian( + user_address: address, + market_id: u64, + side: bool, + market_order_id: u128, + custodian_capability_ref: &CustodianCapability + ) acquires OrderBooks { + cancel_order( + user_address, + market_id, + registry::get_custodian_id(custodian_capability_ref), + side, + market_order_id); + } + + /// Public function wrapper for `change_order_size()` for changing + /// order size under authority of delegated custodian. + /// + /// # Invocation testing + /// + /// * `test_change_order_size_ask_custodian()` + public fun change_order_size_custodian( + user_address: address, + market_id: u64, + side: bool, + market_order_id: u128, + new_size: u64, + custodian_capability_ref: &CustodianCapability + ) acquires OrderBooks { + change_order_size( + user_address, + market_id, + registry::get_custodian_id(custodian_capability_ref), + side, + market_order_id, + new_size); + } + + /// Public function wrapper for `place_limit_order()` for placing + /// order under authority of delegated custodian. + /// + /// # Invocation and return testing + /// + /// * `test_place_limit_order_no_cross_bid_custodian()` + public fun place_limit_order_custodian< + BaseType, + QuoteType + >( + user_address: address, + market_id: u64, + integrator: address, + side: bool, + size: u64, + price: u64, + restriction: u8, + self_match_behavior: u8, + custodian_capability_ref: &CustodianCapability + ): ( + u128, + u64, + u64, + u64 + ) acquires OrderBooks { + place_limit_order< + BaseType, + QuoteType + >( + user_address, + market_id, + registry::get_custodian_id(custodian_capability_ref), + integrator, + side, + size, + price, + restriction, + self_match_behavior, + CRITICAL_HEIGHT) + } + + /// Public function wrapper for + /// `place_limit_order_passive_advance()` for placing order under + /// authority of delegated custodian. + /// + /// # Invocation and return testing + /// + /// * `test_place_limit_order_passive_advance_ticks_bid()` + public fun place_limit_order_passive_advance_custodian< + BaseType, + QuoteType + >( + user_address: address, + market_id: u64, + integrator: address, + side: bool, + size: u64, + advance_style: bool, + target_advance_amount: u64, + custodian_capability_ref: &CustodianCapability + ): u128 + acquires OrderBooks { + place_limit_order_passive_advance< + BaseType, + QuoteType + >( + user_address, + market_id, + registry::get_custodian_id(custodian_capability_ref), + integrator, + side, + size, + advance_style, + target_advance_amount) + } + + /// Public function wrapper for + /// `place_limit_order_passive_advance()` for placing order under + /// authority of signing user. + /// + /// # Invocation and return testing + /// + /// * `test_place_limit_order_passive_advance_no_cross_price_ask()` + /// * `test_place_limit_order_passive_advance_no_cross_price_bid()` + /// * `test_place_limit_order_passive_advance_no_full_advance()` + /// * `test_place_limit_order_passive_advance_no_start_price()`. + /// * `test_place_limit_order_passive_advance_no_target_advance()` + /// * `test_place_limit_order_passive_advance_percent_ask()` + /// * `test_place_limit_order_passive_advance_percent_bid()` + /// * `test_place_limit_order_passive_advance_ticks_ask()` + public fun place_limit_order_passive_advance_user< + BaseType, + QuoteType + >( + user: &signer, + market_id: u64, + integrator: address, + side: bool, + size: u64, + advance_style: bool, + target_advance_amount: u64 + ): u128 + acquires OrderBooks { + place_limit_order_passive_advance< + BaseType, + QuoteType + >( + address_of(user), + market_id, + NO_CUSTODIAN, + integrator, + side, + size, + advance_style, + target_advance_amount) + } + + /// Public function wrapper for `place_limit_order()` for placing + /// order under authority of signing user. + /// + /// # Invocation and return testing + /// + /// * `test_place_limit_order_crosses_ask_exact()` + /// * `test_place_limit_order_crosses_ask_partial()` + /// * `test_place_limit_order_crosses_ask_partial_cancel()` + /// * `test_place_limit_order_crosses_ask_self_match_cancel()` + /// * `test_place_limit_order_crosses_bid_exact()` + /// * `test_place_limit_order_crosses_bid_partial()` + /// * `test_place_limit_order_evict()` + /// * `test_place_limit_order_no_cross_ask_user()` + public fun place_limit_order_user< + BaseType, + QuoteType + >( + user: &signer, + market_id: u64, + integrator: address, + side: bool, + size: u64, + price: u64, + restriction: u8, + self_match_behavior: u8 + ): ( + u128, + u64, + u64, + u64 + ) acquires OrderBooks { + place_limit_order< + BaseType, + QuoteType + >( + address_of(user), + market_id, + NO_CUSTODIAN, + integrator, + side, + size, + price, + restriction, + self_match_behavior, + CRITICAL_HEIGHT) + } + + /// Public function wrapper for `place_market_order()` for placing + /// order under authority of delegated custodian. + /// + /// # Invocation and return testing + /// + /// * `test_place_market_order_max_base_sell_custodian()` + /// * `test_place_market_order_max_quote_buy_custodian()` + public fun place_market_order_custodian< + BaseType, + QuoteType + >( + user_address: address, + market_id: u64, + integrator: address, + direction: bool, + size: u64, + self_match_behavior: u8, + custodian_capability_ref: &CustodianCapability + ): ( + u64, + u64, + u64 + ) acquires OrderBooks { + place_market_order( + user_address, + market_id, + registry::get_custodian_id(custodian_capability_ref), + integrator, + direction, + size, + self_match_behavior) + } + + /// Public function wrapper for `place_market_order()` for placing + /// order under authority of signing user. + /// + /// # Invocation and return testing + /// + /// * `test_place_market_order_max_base_buy_user()` + /// * `test_place_market_order_max_quote_sell_user()` + public fun place_market_order_user< + BaseType, + QuoteType + >( + user: &signer, + market_id: u64, + integrator: address, + direction: bool, + size: u64, + self_match_behavior: u8 + ): ( + u64, + u64, + u64 + ) acquires OrderBooks { + place_market_order( + address_of(user), + market_id, + NO_CUSTODIAN, + integrator, + direction, + size, + self_match_behavior) + } + + /// Register pure coin market, return resultant market ID. + /// + /// See inner function `register_market()`. + /// + /// # Type parameters + /// + /// * `BaseType`: Base coin type for market. + /// * `QuoteType`: Quote coin type for market. + /// * `UtilityType`: Utility coin type, specified at + /// `incentives::IncentiveParameters.utility_coin_type_info`. + /// + /// # Parameters + /// + /// * `lot_size`: `registry::MarketInfo.lot_size` for market. + /// * `tick_size`: `registry::MarketInfo.tick_size` for market. + /// * `min_size`: `registry::MarketInfo.min_size` for market. + /// * `utility_coins`: Utility coins paid to register a market. See + /// `incentives::IncentiveParameters.market_registration_fee`. + /// + /// # Returns + /// + /// * `u64`: Market ID for new market. + /// + /// # Testing + /// + /// * `test_register_markets()` + public fun register_market_base_coin< + BaseType, + QuoteType, + UtilityType + >( + lot_size: u64, + tick_size: u64, + min_size: u64, + utility_coins: Coin + ): u64 + acquires OrderBooks { + // Register market in global registry, storing market ID. + let market_id = registry::register_market_base_coin_internal< + BaseType, QuoteType, UtilityType>(lot_size, tick_size, min_size, + utility_coins); + // Register order book and quote coin fee store, return market + // ID. + register_market( + market_id, string::utf8(b""), lot_size, tick_size, min_size, + NO_UNDERWRITER) + } + + /// Register generic market, return resultant market ID. + /// + /// See inner function `register_market()`. + /// + /// Generic base name restrictions described at + /// `registry::register_market_base_generic_internal()`. + /// + /// # Type parameters + /// + /// * `QuoteType`: Quote coin type for market. + /// * `UtilityType`: Utility coin type, specified at + /// `incentives::IncentiveParameters.utility_coin_type_info`. + /// + /// # Parameters + /// + /// * `base_name_generic`: `registry::MarketInfo.base_name_generic` + /// for market. + /// * `lot_size`: `registry::MarketInfo.lot_size` for market. + /// * `tick_size`: `registry::MarketInfo.tick_size` for market. + /// * `min_size`: `registry::MarketInfo.min_size` for market. + /// * `utility_coins`: Utility coins paid to register a market. See + /// `incentives::IncentiveParameters.market_registration_fee`. + /// * `underwriter_capability_ref`: Immutable reference to market + /// underwriter capability. + /// + /// # Returns + /// + /// * `u64`: Market ID for new market. + /// + /// # Testing + /// + /// * `test_register_markets()` + public fun register_market_base_generic< + QuoteType, + UtilityType + >( + base_name_generic: String, + lot_size: u64, + tick_size: u64, + min_size: u64, + utility_coins: Coin, + underwriter_capability_ref: &UnderwriterCapability + ): u64 + acquires OrderBooks { + // Register market in global registry, storing market ID. + let market_id = registry::register_market_base_generic_internal< + QuoteType, UtilityType>(base_name_generic, lot_size, tick_size, + min_size, underwriter_capability_ref, utility_coins); + // Register order book and quote coin fee store, return market + // ID. + register_market( + market_id, base_name_generic, lot_size, tick_size, min_size, + registry::get_underwriter_id(underwriter_capability_ref)) + } + + /// Swap against the order book between a user's coin stores. + /// + /// Initializes an `aptos_framework::coin::CoinStore` for each coin + /// type that does not yet have one. + /// + /// # Type Parameters + /// + /// * `BaseType`: Same as for `match()`. + /// * `QuoteType`: Same as for `match()`. + /// + /// # Parameters + /// + /// * `user`: Account of swapping user. + /// * `market_id`: Same as for `match()`. + /// * `integrator`: Same as for `match()`. + /// * `direction`: Same as for `match()`. + /// * `min_base`: Same as for `match()`. + /// * `max_base`: Same as for `match()`. If passed as `MAX_POSSIBLE` + /// will attempt to trade maximum possible amount for coin store. + /// * `min_quote`: Same as for `match()`. + /// * `max_quote`: Same as for `match()`. If passed as + /// `MAX_POSSIBLE` will attempt to trade maximum possible amount + /// for coin store. + /// * `limit_price`: Same as for `match()`. + /// + /// # Returns + /// + /// * `u64`: Base asset trade amount, same as for `match()`. + /// * `u64`: Quote coin trade amount, same as for `match()`. + /// * `u64`: Quote coin fees paid, same as for `match()`. + /// + /// # Emits + /// + /// * `PlaceSwapOrderEvent`: Information about the swap order. + /// * `user::FillEvent`(s): Information about fill(s) associated + /// with the swap. + /// * `user::CancelOrderEvent`: Optionally, information about why + /// the swap was cancelled without completely filling. + /// + /// # Testing + /// + /// * `test_swap_between_coinstores_max_possible_base_buy()` + /// * `test_swap_between_coinstores_max_possible_base_sell()` + /// * `test_swap_between_coinstores_max_possible_quote_buy()` + /// * `test_swap_between_coinstores_max_possible_quote_sell()` + /// * `test_swap_between_coinstores_max_quote_traded()` + /// * `test_swap_between_coinstores_not_enough_liquidity()` + /// * `test_swap_between_coinstores_register_base_store()` + /// * `test_swap_between_coinstores_register_quote_store()` + /// * `test_swap_between_coinstores_self_match_taker_cancel()` + public fun swap_between_coinstores< + BaseType, + QuoteType + >( + user: &signer, + market_id: u64, + integrator: address, + direction: bool, + min_base: u64, + max_base: u64, + min_quote: u64, + max_quote: u64, + limit_price: u64 + ): ( + u64, + u64, + u64 + ) acquires + MarketEventHandles, + OrderBooks, + SwapperEventHandles + { + let user_address = address_of(user); // Get user address. + // Register base coin store if user does not have one. + if (!coin::is_account_registered(user_address)) + coin::register(user); + // Register quote coin store if user does not have one. + if (!coin::is_account_registered(user_address)) + coin::register(user); + let (base_value, quote_value) = // Get coin value amounts. + (coin::balance(user_address), + coin::balance(user_address)); + // If max base to trade flagged as max possible, update it: + if (max_base == MAX_POSSIBLE) max_base = if (direction == BUY) + // If a buy, max to trade is amount that can fit in + // coin store, else is the amount in the coin store. + (HI_64 - base_value) else base_value; + // If max quote to trade flagged as max possible, update it: + if (max_quote == MAX_POSSIBLE) max_quote = if (direction == BUY) + // If a buy, max to trade is amount in coin store, else is + // the amount that could fit in the coin store. + quote_value else (HI_64 - quote_value); + range_check_trade( // Range check trade amounts. + direction, min_base, max_base, min_quote, max_quote, + base_value, base_value, quote_value, quote_value); + // Get option-wrapped base coins and quote coins for matching: + let (optional_base_coins, quote_coins) = if (direction == BUY) + // If a buy, need no base but need max quote. + (option::some(coin::zero()), + coin::withdraw(user, max_quote)) else + // If a sell, need max base but not quote. + (option::some(coin::withdraw(user, max_base)), + coin::zero()); + // Swap against the order book, deferring market events. + let fill_event_queue = vector[]; + let ( + optional_base_coins, + quote_coins, + base_traded, + quote_traded, + fees, + place_swap_order_event_option, + cancel_order_event_option + ) = swap( + &mut fill_event_queue, + user_address, + market_id, + NO_UNDERWRITER, + integrator, + direction, + min_base, + max_base, + min_quote, + max_quote, + limit_price, + optional_base_coins, + quote_coins + ); + // Create swapper event handles for market as needed. + if (!exists(user_address)) + move_to(user, SwapperEventHandles{map: table::new()}); + let swapper_event_handles_map_ref_mut = + &mut borrow_global_mut(user_address).map; + let has_handles = + table::contains(swapper_event_handles_map_ref_mut, market_id); + if (!has_handles) { + let handles = SwapperEventHandlesForMarket{ + cancel_order_events: account::new_event_handle(user), + fill_events: account::new_event_handle(user), + place_swap_order_events: account::new_event_handle(user) + }; + table::add( + swapper_event_handles_map_ref_mut, market_id, handles); + }; + let handles_ref_mut = + table::borrow_mut(swapper_event_handles_map_ref_mut, market_id); + // Emit place swap order event. + event::emit_event(&mut handles_ref_mut.place_swap_order_events, + option::destroy_some(place_swap_order_event_option)); + // Emit fill events first-in-first-out. + vector::for_each_ref(&fill_event_queue, |fill_event_ref| { + let fill_event: FillEvent = *fill_event_ref; + event::emit_event(&mut handles_ref_mut.fill_events, fill_event); + }); + // Optionally emit cancel event. + if (option::is_some(&cancel_order_event_option)) + event::emit_event(&mut handles_ref_mut.cancel_order_events, + option::destroy_some(cancel_order_event_option)); + // Deposit base coins back to user's coin store. + coin::deposit(user_address, option::destroy_some(optional_base_coins)); + // Deposit quote coins back to user's coin store. + coin::deposit(user_address, quote_coins); + (base_traded, quote_traded, fees) // Return match results. + } + + /// Swap standalone coins against the order book. + /// + /// If a buy, attempts to spend all quote coins. If a sell, attempts + /// to sell all base coins. + /// + /// Passes all base coins to matching engine if a buy or a sell, and + /// passes all quote coins to matching engine if a buy. If a sell, + /// does not pass any quote coins to matching engine, to avoid + /// intermediate quote match overflow that could occur prior to fee + /// assessment. + /// + /// # Type Parameters + /// + /// * `BaseType`: Same as for `match()`. + /// * `QuoteType`: Same as for `match()`. + /// + /// # Parameters + /// + /// * `market_id`: Same as for `match()`. + /// * `integrator`: Same as for `match()`. + /// * `direction`: Same as for `match()`. + /// * `min_base`: Same as for `match()`. + /// * `max_base`: Same as for `match()`. Ignored if a sell. Else if + /// passed as `MAX_POSSIBLE` will attempt to trade maximum + /// possible amount for passed coin holdings. + /// * `min_quote`: Same as for `match()`. + /// * `max_quote`: Same as for `match()`. Ignored if a buy. Else if + /// passed as `MAX_POSSIBLE` will attempt to trade maximum + /// possible amount for passed coin holdings. + /// * `limit_price`: Same as for `match()`. + /// * `base_coins`: Same as `optional_base_coins` for `match()`, but + /// unpacked. + /// * `quote_coins`: Same as for `match()`. + /// + /// # Returns + /// + /// * `Coin`: Updated base coin holdings, same as for + /// `match()` but unpacked. + /// * `Coin`: Updated quote coin holdings, same as for + /// `match()`. + /// * `u64`: Base coin trade amount, same as for `match()`. + /// * `u64`: Quote coin trade amount, same as for `match()`. + /// * `u64`: Quote coin fees paid, same as for `match()`. + /// + /// # Terminology + /// + /// * The "inbound" asset is the asset received from a trade: base + /// coins in the case of a buy, quote coins in the case of a sell. + /// * The "outbound" asset is the asset traded away: quote coins in + /// the case of a buy, base coins in the case of a sell. + /// + /// # Testing + /// + /// * `test_swap_coins_buy_max_base_limiting()` + /// * `test_swap_coins_buy_no_max_base_limiting()` + /// * `test_swap_coins_buy_no_max_quote_limiting()` + /// * `test_swap_coins_sell_max_quote_limiting()` + /// * `test_swap_coins_sell_no_max_base_limiting()` + /// * `test_swap_coins_sell_no_max_quote_limiting()` + public fun swap_coins< + BaseType, + QuoteType + >( + market_id: u64, + integrator: address, + direction: bool, + min_base: u64, + max_base: u64, + min_quote: u64, + max_quote: u64, + limit_price: u64, + base_coins: Coin, + quote_coins: Coin + ): ( + Coin, + Coin, + u64, + u64, + u64 + ) acquires + MarketEventHandles, + OrderBooks + { + let (base_value, quote_value) = // Get coin value amounts. + (coin::value(&base_coins), coin::value("e_coins)); + // Get option wrapped base coins. + let optional_base_coins = option::some(base_coins); + // Get quote coins to route through matching engine and update + // max match amounts based on side. If a swap buy: + let quote_coins_to_match = if (direction == BUY) { + // Max quote to trade is amount passed in. + max_quote = quote_value; + // If max base amount to trade is max possible flag, update + // to max amount that can be received. + if (max_base == MAX_POSSIBLE) max_base = (HI_64 - base_value); + // Pass all quote coins to matching engine. + coin::extract(&mut quote_coins, max_quote) + } else { // If a swap sell: + // Max base to trade is amount passed in. + max_base = base_value; + // If max quote amount to trade is max possible flag, update + // to max amount that can be received. + if (max_quote == MAX_POSSIBLE) max_quote = (HI_64 - quote_value); + // Do not pass any quote coins to matching engine. + coin::zero() + }; + range_check_trade( // Range check trade amounts. + direction, min_base, max_base, min_quote, max_quote, + base_value, base_value, quote_value, quote_value); + // Swap against order book, discarding events. + let ( + optional_base_coins, + quote_coins_matched, + base_traded, + quote_traded, + fees, + _, + _ + ) = swap( + &mut vector[], + NO_TAKER_ADDRESS, + market_id, + NO_UNDERWRITER, + integrator, + direction, + min_base, + max_base, + min_quote, + max_quote, + limit_price, + optional_base_coins, + quote_coins_to_match + ); + // Merge matched quote coins back into holdings. + coin::merge(&mut quote_coins, quote_coins_matched); + // Get base coins from option. + let base_coins = option::destroy_some(optional_base_coins); + // Return all coins. + (base_coins, quote_coins, base_traded, quote_traded, fees) + } + + /// Swap against the order book for a generic market, under + /// authority of market underwriter. + /// + /// Passes all quote coins to matching engine if a buy. If a sell, + /// does not pass any quote coins to matching engine, to avoid + /// intermediate quote match overflow that could occur prior to fee + /// assessment. + /// + /// # Type Parameters + /// + /// * `QuoteType`: Same as for `match()`. + /// + /// # Parameters + /// + /// * `market_id`: Same as for `match()`. + /// * `integrator`: Same as for `match()`. + /// * `direction`: Same as for `match()`. + /// * `min_base`: Same as for `match()`. + /// * `max_base`: Same as for `match()`. + /// * `min_quote`: Same as for `match()`. + /// * `max_quote`: Same as for `match()`. Ignored if a buy. Else if + /// passed as `MAX_POSSIBLE` will attempt to trade maximum + /// possible amount for passed coin holdings. + /// * `limit_price`: Same as for `match()`. + /// * `quote_coins`: Same as for `match()`. + /// * `underwriter_capability_ref`: Immutable reference to + /// underwriter capability for given market. + /// + /// # Returns + /// + /// * `Coin`: Updated quote coin holdings, same as for + /// `match()`. + /// * `u64`: Base asset trade amount, same as for `match()`. + /// * `u64`: Quote coin trade amount, same as for `match()`. + /// * `u64`: Quote coin fees paid, same as for `match()`. + /// + /// # Testing + /// + /// * `test_swap_generic_buy_base_limiting()` + /// * `test_swap_generic_buy_quote_limiting()` + /// * `test_swap_generic_sell_max_quote_limiting()` + /// * `test_swap_generic_sell_no_max_base_limiting()` + /// * `test_swap_generic_sell_no_max_quote_limiting()` + public fun swap_generic< + QuoteType + >( + market_id: u64, + integrator: address, + direction: bool, + min_base: u64, + max_base: u64, + min_quote: u64, + max_quote: u64, + limit_price: u64, + quote_coins: Coin, + underwriter_capability_ref: &UnderwriterCapability + ): ( + Coin, + u64, + u64, + u64 + ) acquires + MarketEventHandles, + OrderBooks + { + let underwriter_id = // Get underwriter ID. + registry::get_underwriter_id(underwriter_capability_ref); + // Get quote coin value. + let quote_value = coin::value("e_coins); + // Get base asset value holdings and quote coins to route + // through matching engine, and update max match amounts based + // on side. If a swap buy: + let (base_value, quote_coins_to_match) = if (direction == BUY) { + // Max quote to trade is amount passed in. + max_quote = quote_value; + // Do not pass in base asset, and pass all quote coins to + // matching engine. + (0, coin::extract(&mut quote_coins, max_quote)) + } else { // If a swap sell: + // If max quote amount to trade is max possible flag, update + // to max amount that can be received. + if (max_quote == MAX_POSSIBLE) max_quote = (HI_64 - quote_value); + // Effective base asset holdings are max trade amount, do + // not pass and quote coins to matching engine. + (max_base, coin::zero()) + }; + range_check_trade( // Range check trade amounts. + direction, min_base, max_base, min_quote, max_quote, + base_value, base_value, quote_value, quote_value); + // Swap against order book, discarding events. + let ( + optional_base_coins, + quote_coins_matched, + base_traded, + quote_traded, + fees, + _, + _ + ) = swap( + &mut vector[], + NO_TAKER_ADDRESS, + market_id, + underwriter_id, + integrator, + direction, + min_base, + max_base, + min_quote, + max_quote, + limit_price, + option::none(), + quote_coins_to_match + ); + // Destroy empty base coin option. + option::destroy_none>(optional_base_coins); + // Merge matched quote coins back into holdings. + coin::merge(&mut quote_coins, quote_coins_matched); + // Return quote coins, amount of base traded, amount of quote + // traded, and quote fees paid. + (quote_coins, base_traded, quote_traded, fees) + } + + // Public functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Public entry functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Public entry function wrapper for `cancel_all_orders()` for + /// cancelling orders under authority of signing user. + /// + /// # Invocation testing + /// + /// * `test_cancel_all_orders_bid_user()` + public entry fun cancel_all_orders_user( + user: &signer, + market_id: u64, + side: bool, + ) acquires OrderBooks { + cancel_all_orders( + address_of(user), + market_id, + NO_CUSTODIAN, + side); + } + + /// Public entry function wrapper for `cancel_order()` for + /// cancelling order under authority of signing user. + /// + /// # Invocation testing + /// + /// * `test_cancel_order_bid_user()` + public entry fun cancel_order_user( + user: &signer, + market_id: u64, + side: bool, + market_order_id: u128 + ) acquires OrderBooks { + cancel_order( + address_of(user), + market_id, + NO_CUSTODIAN, + side, + market_order_id); + } + + /// Public entry function wrapper for `change_order_size()` for + /// changing order size under authority of signing user. + /// + /// # Invocation testing + /// + /// * `test_change_order_size_bid_user()` + public entry fun change_order_size_user( + user: &signer, + market_id: u64, + side: bool, + market_order_id: u128, + new_size: u64 + ) acquires OrderBooks { + change_order_size( + address_of(user), + market_id, + NO_CUSTODIAN, + side, + market_order_id, + new_size); + } + + /// Public entry function wrapper for + /// `place_limit_order_passive_advance_user()`. + /// + /// # Invocation testing + /// + /// * `test_place_limit_order_passive_advance_ticks_ask()` + public entry fun place_limit_order_passive_advance_user_entry< + BaseType, + QuoteType + >( + user: &signer, + market_id: u64, + integrator: address, + side: bool, + size: u64, + advance_style: bool, + target_advance_amount: u64 + ) acquires OrderBooks { + place_limit_order_passive_advance_user< + BaseType, + QuoteType + >( + user, + market_id, + integrator, + side, + size, + advance_style, + target_advance_amount); + } + + /// Public entry function wrapper for `place_limit_order_user()`. + /// + /// # Invocation testing + /// + /// * `test_place_limit_order_user_entry()` + public entry fun place_limit_order_user_entry< + BaseType, + QuoteType + >( + user: &signer, + market_id: u64, + integrator: address, + side: bool, + size: u64, + price: u64, + restriction: u8, + self_match_behavior: u8 + ) acquires OrderBooks { + place_limit_order_user( + user, market_id, integrator, side, size, price, restriction, + self_match_behavior); + } + + /// Public entry function wrapper for `place_market_order_user()`. + /// + /// # Invocation testing + /// + /// * `test_place_market_order_user_entry()` + public entry fun place_market_order_user_entry< + BaseType, + QuoteType + >( + user: &signer, + market_id: u64, + integrator: address, + direction: bool, + size: u64, + self_match_behavior: u8 + ) acquires OrderBooks { + place_market_order_user( + user, market_id, integrator, direction, size, self_match_behavior); + } + + /// Wrapped call to `register_market_base_coin()` for paying utility + /// coins from an `aptos_framework::coin::CoinStore`. + /// + /// # Testing + /// + /// * `test_register_markets()` + public entry fun register_market_base_coin_from_coinstore< + BaseType, + QuoteType, + UtilityType + >( + user: &signer, + lot_size: u64, + tick_size: u64, + min_size: u64 + ) acquires OrderBooks { + // Get market registration fee, denominated in utility coins. + let fee = incentives::get_market_registration_fee(); + // Register market with base coin, paying fees from coin store. + register_market_base_coin( + lot_size, tick_size, min_size, coin::withdraw(user, fee)); + } + + /// Public entry function wrapper for `swap_between_coinstores()`. + /// + /// # Invocation testing + /// + /// * `test_swap_between_coinstores_register_base_store()` + /// * `test_swap_between_coinstores_register_quote_store()` + public entry fun swap_between_coinstores_entry< + BaseType, + QuoteType + >( + user: &signer, + market_id: u64, + integrator: address, + direction: bool, + min_base: u64, + max_base: u64, + min_quote: u64, + max_quote: u64, + limit_price: u64 + ) acquires + MarketEventHandles, + OrderBooks, + SwapperEventHandles + { + swap_between_coinstores( + user, market_id, integrator, direction, min_base, max_base, + min_quote, max_quote, limit_price); + } + + // Public entry functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Private functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Cancel all of a user's open maker orders. + /// + /// # Parameters + /// + /// * `user`: Same as for `cancel_order()`. + /// * `market_id`: Same as for `cancel_order()`. + /// * `custodian_id`: Same as for `cancel_order()`. + /// * `side`: Same as for `cancel_order()`. + /// + /// # Expected value testing + /// + /// * `test_cancel_all_orders_ask_custodian()` + /// * `test_cancel_all_orders_bid_user()` + fun cancel_all_orders( + user: address, + market_id: u64, + custodian_id: u64, + side: bool + ) acquires OrderBooks { + // Get user's active market order IDs. + let market_order_ids = user::get_active_market_order_ids_internal( + user, market_id, custodian_id, side); + // Get number of market order IDs, init loop index variable. + let (n_orders, i) = (vector::length(&market_order_ids), 0); + while (i < n_orders) { // Loop over all active orders. + // Cancel market order for current iteration. + cancel_order(user, market_id, custodian_id, side, + *vector::borrow(&market_order_ids, i)); + i = i + 1; // Increment loop counter. + } + } + + /// Cancel maker order on order book and in user's market account. + /// + /// The market order ID is first checked to see if the AVL queue + /// access key encoded within can even be used for an AVL queue + /// removal operation in the first place. Then during the call to + /// `user::cancel_order_internal()`, the market order ID is again + /// verified against the order access key derived from the AVL queue + /// removal operation. + /// + /// # Parameters + /// + /// * `user`: Address of user holding maker order. + /// * `market_id`: Market ID of market. + /// * `custodian_id`: Market account custodian ID. + /// * `side`: `ASK` or `BID`, the maker order side. + /// * `market_order_id`: Market order ID of order on order book. + /// + /// # Aborts + /// + /// * `E_INVALID_MARKET_ORDER_ID`: Market order ID does not + /// correspond to a valid order. + /// * `E_INVALID_MARKET_ID`: No market with given ID. + /// * `E_INVALID_USER`: Mismatch between `user` and user for order + /// on book having given market order ID. + /// * `E_INVALID_CUSTODIAN`: Mismatch between `custodian_id` and + /// custodian ID of order on order book having market order ID. + /// + /// # Expected value testing + /// + /// * `test_cancel_order_ask_custodian()` + /// * `test_cancel_order_bid_user()` + /// + /// # Failure testing + /// + /// * `test_cancel_order_invalid_custodian()` + /// * `test_cancel_order_invalid_market_id()` + /// * `test_cancel_order_invalid_market_order_id_bogus()` + /// * `test_cancel_order_invalid_market_order_id_null()` + /// * `test_cancel_order_invalid_user()` + fun cancel_order( + user: address, + market_id: u64, + custodian_id: u64, + side: bool, + market_order_id: u128 + ) acquires OrderBooks { + // Get address of resource account where order books are stored. + let resource_address = resource_account::get_address(); + let order_books_map_ref_mut = // Mutably borrow order books map. + &mut borrow_global_mut(resource_address).map; + // Assert order books map has order book with given market ID. + assert!(tablist::contains(order_books_map_ref_mut, market_id), + E_INVALID_MARKET_ID); + let order_book_ref_mut = // Mutably borrow market order book. + tablist::borrow_mut(order_books_map_ref_mut, market_id); + // Mutably borrow corresponding orders AVL queue. + let orders_ref_mut = if (side == ASK) &mut order_book_ref_mut.asks + else &mut order_book_ref_mut.bids; + // Get AVL queue access key from market order ID. + let avlq_access_key = ((market_order_id & (HI_64 as u128)) as u64); + // Check if removal from the AVL queue is even possible. + let removal_possible = avl_queue::contains_active_list_node_id( + orders_ref_mut, avlq_access_key); + // Assert that removal from the AVL queue is possible. + if (removal_possible) { + // assert!(removal_possible, E_INVALID_MARKET_ORDER_ID); + // Remove order from AVL queue, storing its fields. + let Order{size, price, user: order_user, custodian_id: + order_custodian_id, order_access_key} = avl_queue::remove( + orders_ref_mut, avlq_access_key); + // Assert passed maker address is user holding order. + assert!(user == order_user, E_INVALID_USER); + // Assert passed custodian ID matches that from order. + assert!(custodian_id == order_custodian_id, E_INVALID_CUSTODIAN); + // Cancel order user-side, thus verifying market order ID. + user::cancel_order_internal( + user, market_id, custodian_id, side, size, price, order_access_key, + market_order_id, CANCEL_REASON_MANUAL_CANCEL); + } + } + + /// Change maker order size on book and in user's market account. + /// + /// Priority for given price level is preserved for size decrease, + /// but lost for size increase. + /// + /// The market order ID is first checked to see if the AVL queue + /// access key encoded within can even be used for an AVL queue + /// borrow operation in the first place. Then during the call to + /// `user::change_order_size_internal()`, the market order ID is + /// again verified against the order access key derived from the AVL + /// queue borrow operation. + /// + /// # Parameters + /// + /// * `user`: Address of user holding maker order. + /// * `market_id`: Market ID of market. + /// * `custodian_id`: Market account custodian ID. + /// * `side`: `ASK` or `BID`, the maker order side. + /// * `market_order_id`: Market order ID of order on order book. + /// * `new_size`: The new order size to change to. + /// + /// # Aborts + /// + /// * `E_INVALID_MARKET_ORDER_ID`: Market order ID does not + /// correspond to a valid order. + /// * `E_INVALID_MARKET_ID`: No market with given ID. + /// * `E_INVALID_USER`: Mismatch between `user` and user for order + /// on book having given market order ID. + /// * `E_INVALID_CUSTODIAN`: Mismatch between `custodian_id` and + /// custodian ID of order on order book having market order ID. + /// * `E_SIZE_CHANGE_BELOW_MIN_SIZE`: New order size is less than + /// the minimum order size for market. + /// + /// # Expected value testing + /// + /// * `test_change_order_size_ask_custodian()` + /// * `test_change_order_size_bid_user()` + /// * `test_change_order_size_bid_user_new_tail()` + /// + /// # Failure testing + /// + /// * `test_change_order_size_below_min_size()` + /// * `test_change_order_size_insertion_error()` + /// * `test_change_order_size_invalid_custodian()` + /// * `test_change_order_size_invalid_market_id()` + /// * `test_change_order_size_invalid_market_order_id_bogus()` + /// * `test_change_order_size_invalid_market_order_id_null()` + /// * `test_change_order_size_invalid_user()` + fun change_order_size( + user: address, + market_id: u64, + custodian_id: u64, + side: bool, + market_order_id: u128, + new_size: u64 + ) acquires OrderBooks { + // Get address of resource account where order books are stored. + let resource_address = resource_account::get_address(); + let order_books_map_ref_mut = // Mutably borrow order books map. + &mut borrow_global_mut(resource_address).map; + // Assert order books map has order book with given market ID. + assert!(tablist::contains(order_books_map_ref_mut, market_id), + E_INVALID_MARKET_ID); + let order_book_ref_mut = // Mutably borrow market order book. + tablist::borrow_mut(order_books_map_ref_mut, market_id); + // Assert new size is at least minimum size for market. + assert!(new_size >= order_book_ref_mut.min_size, + E_SIZE_CHANGE_BELOW_MIN_SIZE); + // Mutably borrow corresponding orders AVL queue. + let orders_ref_mut = if (side == ASK) &mut order_book_ref_mut.asks + else &mut order_book_ref_mut.bids; + // Get AVL queue access key from market order ID. + let avlq_access_key = ((market_order_id & (HI_64 as u128)) as u64); + // Check if borrowing from the AVL queue is even possible. + let borrow_possible = avl_queue::contains_active_list_node_id( + orders_ref_mut, avlq_access_key); + // Assert that borrow from the AVL queue is possible. + assert!(borrow_possible, E_INVALID_MARKET_ORDER_ID); + // Check if order is at tail of queue for given price level. + let tail_of_price_level_queue = + avl_queue::is_local_tail(orders_ref_mut, avlq_access_key); + let order_ref_mut = // Mutably borrow order on order book. + avl_queue::borrow_mut(orders_ref_mut, avlq_access_key); + // Assert passed user address is user holding order. + assert!(user == order_ref_mut.user, E_INVALID_USER); + // Assert passed custodian ID matches that from order. + assert!(custodian_id == order_ref_mut.custodian_id, + E_INVALID_CUSTODIAN); + // Change order size user-side, thus verifying market order ID + // and new size. + user::change_order_size_internal( + user, market_id, custodian_id, side, order_ref_mut.size, new_size, + order_ref_mut.price, order_ref_mut.order_access_key, + market_order_id); + // Get order price. + let price = avl_queue::get_access_key_insertion_key(avlq_access_key); + // If size change is for a size decrease or if order is at tail + // of given price level: + if ((new_size < order_ref_mut.size) || tail_of_price_level_queue) { + // Mutate order on book to reflect new size, preserving spot + // in queue for the given price level. + order_ref_mut.size = new_size; + // If new size is more than old size (user-side function + // verifies that size is not equal) but order is not tail of + // queue for the given price level, priority should be lost: + } else { + // Remove order from AVL queue, pushing corresponding AVL + // queue list node onto unused list node stack. + let order = avl_queue::remove(orders_ref_mut, avlq_access_key); + order.size = new_size; // Mutate order size. + // Insert at back of queue for given price level. + let new_avlq_access_key = + avl_queue::insert(orders_ref_mut, price, order); + // Verify that new AVL queue access key is the same as + // before the size change: since list nodes are re-used, the + // AVL queue access key should be the same, even though the + // order is now the new tail of a doubly linked list for the + // given insertion key (back of queue for the given price + // level). Eviction is not checked because the AVL queue + // shape is the same before and after the remove/insert + // compound operation. + assert!(new_avlq_access_key == avlq_access_key, + E_SIZE_CHANGE_INSERTION_ERROR); + }; + } + + /// Get optional cancel reason for market order or swap. + /// + /// # Parameters + /// + /// * `self_match_taker_cancel`: If matching resulted in cancelling + /// the taker side of an order due to a self match. + /// * `base_traded`: The amount of base assets traded. + /// * `max_base`: The maximum indicated amount of base assets to + /// match. + /// * `liquidity_gone`: If the matching engine halted due to + /// insufficient liquidity. + /// * `lot_size`: The lot size for the market. + /// * `violated_limit_price`: `true` if matching halted due to a + /// violated limit price + /// + /// # Returns + /// + /// * `Option`: An optional cancel reason, if the order needs + /// to be cancelled. + inline fun get_cancel_reason_option_for_market_order_or_swap( + self_match_taker_cancel: bool, + base_traded: u64, + max_base: u64, + liquidity_gone: bool, + lot_size: u64, + limit_price_violated: bool + ): Option { + let need_to_cancel = + ((self_match_taker_cancel) || (base_traded < max_base)); + if (need_to_cancel) { + if (self_match_taker_cancel) { + option::some(CANCEL_REASON_SELF_MATCH_TAKER) + } else if (limit_price_violated) { + option::some(CANCEL_REASON_VIOLATED_LIMIT_PRICE) + } else if (liquidity_gone) { + option::some(CANCEL_REASON_NOT_ENOUGH_LIQUIDITY) + } else if ((max_base - base_traded) < lot_size) { + option::some(CANCEL_REASON_TOO_SMALL_TO_FILL_LOT) + } else { + option::some(CANCEL_REASON_MAX_QUOTE_TRADED) + } + } else { + option::none() + } + } + + /// Index specified number of open orders for given side of order + /// book. + /// + /// # Testing + /// + /// * `test_get_open_orders()` + fun get_open_orders_for_side( + market_id: u64, + order_book_ref_mut: &mut OrderBook, + side: bool, + n_orders_max: u64 + ): vector { + let orders = vector[]; // Initialize empty vector of orders. + // Get mutable reference to orders AVL queue for given side. + let avlq_ref_mut = if (side == ASK) &mut order_book_ref_mut.asks else + &mut order_book_ref_mut.bids; + // While there are still orders left to index: + while((vector::length(&orders) < n_orders_max) && + (!avl_queue::is_empty(avlq_ref_mut))) { + // Remove and unpack order at head of queue. + let Order{size, price, user, custodian_id, order_access_key} = + avl_queue::pop_head(avlq_ref_mut); + // Get order ID from user-side order memory. + let order_id = option::destroy_some( + user::get_open_order_id_internal(user, market_id, custodian_id, + side, order_access_key)); + // Push back an order view to orders view vector. + vector::push_back(&mut orders, OrderView{ + market_id, side, order_id, remaining_size: size, price, user, + custodian_id}); + }; + orders // Return vector of view-friendly orders. + } + + /// Index specified number of open orders for given side of order + /// book, from given starting order ID. + /// + /// See `get_open_orders_paginated()`. + /// + /// # Testing + /// + /// * `test_get_open_orders_paginated()` + fun get_open_orders_for_side_paginated( + order_book_ref: &OrderBook, + market_id: u64, + side: bool, + n_orders_to_index_max: u64, + starting_order_id: u128 + ): ( + vector, + u128, + ) { + // Get immutable reference to orders AVL queue for given side. + let avlq_ref = if (side == ASK) &order_book_ref.asks else + &order_book_ref.bids; + let orders = vector[]; // Initialize empty vector of orders. + // Return early if no orders to index. + if (avl_queue::is_empty(avlq_ref) || n_orders_to_index_max == 0) + return (orders, (NIL as u128)); + // Get order ID to index. If starting from best bid/ask: + let order_id = if (starting_order_id == (NIL as u128)) { + // Lookup order ID from user memory and reassign. + let order_ref = avl_queue::borrow_head(avlq_ref); + let optional_order_id = user::get_open_order_id_internal( + order_ref.user, market_id, order_ref.custodian_id, side, + order_ref.order_access_key); + option::destroy_some(optional_order_id) + } else { + starting_order_id + }; + // Get AVL queue access key from order ID. + let avlq_access_key = get_order_id_avl_queue_access_key(order_id); + let n_indexed_orders = 0; + while (n_indexed_orders < n_orders_to_index_max) { + // Borrow next order to index in AVL queue. + let order_ref = avl_queue::borrow(avlq_ref, avlq_access_key); + // Get order ID from user-side order memory. + let order_id = option::destroy_some( + user::get_open_order_id_internal( + order_ref.user, market_id, order_ref.custodian_id, + side, order_ref.order_access_key)); + // Push back an order view to orders view vector. + vector::push_back(&mut orders, OrderView{ + market_id, side, order_id, remaining_size: order_ref.size, + price: order_ref.price, user: order_ref.user, custodian_id: + order_ref.custodian_id}); + // Get access key for next order in AVL queue. + avlq_access_key = avl_queue::next_list_node_id_in_access_key( + avlq_ref, avlq_access_key); + // Stop indexing if no traversals left. + if (avlq_access_key == NIL) break; + n_indexed_orders = n_indexed_orders + 1; + }; + let next_page_start = if (avlq_access_key == NIL) { + (NIL as u128) + } else { + // Borrow order for next page start. + let order_ref = avl_queue::borrow(avlq_ref, avlq_access_key); + let optional_order_id = user::get_open_order_id_internal( + order_ref.user, market_id, order_ref.custodian_id, side, + order_ref.order_access_key); + option::destroy_some(optional_order_id) + }; + (orders, next_page_start) + } + + /// Get AVL queue access key encoded in `order_id`. + /// + /// # Testing + /// + /// * `test_get_market_order_id_avl_queue_access_key()` + fun get_order_id_avl_queue_access_key( + order_id: u128 + ): u64 { + ((order_id & (HI_64 as u128)) as u64) + } + + /// Index specified number of price levels for given side of order + /// book. + /// + /// # Testing + /// + /// * `test_get_price_levels()` + /// * `test_get_price_levels_mismatch()` + fun get_price_levels_for_side( + order_book_ref_mut: &mut OrderBook, + side: bool, + n_price_levels_max: u64 + ): vector { + // Initialize empty price levels vector. + let price_levels = vector[]; + // Get mutable reference to orders AVL queue for given side. + let avlq_ref_mut = if (side == ASK) &mut order_book_ref_mut.asks else + &mut order_book_ref_mut.bids; + // While more price levels can be indexed: + while (vector::length(&price_levels) < n_price_levels_max) { + let size = 0; // Initialize price level size to 0. + // Get optional price of order at head of queue. + let optional_head_price = avl_queue::get_head_key(avlq_ref_mut); + // If there is an order at the head of the queue: + if (option::is_some(&optional_head_price)) { + // Unpack its price as the price tracker for the level. + let price = option::destroy_some(optional_head_price); + // While orders still left on book: + while (!avl_queue::is_empty(avlq_ref_mut)) { + // If order at head of the queue is in price level: + if (option::contains( + &avl_queue::get_head_key(avlq_ref_mut), &price)) { + // Pop order, storing only its size and price. + let Order{ + size: order_size, + price: order_price, + user: _, + custodian_id: _, + order_access_key: _ + } = avl_queue::pop_head(avlq_ref_mut); + // Verify order price equals insertion key. + assert!(order_price == price, E_ORDER_PRICE_MISMATCH); + // Increment tracker for price level size. Note + // that no overflow is checked because an open + // order's size is a u64, and an AVL queue can + // hold at most 2 ^ 14 - 1 open orders. + size = size + (order_size as u128); + } else { // If order at head of queue not in level: + break // Break of out loop over head of queue. + } + }; + // Push back price level to price levels vector. + vector::push_back(&mut price_levels, PriceLevel{price, size}); + } else { // If no order at the head of the queue: + break // Break of out loop on price level vector length. + } + }; + price_levels // Return vector of price levels. + } + + /// Index specified number of open orders for given side of order + /// book into price levels, starting from given starting order ID. + /// + /// See `get_price_levels_paginated()`. + /// + /// # Testing + /// + /// * `test_price_levels_paginated()` + fun get_price_levels_for_side_paginated( + order_book_ref: &OrderBook, + market_id: u64, + side: bool, + n_orders_to_index_max: u64, + starting_order_id: u128 // If `NIL`, start from best bid/ask. + ): ( + vector, + u128, // Order ID for start of next page, `NIL` if done. + ) { + // Get immutable reference to orders AVL queue for given side. + let avlq_ref = if (side == ASK) &order_book_ref.asks else + &order_book_ref.bids; + // Initialize empty price levels vector. + let price_levels = vector[]; + // Return early if no orders to index. + if (avl_queue::is_empty(avlq_ref) || n_orders_to_index_max == 0) + return (price_levels, (NIL as u128)); + // Get order ID to index. If starting from best bid/ask: + let order_id = if (starting_order_id == (NIL as u128)) { + // Lookup order ID from user memory and reassign. + let order_ref = avl_queue::borrow_head(avlq_ref); + let optional_order_id = user::get_open_order_id_internal( + order_ref.user, market_id, order_ref.custodian_id, side, + order_ref.order_access_key); + option::destroy_some(optional_order_id) + } else { + starting_order_id + }; + // Get AVL queue access key from order ID. + let avlq_access_key = get_order_id_avl_queue_access_key(order_id); + // Get price for starting price level. + let price = avl_queue::borrow(avlq_ref, avlq_access_key).price; + // Initialize size for price level to 0. + let size = 0; + let n_indexed_orders = 0; + while (n_indexed_orders < n_orders_to_index_max) { + // Borrow next order to index in AVL queue. + let order_ref = avl_queue::borrow(avlq_ref, avlq_access_key); + // If in same price level, increment size: + if (order_ref.price == price) { + size = size + (order_ref.size as u128); + // If in new level, push back prior one and start anew. + } else { + vector::push_back(&mut price_levels, PriceLevel{price, size}); + price = order_ref.price; + size = (order_ref.size as u128); + }; + // Get access key for next order in AVL queue. + avlq_access_key = avl_queue::next_list_node_id_in_access_key( + avlq_ref, avlq_access_key); + // Stop indexing if no traversals left. + if (avlq_access_key == NIL) break; + n_indexed_orders = n_indexed_orders + 1; + }; + // Push back final price level. + vector::push_back(&mut price_levels, PriceLevel{price, size}); + let next_page_start = if (avlq_access_key == NIL) { + (NIL as u128) + } else { + // Borrow order for next page start. + let order_ref = avl_queue::borrow(avlq_ref, avlq_access_key); + let optional_order_id = user::get_open_order_id_internal( + order_ref.user, market_id, order_ref.custodian_id, side, + order_ref.order_access_key); + option::destroy_some(optional_order_id) + }; + (price_levels, next_page_start) + } + + /// Initialize the order books map upon module publication. + fun init_module( + _econia: &signer + ) { + // Get Econia resource account signer. + let resource_account = resource_account::get_signer(); + // Initialize order books map under resource account. + move_to(&resource_account, OrderBooks{map: tablist::new()}) + } + + /// Match a taker order against the order book. + /// + /// Calculates maximum amount of quote coins to match, matches, then + /// assesses taker fees. Matches up until the point of a self match, + /// then proceeds according to specified self match behavior. + /// + /// # Type Parameters + /// + /// * `BaseType`: Base asset type for market. + /// `registry::GenericAsset` if a generic market. + /// * `QuoteType`: Quote coin type for market. + /// + /// # Parameters + /// + /// * `market_id`: Market ID of market. + /// * `fill_event_queue_ref_mut`: Mutable reference to vector for + /// enqueueing deferred `user::FillEvent`(s). + /// * `order_book_ref_mut`: Mutable reference to market order book. + /// * `taker`: Address of taker whose order is matched. Passed as + /// `NO_TAKER_ADDRESS` when taker order originates from a swap + /// without a signature. + /// * `custodian_id`: Custodian ID associated with a taker market + /// account, if any. Should be passed as `NO_CUSTODIAN` if `taker` + /// is `NO_TAKER_ADDRESS`. + /// * `integrator`: The integrator for the taker order, who collects + /// a portion of taker fees at their + /// `incentives::IntegratorFeeStore` for the given market. May be + /// passed as an address known not to be an integrator, for + /// example `@0x0` or `@econia`, in the service of diverting all + /// fees to Econia. + /// * `direction`: `BUY` or `SELL`, from the taker's perspective. If + /// a `BUY`, fills against asks, else against bids. + /// * `min_base`: Minimum base asset units to be traded by taker, + /// either received or traded away. + /// * `max_base`: Maximum base asset units to be traded by taker, + /// either received or traded away. + /// * `min_quote`: Minimum quote asset units to be traded by taker, + /// either received or traded away. Refers to the net change in + /// taker's quote holdings after matching and fees. + /// * `max_quote`: Maximum quote asset units to be traded by taker, + /// either received or traded away. Refers to the net change in + /// taker's quote holdings after matching and fees. + /// * `limit_price`: If direction is `BUY`, the price above which + /// matching should halt. If direction is `SELL`, the price below + /// which matching should halt. Can be passed as `HI_PRICE` if a + /// `BUY` or `0` if a `SELL` to approve matching at any price. + /// * `self_match_behavior`: `ABORT`, `CANCEL_BOTH`, `CANCEL_MAKER`, + /// or `CANCEL_TAKER`. Ignored if no self matching takes place. + /// * `optional_base_coins`: None if `BaseType` is + /// `registry::GenericAsset` (market is generic), else base coin + /// holdings for pure coin market, which are incremented if + /// `direction` is `BUY` and decremented if `direction` is `SELL`. + /// * `quote_coins`: Quote coin holdings for market, which are + /// decremented if `direction` is `BUY` and incremented if + /// `direction` is `SELL`. + /// + /// # Returns + /// + /// * `Option>`: None if `BaseType` is + /// `registry::GenericAsset`, else updated `optional_base_coins` + /// holdings after matching. + /// * `Coin`: Updated `quote_coins` holdings after + /// matching. + /// * `u64`: Base asset amount traded by taker: net change in + /// taker's base holdings. + /// * `u64`: Quote coin amount traded by taker, inclusive of fees: + /// net change in taker's quote coin holdings. + /// * `u64`: Amount of quote coin fees paid. + /// * `bool`: `true` if a self match that results in a taker cancel. + /// * `bool`: `true` if liquidity is gone from order book on + /// corresponding side after matching. + /// * `bool`: `true` if matching halted due to violated limit price. + /// + /// # Aborts + /// + /// * `E_PRICE_TOO_HIGH`: Order price exceeds maximum allowable + /// price. + /// * `E_HEAD_KEY_PRICE_MISMATCH`: AVL queue head price does not + /// match head order price. + /// * `E_SELF_MATCH`: A self match occurs when `self_match_behavior` + /// is `ABORT`. + /// * `E_INVALID_SELF_MATCH_BEHAVIOR`: A self match occurs but an + /// invalid behavior flag is passed. + /// * `E_MIN_BASE_NOT_TRADED`: Minimum base asset trade amount + /// requirement not met. + /// * `E_MIN_QUOTE_NOT_TRADED`: Minimum quote asset trade amount + /// requirement not met. + /// + /// # Expected value testing + /// + /// * `test_match_complete_fill_no_lots_buy()` + /// * `test_match_complete_fill_no_ticks_sell()` + /// * `test_match_empty()` + /// * `test_match_fill_size_0()` + /// * `test_match_loop_twice()` + /// * `test_match_order_size_0()` + /// * `test_match_partial_fill_lot_limited_sell()` + /// * `test_match_partial_fill_tick_limited_buy()` + /// * `test_match_price_break_buy()` + /// * `test_match_price_break_sell()` + /// * `test_match_self_match_cancel_both()` + /// * `test_match_self_match_cancel_maker()` + /// * `test_match_self_match_cancel_taker()` + /// + /// # Failure testing + /// + /// * `test_match_min_base_not_traded()` + /// * `test_match_min_quote_not_traded()` + /// * `test_match_price_mismatch()` + /// * `test_match_price_too_high()` + /// * `test_match_self_match_abort()` + /// * `test_match_self_match_invalid()` + fun match< + BaseType, + QuoteType + >( + market_id: u64, + fill_event_queue_ref_mut: &mut vector, + order_book_ref_mut: &mut OrderBook, + taker: address, + custodian_id: u64, + integrator: address, + direction: bool, + min_base: u64, + max_base: u64, + min_quote: u64, + max_quote: u64, + limit_price: u64, + self_match_behavior: u8, + optional_base_coins: Option>, + quote_coins: Coin, + ): ( + Option>, + Coin, + u64, + u64, + u64, + bool, + bool, + bool + ) { + // Assert price is not too high. + assert!(limit_price <= HI_PRICE, E_PRICE_TOO_HIGH); + // Taker buy fills against asks, sell against bids. + let side = if (direction == BUY) ASK else BID; + let (lot_size, tick_size) = (order_book_ref_mut.lot_size, + order_book_ref_mut.tick_size); // Get lot and tick sizes. + // Get taker fee divisor. + let taker_fee_divisor = incentives::get_taker_fee_divisor(); + // Get max quote coins to match. + let max_quote_match = incentives::calculate_max_quote_match( + direction, taker_fee_divisor, max_quote); + // Calculate max amounts of lots and ticks to fill. + let (max_lots, max_ticks) = + (max_base / lot_size, max_quote_match / tick_size); + // Initialize counters for number of lots and ticks to fill. + let (lots_until_max, ticks_until_max) = (max_lots, max_ticks); + // Mutably borrow corresponding orders AVL queue. + let orders_ref_mut = if (side == ASK) &mut order_book_ref_mut.asks + else &mut order_book_ref_mut.bids; + // Assume it is not the case that a self match led to a taker + // order cancellation. + let self_match_taker_cancel = false; + // Get new order ID before any potential fills. + order_book_ref_mut.counter = order_book_ref_mut.counter + 1; + let order_id = ((order_book_ref_mut.counter as u128) << SHIFT_COUNTER); + // Initialize counters for fill iteration. + let (fill_count, fees_paid) = (0, 0); + let violated_limit_price = false; // Assume no price violation. + // While there are orders to match against: + while (!avl_queue::is_empty(orders_ref_mut)) { + let price = // Get price of order at head of AVL queue. + *option::borrow(&avl_queue::get_head_key(orders_ref_mut)); + // Break if price too high to buy at or too low to sell at. + if (((direction == BUY ) && (price > limit_price)) || + ((direction == SELL) && (price < limit_price))) { + violated_limit_price = true; + break + }; + // Calculate max number of lots that could be filled + // at order price, limited by ticks left to fill until max. + let max_fill_size_ticks = ticks_until_max / price; + // Max fill size is lesser of tick-limited fill size and + // lot-limited fill size. + let max_fill_size = if (max_fill_size_ticks < lots_until_max) + max_fill_size_ticks else lots_until_max; + // Mutably borrow order at head of AVL queue. + let order_ref_mut = avl_queue::borrow_head_mut(orders_ref_mut); + // Assert AVL queue head price matches that of order. + assert!(order_ref_mut.price == price, E_HEAD_KEY_PRICE_MISMATCH); + // If order at head of queue has size 0, evict it and + // continue to next order. This should never be reached + // during production, but is handled here to explicitly + // verify the assumption of no empty orders on the book. + if (order_ref_mut.size == 0) { + let Order{ + size: evictee_size, + price: evictee_price, + user: evictee_user, + custodian_id: evictee_custodian_id, + order_access_key: evictee_order_access_key + } = avl_queue::pop_head(orders_ref_mut); + user::cancel_order_internal( + evictee_user, + market_id, + evictee_custodian_id, + side, + evictee_size, + evictee_price, + evictee_order_access_key, + (NIL as u128), + CANCEL_REASON_EVICTION, + ); + continue + }; + // Get fill size and if a complete fill against book. + let (fill_size, complete_fill) = + // If max fill size is less than order size, fill size + // is max fill size and is an incomplete fill. Else + // order gets completely filled. + if (max_fill_size < order_ref_mut.size) + (max_fill_size, false) else (order_ref_mut.size, true); + if (fill_size == 0) break; // Break if no lots to fill. + // Get maker user address and custodian ID for maker's + // market account. + let (maker, maker_custodian_id) = + (order_ref_mut.user, order_ref_mut.custodian_id); + let self_match = // Determine if a self match. + ((taker == maker) && (custodian_id == maker_custodian_id)); + if (self_match) { // If a self match: + // Assert self match behavior is not abort. + assert!(self_match_behavior != ABORT, E_SELF_MATCH); + // Assume not cancelling maker order. + let cancel_maker_order = false; + // If self match behavior is cancel both: + if (self_match_behavior == CANCEL_BOTH) { + (cancel_maker_order, self_match_taker_cancel) = + (true, true); // Flag orders for cancellation. + // If self match behavior is cancel maker order: + } else if (self_match_behavior == CANCEL_MAKER) { + cancel_maker_order = true; // Flag for cancellation. + // If self match behavior is cancel taker order: + } else if (self_match_behavior == CANCEL_TAKER) { + // Flag for cancellation. + self_match_taker_cancel = true; + // Otherwise invalid self match behavior specified. + } else abort E_INVALID_SELF_MATCH_BEHAVIOR; + // If maker order should be canceled: + if (cancel_maker_order) { + // Cancel from maker's market account, storing + // market order ID. + let market_order_id = user::cancel_order_internal( + maker, market_id, maker_custodian_id, side, + order_ref_mut.size, price, + order_ref_mut.order_access_key, (NIL as u128), + CANCEL_REASON_SELF_MATCH_MAKER); + // Get AVL queue access key from market order ID. + let avlq_access_key = + ((market_order_id & (HI_64 as u128)) as u64); + // Remove order from AVL queue. + let Order{size: _, price: _, user: _, custodian_id: _, + order_access_key: _} = avl_queue::remove( + orders_ref_mut, avlq_access_key); + }; // Optional maker order cancellation complete. + // Break out of loop if a self match taker cancel. + if (self_match_taker_cancel) break; + } else { // If not a self match: + // Get ticks, quote filled. + let ticks_filled = fill_size * price; + let quote_filled = ticks_filled * tick_size; + // Decrement counter for lots to fill until max reached. + lots_until_max = lots_until_max - fill_size; + // Decrement counter for ticks to fill until max. + ticks_until_max = ticks_until_max - ticks_filled; + // Declare return assignment variable. + let market_order_id; + // Fill matched order user side, store market order ID. + (optional_base_coins, quote_coins, market_order_id) = + user::fill_order_internal( + maker, market_id, maker_custodian_id, side, + order_ref_mut.order_access_key, order_ref_mut.size, + fill_size, complete_fill, optional_base_coins, + quote_coins, fill_size * lot_size, quote_filled); + // Enqueue a fill event with the amount of fees paid. + let fees_paid_for_fill = quote_filled / taker_fee_divisor; + let fill_event = user::create_fill_event_internal( + market_id, fill_size, price, side, maker, + maker_custodian_id, market_order_id, taker, custodian_id, + order_id, fees_paid_for_fill, fill_count); + vector::push_back(fill_event_queue_ref_mut, fill_event); + // Update fill iteration counters. + fill_count = fill_count + 1; + fees_paid = fees_paid + fees_paid_for_fill; + // If order on book completely filled: + if (complete_fill) { + let avlq_access_key = // Get AVL queue access key. + ((market_order_id & (HI_64 as u128)) as u64); + let order = // Remove order from AVL queue. + avl_queue::remove(orders_ref_mut, avlq_access_key); + let Order{size: _, price: _, user: _, custodian_id: _, + order_access_key: _} = order; // Unpack order. + // Break out of loop if no more lots or ticks to fill. + if ((lots_until_max == 0) || (ticks_until_max == 0)) break + } else { // If order on book not completely filled: + // Decrement order size by amount filled. + order_ref_mut.size = order_ref_mut.size - fill_size; + break // Stop matching. + } + }; // Done processing counterparty match. + }; // Done looping over head of AVL queue for given side. + let (base_fill, quote_fill) = // Calculate base and quote fills. + (((max_lots - lots_until_max ) * lot_size), + ((max_ticks - ticks_until_max) * tick_size)); + // Assess taker fees. + let (quote_coins, _) = incentives::assess_taker_fees( + market_id, integrator, taker_fee_divisor, + fees_paid * taker_fee_divisor, quote_coins); + // If a buy, taker pays quote required for fills, and additional + // fee assessed after matching. If a sell, taker receives quote + // from fills, then has a portion assessed as fees. + let quote_traded = if (direction == BUY) (quote_fill + fees_paid) + else (quote_fill - fees_paid); + // Assert minimum base asset trade amount met. + assert!(base_fill >= min_base, E_MIN_BASE_NOT_TRADED); + // Assert minimum quote coin trade amount met. + assert!(quote_traded >= min_quote, E_MIN_QUOTE_NOT_TRADED); + // Return optional base coin, quote coins, trade amounts, + // self match taker cancel flag, if liquidity is gone, and if + // limit price was violated. + (optional_base_coins, quote_coins, base_fill, quote_traded, fees_paid, + self_match_taker_cancel, avl_queue::is_empty(orders_ref_mut), + violated_limit_price) + } + + /// Place limit order against order book from user market account. + /// + /// # Type Parameters + /// + /// * `BaseType`: Same as for `match()`. + /// * `QuoteType`: Same as for `match()`. + /// + /// # Parameters + /// + /// * `user_address`: User address for market account. + /// * `market_id`: Same as for `match()`. + /// * `custodian_id`: Same as for `match()`. + /// * `integrator`: Same as for `match()`, only receives fees if + /// order fills across the spread. + /// * `side`: `ASK` or `BID`, the side on which to place an order as + /// a maker. + /// * `size`: The size, in lots, to fill. + /// * `price`: The limit order price, in ticks per lot. + /// * `restriction`: `FILL_OR_ABORT`, `IMMEDIATE_OR_CANCEL`, + /// `POST_OR_ABORT`, or `NO_RESTRICTION`. + /// * `self_match_behavior`: Same as for `match()`. + /// * `critical_height`: The AVL queue height above which evictions + /// may take place. Should only be passed as `CRITICAL_HEIGHT`. + /// Accepted as an argument to simplify testing. + /// + /// # Returns + /// + /// * `u128`: Order ID assigned to order, unique within a market. + /// * `u64`: Base asset trade amount as a taker, same as for + /// `match()`, if order fills across the spread. + /// * `u64`: Quote asset trade amount as a taker, same as for + /// `match()`, if order fills across the spread. + /// * `u64`: Quote coin fees paid as a taker, same as for `match()`, + /// if order fills across the spread. + /// + /// # Aborts + /// + /// * `E_INVALID_RESTRICTION`: Invalid restriction flag. + /// * `E_PRICE_0`: Order price specified as 0. + /// * `E_PRICE_TOO_HIGH`: Order price exceeds maximum allowed + /// price. + /// * `E_INVALID_BASE`: Base asset type is invalid. + /// * `E_INVALID_QUOTE`: Quote asset type is invalid. + /// * `E_SIZE_TOO_SMALL`: Limit order size does not meet minimum + /// size for market. + /// * `E_FILL_OR_ABORT_NOT_CROSS_SPREAD`: Fill-or-abort price does + /// not cross the spread. + /// * `E_POST_OR_ABORT_CROSSES_SPREAD`: Post-or-abort price crosses + /// the spread. + /// * `E_SIZE_BASE_OVERFLOW`: The product of order size and market + /// lot size results in a base asset unit overflow. + /// * `E_SIZE_PRICE_TICKS_OVERFLOW`: The product of order size and + /// price results in a tick amount overflow. + /// * `E_SIZE_PRICE_QUOTE_OVERFLOW`: The product of order size, + /// price, and market tick size results in a quote asset unit + /// overflow. + /// * `E_PRICE_TIME_PRIORITY_TOO_LOW`: Order would result in lowest + /// price-time priority if inserted to AVL queue, but AVL queue + /// does not have room for any more orders. + /// + /// # Restrictions + /// + /// * A post-or-abort order aborts if its price crosses the spread. + /// * A fill-or-abort order aborts if it is not completely filled + /// as a taker order. Here, a corresponding minimum base trade + /// amount is passed to `match()`, which aborts if the minimum + /// amount is not filled. + /// * An immediate-or-cancel order fills as a taker if possible, + /// then returns. + /// + /// # Self matching + /// + /// Fills up until the point of a self match, cancelling remaining + /// size without posting if: + /// + /// 1. Price crosses the spread, + /// 2. Cross-spread filling is permitted per the indicated + /// restriction, and + /// 3. Self match behavior indicates taker cancellation. + /// + /// # Expected value testing + /// + /// * `test_place_limit_order_crosses_ask_exact()` + /// * `test_place_limit_order_crosses_ask_partial()` + /// * `test_place_limit_order_crosses_ask_partial_cancel()` + /// * `test_place_limit_order_crosses_ask_partial_maker()` + /// * `test_place_limit_order_crosses_ask_self_match_cancel()` + /// * `test_place_limit_order_crosses_bid_exact()` + /// * `test_place_limit_order_crosses_bid_partial()` + /// * `test_place_limit_order_crosses_bid_partial_maker()` + /// * `test_place_limit_order_crosses_bid_partial_post_under_min()` + /// * `test_place_limit_order_evict()` + /// * `test_place_limit_order_no_cross_ask_user()` + /// * `test_place_limit_order_no_cross_ask_user_ioc()` + /// * `test_place_limit_order_no_cross_bid_custodian()` + /// * `test_place_limit_order_remove_event_handles()` + /// * `test_place_limit_order_still_crosses_ask()` + /// * `test_place_limit_order_still_crosses_bid()` + /// + /// # Failure testing + /// + /// * `test_place_limit_order_base_overflow()` + /// * `test_place_limit_order_fill_or_abort_not_cross()` + /// * `test_place_limit_order_fill_or_abort_partial()` + /// * `test_place_limit_order_invalid_base()` + /// * `test_place_limit_order_invalid_quote()` + /// * `test_place_limit_order_invalid_restriction()` + /// * `test_place_limit_order_no_price()` + /// * `test_place_limit_order_post_or_abort_crosses()` + /// * `test_place_limit_order_price_hi()` + /// * `test_place_limit_order_price_time_priority_low()` + /// * `test_place_limit_order_quote_overflow()` + /// * `test_place_limit_order_size_lo()` + /// * `test_place_limit_order_ticks_overflow()` + fun place_limit_order< + BaseType, + QuoteType, + >( + user_address: address, + market_id: u64, + custodian_id: u64, + integrator: address, + side: bool, + size: u64, + price: u64, + restriction: u8, + self_match_behavior: u8, + critical_height: u8 + ): ( + u128, + u64, + u64, + u64 + ) acquires OrderBooks { + // Assert valid order restriction flag. + assert!(restriction <= N_RESTRICTIONS, E_INVALID_RESTRICTION); + assert!(price != 0, E_PRICE_0); // Assert nonzero price. + // Assert price is not too high. + assert!(price <= HI_PRICE, E_PRICE_TOO_HIGH); + // Get user's available and ceiling asset counts. + let (_, base_available, base_ceiling, _, quote_available, + quote_ceiling) = user::get_asset_counts_internal( + user_address, market_id, custodian_id); + // If asset count check does not abort, then market exists, so + // get address of resource account for borrowing order book. + let resource_address = resource_account::get_address(); + let order_books_map_ref_mut = // Mutably borrow order books map. + &mut borrow_global_mut(resource_address).map; + let order_book_ref_mut = // Mutably borrow market order book. + tablist::borrow_mut(order_books_map_ref_mut, market_id); + assert!(type_info::type_of() // Assert base type. + == order_book_ref_mut.base_type, E_INVALID_BASE); + assert!(type_info::type_of() // Assert quote type. + == order_book_ref_mut.quote_type, E_INVALID_QUOTE); + // Assert order size is at least minimum size for market. + assert!(size >= order_book_ref_mut.min_size, E_SIZE_TOO_SMALL); + // Get market underwriter ID. + let underwriter_id = order_book_ref_mut.underwriter_id; + // Order crosses spread if an ask and would trail behind bids + // AVL queue head, or if a bid and would trail behind asks AVL + // queue head. + let crosses_spread = if (side == ASK) + !avl_queue::would_update_head(&order_book_ref_mut.bids, price) else + !avl_queue::would_update_head(&order_book_ref_mut.asks, price); + // Assert order crosses spread if fill-or-abort. + assert!(!((restriction == FILL_OR_ABORT) && !crosses_spread), + E_FILL_OR_ABORT_NOT_CROSS_SPREAD); + // Assert order does not cross spread if post-or-abort. + assert!(!((restriction == POST_OR_ABORT) && crosses_spread), + E_POST_OR_ABORT_CROSSES_SPREAD); + // Calculate base asset amount corresponding to size in lots. + let base = (size as u128) * (order_book_ref_mut.lot_size as u128); + // Assert corresponding base asset amount fits in a u64. + assert!(base <= (HI_64 as u128), E_SIZE_BASE_OVERFLOW); + // Calculate tick amount corresponding to size in lots. + let ticks = (size as u128) * (price as u128); + // Assert corresponding tick amount fits in a u64. + assert!(ticks <= (HI_64 as u128), E_SIZE_PRICE_TICKS_OVERFLOW); + // Calculate amount of quote required to fill size at price. + let quote = ticks * (order_book_ref_mut.tick_size as u128); + // Assert corresponding quote amount fits in a u64. + assert!(quote <= (HI_64 as u128), E_SIZE_PRICE_QUOTE_OVERFLOW); + // Max base to trade is amount calculated from size, lot size. + let max_base = (base as u64); + // If a fill-or-abort order, must fill as a taker order with + // a minimum trade amount equal to max base. Else no min. + let min_base = if (restriction == FILL_OR_ABORT) max_base else 0; + // No need to specify min quote if filling as a taker order + // since min base is specified. + let min_quote = 0; + // Get max quote to trade. If price crosses spread: + let max_quote = if (crosses_spread) { // If fills as taker: + if (side == ASK) { // If an ask, filling as taker sell: + // Order will fill at prices that are at least as high + // as specified order price, and user will receive more + // quote than calculated from order size and price. + // Hence max quote to trade is amount that will fit in + // market account. + (HI_64 - quote_ceiling) + } else { // If a bid, filling as a taker buy: + // Order will fill at prices that are at most as high as + // specified order price, and user will have to pay at + // most the amount from order size and price, plus fees. + // Since max base is marked as amount corresponding to + // order size, matching engine will halt once enough + // base has been filled. Hence mark that max quote to + // trade is amount that user has available to spend, to + // provide a buffer against integer division truncation + // that may occur when matching engine calculates max + // quote to match. + quote_available + } + } else { // If no portion of order fills as a taker: + (quote as u64) // Max quote is amount from size and price. + }; + // If an ask, trade direction to range check is sell, else buy. + let direction = if (side == ASK) SELL else BUY; + range_check_trade( // Range check trade amounts. + direction, min_base, max_base, min_quote, max_quote, + base_available, base_ceiling, quote_available, quote_ceiling); + // Assume no assets traded as a taker. + let (base_traded, quote_traded, fees) = (0, 0, 0); + let cancel_reason_option = option::none(); + let fill_event_queue = vector[]; + let remaining_size = size; + if (crosses_spread) { // If order price crosses spread: + // Calculate max base and quote to withdraw. If a buy: + let (base_withdraw, quote_withdraw) = if (direction == BUY) + // Withdraw quote to buy base, else sell base for quote. + (0, max_quote) else (max_base, 0); + // Withdraw optional base coins and quote coins for match, + // verifying base type and quote type for market. + let (optional_base_coins, quote_coins) = + user::withdraw_assets_internal( + user_address, market_id, custodian_id, base_withdraw, + quote_withdraw, underwriter_id); + // Declare return assignment variable. + let self_match_cancel; + // Match against order book, deferring fill events. + ( + optional_base_coins, + quote_coins, + base_traded, + quote_traded, + fees, + self_match_cancel, + _, + _ + ) = match( + market_id, + &mut fill_event_queue, + order_book_ref_mut, + user_address, + custodian_id, + integrator, + direction, + min_base, + max_base, + min_quote, + max_quote, + price, + self_match_behavior, + optional_base_coins, + quote_coins + ); + // Calculate amount of base deposited back to market account. + let base_deposit = if (direction == BUY) base_traded else + base_withdraw - base_traded; + // Deposit assets back to user's market account. + user::deposit_assets_internal( + user_address, market_id, custodian_id, base_deposit, + optional_base_coins, quote_coins, underwriter_id); + // Remaining size is amount not traded during matching. + remaining_size = + size - (base_traded / order_book_ref_mut.lot_size); + // Get optional order cancel reason. + if (self_match_cancel) { + option::fill(&mut cancel_reason_option, + CANCEL_REASON_SELF_MATCH_TAKER); + } else if (remaining_size > 0) { + if (restriction == IMMEDIATE_OR_CANCEL) { + option::fill(&mut cancel_reason_option, + CANCEL_REASON_IMMEDIATE_OR_CANCEL); + } else { + // Order still crosses spread if an ask and would + // trail behind bids AVL queue head, or if a bid and + // would trail behind asks AVL queue head: can + // happen if an ask (taker sell) and quote ceiling + // reached, or if a bid (taker buy) and all + // available quote spent. + let still_crosses_spread = if (side == ASK) + !avl_queue::would_update_head( + &order_book_ref_mut.bids, price) else + !avl_queue::would_update_head( + &order_book_ref_mut.asks, price); + if (still_crosses_spread) { + option::fill(&mut cancel_reason_option, + CANCEL_REASON_MAX_QUOTE_TRADED); + } + } + }; + } else { // If spread not crossed (matching engine not called): + // Order book counter needs to be updated for new order ID. + order_book_ref_mut.counter = order_book_ref_mut.counter + 1; + // IOC order needs to be cancelled if no fills took place. + if (restriction == IMMEDIATE_OR_CANCEL) { + option::fill(&mut cancel_reason_option, + CANCEL_REASON_IMMEDIATE_OR_CANCEL); + }; + }; + // Assume that limit order will not post. + let market_order_id = + ((order_book_ref_mut.counter as u128) << SHIFT_COUNTER); + // If order eligible to post: + if (option::is_none(&cancel_reason_option) && (remaining_size > 0)) { + // Get next order access key for user-side order placement. + let order_access_key = user::get_next_order_access_key_internal( + user_address, market_id, custodian_id, side); + // Get orders AVL queue for maker side. + let orders_ref_mut = if (side == ASK) + &mut order_book_ref_mut.asks else &mut order_book_ref_mut.bids; + // Declare order to insert to book. + let order = Order{size: remaining_size, price, user: user_address, + custodian_id, order_access_key}; + // Get new AVL queue access key, evictee access key, and evictee + // value by attempting to insert for given critical height. + let (avlq_access_key, evictee_access_key, evictee_value) = + avl_queue::insert_check_eviction( + orders_ref_mut, price, order, critical_height); + // Assert that order could be inserted to AVL queue. + assert!(avlq_access_key != NIL, E_PRICE_TIME_PRIORITY_TOO_LOW); + // Encode AVL queue access key in market order ID. + market_order_id = market_order_id | (avlq_access_key as u128); + user::place_order_internal( // Place order user-side. + user_address, market_id, custodian_id, side, remaining_size, + price, market_order_id, order_access_key); + if (evictee_access_key == NIL) { // If no eviction required: + // Destroy empty evictee value option. + option::destroy_none(evictee_value); + } else { // If had to evict order at AVL queue tail: + // Unpack evicted order. + let Order{size, price, user, custodian_id, order_access_key} = + option::destroy_some(evictee_value); + // Cancel order user-side. + user::cancel_order_internal( + user, market_id, custodian_id, side, size, price, + order_access_key, (NIL as u128), CANCEL_REASON_EVICTION); + }; + }; + // Emit relevant events to user event handles. + user::emit_limit_order_events_internal( + market_id, user_address, custodian_id, integrator, side, size, + price, restriction, self_match_behavior, remaining_size, + market_order_id, &fill_event_queue, &cancel_reason_option); + // Return market order ID and taker trade amounts. + return (market_order_id, base_traded, quote_traded, fees) + } + + /// Place a limit order, passively advancing from the best price on + /// the given side. + /// + /// Computes limit order price based on a target "advance" amount + /// specified as a percentage of the spread, or specified in ticks: + /// if a user places an ask with a 35 percent advance, for example, + /// the "advance price" will be computed as the minimum ask price + /// minus 35 percent of the spread. If a bid with a 10 tick advance, + /// the advance price becomes the maximum bid price plus 10 ticks. + /// + /// Returns without posting an order if the order book is empty on + /// the specified side, or if advance amount is nonzero and the + /// order book is empty on the other side (since the spread cannot + /// be computed). If target advance amount, specified in ticks, + /// exceeds the number of ticks available inside the spread, + /// advances as much as possible without crossing the spread. + /// + /// To ensure passivity, a full advance corresponds to an advance + /// price just short of completely crossing the spread: for a 100 + /// percent passive advance bid on a market where the minimum ask + /// price is 400, the advance price is 399. + /// + /// After computing the advance price, places a post-or-abort limit + /// order that aborts for a self match. Advance price is then + /// range-checked by `place_limit_order()`. + /// + /// # Price calculations + /// + /// For a limit order to be placed on the book, it must fit in + /// 32 bits and be nonzero. Hence no underflow checking for the + /// bid "check price", or overflow checking for the multiplication + /// operation during the advance amount calculation for the percent + /// case. + /// + /// # Type Parameters + /// + /// * `BaseType`: Same as for `match()`. + /// * `QuoteType`: Same as for `match()`. + /// + /// # Parameters + /// + /// * `user_address`: User address for market account. + /// * `market_id`: Same as for `match()`. + /// * `custodian_id`: Same as for `match()`. + /// * `integrator`: Same as for `place_limit_order()`. + /// * `side`: Same as for `place_limit_order()`. + /// * `size`: Same as for `place_limit_order()`. + /// * `advance_style`: `PERCENT` or `TICKS`, denoting a price + /// advance into the spread specified as a percent of a full + /// advance, or a target number of ticks into the spread. + /// * `target_advance_amount`: If `advance_style` is `PERCENT` the + /// percent of the spread to advance, else the number of ticks to + /// advance. + /// + /// # Returns + /// + /// * `u128`: Market order ID, same as for `place_limit_order()`. + /// + /// # Aborts + /// + /// * `E_INVALID_MARKET_ID`: No market with given ID. + /// * `E_INVALID_BASE`: Base asset type is invalid. + /// * `E_INVALID_QUOTE`: Quote asset type is invalid. + /// * `E_INVALID_PERCENT`: `advance_style` is `PERCENT` and + /// `target_advance_amount` is not less than or equal to 100. + /// + /// # Expected value testing + /// + /// * `test_place_limit_order_passive_advance_no_cross_price_ask()` + /// * `test_place_limit_order_passive_advance_no_cross_price_bid()` + /// * `test_place_limit_order_passive_advance_no_full_advance()` + /// * `test_place_limit_order_passive_advance_no_start_price()`. + /// * `test_place_limit_order_passive_advance_no_target_advance()` + /// * `test_place_limit_order_passive_advance_percent_ask()` + /// * `test_place_limit_order_passive_advance_percent_bid()` + /// * `test_place_limit_order_passive_advance_ticks_ask()` + /// * `test_place_limit_order_passive_advance_ticks_bid()` + /// + /// # Failure testing + /// + /// * `test_place_limit_order_passive_advance_invalid_base()` + /// * `test_place_limit_order_passive_advance_invalid_market_id()` + /// * `test_place_limit_order_passive_advance_invalid_percent()` + /// * `test_place_limit_order_passive_advance_invalid_quote()` + fun place_limit_order_passive_advance< + BaseType, + QuoteType, + >( + user_address: address, + market_id: u64, + custodian_id: u64, + integrator: address, + side: bool, + size: u64, + advance_style: bool, + target_advance_amount: u64 + ): u128 + acquires OrderBooks { + // Get address of resource account where order books are stored. + let resource_address = resource_account::get_address(); + let order_books_map_ref = // Immutably borrow order books map. + &borrow_global(resource_address).map; + // Assert order books map has order book with given market ID. + assert!(tablist::contains(order_books_map_ref, market_id), + E_INVALID_MARKET_ID); + // Immutably borrow market order book. + let order_book_ref = tablist::borrow(order_books_map_ref, market_id); + assert!(type_info::type_of() // Assert base type. + == order_book_ref.base_type, E_INVALID_BASE); + assert!(type_info::type_of() // Assert quote type. + == order_book_ref.quote_type, E_INVALID_QUOTE); + // Get option-packed maximum bid and minimum ask prices. + let (max_bid_price_option, min_ask_price_option) = + (avl_queue::get_head_key(&order_book_ref.bids), + avl_queue::get_head_key(&order_book_ref.asks)); + // Get best price on given side, and best price on other side. + let (start_price_option, cross_price_option) = if (side == ASK) + (min_ask_price_option, max_bid_price_option) else + (max_bid_price_option, min_ask_price_option); + // Return if there is no price to advance from. + if (option::is_none(&start_price_option)) return (NIL as u128); + // Get price to start advance from. + let start_price = *option::borrow(&start_price_option); + // If target advance amount is 0, price is start price. Else: + let price = if (target_advance_amount == 0) start_price else { + // Return if no cross price. + if (option::is_none(&cross_price_option)) return (NIL as u128); + // Get cross price. + let cross_price = *option::borrow(&cross_price_option); + // Calculate full advance price. If an ask: + let full_advance_price = if (side == ASK) { + // Check price one tick above max bid price. + let check_price = cross_price + 1; + // If check price is less than start price, full advance + // goes to check price. Otherwise do not advance past + // start price. + if (check_price < start_price) check_price else start_price + } else { // If a bid: + // Check price one tick below min ask price. + let check_price = cross_price - 1; + // If check price greater than start price, full advance + // goes to check price. Otherwise do not advance past + // start price. + if (check_price > start_price) check_price else start_price + }; + // Calculate price. If full advance price equals start + // price, do not advance past start price. Otherwise: + if (full_advance_price == start_price) start_price else { + // Calculate full advance in ticks: + let full_advance = if (side == ASK) + // If an ask, calculate max decrement. + (start_price - full_advance_price) else + // If a bid, calculate max increment. + (full_advance_price - start_price); + // Calculate price. If advance specified as percentage: + if (advance_style == PERCENT) { + // Assert target advance amount is a valid percent. + assert!(target_advance_amount <= PERCENT_100, + E_INVALID_PERCENT); + // Calculate price. If target is 100 percent: + if (target_advance_amount == PERCENT_100) + // Price is full advance price. + full_advance_price else { // Otherwise: + let advance = full_advance * target_advance_amount / + PERCENT_100; // Calculate advance in ticks. + // Price is decremented by advance if an ask, + if (side == ASK) start_price - advance else + start_price + advance // Else incremented. + } + } else { // Advance specified number of ticks. + // Calculate price. If target advance amount greater + // than or equal to full advance in ticks: + if (target_advance_amount >= full_advance) + // Price is full advance price. Else if an ask: + full_advance_price else if (side == ASK) + // Price is decremented by target advance + // amount. + start_price - target_advance_amount else + // If a bid, price incremented instead. + start_price + target_advance_amount + } + } + }; // Price now computed. + // Place post-or-abort limit order that aborts for self match, + // storing market order ID. + let (market_order_id, _, _, _) = + place_limit_order( + user_address, market_id, custodian_id, integrator, side, size, + price, POST_OR_ABORT, ABORT, CRITICAL_HEIGHT); + market_order_id // Return market order ID. + } + + /// Place market order against order book from user market account. + /// + /// # Type Parameters + /// + /// * `BaseType`: Same as for `match()`. + /// * `QuoteType`: Same as for `match()`. + /// + /// # Parameters + /// + /// * `user_address`: User address for market account. + /// * `market_id`: Same as for `match()`. + /// * `custodian_id`: Same as for `match()`. + /// * `integrator`: Same as for `match()`. + /// * `direction`: Same as for `match()`. + /// * `size`: Size, in lots, to fill. + /// * `self_match_behavior`: Same as for `match()`. + /// + /// # Returns + /// + /// * `u64`: Base asset trade amount, same as for `match()`. + /// * `u64`: Quote coin trade amount, same as for `match()`. + /// * `u64`: Quote coin fees paid, same as for `match()`. + /// + /// # Aborts + /// + /// * `E_INVALID_BASE`: Base asset type is invalid. + /// * `E_INVALID_QUOTE`: Quote asset type is invalid. + /// * `E_SIZE_TOO_SMALL`: Market order size does not meet minimum + /// size for market. + /// * `E_SIZE_BASE_OVERFLOW`: The product of order size and market + /// lot size results in a base asset unit overflow. + /// + /// # Expected value testing + /// + /// * `test_place_market_order_max_base_below_buy_user()` + /// * `test_place_market_order_max_base_buy_user()` + /// * `test_place_market_order_max_base_sell_custodian()` + /// * `test_place_market_order_max_quote_buy_custodian()` + /// * `test_place_market_order_max_quote_sell_user()` + /// * `test_place_market_order_max_quote_traded()` + /// * `test_place_market_order_not_enough_liquidity()` + /// * `test_place_market_order_remove_event_handles()` + /// + /// # Failure testing + /// + /// * `test_place_market_order_invalid_base()` + /// * `test_place_market_order_invalid_quote()` + /// * `test_place_market_order_size_base_overflow()` + /// * `test_place_market_order_size_too_small()` + fun place_market_order< + BaseType, + QuoteType + >( + user_address: address, + market_id: u64, + custodian_id: u64, + integrator: address, + direction: bool, + size: u64, + self_match_behavior: u8 + ): ( + u64, + u64, + u64 + ) acquires OrderBooks { + // Get user's available and ceiling asset counts. + let (_, base_available, base_ceiling, _, quote_available, + quote_ceiling) = user::get_asset_counts_internal( + user_address, market_id, custodian_id); + // If asset count check does not abort, then market exists, so + // get address of resource account for borrowing order book. + let resource_address = resource_account::get_address(); + let order_books_map_ref_mut = // Mutably borrow order books map. + &mut borrow_global_mut(resource_address).map; + let order_book_ref_mut = // Mutably borrow market order book. + tablist::borrow_mut(order_books_map_ref_mut, market_id); + assert!(type_info::type_of() // Assert base type. + == order_book_ref_mut.base_type, E_INVALID_BASE); + assert!(type_info::type_of() // Assert quote type. + == order_book_ref_mut.quote_type, E_INVALID_QUOTE); + // Assert order size is at least minimum size for market. + assert!(size >= order_book_ref_mut.min_size, E_SIZE_TOO_SMALL); + // Calculate base asset amount corresponding to size in lots. + let base = (size as u128) * (order_book_ref_mut.lot_size as u128); + // Assert corresponding base asset amount fits in a u64. + assert!(base <= (HI_64 as u128), E_SIZE_BASE_OVERFLOW); + // Get market underwriter ID. + let underwriter_id = order_book_ref_mut.underwriter_id; + // Max base to trade is amount calculated from size, lot size. + let max_base = (base as u64); + // Calculate max quote that can be traded: if a buy, quote + // available in market account. If a sell, max quote that can + // fit in market account. + let max_quote = if (direction == BUY) + quote_available else (HI_64 - quote_ceiling); + // Set min base/quote to match as 0. + let (min_base, min_quote) = (0, 0); + range_check_trade( // Range check trade amounts. + direction, min_base, max_base, min_quote, max_quote, + base_available, base_ceiling, quote_available, quote_ceiling); + // Calculate max base and quote to withdraw. If a buy: + let (base_withdraw, quote_withdraw) = if (direction == BUY) + // Withdraw quote to buy base, else sell base for quote. + (0, max_quote) else (max_base, 0); + // Withdraw optional base coins and quote coins for match, + // verifying base type and quote type for market. + let (optional_base_coins, quote_coins) = + user::withdraw_assets_internal( + user_address, market_id, custodian_id, base_withdraw, + quote_withdraw, underwriter_id); + // Calculate limit price for matching engine: 0 when selling, + // max price possible when buying. + let limit_price = if (direction == SELL) 0 else HI_PRICE; + // Match against order book, deferring fill events. + let fill_event_queue = vector[]; + let ( + optional_base_coins, + quote_coins, + base_traded, + quote_traded, + fees, + self_match_taker_cancel, + liquidity_gone, + _ + ) = match( + market_id, + &mut fill_event_queue, + order_book_ref_mut, + user_address, + custodian_id, + integrator, + direction, + min_base, + max_base, + min_quote, + max_quote, + limit_price, + self_match_behavior, + optional_base_coins, + quote_coins + ); + // Get order ID from order book counter updated during matching. + let market_order_id = + ((order_book_ref_mut.counter as u128) << SHIFT_COUNTER); + // Calculate amount of base deposited back to market account. + let base_deposit = if (direction == BUY) base_traded else + (base_withdraw - base_traded); + // Deposit assets back to user's market account. + user::deposit_assets_internal( + user_address, market_id, custodian_id, base_deposit, + optional_base_coins, quote_coins, underwriter_id); + // Get optional cancel reason. + let cancel_reason_option = + get_cancel_reason_option_for_market_order_or_swap( + self_match_taker_cancel, base_traded, max_base, + liquidity_gone, order_book_ref_mut.lot_size, false); + // Emit relevant events to user event handles. + user::emit_market_order_events_internal( + market_id, user_address, custodian_id, integrator, direction, size, + self_match_behavior, market_order_id, &fill_event_queue, + &cancel_reason_option); + // Return base and quote traded by user, fees paid. + (base_traded, quote_traded, fees) + } + + /// Range check minimum and maximum asset trade amounts. + /// + /// Should be called before `match()`. + /// + /// # Terminology + /// + /// * "Inbound asset" is asset received by user. + /// * "Outbound asset" is asset traded away by by user. + /// * "Available asset" is the the user's holdings for either base + /// or quote. When trading from a user's market account, + /// corresponds to either `user::MarketAccount.base_available` or + /// `user::MarketAccount.quote_available`. When trading from a + /// user's `aptos_framework::coin::CoinStore` or from standalone + /// coins, corresponds to coin value. + /// * "Asset ceiling" is the amount that the available asset amount + /// could increase to beyond its present amount, even if the + /// indicated trade were not executed. When trading from a user's + /// market account, corresponds to either + /// `user::MarketAccount.base_ceiling` or + /// `user::MarketAccount.quote_ceiling`. When trading from a + /// user's `aptos_framework::coin::CoinStore` or from standalone + /// coins, is the same as available amount. + /// + /// # Parameters + /// + /// * `direction`: `BUY` or `SELL`. + /// * `min_base`: Minimum amount of change in base holdings after + /// trade. + /// * `max_base`: Maximum amount of change in base holdings after + /// trade. + /// * `min_quote`: Minimum amount of change in quote holdings after + /// trade. + /// * `max_quote`: Maximum amount of change in quote holdings after + /// trade. + /// * `base_available`: Available base asset amount. + /// * `base_ceiling`: Base asset ceiling, only checked when a `BUY`. + /// * `quote_available`: Available quote asset amount. + /// * `quote_ceiling`: Quote asset ceiling, only checked when a + /// `SELL`. + /// + /// # Aborts + /// + /// * `E_MAX_BASE_0`: Maximum base trade amount specified as 0. + /// * `E_MAX_QUOTE_0`: Maximum quote trade amount specified as 0. + /// * `E_MIN_BASE_EXCEEDS_MAX`: Minimum base trade amount is larger + /// than maximum base trade amount. + /// * `E_MIN_QUOTE_EXCEEDS_MAX`: Minimum quote trade amount is + /// larger than maximum quote trade amount. + /// * `E_OVERFLOW_ASSET_IN`: Filling order would overflow asset + /// received from trade. + /// * `E_NOT_ENOUGH_ASSET_OUT`: Not enough asset to trade away. + /// + /// # Failure testing + /// + /// * `test_range_check_trade_asset_in_buy()` + /// * `test_range_check_trade_asset_in_sell()` + /// * `test_range_check_trade_asset_out_buy()` + /// * `test_range_check_trade_asset_out_sell()` + /// * `test_range_check_trade_base_0()` + /// * `test_range_check_trade_min_base_exceeds_max()` + /// * `test_range_check_trade_min_quote_exceeds_max()` + /// * `test_range_check_trade_quote_0()` + fun range_check_trade( + direction: bool, + min_base: u64, + max_base: u64, + min_quote: u64, + max_quote: u64, + base_available: u64, + base_ceiling: u64, + quote_available: u64, + quote_ceiling: u64 + ) { + // Assert nonzero max base trade amount. + assert!(max_base > 0, E_MAX_BASE_0); + // Assert nonzero max quote trade amount. + assert!(max_quote > 0, E_MAX_QUOTE_0); + // Assert minimum base less than or equal to maximum. + assert!(min_base <= max_base, E_MIN_BASE_EXCEEDS_MAX); + // Assert minimum quote less than or equal to maximum. + assert!(min_quote <= max_quote, E_MIN_QUOTE_EXCEEDS_MAX); + // Get inbound asset ceiling and max trade amount, outbound + // asset available and max trade amount. + let (in_ceiling, in_max, out_available, out_max) = + if (direction == BUY) // If trade is in buy direction: + // Getting base and trading away quote. + (base_ceiling, max_base, quote_available, max_quote) else + // Else a sell, so getting quote and trading away base. + (quote_ceiling, max_quote, base_available, max_base); + // Calculate maximum possible inbound asset ceiling post-match. + let in_ceiling_max = (in_ceiling as u128) + (in_max as u128); + // Assert max possible inbound asset ceiling does not overflow. + assert!(in_ceiling_max <= (HI_64 as u128), E_OVERFLOW_ASSET_IN); + // Assert enough outbound asset to cover max trade amount. + assert!(out_max <= out_available, E_NOT_ENOUGH_ASSET_OUT); + } + + /// Register order book, fee store under Econia resource account. + /// + /// Should only be called by `register_market_base_coin()` or + /// `register_market_base_generic()`. + /// + /// See `registry::MarketInfo` for commentary on lot size, tick + /// size, minimum size, and 32-bit prices. + /// + /// # Type parameters + /// + /// * `BaseType`: Base type for market. + /// * `QuoteType`: Quote coin type for market. + /// + /// # Parameters + /// + /// * `market_id`: Market ID for new market. + /// * `base_name_generic`: `registry::MarketInfo.base_name_generic` + /// for market. + /// * `lot_size`: `registry::MarketInfo.lot_size` for market. + /// * `tick_size`: `registry::MarketInfo.tick_size` for market. + /// * `min_size`: `registry::MarketInfo.min_size` for market. + /// * `underwriter_id`: `registry::MarketInfo.min_size` for market. + /// + /// # Returns + /// + /// * `u64`: Market ID for new market. + /// + /// # Testing + /// + /// * `test_register_markets()` + fun register_market< + BaseType, + QuoteType + >( + market_id: u64, + base_name_generic: String, + lot_size: u64, + tick_size: u64, + min_size: u64, + underwriter_id: u64 + ): u64 + acquires OrderBooks { + // Get Econia resource account signer. + let resource_account = resource_account::get_signer(); + // Get resource account address. + let resource_address = address_of(&resource_account); + let order_books_map_ref_mut = // Mutably borrow order books map. + &mut borrow_global_mut(resource_address).map; + // Add order book entry to order books map. + tablist::add(order_books_map_ref_mut, market_id, OrderBook{ + base_type: type_info::type_of(), + base_name_generic, + quote_type: type_info::type_of(), + lot_size, + tick_size, + min_size, + underwriter_id, + asks: avl_queue::new(ASCENDING, 0, 0), + bids: avl_queue::new(DESCENDING, 0, 0), + counter: 0, + maker_events: + account::new_event_handle(&resource_account), + taker_events: + account::new_event_handle(&resource_account)}); + // Register an Econia fee store entry for market quote coin. + incentives::register_econia_fee_store_entry(market_id); + market_id // Return market ID. + } + + /// Match a taker's swap order against order book for given market. + /// + /// # Type Parameters + /// + /// * `BaseType`: Same as for `match()`. + /// * `QuoteType`: Same as for `match()`. + /// + /// # Parameters + /// + /// * `fill_event_queue_ref_mut`: Mutable reference to vector for + /// enqueueing deferred `user::FillEvent`(s). + /// * `signer_address`: Address of signing user if applicable, else + /// `NO_TAKER_ADDRESS`. + /// * `market_id`: Same as for `match()`. + /// * `underwriter_id`: ID of underwriter to verify if `BaseType` + /// is `registry::GenericAsset`, else may be passed as + /// `NO_UNDERWRITER`. + /// * `integrator`: Same as for `match()`. + /// * `direction`: Same as for `match()`. + /// * `min_base`: Same as for `match()`. + /// * `max_base`: Same as for `match()`. + /// * `min_quote`: Same as for `match()`. + /// * `max_quote`: Same as for `match()`. + /// * `limit_price`: Same as for `match()`. + /// * `optional_base_coins`: Same as for `match()`. + /// * `quote_coins`: Same as for `match()`. + /// + /// # Returns + /// + /// * `Option>`: Optional updated base coin holdings, + /// same as for `match()`. + /// * `Coin`: Updated quote coin holdings, same as for + /// `match()`. + /// * `u64`: Base asset trade amount, same as for `match()`. + /// * `u64`: Quote coin trade amount, same as for `match()`. + /// * `u64`: Quote coin fees paid, same as for `match()`. + /// * `Option`: `PlaceSwapOrderEvent` to emit + /// if swap is from a signing swapper. + /// * `Option`: Optional + /// `user::CancelOrderEvent` to emit if swap is from a signing + /// swapper. + /// + /// # Emits + /// + /// * `PlaceSwapOrderEvent`: Information about swap order, emitted + /// when swap is from a non-signing swapper. + /// * `user::CancelOrderEvent`: Information about order + /// cancellation, if order was cancelled without completely + /// filling, when swap is from non-signing swapper. + /// + /// # Aborts + /// + /// * `E_INVALID_MARKET_ID`: No market with given ID. + /// * `E_INVALID_UNDERWRITER`: Underwriter invalid for given market. + /// * `E_INVALID_BASE`: Base asset type is invalid. + /// * `E_INVALID_QUOTE`: Quote asset type is invalid. + /// + /// # Expected value testing + /// + /// * Covered by `swap_between_coinstores()`, `swap_coins()`, and + /// `swap_generic()` testing. + /// + /// # Failure testing + /// + /// * `test_swap_invalid_base()` + /// * `test_swap_invalid_market_id()` + /// * `test_swap_invalid_quote()` + /// * `test_swap_invalid_underwriter()` + fun swap< + BaseType, + QuoteType + >( + fill_event_queue_ref_mut: &mut vector, + signer_address: address, + market_id: u64, + underwriter_id: u64, + integrator: address, + direction: bool, + min_base: u64, + max_base: u64, + min_quote: u64, + max_quote: u64, + limit_price: u64, + optional_base_coins: Option>, + quote_coins: Coin + ): ( + Option>, + Coin, + u64, + u64, + u64, + Option, + Option + ) acquires + MarketEventHandles, + OrderBooks + { + // Get address of resource account where order books are stored. + let resource_address = resource_account::get_address(); + let order_books_map_ref_mut = // Mutably borrow order books map. + &mut borrow_global_mut(resource_address).map; + // Assert order books map has order book with given market ID. + assert!(tablist::contains(order_books_map_ref_mut, market_id), + E_INVALID_MARKET_ID); + let order_book_ref_mut = // Mutably borrow market order book. + tablist::borrow_mut(order_books_map_ref_mut, market_id); + // If passed an underwriter ID, verify it matches market. + if (underwriter_id != NO_UNDERWRITER) + assert!(underwriter_id == order_book_ref_mut.underwriter_id, + E_INVALID_UNDERWRITER); + assert!(type_info::type_of() // Assert base type. + == order_book_ref_mut.base_type, E_INVALID_BASE); + assert!(type_info::type_of() // Assert quote type. + == order_book_ref_mut.quote_type, E_INVALID_QUOTE); + // Match against order book, deferring fill events. + let ( + optional_base_coins, + quote_coins, + base_traded, + quote_traded, + fees, + self_match_taker_cancel, + liquidity_gone, + violated_limit_price + ) = match( + market_id, + fill_event_queue_ref_mut, + order_book_ref_mut, + signer_address, + NO_CUSTODIAN, + integrator, + direction, + min_base, + max_base, + min_quote, + max_quote, + limit_price, + CANCEL_TAKER, + optional_base_coins, + quote_coins + ); + // Get order ID from order book counter updated during matching. + let market_order_id = + ((order_book_ref_mut.counter as u128) << SHIFT_COUNTER); + // Create market event handles for market as needed. + if (!exists(resource_address)) + move_to(&resource_account::get_signer(), + MarketEventHandles{map: table::new()}); + let market_event_handles_map_ref_mut = + &mut borrow_global_mut(resource_address).map; + let has_handles = + table::contains(market_event_handles_map_ref_mut, market_id); + if (!has_handles) { + let resource_signer = resource_account::get_signer(); + let handles = MarketEventHandlesForMarket{ + cancel_order_events: + account::new_event_handle(&resource_signer), + place_swap_order_events: + account::new_event_handle(&resource_signer) + }; + table::add( + market_event_handles_map_ref_mut, market_id, handles); + }; + let handles_ref_mut = + table::borrow_mut(market_event_handles_map_ref_mut, market_id); + // Create market events as necessary. + let place_swap_order_event = PlaceSwapOrderEvent{ + market_id, + signing_account: signer_address, + integrator, + direction, + min_base, + max_base, + min_quote, + max_quote, + limit_price, + order_id: market_order_id + }; + let cancel_reason_option = + get_cancel_reason_option_for_market_order_or_swap( + self_match_taker_cancel, base_traded, max_base, + liquidity_gone, order_book_ref_mut.lot_size, + violated_limit_price); + let need_to_cancel = option::is_some(&cancel_reason_option); + let cancel_order_event_option = if (need_to_cancel) + option::some(user::create_cancel_order_event_internal( + market_id, market_order_id, signer_address, NO_CUSTODIAN, + option::destroy_some(cancel_reason_option))) else + option::none(); + // Assume do not need to return place swap order event. + let place_swap_order_event_option = option::none(); + // If swap not placed by a signing swapper: + if (signer_address == NO_TAKER_ADDRESS) { + event::emit_event(&mut handles_ref_mut.place_swap_order_events, + place_swap_order_event); + if (need_to_cancel) event::emit_event( + &mut handles_ref_mut.cancel_order_events, + option::extract(&mut cancel_order_event_option)); + } else { // Otherwise swap order placed by signing swapper. + option::fill(&mut place_swap_order_event_option, + place_swap_order_event); + }; + user::emit_swap_maker_fill_events_internal(fill_event_queue_ref_mut); + // Return optionally modified asset inputs, trade amounts, fees, + // place swap order event option, and cancel order event option. + (optional_base_coins, quote_coins, base_traded, quote_traded, fees, + place_swap_order_event_option, cancel_order_event_option) + } + + /// Verify pagination function order IDs are valid for market. + /// + /// # Failure testing + /// + /// * `test_verify_pagination_order_ids_ask_does_not_exist()` + /// * `test_verify_pagination_order_ids_ask_wrong_side()` + /// * `test_verify_pagination_order_ids_bid_does_not_exist()` + /// * `test_verify_pagination_order_ids_bid_wrong_side()` + // fun verify_pagination_order_ids( + // market_id: u64, + // starting_ask_order_id: u128, + // starting_bid_order_id: u128, + // ) acquires OrderBooks { + // if (starting_ask_order_id != (NIL as u128)) { + // assert!(has_open_order(market_id, starting_ask_order_id), + // E_INVALID_MARKET_ORDER_ID); + // assert!(get_posted_order_id_side(starting_ask_order_id) == ASK, + // E_INVALID_MARKET_ORDER_ID); + // }; + // if (starting_bid_order_id != (NIL as u128)) { + // assert!(has_open_order(market_id, starting_bid_order_id), + // E_INVALID_MARKET_ORDER_ID); + // assert!(get_posted_order_id_side(starting_bid_order_id) == BID, + // E_INVALID_MARKET_ORDER_ID); + // }; + // } + + // Private functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Deprecated structs >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Deprecated struct retained for compatible upgrade policy. + struct MakerEvent has drop, store { + market_id: u64, + side: bool, + market_order_id: u128, + user: address, + custodian_id: u64, + type: u8, + size: u64, + price: u64 + } + + /// Deprecated struct retained for compatible upgrade policy. + struct Orders has key {asks: vector, bids: vector} + + /// Deprecated struct retained for compatible upgrade policy. + struct TakerEvent has drop, store { + market_id: u64, + side: bool, + market_order_id: u128, + maker: address, + custodian_id: u64, + size: u64, + price: u64 + } + + // Deprecated structs <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Deprecated functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Deprecated functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Deprecated function retained for compatible upgrade policy. + /// + /// # Coverage testing + /// + /// * `test_index_orders_sdk_coverage()` + public entry fun index_orders_sdk(_0: &signer, _1: u64) {} + + // Deprecated functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Test-only functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + // Tests <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +} \ No newline at end of file diff --git a/testsuite/module-publish/src/packages/econia/sources/registry.move b/testsuite/module-publish/src/packages/econia/sources/registry.move new file mode 100644 index 0000000000000..dcce1ed1c1372 --- /dev/null +++ b/testsuite/module-publish/src/packages/econia/sources/registry.move @@ -0,0 +1,2753 @@ +/// Market and capability registration operations. +/// +/// Econia relies on a global market registry, which supports +/// permissionless registration of markets, as well as capabilities. +/// Custodian capabilities are required to approve order operations and +/// withdrawals, while underwriter capabilities are required to approve +/// generic asset amounts. +/// +/// The registry is paired with a recognized market list that tabulates +/// a recognized market for select trading pairs. The recognized market +/// list can only be managed by the Econia account, and provides a set +/// of public APIs that allow lookup of an official market based on a +/// trading pair. +/// +/// Custodian capabilities and underwriter capabilities are 1-indexed, +/// with an ID of 0 reserved as a flag for null. For consistency, market +/// IDs are 1-indexed too. +/// +/// # General overview sections +/// +/// [View functions](#view-functions) +/// +/// * [Constant getters](#constant-getters) +/// * [Market lookup](#market-lookup) +/// +/// [Public function index](#public-function-index) +/// +/// * [Capability management](#capability-management) +/// * [Integrator fee store setup](#integrator-fee-store-setup) +/// * [Recognized market lookup](#recognized-market-lookup) +/// * [Recognized market management](#recognized-market-management) +/// +/// [Dependency charts](#dependency-charts) +/// +/// * [Capability registration](#capability-registration) +/// * [Fee store registration](#fee-store-registration) +/// * [Market getters](#market-getters) +/// * [Recognized market setters](#recognized-market-setters) +/// * [Internal market registration](#internal-market-registration) +/// +/// [Complete DocGen index](#complete-docgen-index) +/// +/// # View functions +/// +/// ## Constant getters +/// +/// * `get_MAX_CHARACTERS_GENERIC()` +/// * `get_MIN_CHARACTERS_GENERIC()` +/// * `get_NO_CUSTODIAN()` +/// * `get_NO_UNDERWRITER()` +/// +/// ## Market lookup +/// +/// * `get_market_counts()` +/// * `get_market_info()` +/// * `get_market_id_base_coin()` +/// * `get_market_id_base_generic()` +/// * `get_recognized_market_id_base_coin()` +/// * `get_recognized_market_id_base_generic()` +/// * `has_recognized_market_base_coin_by_type()` +/// * `has_recognized_market_base_generic_by_type()` +/// +/// # Public function index +/// +/// ## Capability management +/// +/// * `get_custodian_id()` +/// * `get_underwriter_id()` +/// * `register_custodian_capability()` +/// * `register_underwriter_capability()` +/// +/// ## Integrator fee store setup +/// +/// * `register_integrator_fee_store()` +/// * `register_integrator_fee_store_base_tier()` +/// * `register_integrator_fee_store_from_coinstore()` +/// +/// ## Recognized market lookup +/// +/// * `get_recognized_market_info_base_coin()` +/// * `get_recognized_market_info_base_coin_by_type()` +/// * `get_recognized_market_info_base_generic()` +/// * `get_recognized_market_info_base_generic_by_type()` +/// * `has_recognized_market_base_coin()` +/// * `has_recognized_market_base_generic()` +/// +/// ## Recognized market management +/// +/// * `remove_recognized_market()` +/// * `remove_recognized_markets()` +/// * `set_recognized_market()` +/// * `set_recognized_markets()` +/// +/// (These are public entry functions.) +/// +/// # Dependency charts +/// +/// The below dependency charts use `mermaid.js` syntax, which can be +/// automatically rendered into a diagram (depending on the browser) +/// when viewing the documentation file generated from source code. If +/// a browser renders the diagrams with coloring that makes it difficult +/// to read, try a different browser. +/// +/// ## Capability registration +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// register_custodian_capability --> +/// incentives::deposit_custodian_registration_utility_coins +/// register_underwriter_capability --> +/// incentives::deposit_underwriter_registration_utility_coins +/// +/// ``` +/// +/// ## Fee store registration +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// register_integrator_fee_store_base_tier --> +/// register_integrator_fee_store +/// register_integrator_fee_store_from_coinstore --> +/// register_integrator_fee_store +/// +/// ``` +/// +/// ## Market getters +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// get_recognized_market_info_base_coin --> +/// get_recognized_market_info +/// get_recognized_market_info_base_coin_by_type --> +/// get_recognized_market_info_base_coin +/// get_recognized_market_info_base_generic --> +/// get_recognized_market_info +/// get_recognized_market_info_base_generic_by_type --> +/// get_recognized_market_info_base_generic +/// +/// has_recognized_market_base_coin --> has_recognized_market +/// has_recognized_market_base_coin_by_type --> +/// has_recognized_market_base_coin +/// has_recognized_market_base_generic --> has_recognized_market +/// has_recognized_market_base_generic_by_type --> +/// has_recognized_market_base_generic +/// +/// get_recognized_market_id_base_coin --> +/// get_recognized_market_info_base_coin_by_type +/// +/// get_recognized_market_id_base_generic --> +/// get_recognized_market_info_base_generic_by_type +/// +/// get_market_info --> has_recognized_market +/// get_market_info --> get_recognized_market_info +/// +/// get_market_id_base_coin --> get_market_id +/// get_market_id_base_generic --> get_market_id +/// +/// ``` +/// +/// ## Recognized market setters +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// remove_recognized_markets --> remove_recognized_market +/// set_recognized_markets --> set_recognized_market +/// +/// ``` +/// +/// ## Internal market registration +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// register_market_base_coin_internal --> register_market_internal +/// register_market_base_generic_internal --> register_market_internal +/// +/// register_market_internal --> +/// incentives::deposit_market_registration_utility_coins +/// +/// ``` +/// +/// # Complete DocGen index +/// +/// The below index is automatically generated from source code: +module econia::registry { + + // Uses >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + use aptos_framework::coin::{Self, Coin}; + use aptos_framework::account; + use aptos_framework::event::{Self, EventHandle}; + use aptos_framework::table::{Self, Table}; + use aptos_framework::type_info::{Self, TypeInfo}; + use econia::incentives; + use econia::tablist::{Self, Tablist}; + use std::option::{Self, Option}; + use std::signer::address_of; + use std::string::{Self, String}; + use std::vector; + + // Uses <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Friends >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + friend econia::user; + friend econia::market; + + // Friends <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Test-only uses >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test_only] + use econia::assets::{Self, BC, QC, UC}; + + // Test-only uses <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Structs >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Human-readable asset type descriptor for view functions. + struct AssetTypeView has copy, drop { + /// Address of package containing type definition. + package_address: address, + /// Module name where type is defined. + module_name: String, + /// Struct type name, either a phantom `CoinType` or + /// `GenericAsset` + type_name: String + } + + /// Custodian capability required to approve order operations and + /// withdrawals. Administered to third-party registrants who may + /// store it as they wish. + struct CustodianCapability has store { + /// Serial ID, 1-indexed, generated upon registration as a + /// custodian. + custodian_id: u64 + } + + /// Type flag for generic asset. Must be passed as base asset type + /// argument for generic market operations. Has key ability to + /// restrict unexpected malicious attack vectors. + struct GenericAsset has key {} + + /// View function return specifying number of markets and recognized + /// markets that have been registered. + struct MarketCounts has copy, drop { + /// Number of markets. + n_markets: u64, + /// Number of recognized markets. + n_recognized_markets: u64 + } + + /// Information about a market. + struct MarketInfo has copy, drop, store { + /// Base asset type info. When base asset is an + /// `aptos_framework::coin::Coin`, corresponds to the phantom + /// `CoinType` (`address:module::MyCoin` rather than + /// `aptos_framework::coin::Coin`). + /// Otherwise should be `GenericAsset`. + base_type: TypeInfo, + /// Custom base asset name for a generic market, provided by the + /// underwriter who registers the market. Empty if a pure coin + /// market. + base_name_generic: String, + /// Quote asset coin type info. Corresponds to a phantom + /// `CoinType` (`address:module::MyCoin` rather than + /// `aptos_framework::coin::Coin`). + quote_type: TypeInfo, + /// Number of base units exchanged per lot (when base asset is + /// a coin, corresponds to `aptos_framework::coin::Coin.value`). + lot_size: u64, + /// Number of quote coin units exchanged per tick (corresponds + /// to `aptos_framework::coin::Coin.value`). + tick_size: u64, + /// Minimum number of lots per order. + min_size: u64, + /// `NO_UNDERWRITER` if a pure coin market, otherwise ID of + /// underwriter capability required to verify generic asset + /// amounts. A market-wide ID that only applies to markets + /// having a generic base asset. + underwriter_id: u64 + } + + /// Human-readable market info return for view functions. + struct MarketInfoView has copy, drop { + /// 1-indexed Market ID. + market_id: u64, + /// Marked `true` if market is recognized. + is_recognized: bool, + /// `MarketInfo.base_type` as an `AssetTypeView`. + base_type: AssetTypeView, + /// `MarketInfo.base_name_generic`. + base_name_generic: String, + /// `MarketInfo.quote_type` as an `AssetTypeView`. + quote_type: AssetTypeView, + /// `MarketInfo.lot_size`. + lot_size: u64, + /// `MarketInfo.tick_size`. + tick_size: u64, + /// `MarketInfo.min_size`. + min_size: u64, + /// `MarketInfo.underwriter_id`. + underwriter_id: u64 + } + + /// Emitted when a market is registered. + struct MarketRegistrationEvent has drop, store { + /// Market ID of the market just registered. + market_id: u64, + /// Base asset type info. + base_type: TypeInfo, + /// Base asset generic name, if any. + base_name_generic: String, + /// Quote asset type info. + quote_type: TypeInfo, + /// Number of base units exchanged per lot. + lot_size: u64, + /// Number of quote units exchanged per tick. + tick_size: u64, + /// Minimum number of lots per order. + min_size: u64, + /// `NO_UNDERWRITER` if a pure coin market, otherwise ID of + /// underwriter capability required to verify generic asset + /// amounts. + underwriter_id: u64, + } + + /// Emitted when a recognized market is added, removed, or updated. + struct RecognizedMarketEvent has drop, store { + /// The associated trading pair. + trading_pair: TradingPair, + /// The recognized market info for the given trading pair after + /// an addition or update. None if a removal. + recognized_market_info: Option + } + + /// Recognized market info for a given trading pair. + struct RecognizedMarketInfo has copy, drop, store { + /// Market ID of recognized market, 0-indexed. + market_id: u64, + /// Number of base units exchanged per lot. + lot_size: u64, + /// Number of quote units exchanged per tick. + tick_size: u64, + /// Minimum number of lots per order. + min_size: u64, + /// `NO_UNDERWRITER` if a pure coin market, otherwise ID of + /// underwriter capability required to verify generic asset + /// amounts. + underwriter_id: u64 + } + + /// Recognized markets for specific trading pairs. + struct RecognizedMarkets has key { + /// Map from trading pair info to market information for the + /// recognized market, if any, for given trading pair. Enables + /// off-chain iterated indexing by market ID. + map: Tablist, + /// Event handle for recognized market events. + recognized_market_events: EventHandle + } + + /// Global registration information. + struct Registry has key { + /// Map from 1-indexed market ID to corresponding market info, + /// enabling off-chain iterated indexing by market ID. + market_id_to_info: Tablist, + /// Map from market info to corresponding 1-indexed market ID, + /// enabling market duplicate checks. + market_info_to_id: Table, + /// The number of registered custodians. + n_custodians: u64, + /// The number of registered underwriters. + n_underwriters: u64, + /// Event handle for market registration events. + market_registration_events: EventHandle + } + + /// A combination of a base asset and a quote asset. + struct TradingPair has copy, drop, store { + /// Base asset type info. + base_type: TypeInfo, + /// Base asset generic name, if any. + base_name_generic: String, + /// Quote asset type info. + quote_type: TypeInfo + } + + /// Underwriter capability required to verify generic asset + /// amounts. Administered to third-party registrants who may store + /// it as they wish. + struct UnderwriterCapability has store { + /// Serial ID, 1-indexed, generated upon registration as an + /// underwriter. + underwriter_id: u64 + } + + // Structs <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Error codes >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Lot size specified as 0. + const E_LOT_SIZE_0: u64 = 0; + /// Tick size specified as 0. + const E_TICK_SIZE_0: u64 = 1; + /// Minimum order size specified as 0. + const E_MIN_SIZE_0: u64 = 2; + /// Quote asset type has not been initialized as a coin. + const E_QUOTE_NOT_COIN: u64 = 3; + /// Base and quote asset descriptors are identical. + const E_BASE_QUOTE_SAME: u64 = 4; + /// Market is already registered. + const E_MARKET_REGISTERED: u64 = 5; + /// Base coin type has not been initialized for a pure coin market. + const E_BASE_NOT_COIN: u64 = 6; + /// Generic base asset descriptor has too few characters. + const E_GENERIC_TOO_FEW_CHARACTERS: u64 = 7; + /// Generic base asset descriptor has too many characters. + const E_GENERIC_TOO_MANY_CHARACTERS: u64 = 8; + /// Caller is not Econia, but should be. + const E_NOT_ECONIA: u64 = 9; + /// Trading pair does not have recognized market. + const E_NO_RECOGNIZED_MARKET: u64 = 10; + /// Market ID is not recognized for corresponding trading pair. + const E_WRONG_RECOGNIZED_MARKET: u64 = 11; + /// Market ID is invalid. + const E_INVALID_MARKET_ID: u64 = 12; + /// Base asset type is invalid. + const E_INVALID_BASE: u64 = 13; + /// Quote asset type is invalid. + const E_INVALID_QUOTE: u64 = 14; + + // Error codes <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Constants >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Maximum number of characters permitted in a generic asset name, + /// equal to the maximum number of characters permitted in a comment + /// line per PEP 8. + const MAX_CHARACTERS_GENERIC: u64 = 72; + /// Minimum number of characters permitted in a generic asset name, + /// equal to the number of spaces in an indentation level per PEP 8. + const MIN_CHARACTERS_GENERIC: u64 = 4; + /// Custodian ID flag for no custodian. + const NO_CUSTODIAN: u64 = 0; + /// Underwriter ID flag for no underwriter. + const NO_UNDERWRITER: u64 = 0; + + // Constants <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // View functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[view] + /// Public constant getter for `MAX_CHARACTERS_GENERIC`. + /// + /// # Testing + /// + /// * `test_get_MAX_CHARACTERS_GENERIC()` + public fun get_MAX_CHARACTERS_GENERIC(): u64 {MAX_CHARACTERS_GENERIC} + + #[view] + /// Public constant getter for `MIN_CHARACTERS_GENERIC`. + /// + /// # Testing + /// + /// * `test_get_MIN_CHARACTERS_GENERIC()` + public fun get_MIN_CHARACTERS_GENERIC(): u64 {MIN_CHARACTERS_GENERIC} + + #[view] + /// Public constant getter for `NO_CUSTODIAN`. + /// + /// # Testing + /// + /// * `test_get_NO_CUSTODIAN()` + public fun get_NO_CUSTODIAN(): u64 {NO_CUSTODIAN} + + #[view] + /// Public constant getter for `NO_UNDERWRITER`. + /// + /// # Testing + /// + /// * `test_get_NO_UNDERWRITER()` + public fun get_NO_UNDERWRITER(): u64 {NO_UNDERWRITER} + + #[view] + /// Return a `MarketCounts` for current registry state. + /// + /// Restricted to private view function to prevent runtime + /// transaction collisions against the registry. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + fun get_market_counts(): + MarketCounts + acquires + RecognizedMarkets, + Registry + { + let markets_map_ref = // Immutably borrow markets map. + &borrow_global(@econia).market_id_to_info; + // Get number of markets. + let n_markets = tablist::length(markets_map_ref); + // Immutably borrow recognized markets map. + let recognized_markets_map_ref = + &borrow_global(@econia).map; + // Get number of recognized markets. + let n_recognized_markets = tablist::length(recognized_markets_map_ref); + // Return market counts. + MarketCounts{n_markets, n_recognized_markets} + } + + #[view] + /// Return optional market ID corresponding to given market + /// parameters when the base asset is a coin type. + /// + /// Restricted to private view function to prevent runtime + /// transaction collisions against the registry. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + fun get_market_id_base_coin( + lot_size: u64, + tick_size: u64, + min_size: u64, + ): Option + acquires Registry { + get_market_id(MarketInfo{ + base_type: type_info::type_of(), + base_name_generic: string::utf8(b""), + quote_type: type_info::type_of(), + lot_size, + tick_size, + min_size, + underwriter_id: NO_UNDERWRITER + }) + } + + #[view] + /// Return optional market ID corresponding to given market + /// parameters when the base asset is generic. + /// + /// Restricted to private view function to prevent runtime + /// transaction collisions against the registry. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + fun get_market_id_base_generic( + base_name_generic: String, + lot_size: u64, + tick_size: u64, + min_size: u64, + underwriter_id: u64 + ): Option + acquires Registry { + get_market_id(MarketInfo{ + base_type: type_info::type_of(), + base_name_generic, + quote_type: type_info::type_of(), + lot_size, + tick_size, + min_size, + underwriter_id + }) + } + + #[view] + /// Return a `MarketInfoView` for given `market_id`. + /// + /// Restricted to private view function to prevent runtime + /// transaction collisions against the registry. + /// + /// # Testing + /// + /// * `test_get_market_info_invalid_market_id()` + /// * `test_set_remove_check_recognized_markets()` + fun get_market_info( + market_id: u64 + ): MarketInfoView + acquires + RecognizedMarkets, + Registry + { + let markets_map_ref = // Immutably borrow markets map. + &borrow_global(@econia).market_id_to_info; + assert!( // Assert market ID corresponds to registered market. + tablist::contains(markets_map_ref, market_id), + E_INVALID_MARKET_ID); + // Immutably borrow market info for market ID. + let market_info_ref = tablist::borrow(markets_map_ref, market_id); + // Get base type for market. + let base_type = market_info_ref.base_type; + // Get generic base name for market. + let base_name_generic = market_info_ref.base_name_generic; + // Get quote type for market. + let quote_type = market_info_ref.quote_type; + let trading_pair = // Get trading pair for market. + TradingPair{base_type, base_name_generic, quote_type}; + // Check if market is recognized. If a recognized market exists + // for given trading pair: + let is_recognized = if (has_recognized_market(trading_pair)) { + // Get recognized market ID for given trading pair. + let (recognized_market_id_for_trading_pair, _, _, _, _) = + get_recognized_market_info(trading_pair); + // Indicated market ID is recognized if it is the same as + // the recognized market ID for the given trading pair. + market_id == recognized_market_id_for_trading_pair + } else { // If no recognized market for given trading pair: + false // Market is necessarily not recognized. + }; + // Pack and return a human-readable market info view. + MarketInfoView{ + market_id, + is_recognized, + base_type: to_asset_type_view(&base_type), + base_name_generic, + quote_type: to_asset_type_view("e_type), + lot_size: market_info_ref.lot_size, + tick_size: market_info_ref.tick_size, + min_size: market_info_ref.min_size, + underwriter_id: market_info_ref.underwriter_id + } + } + + #[view] + /// Return recognized market ID for a pure coin trading pair. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + public fun get_recognized_market_id_base_coin< + BaseCoinType, + QuoteCoinType + >(): u64 + acquires RecognizedMarkets { + // Check market info for coin types, dropping all but market ID. + let (market_id, _, _, _, _) = + get_recognized_market_info_base_coin_by_type< + BaseCoinType, QuoteCoinType>(); + market_id // Return resultant market ID + } + + #[view] + /// Return recognized market ID for trading pair with generic base + /// asset. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + public fun get_recognized_market_id_base_generic< + QuoteCoinType + >( + base_name_generic: String + ): u64 + acquires RecognizedMarkets { + // Check market info for coin types, dropping all but market ID. + let (market_id, _, _, _, _) = + get_recognized_market_info_base_generic_by_type( + base_name_generic); + market_id // Return resultant market ID + } + + #[view] + /// Wrapper for `has_recognized_market_base_coin()` with type + /// parameters. + /// + /// # Type parameters + /// + /// * `BaseCoinType`: Base asset phantom coin type. + /// * `QuoteCoinType`: Quote asset phantom coin type. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + public fun has_recognized_market_base_coin_by_type< + BaseCoinType, + QuoteCoinType + >(): bool + acquires RecognizedMarkets { + has_recognized_market_base_coin( + type_info::type_of(), + type_info::type_of()) + } + + #[view] + /// Wrapper for `has_recognized_market_base_generic()` with quote + /// type parameter. + /// + /// # Type parameters + /// + /// * `QuoteCoinType`: Quote asset phantom coin type. + /// + /// # Parameters + /// + /// * `base_name_generic`: Generic base asset name. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + public fun has_recognized_market_base_generic_by_type< + QuoteCoinType + >( + base_name_generic: String + ): bool + acquires RecognizedMarkets { + has_recognized_market_base_generic( + base_name_generic, + type_info::type_of()) + } + + // View functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Public functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Return serial ID of given `CustodianCapability`. + /// + /// # Testing + /// + /// * `test_register_capabilities()` + public fun get_custodian_id( + custodian_capability_ref: &CustodianCapability + ): u64 { + custodian_capability_ref.custodian_id + } + + /// Wrapper for `get_recognized_market_info()` for coin base asset. + /// + /// # Parameters + /// + /// * `base_type`: Base asset phantom coin type info. + /// * `quote_type`: Quote asset phantom coin type info. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + public fun get_recognized_market_info_base_coin( + base_type: TypeInfo, + quote_type: TypeInfo + ): ( + u64, + u64, + u64, + u64, + u64 + ) acquires RecognizedMarkets { + // Get empty generic base asset name. + let base_name_generic = string::utf8(b""); + let trading_pair = // Pack trading pair. + TradingPair{base_type, base_name_generic, quote_type}; + // Get recognized market info. + get_recognized_market_info(trading_pair) + } + + /// Wrapper for `get_recognized_market_info_base_coin()` with + /// type parameters. + /// + /// # Type parameters + /// + /// * `BaseCoinType`: Base asset phantom coin type. + /// * `QuoteCoinType`: Quote asset phantom coin type. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + public fun get_recognized_market_info_base_coin_by_type< + BaseCoinType, + QuoteCoinType + >(): ( + u64, + u64, + u64, + u64, + u64 + ) acquires RecognizedMarkets { + get_recognized_market_info_base_coin( + type_info::type_of(), + type_info::type_of()) + } + + /// Wrapper for `get_recognized_market_info()` for generic base + /// asset. + /// + /// # Parameters + /// + /// * `base_name_generic`: Generic base asset name. + /// * `quote_type`: Quote asset phantom coin type info. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + public fun get_recognized_market_info_base_generic( + base_name_generic: String, + quote_type: TypeInfo + ): ( + u64, + u64, + u64, + u64, + u64 + ) acquires RecognizedMarkets { + // Get generic base asset type info. + let base_type = type_info::type_of(); + let trading_pair = // Pack trading pair. + TradingPair{base_type, base_name_generic, quote_type}; + // Get recognized market info. + get_recognized_market_info(trading_pair) + } + + /// Wrapper for `get_recognized_market_info_base_generic()` with + /// quote type parameter. + /// + /// # Type parameters + /// + /// * `QuoteCoinType`: Quote asset phantom coin type. + /// + /// # Parameters + /// + /// * `base_name_generic`: Generic base asset name. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + public fun get_recognized_market_info_base_generic_by_type< + QuoteCoinType + >( + base_name_generic: String, + ): ( + u64, + u64, + u64, + u64, + u64 + ) acquires RecognizedMarkets { + get_recognized_market_info_base_generic( + base_name_generic, + type_info::type_of()) + } + + /// Return serial ID of given `UnderwriterCapability`. + /// + /// # Testing + /// + /// * `test_register_capabilities()` + public fun get_underwriter_id( + underwriter_capability_ref: &UnderwriterCapability + ): u64 { + underwriter_capability_ref.underwriter_id + } + + /// Wrapper for `has_recognized_market()` for coin base asset. + /// + /// # Parameters + /// + /// * `base_type`: Base asset phantom coin type info. + /// * `quote_type`: Quote asset phantom coin type info. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + public fun has_recognized_market_base_coin( + base_type: TypeInfo, + quote_type: TypeInfo + ): bool + acquires RecognizedMarkets { + // Get empty generic base asset name. + let base_name_generic = string::utf8(b""); + let trading_pair = // Pack trading pair. + TradingPair{base_type, base_name_generic, quote_type}; + // Check if trading pair has recognized market. + has_recognized_market(trading_pair) + } + + /// Wrapper for `has_recognized_market()` for generic base asset. + /// + /// # Parameters + /// + /// * `base_name_generic`: Generic base asset name. + /// * `quote_type`: Quote asset phantom coin type info. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + public fun has_recognized_market_base_generic( + base_name_generic: String, + quote_type: TypeInfo + ): bool + acquires RecognizedMarkets { + // Get generic base asset type info. + let base_type = type_info::type_of(); + let trading_pair = // Pack trading pair. + TradingPair{base_type, base_name_generic, quote_type}; + // Check if trading pair has recognized market. + has_recognized_market(trading_pair) + } + + /// Return a unique `CustodianCapability`. + /// + /// Increment the number of registered custodians, then issue a + /// capability with the corresponding serial ID. Requires utility + /// coins to cover the custodian registration fee. + /// + /// # Testing + /// + /// * `test_register_capabilities()` + public fun register_custodian_capability( + utility_coins: Coin + ): CustodianCapability + acquires Registry { + // Borrow mutable reference to registry. + let registry_ref_mut = borrow_global_mut(@econia); + // Set custodian serial ID to the new number of custodians. + let custodian_id = registry_ref_mut.n_custodians + 1; + // Update the registry for the new count. + registry_ref_mut.n_custodians = custodian_id; + incentives:: // Deposit provided utility coins. + deposit_custodian_registration_utility_coins(utility_coins); + // Pack and return corresponding capability. + CustodianCapability{custodian_id} + } + + /// Register integrator fee store to given tier on given market. + /// + /// # Type parameters + /// + /// * `QuoteCoinType`: The quote coin type for market. + /// * `UtilityCoinType`: The utility coin type. + /// + /// # Parameters + /// + /// * `integrator`: Integrator account. + /// * `market_id`: Market ID for corresponding market. + /// * `tier`: `incentives::IntegratorFeeStore` tier to activate to. + /// * `utility_coins`: Utility coins paid to activate to given tier. + /// + /// # Aborts + /// + /// * `E_INVALID_MARKET_ID`: No such registered market ID. + /// * `E_INVALID_QUOTE`: Invalid quote coin type for market. + /// + /// # Testing + /// + /// * `test_register_integrator_fee_store_invalid_market_id()` + /// * `test_register_integrator_fee_store_invalid_quote()` + /// * `test_register_integrator_fee_stores()` + public fun register_integrator_fee_store< + QuoteCoinType, + UtilityCoinType + >( + integrator: &signer, + market_id: u64, + tier: u8, + utility_coins: Coin + ) acquires Registry { + let market_map_ref = // Immutably borrow markets map. + &borrow_global(@econia).market_id_to_info; + // Assert market ID is registered. + assert!(tablist::contains(market_map_ref, market_id), + E_INVALID_MARKET_ID); + // Immutably borrow market info. + let market_info_ref = tablist::borrow(market_map_ref, market_id); + // Assert quote type. + assert!(market_info_ref.quote_type == + type_info::type_of(), E_INVALID_QUOTE); + // Register an integrator fee store at integrator's account. + incentives::register_integrator_fee_store< + QuoteCoinType, UtilityCoinType>(integrator, market_id, tier, + utility_coins); + } + + /// Return a unique `UnderwriterCapability`. + /// + /// Increment the number of registered underwriters, then issue a + /// capability with the corresponding serial ID. Requires utility + /// coins to cover the underwriter registration fee. + /// + /// # Testing + /// + /// * `test_register_capabilities()` + public fun register_underwriter_capability( + utility_coins: Coin + ): UnderwriterCapability + acquires Registry { + // Borrow mutable reference to registry. + let registry_ref_mut = borrow_global_mut(@econia); + // Set underwriter serial ID to the new number of underwriters. + let underwriter_id = registry_ref_mut.n_underwriters + 1; + // Update the registry for the new count. + registry_ref_mut.n_underwriters = underwriter_id; + incentives:: // Deposit provided utility coins. + deposit_underwriter_registration_utility_coins(utility_coins); + // Pack and return corresponding capability. + UnderwriterCapability{underwriter_id} + } + + // Public functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Public entry functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Wrapped call to `register_integrator_fee_store()` for activating + /// to base tier, which does not require utility coins. + /// + /// # Testing + /// + /// * `test_register_integrator_fee_stores()` + public entry fun register_integrator_fee_store_base_tier< + QuoteCoinType, + UtilityCoinType + >( + integrator: &signer, + market_id: u64, + ) acquires Registry { + register_integrator_fee_store( + integrator, market_id, 0, coin::zero()); + } + + /// Wrapped call to `register_integrator_fee_store()` for paying + /// utility coins from an `aptos_framework::coin::CoinStore`. + /// + /// # Testing + /// + /// * `test_register_integrator_fee_stores()` + public entry fun register_integrator_fee_store_from_coinstore< + QuoteCoinType, + UtilityCoinType + >( + integrator: &signer, + market_id: u64, + tier: u8 + ) acquires Registry { + register_integrator_fee_store( + integrator, market_id, tier, coin::withdraw( + integrator, incentives::get_tier_activation_fee(tier))); + } + + /// Remove market having given ID from recognized markets list. + /// + /// # Parameters + /// + /// * `account`: Econia account. + /// * `market_id`: Market ID to recognize. + /// + /// # Emits + /// + /// * `RecognizedMarketEvent`: Info about recognized market for + /// given trading pair. + /// + /// # Aborts + /// + /// * `E_NOT_ECONIA`: `account` is not Econia. + /// * `E_NO_RECOGNIZED_MARKET`: Market having given ID is not a + /// recognized market. + /// * `E_WRONG_RECOGNIZED_MARKET`: Market ID is not recognized for + /// corresponding trading pair. + /// + /// # Assumptions + /// + /// * `market_id` corresponds to a registered market. + /// + /// # Testing + /// + /// * `test_remove_recognized_market_no_recognized()` + /// * `test_remove_recognized_market_not_econia()` + /// * `test_remove_recognized_market_wrong_market()` + /// * `test_set_remove_check_recognized_markets()` + public entry fun remove_recognized_market( + account: &signer, + market_id: u64 + ) acquires + RecognizedMarkets, + Registry + { + // Assert account is Econia. + assert!(address_of(account) == @econia, E_NOT_ECONIA); + let markets_map_ref = // Immutably borrow markets map. + &borrow_global(@econia).market_id_to_info; + // Immutably borrow info for market having given ID. + let market_info_ref = tablist::borrow(markets_map_ref, market_id); + let trading_pair = // Pack trading pair from market info. + TradingPair{base_type: market_info_ref.base_type, + base_name_generic: market_info_ref.base_name_generic, + quote_type: market_info_ref.quote_type}; + // Mutably borrow recognized markets resource. + let recognized_markets_ref_mut = + borrow_global_mut(@econia); + // Mutably borrow recognized markets map. + let recognized_map_ref_mut = &mut recognized_markets_ref_mut.map; + assert!( // Assert trading pair has a recognized market. + tablist::contains(recognized_map_ref_mut, trading_pair), + E_NO_RECOGNIZED_MARKET); + // Get recognized market ID for corresponding trading pair. + let recognized_market_id_for_trading_pair = + tablist::borrow(recognized_map_ref_mut, trading_pair).market_id; + // Assert passed market ID matches that of recognized market ID + // for given trading pair. + assert!(recognized_market_id_for_trading_pair == market_id, + E_WRONG_RECOGNIZED_MARKET); + // Remove entry for given trading pair. + tablist::remove(recognized_map_ref_mut, trading_pair); + // Mutably borrow recognized markets events handle. + let event_handle_ref_mut = + &mut recognized_markets_ref_mut.recognized_market_events; + // Emit a recognized market event. + event::emit_event(event_handle_ref_mut, RecognizedMarketEvent{ + trading_pair, recognized_market_info: option::none()}); + } + + /// Wrapper for `remove_recognized_market()` with market IDs vector. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + public entry fun remove_recognized_markets( + account: &signer, + market_ids: vector + ) acquires + RecognizedMarkets, + Registry + { + // Get number of markets to remove. + let n_markets = vector::length(&market_ids); + let i = 0; // Declare loop counter. + while (i < n_markets) { // Loop over all markets in vector: + // Get market ID to remove. + let market_id = *vector::borrow(&market_ids, i); + // Remove as recognized market. + remove_recognized_market(account, market_id); + i = i + 1; // Increment loop counter. + } + } + + /// Set market having given ID as recognized market. + /// + /// # Parameters + /// + /// * `account`: Econia account. + /// * `market_id`: Market ID to recognize. + /// + /// # Emits + /// + /// * `RecognizedMarketEvent`: Info about recognized market for + /// given trading pair. + /// + /// # Aborts + /// + /// * `E_NOT_ECONIA`: `account` is not Econia. + /// + /// # Assumptions + /// + /// * `market_id` corresponds to a registered market. + /// + /// # Testing + /// + /// * `test_set_recognized_market_not_econia()` + /// * `test_set_recognized_market_update()` + /// * `test_set_remove_check_recognized_markets()` + public entry fun set_recognized_market( + account: &signer, + market_id: u64 + ) acquires + RecognizedMarkets, + Registry + { + // Assert account is Econia. + assert!(address_of(account) == @econia, E_NOT_ECONIA); + let markets_map_ref = // Immutably borrow markets map. + &borrow_global(@econia).market_id_to_info; + // Immutably borrow info for market having given ID. + let market_info_ref = tablist::borrow(markets_map_ref, market_id); + // Get recognized market info parameters. + let (base_type, base_name_generic, quote_type, lot_size, tick_size, + min_size, underwriter_id) = + (market_info_ref.base_type, market_info_ref.base_name_generic, + market_info_ref.quote_type, market_info_ref.lot_size, + market_info_ref.tick_size, market_info_ref.min_size, + market_info_ref.underwriter_id); + let trading_pair = // Pack trading pair. + TradingPair{base_type, base_name_generic, quote_type}; + // Pack recognized market info. + let recognized_market_info = RecognizedMarketInfo{ + market_id, lot_size, tick_size, min_size, underwriter_id}; + // Mutably borrow recognized markets resource. + let recognized_markets_ref_mut = + borrow_global_mut(@econia); + // Mutably borrow recognized markets map. + let recognized_map_ref_mut = &mut recognized_markets_ref_mut.map; + let new = // New if trading pair not already recognized. + !tablist::contains(recognized_map_ref_mut, trading_pair); + // If new trading pair, add an entry to map. + if (new) tablist::add( + recognized_map_ref_mut, trading_pair, recognized_market_info) + // Otherwise update existing entry. + else *tablist::borrow_mut(recognized_map_ref_mut, trading_pair) = + recognized_market_info; + // Pack market info in an option. + let optional_market_info = option::some(recognized_market_info); + // Mutably borrow recognized markets events handle. + let event_handle_ref_mut = + &mut recognized_markets_ref_mut.recognized_market_events; + // Emit a recognized market event. + event::emit_event(event_handle_ref_mut, RecognizedMarketEvent{ + trading_pair, recognized_market_info: optional_market_info}); + } + + /// Wrapper for `set_recognized_market()` with market IDs vector. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + public entry fun set_recognized_markets( + account: &signer, + market_ids: vector + ) acquires + RecognizedMarkets, + Registry + { + // Get number of markets to set. + let n_markets = vector::length(&market_ids); + let i = 0; // Declare loop counter. + while (i < n_markets) { // Loop over all markets in vector: + // Get market ID to set. + let market_id = *vector::borrow(&market_ids, i); + // Set as recognized market. + set_recognized_market(account, market_id); + i = i + 1; // Increment loop counter. + } + } + + // Public entry functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Public friend functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Check types, return market info for market account registration. + /// + /// Restricted to friends to prevent excessive public queries + /// against the registry. + /// + /// # Parameters + /// + /// * `market_id`: Market ID to check. + /// * `base_type`: Base type to check. + /// * `quote_type`: Quote type to check. + /// + /// # Returns + /// + /// * `String`: `MarketInfo.base_name_generic`. + /// * `u64`: `MarketInfo.lot_size`. + /// * `u64`: `MarketInfo.tick_size`. + /// * `u64`: `MarketInfo.min_size`. + /// * `u64`: `MarketInfo.underwriter_id`. + /// + /// # Aborts + /// + /// * `E_INVALID_MARKET_ID`: Market ID is invalid. + /// * `E_INVALID_BASE`: Base asset type is invalid. + /// * `E_INVALID_QUOTE`: Quote asset type is invalid. + /// + /// # Testing + /// + /// * `test_get_market_info_for_market_account()` + /// * `test_get_market_info_for_market_account_invalid_base()` + /// * `test_get_market_info_for_market_account_invalid_market_id()` + /// * `test_get_market_info_for_market_account_invalid_quote()` + public(friend) fun get_market_info_for_market_account( + market_id: u64, + base_type: TypeInfo, + quote_type: TypeInfo + ): ( + String, + u64, + u64, + u64, + u64 + ) acquires Registry { + let markets_map_ref = // Immutably borrow markets map. + &borrow_global(@econia).market_id_to_info; + assert!( // Assert market ID corresponds to registered market. + tablist::contains(markets_map_ref, market_id), + E_INVALID_MARKET_ID); + // Immutably borrow market info for market ID. + let market_info_ref = tablist::borrow(markets_map_ref, market_id); + // Assert valid base asset type info. + assert!(base_type == market_info_ref.base_type, E_INVALID_BASE); + // Assert valid quote asset type info. + assert!(quote_type == market_info_ref.quote_type, E_INVALID_QUOTE); + (market_info_ref.base_name_generic, // Return market info. + market_info_ref.lot_size, + market_info_ref.tick_size, + market_info_ref.min_size, + market_info_ref.underwriter_id) + } + + /// Return `true` if `custodian_id` has been registered. + /// + /// Restricted to friends to prevent excessive public queries + /// against the registry. + /// + /// # Testing + /// + /// * `test_register_capabilities()` + public(friend) fun is_registered_custodian_id( + custodian_id: u64 + ): bool + acquires Registry { + // Get number of registered custodians. + let n_custodians = borrow_global(@econia).n_custodians; + // Return if custodian ID is less than or equal to number of + // registered custodians and, if is not flag for no custodian. + (custodian_id <= n_custodians) && (custodian_id != NO_CUSTODIAN) + } + + /// Wrapped market registration call for a base coin type. + /// + /// See inner function `register_market_internal()`. + /// + /// # Aborts + /// + /// * `E_BASE_NOT_COIN`: Base coin type is not initialized. + /// + /// # Testing + /// + /// * `test_register_market_base_not_coin()` + /// * `test_register_market_base_coin_internal()` + public(friend) fun register_market_base_coin_internal< + BaseCoinType, + QuoteCoinType, + UtilityCoinType + >( + lot_size: u64, + tick_size: u64, + min_size: u64, + utility_coins: Coin + ): u64 + acquires Registry { + // Assert base coin type is initialized. + assert!(coin::is_coin_initialized(), E_BASE_NOT_COIN); + // Add to the registry a corresponding entry, returning new + // market ID. + register_market_internal( + type_info::type_of(), string::utf8(b""), lot_size, + tick_size, min_size, NO_UNDERWRITER, utility_coins) + } + + /// Wrapped market registration call for a generic base type, + /// requiring immutable reference to corresponding + /// `UnderwriterCapability` for the market, and `base_type` + /// descriptor. + /// + /// See inner function `register_market_internal()`. + /// + /// # Aborts + /// + /// * `E_GENERIC_TOO_FEW_CHARACTERS`: Asset descriptor is too short. + /// * `E_GENERIC_TOO_MANY_CHARACTERS`: Asset descriptor is too long. + /// + /// # Testing + /// + /// * `test_register_market_base_generic_internal()` + /// * `test_register_market_generic_name_too_few()` + /// * `test_register_market_generic_name_too_many()` + public(friend) fun register_market_base_generic_internal< + QuoteCoinType, + UtilityCoinType + >( + base_name_generic: String, + lot_size: u64, + tick_size: u64, + min_size: u64, + underwriter_capability_ref: &UnderwriterCapability, + utility_coins: Coin + ): u64 + acquires Registry { + // Get generic asset name length. + let name_length = string::length(&base_name_generic); + assert!( // Assert generic base asset string is not too short. + name_length >= MIN_CHARACTERS_GENERIC, + E_GENERIC_TOO_FEW_CHARACTERS); + assert!( // Assert generic base asset string is not too long. + name_length <= MAX_CHARACTERS_GENERIC, + E_GENERIC_TOO_MANY_CHARACTERS); + // Get underwriter ID. + let underwriter_id = underwriter_capability_ref.underwriter_id; + // Add to the registry a corresponding entry, returning new + // market ID. + register_market_internal( + type_info::type_of(), base_name_generic, lot_size, + tick_size, min_size, underwriter_id, utility_coins) + } + + // Public friend functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Private functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Return optional market ID corresponding to given `MarketInfo`. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + fun get_market_id( + market_info: MarketInfo + ): Option + acquires Registry { + let market_id_map_ref = // Immutably borrow market ID map. + &borrow_global(@econia).market_info_to_id; + // Return optional market ID if one exists, else empty option. + if (table::contains(market_id_map_ref, market_info)) + option::some(*table::borrow(market_id_map_ref, market_info)) else + option::none() + } + + /// Return recognized market info for given trading pair. + /// + /// # Parameters + /// + /// * `trading_pair`: Trading pair to look up. + /// + /// # Returns + /// + /// * `u64`: `RecognizedMarketInfo.market_id` + /// * `u64`: `RecognizedMarketInfo.lot_size` + /// * `u64`: `RecognizedMarketInfo.tick_size` + /// * `u64`: `RecognizedMarketInfo.min_size` + /// * `u64`: `RecognizedMarketInfo.underwriter_id` + /// + /// # Aborts + /// + /// * `E_NO_RECOGNIZED_MARKET`: Trading pair has no recognized + /// market. + /// + /// # Testing + /// + /// * `test_get_recognized_market_info_no_market()` + /// * `test_set_remove_check_recognized_markets()` + fun get_recognized_market_info( + trading_pair: TradingPair + ): ( + u64, + u64, + u64, + u64, + u64 + ) acquires RecognizedMarkets { + // Mutably borrow recognized markets map. + let recognized_map_ref = + &borrow_global(@econia).map; + // Assert is actually recognized. + assert!(tablist::contains(recognized_map_ref, trading_pair), + E_NO_RECOGNIZED_MARKET); + // Immutably borrow corresponding recognized market info. + let recognized_market_info_ref = + *tablist::borrow(recognized_map_ref, trading_pair); + // Return recognized market info. + (recognized_market_info_ref.market_id, + recognized_market_info_ref.lot_size, + recognized_market_info_ref.tick_size, + recognized_market_info_ref.min_size, + recognized_market_info_ref.underwriter_id) + } + + /// Return `true` if given `TradingPair` has recognized market. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + fun has_recognized_market( + trading_pair: TradingPair + ): bool + acquires RecognizedMarkets { + // Mutably borrow recognized markets map. + let recognized_map_ref = + &borrow_global(@econia).map; + // Return if map contains entry for given trading pair. + tablist::contains(recognized_map_ref, trading_pair) + } + + /// Initialize the Econia registry and recognized markets list upon + /// module publication. + fun init_module( + econia: &signer + ) { + // Initialize registry. + move_to(econia, Registry{ + market_id_to_info: tablist::new(), + market_info_to_id: table::new(), + n_custodians: 0, + n_underwriters: 0, + market_registration_events: + account::new_event_handle(econia)}); + // Initialize recognized markets list. + move_to(econia, RecognizedMarkets{ + map: tablist::new(), + recognized_market_events: + account::new_event_handle(econia)}); + } + + /// Register a market in the global registry. + /// + /// # Type parameters + /// + /// * `QuoteCoinType`: The quote coin type for the market. + /// * `UtilityCoinType`: The utility coin type. + /// + /// # Parameters + /// + /// * `base_type`: The base coin type info for a pure coin market, + /// otherwise that of `GenericAsset`. + /// * `base_name_generic`: Base asset generic name, if any. + /// * `lot_size`: Lot size for the market. + /// * `tick_size`: Tick size for the market. + /// * `min_size`: Minimum lots per order for market. + /// * `underwriter_id`: `NO_UNDERWRITER` if a pure coin market, + /// otherwise ID of market underwriter. + /// * `utility_coins`: Utility coins paid to register a market. + /// + /// # Emits + /// + /// * `MarketRegistrationEvent`: Parameters of market just + /// registered. + /// + /// # Aborts + /// + /// * `E_LOT_SIZE_0`: Lot size is 0. + /// * `E_TICK_SIZE_0`: Tick size is 0. + /// * `E_MIN_SIZE_0`: Minimum size is 0. + /// * `E_QUOTE_NOT_COIN`: Quote coin type not initialized as coin. + /// * `E_BASE_QUOTE_SAME`: Base and quote type are the same. + /// * `E_MARKET_REGISTERED`: Markets map already contains an entry + /// for specified market info. + /// + /// # Assumptions + /// + /// * `underwriter_id` has been properly passed by either + /// `register_market_base_coin_internal()` or + /// `register_market_base_generic_internal()`. + /// + /// # Testing + /// + /// * `test_register_market_base_coin_internal()` + /// * `test_register_market_base_generic_internal()` + /// * `test_register_market_lot_size_0()` + /// * `test_register_market_min_size_0()` + /// * `test_register_market_quote_not_coin()` + /// * `test_register_market_registered()` + /// * `test_register_market_same_type()` + /// * `test_register_market_tick_size_0()` + fun register_market_internal< + QuoteCoinType, + UtilityCoinType + >( + base_type: TypeInfo, + base_name_generic: String, + lot_size: u64, + tick_size: u64, + min_size: u64, + underwriter_id: u64, + utility_coins: Coin + ): u64 + acquires Registry { + // Assert lot size is nonzero. + assert!(lot_size > 0, E_LOT_SIZE_0); + // Assert tick size is nonzero. + assert!(tick_size > 0, E_TICK_SIZE_0); + // Assert minimum size is nonzero. + assert!(min_size > 0, E_MIN_SIZE_0); + // Assert quote coin type is initialized. + assert!(coin::is_coin_initialized(), E_QUOTE_NOT_COIN); + // Get quote coin type. + let quote_type = type_info::type_of(); + // Assert base and quote type names are not the same. + assert!(base_type != quote_type, E_BASE_QUOTE_SAME); + let market_info = MarketInfo{ // Pack market info. + base_type, base_name_generic, quote_type, lot_size, tick_size, + min_size, underwriter_id}; + // Mutably borrow registry. + let registry_ref_mut = borrow_global_mut(@econia); + // Mutably borrow map from market info to market ID. + let info_to_id_ref_mut = &mut registry_ref_mut.market_info_to_id; + assert!( // Assert market not registered. + !table::contains(info_to_id_ref_mut, market_info), + E_MARKET_REGISTERED); + // Mutably borrow map from market ID to market info. + let id_to_info_ref_mut = &mut registry_ref_mut.market_id_to_info; + // Get 1-indexed market ID. + let market_id = tablist::length(id_to_info_ref_mut) + 1; + // Register a market entry in map from market info to market ID. + table::add(info_to_id_ref_mut, market_info, market_id); + // Register a market entry in map from market ID to market info. + tablist::add(id_to_info_ref_mut, market_id, market_info); + // Mutably borrow market registration events handle. + let event_handle_ref_mut = + &mut registry_ref_mut.market_registration_events; + // Emit a market registration event. + event::emit_event(event_handle_ref_mut, MarketRegistrationEvent{ + market_id, base_type, base_name_generic, quote_type, lot_size, + tick_size, min_size, underwriter_id}); + incentives::deposit_market_registration_utility_coins( + utility_coins); // Deposit utility coins. + market_id // Return market ID. + } + + /// Convert a `TypeInfo` to an `AssetTypeView`. + /// + /// # Testing + /// + /// * `test_set_remove_check_recognized_markets()` + fun to_asset_type_view( + type_info_ref: &TypeInfo + ): AssetTypeView { + AssetTypeView{ + package_address: type_info::account_address(type_info_ref), + module_name: string::utf8(type_info::module_name(type_info_ref)), + type_name: string::utf8(type_info::struct_name(type_info_ref)), + } + } + + // Private functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Test-only constants >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test_only] + /// For `register_markets_test()`. + const LOT_SIZE_PURE_COIN: u64 = 1; + #[test_only] + /// For `register_markets_test()`. + const TICK_SIZE_PURE_COIN: u64 = 2; + #[test_only] + /// For `register_markets_test()`. + const MIN_SIZE_PURE_COIN: u64 = 3; + #[test_only] + /// For `register_markets_test()`. + const BASE_NAME_GENERIC_GENERIC: vector = b"Generic asset"; + #[test_only] + /// For `register_markets_test()`. + const LOT_SIZE_GENERIC: u64 = 4; + #[test_only] + /// For `register_markets_test()`. + const TICK_SIZE_GENERIC: u64 = 5; + #[test_only] + /// For `register_markets_test()`. + const MIN_SIZE_GENERIC: u64 = 6; + #[test_only] + /// For `register_markets_test()`. + const UNDERWRITER_ID_GENERIC: u64 = 7; + + // Test-only constants <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Test-only functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test_only] + /// Drop the given `CustodianCapability`. + public fun drop_custodian_capability_test( + custodian_capability: CustodianCapability + ) { + // Unpack provided capability. + let CustodianCapability{custodian_id: _} = custodian_capability; + } + + #[test_only] + /// Drop the given `UnderwriterCapability`. + public fun drop_underwriter_capability_test( + underwriter_capability: UnderwriterCapability + ) { + // Unpack provided capability. + let UnderwriterCapability{underwriter_id: _} = underwriter_capability; + } + + #[test_only] + /// Return a `CustodianCapabilty` having given ID, setting it as + /// a valid ID in the registry. + public fun get_custodian_capability_test( + custodian_id: u64 + ): CustodianCapability + acquires Registry { + // If proposed custodian ID is less than number registered: + if (custodian_id < borrow_global(@econia).n_custodians) + // Update registry to have provided ID as number registered. + borrow_global_mut(@econia).n_custodians = + custodian_id; + // Return corresponding custodian capability. + CustodianCapability{custodian_id} + } + + #[test_only] + /// Return an `UnderwriterCapabilty` having given ID, setting it as + /// a valid ID in the registry. + public fun get_underwriter_capability_test( + underwriter_id: u64 + ): UnderwriterCapability + acquires Registry { + // If proposed underwriter ID is less than number registered: + if (underwriter_id < borrow_global(@econia).n_underwriters) + // Update registry to have provided ID as number registered. + borrow_global_mut(@econia).n_underwriters = + underwriter_id; + // Return corresponding underwriter capability. + UnderwriterCapability{underwriter_id} + } + + #[test_only] + /// Initialize registry for testing, returning Econia signer. + public fun init_test(): + signer { + // Create Aptos-style account for Econia, storing signer. + let econia = account::create_account_for_test(@econia); + init_module(&econia); // Init registry. + incentives::init_test(); // Init incentives. + econia // Return signer. + } + + #[test_only] + /// Register pure coin and generic markets, returning market info. + public fun register_markets_test(): ( + u64, + String, + u64, + u64, + u64, + u64, + u64, + String, + u64, + u64, + u64, + u64 + ) acquires Registry { + init_test(); // Initialize for testing. + // Get market registration fee. + let fee = incentives::get_market_registration_fee(); + // Declare market parameters. + let base_name_generic_pure_coin = string::utf8(b""); + let lot_size_pure_coin = LOT_SIZE_PURE_COIN; + let tick_size_pure_coin = TICK_SIZE_PURE_COIN; + let min_size_pure_coin = MIN_SIZE_PURE_COIN; + let underwriter_id_pure_coin = NO_UNDERWRITER; + let base_name_generic_generic + = string::utf8(BASE_NAME_GENERIC_GENERIC); + let lot_size_generic = LOT_SIZE_GENERIC; + let tick_size_generic = TICK_SIZE_GENERIC; + let min_size_generic = MIN_SIZE_GENERIC; + let underwriter_id_generic = UNDERWRITER_ID_GENERIC; + let underwriter_capability = // Get underwriter capability. + get_underwriter_capability_test(underwriter_id_generic); + // Register markets. + let market_id_pure_coin = register_market_base_coin_internal< + BC, QC, UC>(lot_size_pure_coin, tick_size_pure_coin, + min_size_pure_coin, assets::mint_test(fee)); + let market_id_generic = register_market_base_generic_internal( + base_name_generic_generic, lot_size_generic, tick_size_generic, + min_size_generic, &underwriter_capability, assets::mint_test(fee)); + // Drop underwriter capability. + drop_underwriter_capability_test(underwriter_capability); + // Return market info. + (market_id_pure_coin, + base_name_generic_pure_coin, + lot_size_pure_coin, + tick_size_pure_coin, + min_size_pure_coin, + underwriter_id_pure_coin, + market_id_generic, + base_name_generic_generic, + lot_size_generic, + tick_size_generic, + min_size_generic, + underwriter_id_generic) + } + + #[test_only] + /// Update registry to indicate custodian ID is valid. + public fun set_registered_custodian_test( + custodian_id: u64 + ) acquires Registry { + let n_custodians_ref_mut = // Mutably borrow custodian count. + &mut borrow_global_mut(@econia).n_custodians; + // If custodian ID is greater than number of registered + // custodians, update count to ID. + if (custodian_id > *n_custodians_ref_mut) + *n_custodians_ref_mut = custodian_id; + } + + // Test-only functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Tests >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test] + /// Verify returns. + fun test_get_market_info_for_market_account() + acquires Registry { + init_test(); // Initialize for testing. + let underwriter_id = 100; // Declare underwriter ID. + let underwriter_capability = // Get underwriter capability. + get_underwriter_capability_test(underwriter_id); + // Declare arguments. + let lot_size = 123; + let tick_size = 456; + let min_size = 789; + let base_name_generic = string::utf8(b"Generic asset"); + // Get market registration fee. + let fee = incentives::get_market_registration_fee(); + // Register market, storing market ID. + let market_id = register_market_base_generic_internal( + base_name_generic, lot_size, tick_size, min_size, + &underwriter_capability, assets::mint_test(fee)); + // Drop underwriter capability. + drop_underwriter_capability_test(underwriter_capability); + let (base_type, quote_type) = // Get asset types. + (type_info::type_of(), type_info::type_of()); + // Get market info returns. + let (base_name_generic_r, lot_size_r, tick_size_r, min_size_r, + underwriter_id_r) = get_market_info_for_market_account( + market_id, base_type, quote_type); + // Assert returns. + assert!(base_name_generic_r == base_name_generic, 0); + assert!(lot_size_r == lot_size, 0); + assert!(tick_size_r == tick_size, 0); + assert!(min_size_r == min_size, 0); + assert!(underwriter_id_r == underwriter_id, 0); + } + + #[test] + #[expected_failure(abort_code = E_INVALID_BASE)] + /// Verify failure for invalid base asset. + fun test_get_market_info_for_market_account_invalid_base() + acquires Registry { + init_test(); // Initialize for testing. + let underwriter_id = 100; // Declare underwriter ID. + let underwriter_capability = // Get underwriter capability. + get_underwriter_capability_test(underwriter_id); + // Declare arguments. + let lot_size = 123; + let tick_size = 456; + let min_size = 789; + let base_name_generic = string::utf8(b"Generic asset"); + // Get market registration fee. + let fee = incentives::get_market_registration_fee(); + // Register market, storing market ID. + let market_id = register_market_base_generic_internal( + base_name_generic, lot_size, tick_size, min_size, + &underwriter_capability, assets::mint_test(fee)); + // Drop underwriter capability. + drop_underwriter_capability_test(underwriter_capability); + let (base_type, quote_type) = // Get asset types (invalid base). + (type_info::type_of(), type_info::type_of()); + // Attempt invalid invocation. + get_market_info_for_market_account(market_id, base_type, quote_type); + } + + #[test] + #[expected_failure(abort_code = E_INVALID_MARKET_ID)] + /// Verify failure for invalid market ID. + fun test_get_market_info_for_market_account_invalid_market_id() + acquires Registry { + init_test(); // Initialize for testing. + let (base_type, quote_type) = // Get asset types. + (type_info::type_of(), type_info::type_of()); + // Attempt invalid invocation. + get_market_info_for_market_account(123, base_type, quote_type); + } + + #[test] + #[expected_failure(abort_code = E_INVALID_QUOTE)] + /// Verify failure for invalid quote asset. + fun test_get_market_info_for_market_account_invalid_quote() + acquires Registry { + init_test(); // Initialize for testing. + let underwriter_id = 100; // Declare underwriter ID. + let underwriter_capability = // Get underwriter capability. + get_underwriter_capability_test(underwriter_id); + // Declare arguments. + let lot_size = 123; + let tick_size = 456; + let min_size = 789; + let base_name_generic = string::utf8(b"Generic asset"); + // Get market registration fee. + let fee = incentives::get_market_registration_fee(); + // Register market, storing market ID. + let market_id = register_market_base_generic_internal( + base_name_generic, lot_size, tick_size, min_size, + &underwriter_capability, assets::mint_test(fee)); + // Drop underwriter capability. + drop_underwriter_capability_test(underwriter_capability); + let (base_type, quote_type) = // Get asset types (wrong quote). + (type_info::type_of(), type_info::type_of()); + // Attempt invalid invocation. + get_market_info_for_market_account(market_id, base_type, quote_type); + } + + #[test] + /// Verify constant getter return. + fun test_get_MAX_CHARACTERS_GENERIC() { + assert!(get_MAX_CHARACTERS_GENERIC() == MAX_CHARACTERS_GENERIC, 0) + } + + #[test] + /// Verify constant getter return. + fun test_get_MIN_CHARACTERS_GENERIC() { + assert!(get_MIN_CHARACTERS_GENERIC() == MIN_CHARACTERS_GENERIC, 0) + } + + #[test] + /// Verify constant getter return. + fun test_get_NO_CUSTODIAN() { + assert!(get_NO_CUSTODIAN() == NO_CUSTODIAN, 0); + } + + #[test] + /// Verify constant getter return. + fun test_get_NO_UNDERWRITER() { + assert!(get_NO_UNDERWRITER() == NO_UNDERWRITER, 0) + } + + #[test] + #[expected_failure(abort_code = E_INVALID_MARKET_ID)] + /// Verify failure for no such market ID. + fun test_get_market_info_invalid_market_id() + acquires + RecognizedMarkets, + Registry + { + init_test(); // Initialize for testing. + get_market_info(1); // Attempt invalid invocation. + } + + #[test] + #[expected_failure(abort_code = E_NO_RECOGNIZED_MARKET)] + /// Verify failure for no recognized market. + fun test_get_recognized_market_info_no_market() + acquires RecognizedMarkets { + init_test(); // Initialize for testing. + // Attempt invalid invocation. + get_recognized_market_info_base_coin_by_type(); + } + + #[test] + /// Verify custodian then underwriter capability registration. + fun test_register_capabilities() + acquires Registry { + init_test(); // Initialize for testing. + // Get custodian registration fee. + let custodian_registration_fee = + incentives::get_custodian_registration_fee(); + // Assert custodian ID 1 marked as not registered. + assert!(!is_registered_custodian_id(1), 0); + // Get custodian capability. + let custodian_capability = register_custodian_capability( + assets::mint_test(custodian_registration_fee)); + // Assert it has ID 1. + assert!(get_custodian_id(&custodian_capability) == 1, 0); + // Assert custodian ID 1 marked as registered. + assert!(is_registered_custodian_id(1), 0); + // Assert custodian ID 2 marked as not registered. + assert!(!is_registered_custodian_id(2), 0); + // Drop custodian capability. + drop_custodian_capability_test(custodian_capability); + // Get another custodian capability. + custodian_capability = register_custodian_capability( + assets::mint_test(custodian_registration_fee)); + // Assert it has ID 2. + assert!(get_custodian_id(&custodian_capability) == 2, 0); + // Assert custodian ID 2 marked as registered. + assert!(is_registered_custodian_id(2), 0); + // Drop custodian capability. + drop_custodian_capability_test(custodian_capability); + // Get another custodian capability. + custodian_capability = register_custodian_capability( + assets::mint_test(custodian_registration_fee)); + // Assert it has ID 3. + assert!(get_custodian_id(&custodian_capability) == 3, 0); + // Drop custodian capability. + drop_custodian_capability_test(custodian_capability); + // Get underwriter registration fee. + let underwriter_registration_fee = + incentives::get_underwriter_registration_fee(); + // Get underwriter capability. + let underwriter_capability = register_underwriter_capability( + assets::mint_test(underwriter_registration_fee)); + // Assert it has ID 1. + assert!(get_underwriter_id(&underwriter_capability) == 1, 0); + // Drop underwriter capability. + drop_underwriter_capability_test(underwriter_capability); + // Get another underwriter capability. + underwriter_capability = register_underwriter_capability( + assets::mint_test(underwriter_registration_fee)); + // Assert it has ID 2. + assert!(get_underwriter_id(&underwriter_capability) == 2, 0); + // Drop underwriter capability. + drop_underwriter_capability_test(underwriter_capability); + // Get another underwriter capability. + underwriter_capability = register_underwriter_capability( + assets::mint_test(underwriter_registration_fee)); + // Assert it has ID 3. + assert!(get_underwriter_id(&underwriter_capability) == 3, 0); + // Drop underwriter capability. + drop_underwriter_capability_test(underwriter_capability); + // Assert no custodian flag not marked as registered. + assert!(!is_registered_custodian_id(NO_CUSTODIAN), 0); + } + + #[test(user = @user)] + #[expected_failure(abort_code = E_INVALID_MARKET_ID)] + /// Verify failure for no such market ID. + fun test_register_integrator_fee_store_invalid_market_id( + user: &signer + ) acquires Registry { + init_test(); // Initialize for testing. + // Attempt invalid invocation. + register_integrator_fee_store(user, 1, 1, coin::zero()); + } + + #[test(user = @user)] + #[expected_failure(abort_code = E_INVALID_QUOTE)] + /// Verify failure for invalid quote coin. + fun test_register_integrator_fee_store_invalid_quote( + user: &signer + ) acquires Registry { + register_markets_test(); // Register test markets. + // Attempt invalid invocation. + register_integrator_fee_store(user, 1, 1, coin::zero()); + } + + #[test] + /// Verify successful registration. + fun test_register_integrator_fee_stores() + acquires Registry { + register_markets_test(); // Register test markets. + // Create integrator accounts. + let integrator_0 = account::create_account_for_test(@user_0); + let integrator_1 = account::create_account_for_test(@user_1); + // Register utility coin stores. + coin::register(&integrator_0); + coin::register(&integrator_1); + // Deposit utility coins. + coin::deposit(@user_0, assets::mint_test(10000000000000000)); + coin::deposit(@user_0, assets::mint_test(10000000000000000)); + // Register first integrator to base tier on first market. + register_integrator_fee_store_base_tier(&integrator_0, 1); + // Assert activation tier. + assert!(incentives::get_integrator_fee_store_tier_test(@user_0, 1) + == 0, 0); + // Register first integrator to tier 1 on second market. + register_integrator_fee_store_from_coinstore( + &integrator_0, 2, 1); + // Assert activation tier. + assert!(incentives::get_integrator_fee_store_tier_test(@user_0, 2) + == 1, 0); + // Register second integrator to base 0 on second market with + // redundant call to coinstore version function. + register_integrator_fee_store_from_coinstore( + &integrator_1, 2, 0); + // Assert activation tier. + assert!(incentives::get_integrator_fee_store_tier_test(@user_1, 2) + == 0, 0); + } + + #[test] + /// Verify state updates, return, for pure coin market. + fun test_register_market_base_coin_internal() + acquires Registry { + init_test(); // Initialize for testing. + // Declare arguments. + let lot_size = 123; + let tick_size = 456; + let min_size = 789; + // Get market registration fee. + let fee = incentives::get_market_registration_fee(); + // Register market, storing market ID. + let market_id = register_market_base_coin_internal( + lot_size - 1, tick_size, min_size, assets::mint_test(fee)); + assert!(market_id == 1, 0); // Assert market ID. + // Register another market, storing market ID. + market_id = register_market_base_coin_internal( + lot_size, tick_size, min_size, assets::mint_test(fee)); + assert!(market_id == 2, 0); // Assert market ID. + let markets_tablist_ref = // Immutably borrow markets tablist. + &borrow_global(@econia).market_id_to_info; + // Immutably borrow market info. + let market_info_ref = tablist::borrow(markets_tablist_ref, market_id); + // Assert fields. + assert!(market_info_ref.base_type == type_info::type_of(), 0); + assert!(string::is_empty(&market_info_ref.base_name_generic), 0); + assert!(market_info_ref.quote_type == type_info::type_of(), 0); + assert!(market_info_ref.lot_size == lot_size, 0); + assert!(market_info_ref.tick_size == tick_size, 0); + assert!(market_info_ref.min_size == min_size, 0); + assert!(market_info_ref.underwriter_id == NO_UNDERWRITER, 0); + let market_info_map_ref = // Immutably borrow market info map. + &borrow_global(@econia).market_info_to_id; + assert!( // Assert lookup on market info. + *table::borrow(market_info_map_ref, *market_info_ref) == market_id, + 0); + } + + #[test] + /// Verify state updates, return, for generic asset market. + fun test_register_market_base_generic_internal() + acquires Registry { + init_test(); // Initialize for testing. + let underwriter_id = 100; // Declare underwriter ID. + let underwriter_capability = // Get underwriter capability. + get_underwriter_capability_test(underwriter_id); + // Declare arguments. + let lot_size = 123; + let tick_size = 456; + let min_size = 789; + let base_name_generic = string::utf8(b"Generic asset"); + // Get market registration fee. + let fee = incentives::get_market_registration_fee(); + // Register market, storing market ID. + let market_id = register_market_base_generic_internal( + base_name_generic, lot_size - 1, tick_size, min_size, + &underwriter_capability, assets::mint_test(fee)); + assert!(market_id == 1, 0); // Assert market ID. + // Register another market, storing market ID. + let market_id = register_market_base_generic_internal( + base_name_generic, lot_size, tick_size, min_size, + &underwriter_capability, assets::mint_test(fee)); + assert!(market_id == 2, 0); // Assert market ID. + let markets_tablist_ref = // Immutably borrow markets tablist. + &borrow_global(@econia).market_id_to_info; + // Immutably borrow market info. + let market_info_ref = tablist::borrow(markets_tablist_ref, market_id); + assert!( // Assert fields. + market_info_ref.base_type == type_info::type_of(), + 0); + assert!(market_info_ref.base_name_generic == base_name_generic, 0); + assert!(market_info_ref.quote_type == type_info::type_of(), 0); + assert!(market_info_ref.lot_size == lot_size, 0); + assert!(market_info_ref.tick_size == tick_size, 0); + assert!(market_info_ref.min_size == min_size, 0); + assert!(market_info_ref.underwriter_id == underwriter_id, 0); + let market_info_map_ref = // Immutably borrow market info map. + &borrow_global(@econia).market_info_to_id; + assert!( // Assert lookup on market info. + *table::borrow(market_info_map_ref, *market_info_ref) == market_id, + 0); + // Drop underwriter capability. + drop_underwriter_capability_test(underwriter_capability); + } + + #[test] + #[expected_failure(abort_code = E_BASE_NOT_COIN)] + /// Verify failure for non-coin type. + fun test_register_market_base_not_coin() + acquires Registry { + // Declare arguments. + let lot_size = 0; + let tick_size = 0; + let min_size = 0; + // Attempt invalid invocation. + register_market_base_coin_internal( + lot_size, tick_size, min_size, coin::zero()); + } + + #[test] + #[expected_failure(abort_code = E_GENERIC_TOO_FEW_CHARACTERS)] + /// Verify failure for too few characters in generic asset name. + fun test_register_market_generic_name_too_few() + acquires Registry { + init_test(); // Initialize for testing. + // Get underwriter capability. + let underwriter_capability = get_underwriter_capability_test(1); + // Declare arguments. + let base_name_generic = string::utf8(b"ABC"); + let lot_size = 0; + let tick_size = 0; + let min_size = 0; + // Attempt invalid invocation. + register_market_base_generic_internal( + base_name_generic, lot_size, tick_size, min_size, + &underwriter_capability, coin::zero()); + // Drop underwriter capability. + drop_underwriter_capability_test(underwriter_capability); + } + + #[test] + #[expected_failure(abort_code = E_GENERIC_TOO_MANY_CHARACTERS)] + /// Verify failure for too many characters in generic asset name. + fun test_register_market_generic_name_too_many() + acquires Registry { + init_test(); // Initialize for testing. + // Get underwriter capability. + let underwriter_capability = get_underwriter_capability_test(1); + // Declare arguments. + let base_name_generic = // Get 36-character string. + string::utf8(b"123456789012345678901234567890123456"); + string::append(&mut base_name_generic, // Append 37 characters. + string::utf8(b"1111111111111111111111111111111111111")); + let lot_size = 0; + let tick_size = 0; + let min_size = 0; + // Attempt invalid invocation. + register_market_base_generic_internal( + base_name_generic, lot_size, tick_size, min_size, + &underwriter_capability, coin::zero()); + // Drop underwriter capability. + drop_underwriter_capability_test(underwriter_capability); + } + + #[test] + #[expected_failure(abort_code = E_LOT_SIZE_0)] + /// Verify failure for lot size 0. + fun test_register_market_lot_size_0() + acquires Registry { + init_test(); // Initialize for testing. + // Declare arguments. + let lot_size = 0; + let tick_size = 0; + let min_size = 0; + // Attempt invalid invocation. + register_market_base_coin_internal( + lot_size, tick_size, min_size, assets::mint_test(1)); + } + + #[test] + #[expected_failure(abort_code = E_MIN_SIZE_0)] + /// Verify failure for minimum size 0. + fun test_register_market_min_size_0() + acquires Registry { + init_test(); // Initialize for testing. + // Declare arguments. + let lot_size = 1; + let tick_size = 1; + let min_size = 0; + // Attempt invalid invocation. + register_market_base_coin_internal( + lot_size, tick_size, min_size, coin::zero()); + } + + #[test] + #[expected_failure(abort_code = E_QUOTE_NOT_COIN)] + /// Verify failure for quote asset not coin. + fun test_register_market_quote_not_coin() + acquires Registry { + init_test(); // Initialize for testing. + // Declare arguments. + let lot_size = 1; + let tick_size = 1; + let min_size = 1; + // Attempt invalid invocation. + register_market_base_coin_internal( + lot_size, tick_size, min_size, coin::zero()); + } + + #[test] + #[expected_failure(abort_code = E_MARKET_REGISTERED)] + /// Verify failure for market already registered. + fun test_register_market_registered() + acquires Registry { + init_test(); // Initialize for testing. + // Declare arguments. + let lot_size = 1; + let tick_size = 1; + let min_size = 1; + // Get market registration fee. + let fee = incentives::get_market_registration_fee(); + // Register valid market. + register_market_base_coin_internal( + lot_size, tick_size, min_size, assets::mint_test(fee)); + // Attempt invalid re-registration. + register_market_base_coin_internal( + lot_size, tick_size, min_size, coin::zero()); + } + + #[test] + #[expected_failure(abort_code = E_BASE_QUOTE_SAME)] + /// Verify failure for base and quote same coin type. + fun test_register_market_same_type() + acquires Registry { + init_test(); // Initialize for testing. + // Declare arguments. + let lot_size = 1; + let tick_size = 1; + let min_size = 1; + // Attempt invalid invocation. + register_market_base_coin_internal( + lot_size, tick_size, min_size, coin::zero()); + } + + #[test] + #[expected_failure(abort_code = E_TICK_SIZE_0)] + /// Verify failure for tick size 0. + fun test_register_market_tick_size_0() + acquires Registry { + init_test(); // Initialize for testing. + // Declare arguments. + let lot_size = 1; + let tick_size = 0; + let min_size = 0; + // Attempt invalid invocation. + register_market_base_coin_internal( + lot_size, tick_size, min_size, coin::zero()); + } + + #[test(account = @econia)] + #[expected_failure(abort_code = E_NO_RECOGNIZED_MARKET)] + /// Verify failure for market no recognized market. + fun test_remove_recognized_market_no_recognized( + account: &signer + ) acquires + RecognizedMarkets, + Registry + { + init_test(); // Initialize for testing. + // Declare arguments. + let lot_size = 123; + let tick_size = 456; + let min_size = 789; + // Get market registration fee. + let fee = incentives::get_market_registration_fee(); + // Register market, storing ID. + let market_id = register_market_base_coin_internal( + lot_size, tick_size, min_size, assets::mint_test(fee)); + // Attempt invalid invocation. + remove_recognized_market(account, market_id); + } + + #[test(account = @user)] + #[expected_failure(abort_code = E_NOT_ECONIA)] + /// Verify failure for account is not Econia. + fun test_remove_recognized_market_not_econia( + account: &signer + ) acquires + RecognizedMarkets, + Registry + { + // Attempt invalid invocation. + remove_recognized_market(account, 0); + } + + #[test(account = @econia)] + #[expected_failure(abort_code = E_WRONG_RECOGNIZED_MARKET)] + /// Verify failure for wrong recognized market. + fun test_remove_recognized_market_wrong_market( + account: &signer + ) acquires + RecognizedMarkets, + Registry + { + init_test(); // Initialize for testing. + // Declare arguments. + let lot_size = 123; + let tick_size = 456; + let min_size = 789; + // Get market registration fee. + let fee = incentives::get_market_registration_fee(); + // Register market, storing ID. + let market_id = register_market_base_coin_internal( + lot_size, tick_size, min_size, assets::mint_test(fee)); + set_recognized_market(account, market_id); // Set as recognized. + // Register different market with same trading pair, storing ID. + let market_id_2 = register_market_base_coin_internal( + lot_size - 1, tick_size, min_size, assets::mint_test(fee)); + // Attempt invalid invocation. + remove_recognized_market(account, market_id_2); + } + + #[test(account = @user)] + #[expected_failure(abort_code = E_NOT_ECONIA)] + /// Verify failure for account is not Econia. + fun test_set_recognized_market_not_econia( + account: &signer + ) acquires + RecognizedMarkets, + Registry + { + // Attempt invalid invocation. + set_recognized_market(account, 0); + } + + #[test(account = @econia)] + /// Verify state updates for updating recognized market info for + /// given trading pair. + fun test_set_recognized_market_update( + account: &signer + ) acquires + RecognizedMarkets, + Registry + { + init_test(); // Initialize for testing. + // Declare arguments. + let lot_size_1 = 123; + let tick_size_1 = 456; + let min_size_1 = 789; + let lot_size_2 = lot_size_1 - 1; + let tick_size_2 = tick_size_1 - 1; + let min_size_2 = min_size_1 - 1; + // Get market registration fee. + let fee = incentives::get_market_registration_fee(); + // Register markets, storing IDs. + let market_id_1 = register_market_base_coin_internal( + lot_size_1, tick_size_1, min_size_1, assets::mint_test(fee)); + let market_id_2 = register_market_base_coin_internal( + lot_size_2, tick_size_2, min_size_2, assets::mint_test(fee)); + // Set first market as recognized. + set_recognized_market(account, market_id_1); + // Assert lookup. + assert!(has_recognized_market_base_coin_by_type(), 0); + // Assert pure coin asset market info. + let (market_id, lot_size, tick_size, min_size, underwriter_id) = + get_recognized_market_info_base_coin_by_type(); + assert!(market_id == market_id_1, 0); + assert!(lot_size == lot_size_1, 0); + assert!(tick_size == tick_size_1, 0); + assert!(min_size == min_size_1, 0); + assert!(underwriter_id == NO_UNDERWRITER, 0); + // Set second market as recognized. + set_recognized_market(account, market_id_2); + // Assert update. + (market_id, lot_size, tick_size, min_size, underwriter_id) = + get_recognized_market_info_base_coin_by_type(); + assert!(market_id == market_id_2, 0); + assert!(lot_size == lot_size_2, 0); + assert!(tick_size == tick_size_2, 0); + assert!(min_size == min_size_2, 0); + assert!(underwriter_id == NO_UNDERWRITER, 0); + } + + #[test(econia = @econia)] + /// Verify returns, state updates for setting and removing + /// registered markets, lookup operations. + fun test_set_remove_check_recognized_markets( + econia: &signer + ) acquires + RecognizedMarkets, + Registry + { + init_test(); // Initialize for testing. + // Assert market counts. + assert!(get_market_counts() == + MarketCounts{n_markets: 0, n_recognized_markets: 0}, 0); + // Get generic market underwriter capability. + let underwriter_id_generic = 123; + let underwriter_capability = // Get underwriter capability. + get_underwriter_capability_test(underwriter_id_generic); + // Declare market parameters. + let base_name_generic = string::utf8(b"Generic asset"); + let lot_size_1 = 234; + let tick_size_1 = 345; + let min_size_1 = 456; + let lot_size_2 = lot_size_1 - 1; + let tick_size_2 = tick_size_1 - 1; + let min_size_2 = min_size_1 - 1; + let lot_size_3 = lot_size_2 - 1; + let tick_size_3 = tick_size_2 - 1; + let min_size_3 = min_size_2 - 1; + let base_type_view = AssetTypeView{ + package_address: @econia, + module_name: string::utf8(b"assets"), + type_name: string::utf8(b"BC"), + }; + let base_type_view_generic = AssetTypeView{ + package_address: @econia, + module_name: string::utf8(b"registry"), + type_name: string::utf8(b"GenericAsset"), + }; + let quote_type_view = AssetTypeView{ + package_address: @econia, + module_name: string::utf8(b"assets"), + type_name: string::utf8(b"QC"), + }; + // Get market registration fee. + let fee = incentives::get_market_registration_fee(); + // Assert existence checks. + assert!( + !has_recognized_market_base_generic_by_type(base_name_generic), + 0); + assert!(!has_recognized_market_base_coin_by_type(), 0); + assert!(get_market_id_base_generic( + base_name_generic, lot_size_1, tick_size_1, min_size_1, + underwriter_id_generic) + == option::none(), 0); + assert!(get_market_id_base_coin( + lot_size_2, tick_size_2, min_size_2) == option::none(), 0); + // Assert events. + let market_registration_events = event::emitted_events_by_handle( + &borrow_global(@econia).market_registration_events); + assert!(market_registration_events == vector[], 0); + // Register markets. + register_market_base_generic_internal( + base_name_generic, lot_size_1, tick_size_1, min_size_1, + &underwriter_capability, assets::mint_test(fee)); + register_market_base_coin_internal( + lot_size_2, tick_size_2, min_size_2, assets::mint_test(fee)); + // Assert events. + market_registration_events = event::emitted_events_by_handle( + &borrow_global(@econia).market_registration_events); + assert!(market_registration_events == vector[ + MarketRegistrationEvent{ + market_id: 1, + base_type: type_info::type_of(), + base_name_generic, + quote_type: type_info::type_of(), + lot_size: lot_size_1, + tick_size: tick_size_1, + min_size: min_size_1, + underwriter_id: underwriter_id_generic + }, + MarketRegistrationEvent{ + market_id: 2, + base_type: type_info::type_of(), + base_name_generic: string::utf8(b""), + quote_type: type_info::type_of(), + lot_size: lot_size_2, + tick_size: tick_size_2, + min_size: min_size_2, + underwriter_id: NO_UNDERWRITER + }, + ], 0); + // Verify market info. + assert!(get_market_info(1) == MarketInfoView{ + market_id: 1, + is_recognized: false, + base_type: base_type_view_generic, + base_name_generic, + quote_type: quote_type_view, + lot_size: lot_size_1, + tick_size: tick_size_1, + min_size: min_size_1, + underwriter_id: underwriter_id_generic + }, 0); + assert!(get_market_info(2) == MarketInfoView{ + market_id: 2, + is_recognized: false, + base_type: base_type_view, + base_name_generic: string::utf8(b""), + quote_type: quote_type_view, + lot_size: lot_size_2, + tick_size: tick_size_2, + min_size: min_size_2, + underwriter_id: NO_UNDERWRITER + }, 0); + // Drop underwriter capability. + drop_underwriter_capability_test(underwriter_capability); + // Assert events. + let recognized_market_events = event::emitted_events_by_handle( + &borrow_global(@econia). + recognized_market_events); + assert!(recognized_market_events == vector[], 0); + // Set both as recognized markets. + set_recognized_markets(econia, vector[1, 2]); + // Assert events. + recognized_market_events = event::emitted_events_by_handle( + &borrow_global(@econia). + recognized_market_events); + assert!(recognized_market_events == vector[ + RecognizedMarketEvent{ + trading_pair: TradingPair{ + base_type: type_info::type_of(), + base_name_generic, + quote_type: type_info::type_of() + }, + recognized_market_info: option::some(RecognizedMarketInfo{ + market_id: 1, + lot_size: lot_size_1, + tick_size: tick_size_1, + min_size: min_size_1, + underwriter_id: underwriter_id_generic + }) + }, + RecognizedMarketEvent{ + trading_pair: TradingPair{ + base_type: type_info::type_of(), + base_name_generic: string::utf8(b""), + quote_type: type_info::type_of() + }, + recognized_market_info: option::some(RecognizedMarketInfo{ + market_id: 2, + lot_size: lot_size_2, + tick_size: tick_size_2, + min_size: min_size_2, + underwriter_id: NO_UNDERWRITER + }) + }, + ], 0); + // Assert existence checks. + assert!( + has_recognized_market_base_generic_by_type(base_name_generic), + 0); + assert!(has_recognized_market_base_coin_by_type(), 0); + assert!(get_market_id_base_generic( + base_name_generic, lot_size_1, tick_size_1, min_size_1, + underwriter_id_generic) + == option::some(1), 0); + assert!(get_market_id_base_coin( + lot_size_2, tick_size_2, min_size_2) + == option::some(2), 0); + // Assert generic asset market info. + let (market_id, lot_size, tick_size, min_size, underwriter_id) = + get_recognized_market_info_base_generic_by_type( + base_name_generic); + assert!(market_id == 1, 0); + assert!(lot_size == lot_size_1, 0); + assert!(tick_size == tick_size_1, 0); + assert!(min_size == min_size_1, 0); + assert!(underwriter_id == underwriter_id_generic, 0); + assert!(get_recognized_market_id_base_generic(base_name_generic) + == 1, 0); + // Assert pure coin asset market info. + let (market_id, lot_size, tick_size, min_size, underwriter_id) = + get_recognized_market_info_base_coin_by_type(); + assert!(market_id == 2, 0); + assert!(lot_size == lot_size_2, 0); + assert!(tick_size == tick_size_2, 0); + assert!(min_size == min_size_2, 0); + assert!(underwriter_id == NO_UNDERWRITER, 0); + assert!(get_recognized_market_id_base_coin() == 2, 0); + // Assert market counts. + assert!(get_market_counts() == + MarketCounts{n_markets: 2, n_recognized_markets: 2}, 0); + // Verify market info. + assert!(get_market_info(1) == MarketInfoView{ + market_id: 1, + is_recognized: true, + base_type: base_type_view_generic, + base_name_generic, + quote_type: quote_type_view, + lot_size: lot_size_1, + tick_size: tick_size_1, + min_size: min_size_1, + underwriter_id: underwriter_id_generic + }, 0); + assert!(get_market_info(2) == MarketInfoView{ + market_id: 2, + is_recognized: true, + base_type: base_type_view, + base_name_generic: string::utf8(b""), + quote_type: quote_type_view, + lot_size: lot_size_2, + tick_size: tick_size_2, + min_size: min_size_2, + underwriter_id: NO_UNDERWRITER + }, 0); + // Remove both recognized markets. + remove_recognized_markets(econia, vector[1, 2]); + // Assert events. + recognized_market_events = event::emitted_events_by_handle( + &borrow_global(@econia). + recognized_market_events); + assert!(vector::length(&recognized_market_events) == 4, 0); + assert!(vector::pop_back(&mut recognized_market_events) == + RecognizedMarketEvent{ + trading_pair: TradingPair{ + base_type: type_info::type_of(), + base_name_generic: string::utf8(b""), + quote_type: type_info::type_of() + }, + recognized_market_info: option::none() + }, 0); + assert!(vector::pop_back(&mut recognized_market_events) == + RecognizedMarketEvent{ + trading_pair: TradingPair{ + base_type: type_info::type_of(), + base_name_generic, + quote_type: type_info::type_of() + }, + recognized_market_info: option::none() + }, 0); + // Assert existence checks. + assert!( + !has_recognized_market_base_generic_by_type(base_name_generic), + 0); + assert!(!has_recognized_market_base_coin_by_type(), 0); + // Assert market counts. + assert!(get_market_counts() == + MarketCounts{n_markets: 2, n_recognized_markets: 0}, 0); + // Register a third market having the same trading pair as the + // second market, and set it as recognized. + register_market_base_coin_internal( + lot_size_3, tick_size_3, min_size_3, assets::mint_test(fee)); + set_recognized_markets(econia, vector[3]); + // Verify that second market, which has same trading pair, is + // not marked as recognized. + assert!(get_market_info(2) == MarketInfoView{ + market_id: 2, + is_recognized: false, + base_type: base_type_view, + base_name_generic: string::utf8(b""), + quote_type: quote_type_view, + lot_size: lot_size_2, + tick_size: tick_size_2, + min_size: min_size_2, + underwriter_id: NO_UNDERWRITER + }, 0); + // Assert events. + market_registration_events = event::emitted_events_by_handle( + &borrow_global(@econia).market_registration_events); + assert!(vector::length(&market_registration_events) == 3, 0); + assert!(vector::pop_back(&mut market_registration_events) == + MarketRegistrationEvent{ + market_id: 3, + base_type: type_info::type_of(), + base_name_generic: string::utf8(b""), + quote_type: type_info::type_of(), + lot_size: lot_size_3, + tick_size: tick_size_3, + min_size: min_size_3, + underwriter_id: NO_UNDERWRITER + }, 0); + recognized_market_events = event::emitted_events_by_handle( + &borrow_global(@econia). + recognized_market_events); + assert!(vector::length(&recognized_market_events) == 5, 0); + assert!(vector::pop_back(&mut recognized_market_events) == + RecognizedMarketEvent{ + trading_pair: TradingPair{ + base_type: type_info::type_of(), + base_name_generic: string::utf8(b""), + quote_type: type_info::type_of() + }, + recognized_market_info: option::some(RecognizedMarketInfo{ + market_id: 3, + lot_size: lot_size_3, + tick_size: tick_size_3, + min_size: min_size_3, + underwriter_id: NO_UNDERWRITER + }) + }, 0); + // Set the second market as the recognized market, thus removing + // the third market as recognized, and assert event. + set_recognized_markets(econia, vector[2]); + recognized_market_events = event::emitted_events_by_handle( + &borrow_global(@econia). + recognized_market_events); + assert!(vector::length(&recognized_market_events) == 6, 0); + assert!(vector::pop_back(&mut recognized_market_events) == + RecognizedMarketEvent{ + trading_pair: TradingPair{ + base_type: type_info::type_of(), + base_name_generic: string::utf8(b""), + quote_type: type_info::type_of() + }, + recognized_market_info: option::some(RecognizedMarketInfo{ + market_id: 2, + lot_size: lot_size_2, + tick_size: tick_size_2, + min_size: min_size_2, + underwriter_id: NO_UNDERWRITER + }) + }, 0); + } + + // Tests <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +} \ No newline at end of file diff --git a/testsuite/module-publish/src/packages/econia/sources/resource_account.move b/testsuite/module-publish/src/packages/econia/sources/resource_account.move new file mode 100644 index 0000000000000..52f47ef9ea809 --- /dev/null +++ b/testsuite/module-publish/src/packages/econia/sources/resource_account.move @@ -0,0 +1,138 @@ +/// Manages an Econia-owned resource account. +module econia::resource_account { + + // Uses >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + use aptos_framework::account::{Self, SignerCapability}; + use aptos_framework::timestamp; + use std::bcs; + + // Uses <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Friends >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + friend econia::incentives; + friend econia::market; + + // Friends <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Structs >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Stores a signing capability for the Econia resource account. + struct SignerCapabilityStore has key { + /// Signer capability for Econia resource account. + signer_capability: SignerCapability + } + + // Structs <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // View functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[view] + /// Return resource account address. + /// + /// # Testing + /// + /// * `test_mixed()` + public(friend) fun get_address(): + address + acquires SignerCapabilityStore { + // Immutably borrow signer capability. + let signer_capability_ref = + &borrow_global(@econia).signer_capability; + // Return its address. + account::get_signer_capability_address(signer_capability_ref) + } + + // View functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Public friend functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Return resource account signer. + /// + /// # Testing + /// + /// * `test_mixed()` + public(friend) fun get_signer(): + signer + acquires SignerCapabilityStore { + // Immutably borrow signer capability. + let signer_capability_ref = + &borrow_global(@econia).signer_capability; + // Return associated signer. + account::create_signer_with_capability(signer_capability_ref) + } + + // Public friend functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Private functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Initialize the Econia resource account upon module publication. + /// + /// # Seed considerations + /// + /// Uses block timestamp as a seed for future-proofing the potential + /// creation of additional resource accounts via the Econia account: + /// the use of a seed that is not hard-coded mitigates the threat + /// of resource account creation blockage via mismanaged seeds, + /// assuming in this case that multiple resource accounts are not + /// created during the same block. + /// + /// # Testing + /// + /// * `test_mixed()` + fun init_module( + econia: &signer + ) { + // Get resource account time seed. + let time_seed = bcs::to_bytes(×tamp::now_microseconds()); + // Create resource account, storing signer capability. + let (_, signer_capability) = + account::create_resource_account(econia, time_seed); + // Store signing capability under Econia account. + move_to(econia, SignerCapabilityStore{signer_capability}); + } + + // Private functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Test-only structs >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test_only] + struct TestStruct has key {} + + // Test-only structs <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Test-only functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test_only] + /// Initialize resource account for testing. + public fun init_test() { + // Get signer for Aptos framework account. + let aptos_framework = account::create_signer_with_capability( + &account::create_test_signer_cap(@aptos_framework)); + // Set time for seed. + timestamp::set_time_has_started_for_testing(&aptos_framework); + // Get signer for Econia account. + let econia = account::create_signer_with_capability( + &account::create_test_signer_cap(@econia)); + init_module(&econia); // Init resource account. + } + + // Test-only functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Tests >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test] + /// Verify initialization, signer use, address lookup. + fun test_mixed() + acquires SignerCapabilityStore { + init_test(); // Init the resource account. + // Move to resource account a test struct. + move_to(&get_signer(), TestStruct{}); + // Verify existence via address lookup. + assert!(exists(get_address()), 0); + } + + // Tests <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +} \ No newline at end of file diff --git a/testsuite/module-publish/src/packages/econia/sources/tablist.move b/testsuite/module-publish/src/packages/econia/sources/tablist.move new file mode 100644 index 0000000000000..2157f20b0d451 --- /dev/null +++ b/testsuite/module-publish/src/packages/econia/sources/tablist.move @@ -0,0 +1,571 @@ +/// Tablist: a hybrid between a table and a doubly linked list. +/// +/// Modeled off of what was previously `aptos_std::iterable_table.move`, +/// which had been removed from `aptos_std` as of the time of this +/// writing. +/// +/// Accepts key-value pairs having key type `K` and value type `V`. +/// +/// See `test_iterate()` and `test_iterate_remove()` for iteration +/// syntax. +/// +/// # Complete docgen index +/// +/// The below index is automatically generated from source code: +module econia::tablist { + + // Uses >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + use aptos_std::table_with_length::{Self, TableWithLength}; + use std::option::{Self, Option}; + + // Uses <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Structs >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// A tablist node, pointing to the previous and next nodes, if any. + struct Node< + K: copy + drop + store, + V: store + > has store { + /// Value from a key-value pair. + value: V, + /// Key of previous tablist node, if any. + previous: Option, + /// Key of next tablist node, if any. + next: Option + } + + /// A hybrid between a table and a doubly linked list. + struct Tablist< + K: copy + drop + store, + V: store + > has store { + /// All nodes in the tablist. + table: TableWithLength>, + /// Key of tablist head node, if any. + head: Option, + /// Key of tablist tail node, if any. + tail: Option + } + + // Structs <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Error codes >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Attempting to destroy a tablist that is not empty. + const E_DESTROY_NOT_EMPTY: u64 = 0; + + // Error codes <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Public functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Add `key`-`value` pair to given `Tablist`, aborting if `key` + /// already present. + /// + /// # Testing + /// + /// * `test_mixed()` + public fun add< + K: copy + drop + store, + V: store + >( + tablist_ref_mut: &mut Tablist, + key: K, + value: V + ) { + let node = Node{value, previous: tablist_ref_mut.tail, + next: option::none()}; // Wrap value in a node. + // Add node to the inner table. + table_with_length::add(&mut tablist_ref_mut.table, key, node); + // If adding the first node in the tablist: + if (option::is_none(&tablist_ref_mut.head)) { + // Mark key as the new head. + tablist_ref_mut.head = option::some(key); + } else { // If adding node that is not first node in tablist: + // Get the old tail node key. + let old_tail = option::borrow(&tablist_ref_mut.tail); + // Update the old tail node to have the new key as next. + table_with_length::borrow_mut( + &mut tablist_ref_mut.table, *old_tail).next = + option::some(key); + }; + // Update the tablist tail to the new key. + tablist_ref_mut.tail = option::some(key); + } + + /// Return immutable reference to the value that `key` maps to, + /// aborting if `key` is not in given `Tablist`. + /// + /// # Testing + /// + /// * `test_mixed()` + public fun borrow< + K: copy + drop + store, + V: store + >( + tablist_ref: &Tablist, + key: K, + ): &V { + &table_with_length::borrow(&tablist_ref.table, key).value + } + + /// Borrow the `Node` in the given `Tablist` having key, returning: + /// + /// * Immutable reference to corresponding value. + /// * Key of previous `Node` in the `Tablist`, if any. + /// * Key of next `Node` in the `Tablist`, if any. + /// + /// Aborts if there is no entry for `key`. + /// + /// # Testing + /// + /// * `test_iterate()` + public fun borrow_iterable< + K: copy + drop + store, + V: store + >( + tablist_ref: &Tablist, + key: K, + ): ( + &V, + Option, + Option + ) { + let node_ref = // Borrow immutable reference to node having key. + table_with_length::borrow(&tablist_ref.table, key); + // Return corresponding fields. + (&node_ref.value, node_ref.previous, node_ref.next) + } + + /// Mutably borrow the `Node` in given `Tablist` having `key`, + /// returning: + /// + /// * Mutable reference to corresponding value. + /// * Key of previous `Node` in the `Tablist`, if any. + /// * Key of next `Node` in the `Tablist`, if any. + /// + /// Aborts if there is no entry for `key`. + /// + /// # Testing + /// + /// * `test_iterate()` + public fun borrow_iterable_mut< + K: copy + drop + store, + V: store + >( + tablist_ref_mut: &mut Tablist, + key: K, + ): ( + &mut V, + Option, + Option + ) { + // Borrow mutable reference to node having key. + let node_ref_mut = table_with_length::borrow_mut( + &mut tablist_ref_mut.table, key); + // Return corresponding fields. + (&mut node_ref_mut.value, node_ref_mut.previous, node_ref_mut.next) + } + + /// Return mutable reference to the value that `key` maps to, + /// aborting if `key` is not in given `Tablist`. + /// + /// Aborts if there is no entry for `key`. + /// + /// # Testing + /// + /// * `test_mixed()` + public fun borrow_mut< + K: copy + drop + store, + V: store + >( + tablist_ref_mut: &mut Tablist, + key: K, + ): &mut V { + &mut table_with_length::borrow_mut( + &mut tablist_ref_mut.table, key).value + } + + /// Return `true` if given `Tablist` contains `key`, else `false`. + /// + /// # Testing + /// + /// * `test_mixed()` + public fun contains< + K: copy + drop + store, + V: store + >( + tablist_ref: &Tablist, + key: K, + ): bool { + table_with_length::contains(&tablist_ref.table, key) + } + + /// Destroy an empty `Tablist`, aborting if not empty. + /// + /// # Aborts + /// + /// * `E_DESTROY_NOT_EMPTY`: The tablist is not empty. + /// + /// # Testing + /// + /// * `test_destroy_empty_not_empty()` + /// * `test_mixed()` + public fun destroy_empty< + K: copy + drop + store, + V: store + >( + tablist: Tablist + ) { + // Assert tablist is empty before attempting to unpack. + assert!(is_empty(&tablist), E_DESTROY_NOT_EMPTY); + // Unpack, destroying head and tail fields. + let Tablist{table, head: _, tail: _} = tablist; + // Destroy empty inner table. + table_with_length::destroy_empty(table); + } + + /// Return optional head key from given `Tablist`. + /// + /// # Testing + /// + /// * `test_mixed()` + public fun get_head_key< + K: copy + drop + store, + V: store + >( + tablist_ref: &Tablist + ): Option { + tablist_ref.head + } + + /// Return optional tail key in given `Tablist`. + /// + /// # Testing + /// + /// * `test_mixed()` + public fun get_tail_key< + K: copy + drop + store, + V: store + >( + tablist_ref: &Tablist + ): Option { + tablist_ref.tail + } + + /// Return number of elements in given `Tablist`. + /// + /// # Testing + /// + /// * `test_mixed()` + public fun length< + K: copy + drop + store, + V: store + >( + tablist_ref: &Tablist + ): u64 { + table_with_length::length(&tablist_ref.table) + } + + /// Return an empty `Tablist`. + /// + /// # Testing + /// + /// * `test_mixed()` + public fun new< + K: copy + drop + store, + V: store + >(): Tablist { + Tablist{ + table: table_with_length::new(), + head: option::none(), + tail: option::none() + } + } + + /// Return `true` if given `Tablist` is empty, else `false`. + /// + /// # Testing + /// + /// * `test_mixed()` + public fun is_empty< + K: copy + drop + store, + V: store + >( + tablist_ref: &Tablist + ): bool { + table_with_length::empty(&tablist_ref.table) + } + + /// Remove `key` from given `Tablist`, returning the value `key` + /// mapped to. + /// + /// See wrapped function `remove_iterable()`. + /// + /// Aborts if there is no entry for `key`. + /// + /// # Testing + /// + /// * `test_mixed()` + public fun remove< + K: copy + drop + store, + V: store + >( + tablist_ref_mut: &mut Tablist, + key: K + ): V { + // Get value via iterable removal. + let (value, _, _) = remove_iterable(tablist_ref_mut, key); + value // Return value. + } + + /// Remove `key` from given `Tablist`, returning the value `key` + /// mapped to, the previous key it mapped to (if any), and the + /// next key it mapped to (if any). + /// + /// Aborts if there is no entry for `key`. + /// + /// # Testing + /// + /// * `test_iterate_remove()` + public fun remove_iterable< + K: copy + drop + store, + V: store + >( + tablist_ref_mut: &mut Tablist, + key: K + ): ( + V, + Option, + Option + ) { + // Unpack from inner table the node with the given key. + let Node{value, previous, next} = table_with_length::remove( + &mut tablist_ref_mut.table, key); + // If the node was the head of the tablist: + if (option::is_none(&previous)) { // If no previous node: + // Set as the tablist head the node's next field. + tablist_ref_mut.head = next; + } else { // If node was not head of the tablist: + // Update the node having the previous key to have as its + // next field the next field of the removed node. + table_with_length::borrow_mut(&mut tablist_ref_mut.table, + *option::borrow(&previous)).next = next; + }; + // If the node was the tail of the tablist: + if (option::is_none(&next)) { // If no next node: + // Set as the tablist tail the node's previous field. + tablist_ref_mut.tail = previous; + } else { // If node was not tail of tablist: + // Update the node having the next key to have as its + // previous field the previous field of the removed node. + table_with_length::borrow_mut(&mut tablist_ref_mut.table, + *option::borrow(&next)).previous = previous; + }; + // Return node value, previous field, and next field. + (value, previous, next) + } + + /// Return a new `Tablist` containing `key`-`value` pair. + /// + /// # Testing + /// + /// * `test_mixed()` + public fun singleton< + K: copy + drop + store, + V: store + >( + key: K, + value: V + ): Tablist { + let tablist = new(); // Declare empty tablist + add(&mut tablist, key, value); // Insert key-value pair. + tablist // Return tablist. + } + + // Public functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Tests >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test] + #[expected_failure(abort_code = E_DESTROY_NOT_EMPTY)] + /// Verify failure for non-empty tablist destruction. + fun test_destroy_empty_not_empty() {destroy_empty(singleton(0, 0));} + + #[test] + /// Verify iteration in the following sequence: + /// + /// * Immutably, from head to tail. + /// * Mutably, from tail to head. + /// * Mutably, from head to tail. + /// * Immutably, from tail to head. + fun test_iterate(): Tablist { + let tablist = new(); // Declare new tablist. + let i = 0; // Declare counter. + while (i < 100) { // For 100 iterations: + // Add key-value pair to table where key and value are both + // the value of the counter. + add(&mut tablist, i, i); + i = i + 1; // Increment counter. + }; + assert!(length(&tablist) == 100, 0); // Assert proper length. + let key = get_head_key(&tablist); // Get head key. + i = 0; // Re-init counter. + // While keys left to iterate on, iterate from head to tail: + while (option::is_some(&key)) { + // Get value for key and next key in tablist. + let (value_ref, _, next) = + borrow_iterable(&tablist, *option::borrow(&key)); + // Assert key-value pair. + assert!(*option::borrow(&key) == i, 0); + assert!(*value_ref == i, 0); + key = next; // Review the next key. + i = i + 1; // Increment counter. + }; + key = get_tail_key(&tablist); // Get tail key. + i = 0; // Re-init counter + // While keys left to iterate on, iterate from tail to head: + while (option::is_some(&key)) { + // Get value for key and previous key in tablist. + let (value_ref_mut, previous, _) = + borrow_iterable_mut(&mut tablist, *option::borrow(&key)); + // Assert key-value pair. + assert!(*option::borrow(&key) == (99 - i), 0); + assert!(*value_ref_mut == (99 - i), 0); + // Mutate the value by adding 1. + *value_ref_mut = *value_ref_mut + 1; + key = previous; // Review the previous key. + i = i + 1; // Increment counter. + }; + let key = get_head_key(&tablist); // Get head key. + i = 0; // Re-init counter. + // While keys left to iterate on, iterate from head to tail: + while (option::is_some(&key)) { + // Get value for key and next key in tablist. + let (value_ref_mut, _, next) = + borrow_iterable_mut(&mut tablist, *option::borrow(&key)); + // Assert key-value pair. + assert!(*option::borrow(&key) == i, 0); + assert!(*value_ref_mut == i + 1, 0); + // Mutate the value by adding 1. + *value_ref_mut = *value_ref_mut + 1; + key = next; // Review the next key. + i = i + 1; // Increment counter. + }; + key = get_tail_key(&tablist); // Get tail key. + i = 0; // Re-init counter + // While keys left to iterate on, iterate from tail to head: + while (option::is_some(&key)) { + // Get value for key and previous key in tablist. + let (value_ref, previous, _) = + borrow_iterable(&tablist, *option::borrow(&key)); + // Assert key-value pair. + assert!(*option::borrow(&key) == (99 - i), 0); + assert!(*value_ref == (99 - i) + 2, 0); + key = previous; // Review the previous key. + i = i + 1; // Increment counter. + }; + tablist // Return tablist. + } + + #[test] + /// Verify iterated removal, first from head to tail, then from + /// tail to head. + fun test_iterate_remove() { + let tablist = new(); // Declare new tablist. + let i = 0; // Declare counter. + while (i < 100) { // For 100 iterations: + // Add key-value pair to tablist where key and value are + // both the value of the counter. + add(&mut tablist, i, i); + i = i + 1; // Increment counter. + }; + let key = get_head_key(&tablist); // Get head key. + i = 0; // Re-initialize counter. + // While keys left to iterate on: + while (option::is_some(&key)) { + // Get value for key and next key in tablist. + let (value, _, next) = remove_iterable( + &mut tablist, *option::borrow(&key)); + // Assert key-value pair. + assert!(*option::borrow(&key) == i, 0); + assert!(value == i, 0); + key = next; // Review the next key. + i = i + 1; // Increment counter + }; + i = 0; // Re-initialize counter. + while (i < 100) { // For 100 iterations: + // Add key-value pair to tablist where key and value are + // both the value of the counter. + add(&mut tablist, i, i); + i = i + 1; // Increment counter. + }; + key = get_tail_key(&tablist); // Get tail key. + i = 0; // Re-initialize counter. + // While keys left to iterate on: + while (option::is_some(&key)) { + // Get value for key and previous key in tablist. + let (value, previous, _) = remove_iterable( + &mut tablist, *option::borrow(&key)); + // Assert key-value pair. + assert!(*option::borrow(&key) == (99 - i), 0); + assert!(value == (99 - i), 0); + key = previous; // Review the previous key. + i = i + 1; // Increment counter + }; + destroy_empty(tablist); // Destroy empty tablist. + } + + #[test] + /// Verify assorted functionality, without iteration. + fun test_mixed() { + // Declare key-value pairs. + let (key_0, value_0, key_1, value_1) = (1u8, true, 2u8, false); + // Declare empty tablist. + let empty_tablist = new(); + // Assert state. + assert!(length(&empty_tablist) == 0, 0); + assert!(is_empty(&empty_tablist), 0); + assert!(option::is_none(&get_head_key(&empty_tablist)), 0); + assert!(option::is_none(&get_tail_key(&empty_tablist)), 0); + assert!(!contains(&empty_tablist, key_0), 0); + assert!(!contains(&empty_tablist, key_1), 0); + // Destroy empty tablist. + destroy_empty(empty_tablist); + // Declare singleton. + let tablist = singleton(key_0, value_0); + // Assert state. + assert!(length(&tablist) == 1, 0); + assert!(!is_empty(&tablist), 0); + assert!(*option::borrow(&get_head_key(&tablist)) == key_0, 0); + assert!(*option::borrow(&get_tail_key(&tablist)) == key_0, 0); + assert!(contains(&tablist, key_0), 0); + assert!(!contains(&tablist, key_1), 0); + assert!(*borrow(&tablist, key_0) == value_0, 0); + // Mutate value. + *borrow_mut(&mut tablist, key_0) = !value_0; + // Assert mutation. + assert!(*borrow(&tablist, key_0) == !value_0, 0); + // Mutate value back. + *borrow_mut(&mut tablist, key_0) = value_0; + // Add another key-value pair. + add(&mut tablist, key_1, value_1); + // Assert state. + assert!(length(&tablist) == 2, 0); + assert!(!is_empty(&tablist), 0); + assert!(*option::borrow(&get_head_key(&tablist)) == key_0, 0); + assert!(*option::borrow(&get_tail_key(&tablist)) == key_1, 0); + assert!(contains(&tablist, key_0), 0); + assert!(contains(&tablist, key_1), 0); + assert!(*borrow(&tablist, key_0) == value_0, 0); + assert!(*borrow(&tablist, key_1) == value_1, 0); + // Remove both nodes, asserting values. + assert!(remove(&mut tablist, key_0) == value_0, 0); + assert!(remove(&mut tablist, key_1) == value_1, 0); + destroy_empty(tablist); // Destroy empty tablist. + } + + // Tests <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +} \ No newline at end of file diff --git a/testsuite/module-publish/src/packages/econia/sources/txn_generator_utils.move b/testsuite/module-publish/src/packages/econia/sources/txn_generator_utils.move new file mode 100644 index 0000000000000..206c0516a64a3 --- /dev/null +++ b/testsuite/module-publish/src/packages/econia/sources/txn_generator_utils.move @@ -0,0 +1,433 @@ +module econia::txn_generator_utils { + use econia::market; + use econia::incentives; + use econia::assets::{Self, QC, UC, AAC, ABC, ACC, ADC, AEC, AFC, AGC, AHC, AIC, AJC, AKC, ALC, AMC, ANC, AOC, APC, AQC, ARC, ASC, ATC, AUC, AVC, AWC, AXC, AYC, AZC, BAC, BBC, BCC, BDC, BEC, BFC, BGC, BHC, BIC, BJC, BKC, BLC, BMC, BNC, BOC, BPC, BQC, BRC, BSC, BTC, BUC, BVC, BWC, BXC, BYC, BZC, CAC, CBC, CCC, CDC, CEC, CFC, CGC, CHC, CIC, CJC, CKC, CLC, CMC, CNC, COC, CPC, CQC, CRC, CSC, CTC, CUC, CVC, CWC, CXC, CYC, CZC, DAC, DBC, DCC, DDC, DEC, DFC, DGC, DHC, DIC, DJC, DKC, DLC, DMC, DNC, DOC, DPC, DQC, DRC, DSC, DTC, DUC, DVC, DWC, DXC, DYC, DZC}; + use econia::user; + use aptos_framework::signer; + use std::vector; + // use aptos_framework::account; + + const E_NUM_MARKETS_ZERO: u64 = 101; + const E_NUM_MARKETS_HIGH: u64 = 102; + + /// Lot size for pure coin test market. + const LOT_SIZE_COIN: u64 = 2; + /// Tick size for pure coin test market. + const TICK_SIZE_COIN: u64 = 3; + /// Minimum size for pure coin test market. + const MIN_SIZE_COIN: u64 = 4; + /// Underwriter ID for generic test market. + const UNDERWRITER_ID: u64 = 345; + /// Custodian ID flag for no custodian. + const NO_CUSTODIAN: u64 = 0; + /// Market ID for pure coin test market. + const MARKET_ID_COIN: u64 = 1; + /// Flag for ask side. + const ASK: bool = true; + /// Flag for bid side. + const BID: bool = false; + /// Flag to abort during a self match. + const ABORT: u8 = 0; + /// Flag to cancel maker order only during a self match. + const CANCEL_MAKER: u8 = 2; + + public entry fun register_multiple_markets(publisher: &signer, num_markets: u64) { + assert!(num_markets > 0, E_NUM_MARKETS_ZERO); + assert!(num_markets < 105, E_NUM_MARKETS_HIGH); + + if (num_markets > 0) { + register_market(publisher); + }; + if (num_markets > 1) { + register_market(publisher); + }; + if (num_markets > 2) { + register_market(publisher); + }; + if (num_markets > 3) { + register_market(publisher); + }; + if (num_markets > 4) { + register_market(publisher); + }; + if (num_markets > 5) { + register_market(publisher); + }; + if (num_markets > 6) { + register_market(publisher); + }; + if (num_markets > 7) { + register_market(publisher); + }; + if (num_markets > 8) { + register_market(publisher); + }; + if (num_markets > 9) { + register_market(publisher); + }; + if (num_markets > 10) { + register_market(publisher); + }; + if (num_markets > 11) { + register_market(publisher); + }; + if (num_markets > 12) { + register_market(publisher); + }; + if (num_markets > 13) { + register_market(publisher); + }; + if (num_markets > 14) { + register_market(publisher); + }; + if (num_markets > 15) { + register_market(publisher); + }; + if (num_markets > 16) { + register_market(publisher); + }; + if (num_markets > 17) { + register_market(publisher); + }; + if (num_markets > 18) { + register_market(publisher); + }; + if (num_markets > 19) { + register_market(publisher); + }; + if (num_markets > 20) { + register_market(publisher); + }; + if (num_markets > 21) { + register_market(publisher); + }; + if (num_markets > 22) { + register_market(publisher); + }; + if (num_markets > 23) { + register_market(publisher); + }; + if (num_markets > 24) { + register_market(publisher); + }; + if (num_markets > 25) { + register_market(publisher); + }; + if (num_markets > 26) { + register_market(publisher); + }; + if (num_markets > 27) { + register_market(publisher); + }; + if (num_markets > 28) { + register_market(publisher); + }; + if (num_markets > 29) { + register_market(publisher); + }; + if (num_markets > 30) { + register_market(publisher); + }; + if (num_markets > 31) { + register_market(publisher); + }; + if (num_markets > 32) { + register_market(publisher); + }; + if (num_markets > 33) { + register_market(publisher); + }; + if (num_markets > 34) { + register_market(publisher); + }; + if (num_markets > 35) { + register_market(publisher); + }; + if (num_markets > 36) { + register_market(publisher); + }; + if (num_markets > 37) { + register_market(publisher); + }; + if (num_markets > 38) { + register_market(publisher); + }; + if (num_markets > 39) { + register_market(publisher); + }; + if (num_markets > 40) { + register_market(publisher); + }; + if (num_markets > 41) { + register_market(publisher); + }; + if (num_markets > 42) { + register_market(publisher); + }; + if (num_markets > 43) { + register_market(publisher); + }; + if (num_markets > 44) { + register_market(publisher); + }; + if (num_markets > 45) { + register_market(publisher); + }; + if (num_markets > 46) { + register_market(publisher); + }; + if (num_markets > 47) { + register_market(publisher); + }; + if (num_markets > 48) { + register_market(publisher); + }; + if (num_markets > 49) { + register_market(publisher); + }; + if (num_markets > 50) { + register_market(publisher); + }; + if (num_markets > 51) { + register_market(publisher); + }; + if (num_markets > 52) { + register_market(publisher); + }; + if (num_markets > 53) { + register_market(publisher); + }; + if (num_markets > 54) { + register_market(publisher); + }; + if (num_markets > 55) { + register_market(publisher); + }; + if (num_markets > 56) { + register_market(publisher); + }; + if (num_markets > 57) { + register_market(publisher); + }; + if (num_markets > 58) { + register_market(publisher); + }; + if (num_markets > 59) { + register_market(publisher); + }; + if (num_markets > 60) { + register_market(publisher); + }; + if (num_markets > 61) { + register_market(publisher); + }; + if (num_markets > 62) { + register_market(publisher); + }; + if (num_markets > 63) { + register_market(publisher); + }; + if (num_markets > 64) { + register_market(publisher); + }; + if (num_markets > 65) { + register_market(publisher); + }; + if (num_markets > 66) { + register_market(publisher); + }; + if (num_markets > 67) { + register_market(publisher); + }; + if (num_markets > 68) { + register_market(publisher); + }; + if (num_markets > 69) { + register_market(publisher); + }; + if (num_markets > 70) { + register_market(publisher); + }; + if (num_markets > 71) { + register_market(publisher); + }; + if (num_markets > 72) { + register_market(publisher); + }; + if (num_markets > 73) { + register_market(publisher); + }; + if (num_markets > 74) { + register_market(publisher); + }; + if (num_markets > 75) { + register_market(publisher); + }; + if (num_markets > 76) { + register_market(publisher); + }; + if (num_markets > 77) { + register_market(publisher); + }; + if (num_markets > 78) { + register_market(publisher); + }; + if (num_markets > 79) { + register_market(publisher); + }; + if (num_markets > 80) { + register_market(publisher); + }; + if (num_markets > 81) { + register_market(publisher); + }; + if (num_markets > 82) { + register_market(publisher); + }; + if (num_markets > 83) { + register_market(publisher); + }; + if (num_markets > 84) { + register_market(publisher); + }; + if (num_markets > 85) { + register_market(publisher); + }; + if (num_markets > 86) { + register_market(publisher); + }; + if (num_markets > 87) { + register_market(publisher); + }; + if (num_markets > 88) { + register_market(publisher); + }; + if (num_markets > 89) { + register_market(publisher); + }; + if (num_markets > 90) { + register_market(publisher); + }; + if (num_markets > 91) { + register_market(publisher); + }; + if (num_markets > 92) { + register_market(publisher); + }; + if (num_markets > 93) { + register_market(publisher); + }; + if (num_markets > 94) { + register_market(publisher); + }; + if (num_markets > 95) { + register_market(publisher); + }; + if (num_markets > 96) { + register_market(publisher); + }; + if (num_markets > 97) { + register_market(publisher); + }; + if (num_markets > 98) { + register_market(publisher); + }; + if (num_markets > 99) { + register_market(publisher); + }; + if (num_markets > 100) { + register_market(publisher); + }; + if (num_markets > 101) { + register_market(publisher); + }; + if (num_markets > 102) { + register_market(publisher); + }; + if (num_markets > 103) { + register_market(publisher); + }; + } + + public entry fun register_market(publisher: &signer) { + assert!(@econia == signer::address_of(publisher), 101); + // Get market registration fee. + let fee = incentives::get_market_registration_fee(); + // Register pure coin market. + market::register_market_base_coin( + LOT_SIZE_COIN, TICK_SIZE_COIN, MIN_SIZE_COIN, + assets::mint(publisher, fee)); + } + + public entry fun register_market_accounts(user: &signer, market_id: u64) { + user::register_market_account(user, market_id, NO_CUSTODIAN); + } + + public entry fun deposit_coins(user: &signer, publisher: &signer, market_id: u64) { + user::deposit_coins(signer::address_of(user), market_id, NO_CUSTODIAN, assets::mint(publisher, 10000000000)); + user::deposit_coins(signer::address_of(user), market_id, NO_CUSTODIAN, assets::mint(publisher, 10000000000)); + } + + public entry fun place_bid_limit_order(user: &signer, size: u64, price: u64, market_id: u64) { + market::place_limit_order_user(user, market_id, @econia, BID, size, price, 3, ABORT); + } + + public entry fun place_ask_limit_order(user: &signer, size: u64, price: u64, market_id: u64) { + market::place_limit_order_user(user, market_id, @econia, ASK, size, price, 3, ABORT); + } + + public entry fun place_bid_market_order(user: &signer, size: u64, market_id: u64) { + market::place_market_order_user(user, market_id, @econia, BID, size, CANCEL_MAKER); + } + + public entry fun place_ask_market_order(user: &signer, size: u64, market_id: u64) { + market::place_market_order_user(user, market_id, @econia, ASK, size, CANCEL_MAKER); + } + + struct Order has drop, store { + market_id: u64, + direction: bool, + order_id: u128, + } + + struct Orders has key, drop { + orders: vector + } + + public entry fun place_limit_order(user: &signer, market_id: u64, direction: bool, size: u64, price: u64, restriction: u8, self_match_behavior: u8) acquires Orders { + let (order_id, _, _, _) = market::place_limit_order_user(user, market_id, @econia, direction, size, price, restriction, self_match_behavior); + + if (exists(signer::address_of(user))) { + vector::push_back(&mut borrow_global_mut(signer::address_of(user)).orders, + Order{ + market_id: market_id, + direction: direction, + order_id: order_id + } + ); + } else { + let orders = vector::empty(); + vector::push_back(&mut orders, Order{ + market_id: market_id, + direction: direction, + order_id: order_id + } + ); + move_to(user, Orders {orders: orders}); + } + } + + public entry fun place_market_order(user: &signer, market_id: u64, direction: bool, size: u64, self_match_behavior: u8) { + market::place_market_order_user(user, market_id, @econia, direction, size, self_match_behavior); + } + + public entry fun place_cancel_order(user: &signer) acquires Orders { + if (exists(signer::address_of(user))) { + let orders = borrow_global_mut(signer::address_of(user)); + if (!vector::is_empty(&orders.orders)) { + let order = vector::pop_back(&mut orders.orders); + market::cancel_order_user(user, order.market_id, order.direction, order.order_id); + } + } + } +} \ No newline at end of file diff --git a/testsuite/module-publish/src/packages/econia/sources/user.move b/testsuite/module-publish/src/packages/econia/sources/user.move new file mode 100644 index 0000000000000..0cc839dff8c5d --- /dev/null +++ b/testsuite/module-publish/src/packages/econia/sources/user.move @@ -0,0 +1,5874 @@ +/// User-side asset, collateral, and order management. +/// +/// Contains data structures and functionality for tracking a user's +/// assets and open orders. Upon market account registration, users can +/// either preside over their own account, or delegate custody to a +/// custodian who manage their orders and withdrawals. For each market, +/// a user can open multiple market accounts, each with a unique +/// custodian. +/// +/// # General overview sections +/// +/// [Architecture](#architecture) +/// +/// * [Market account IDs](#market-account-IDs) +/// * [Market accounts](#market-accounts) +/// * [Orders and access keys](#orders-and-access-keys) +/// * [Market order IDs](#market-order-IDs) +/// +/// [Function index](#function-index) +/// +/// * [View functions](#view-functions) +/// * [Public functions](#public-functions) +/// * [Public entry functions](#public-entry-functions) +/// * [Public friend functions](#public-friend-functions) +/// * [Dependency charts](#dependency-charts) +/// +/// [Complete DocGen index](#complete-docgen-index) +/// +/// # Architecture +/// +/// ## Market account IDs +/// +/// Markets, defined in the global registry, are assigned a 1-indexed +/// `u64` market ID, as are custodians. The concatenated result of a +/// market ID and a custodian ID is known as a market account ID, which +/// is used as a key in assorted user-side lookup operations: the 64 +/// least-significant bits in a market account ID are the custodian ID +/// for the given market account (`NIL` if no delegated custodian), +/// while the 64 most-significant bits are the market ID. See +/// `get_custodian_id()`, `get_market_account_id()`, and +/// `get_market_id()` for implementation details. +/// +/// ## Market accounts +/// +/// When a user opens a market account, a `MarketAccount` entry is +/// added to their `MarketAccounts`, and a coin entry is added to their +/// `Collateral` for the given market's quote coin type. If the market's +/// base asset is a coin, a `Collateral` entry is similarly created for +/// the base coin type. +/// +/// ## Orders and access keys +/// +/// When users place an order on the order book, an `Order` is added to +/// their corresponding `MarketAccount`. If they then cancel the order, +/// the corresponding `Order` is not deallocated, but rather, marked +/// "inactive" and pushed onto a stack of inactive orders for the +/// corresponding side (`MarketAccount.asks_stack_top` or +/// `MarketAccount.bids_stack_top`). Then, when a user places another +/// order, rather than allocating a new `Order`, the inactive order at +/// the top of the stack is popped off the stack and marked active. +/// +/// This approach is motivated by global storage gas costs: as of the +/// time of this writing, per-item creations cost approximately 16.7 +/// times as much as per-item writes, and there is no incentive to +/// deallocate from memory. Hence the inactive stack paradigm allows +/// for orders to be recycled in a way that reduces overall storage +/// costs. In practice, however, this means that each `Order` is +/// assigned a static "access key" that persists throughout subsequent +/// active order states: if a user places an order, cancels the order, +/// then places another order, the `Order` will have the same access key +/// in each active instance. In other words, access keys are the lookup +/// ID in the relevant `Order` data structure for the given side +/// (`MarketAccount.asks` or `MarketAccount.bids`), and are not +/// necessarily unique for orders across time. +/// +/// ## Market order IDs +/// +/// Market order IDs, however, are unique across time for a given market +/// ID, and are tracked in a users' `Order.market_order_id`. A market +/// order ID is a unique identifier for an order on a given order book. +/// +/// # Function index +/// +/// ## View functions +/// +/// Constant getters: +/// +/// * `get_ASK()` +/// * `get_BID()` +/// * `get_CANCEL_REASON_EVICTION()` +/// * `get_CANCEL_REASON_IMMEDIATE_OR_CANCEL()` +/// * `get_CANCEL_REASON_MANUAL_CANCEL()` +/// * `get_CANCEL_REASON_MAX_QUOTE_TRADED()` +/// * `get_CANCEL_REASON_NOT_ENOUGH_LIQUIDITY()` +/// * `get_CANCEL_REASON_SELF_MATCH_MAKER()` +/// * `get_CANCEL_REASON_SELF_MATCH_TAKER()` +/// * `get_CANCEL_REASON_TOO_SMALL_TO_FILL_LOT()` +/// * `get_CANCEL_REASON_VIOLATED_LIMIT_PRICE()` +/// * `get_NO_CUSTODIAN()` +/// +/// Market account lookup: +/// +/// * `get_all_market_account_ids_for_market_id()` +/// * `get_all_market_account_ids_for_user()` +/// * `get_market_account()` +/// * `get_market_accounts()` +/// * `get_market_event_handle_creation_numbers()` +/// * `has_market_account()` +/// * `has_market_account_by_market_account_id()` +/// * `has_market_account_by_market_id()` +/// +/// Market account ID lookup: +/// +/// * `get_custodian_id()` +/// * `get_market_account_id()` +/// * `get_market_id()` +/// +/// ## Public functions +/// +/// Market account lookup +/// +/// * `get_asset_counts_custodian()` +/// * `get_asset_counts_user()` +/// * `get_market_account_market_info_custodian()` +/// * `get_market_account_market_info_user()` +/// +/// Asset transfer: +/// +/// * `deposit_coins()` +/// * `deposit_generic_asset()` +/// * `withdraw_coins_custodian()` +/// * `withdraw_coins_user()` +/// * `withdraw_generic_asset_custodian()` +/// * `withdraw_generic_asset_user()` +/// +/// ## Public entry functions +/// +/// Asset transfer: +/// +/// * `deposit_from_coinstore()` +/// * `withdraw_to_coinstore()` +/// +/// Account registration: +/// +/// * `init_market_event_handles_if_missing()` +/// * `register_market_account()` +/// * `register_market_account_generic_base()` +/// +/// ## Public friend functions +/// +/// Order management: +/// +/// * `cancel_order_internal()` +/// * `change_order_size_internal()` +/// * `get_open_order_id_internal()` +/// * `fill_order_internal()` +/// * `place_order_internal()` +/// +/// Asset management: +/// +/// * `deposit_assets_internal()` +/// * `get_asset_counts_internal()` +/// * `withdraw_assets_internal()` +/// +/// Order identifiers: +/// +/// * `get_next_order_access_key_internal()` +/// * `get_active_market_order_ids_internal()` +/// +/// Market events: +/// +/// * `create_cancel_order_event_internal()` +/// * `create_fill_event_internal()` +/// * `emit_limit_order_events_internal()` +/// * `emit_market_order_events_internal()` +/// * `emit_swap_maker_fill_events_internal()` +/// +/// ## Dependency charts +/// +/// The below dependency charts use `mermaid.js` syntax, which can be +/// automatically rendered into a diagram (depending on the browser) +/// when viewing the documentation file generated from source code. If +/// a browser renders the diagrams with coloring that makes it difficult +/// to read, try a different browser. +/// +/// Deposits: +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// deposit_coins --> deposit_asset +/// +/// deposit_from_coinstore --> deposit_coins +/// +/// deposit_assets_internal --> deposit_asset +/// deposit_assets_internal --> deposit_coins +/// +/// deposit_generic_asset --> deposit_asset +/// deposit_generic_asset --> registry::get_underwriter_id +/// +/// ``` +/// +/// Withdrawals: +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// withdraw_generic_asset_user --> withdraw_generic_asset +/// +/// withdraw_generic_asset_custodian --> withdraw_generic_asset +/// withdraw_generic_asset_custodian --> registry::get_custodian_id +/// +/// withdraw_coins_custodian --> withdraw_coins +/// withdraw_coins_custodian --> registry::get_custodian_id +/// +/// withdraw_coins_user --> withdraw_coins +/// +/// withdraw_to_coinstore --> withdraw_coins_user +/// +/// withdraw_generic_asset --> withdraw_asset +/// withdraw_generic_asset --> registry::get_underwriter_id +/// +/// withdraw_coins --> withdraw_asset +/// +/// withdraw_assets_internal --> withdraw_asset +/// withdraw_assets_internal --> withdraw_coins +/// +/// ``` +/// +/// Market account lookup: +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// get_asset_counts_user --> get_asset_counts_internal +/// +/// get_asset_counts_custodian --> get_asset_counts_internal +/// get_asset_counts_custodian --> registry::get_custodian_id +/// +/// get_market_account_market_info_custodian --> +/// get_market_account_market_info +/// get_market_account_market_info_custodian --> +/// registry::get_custodian_id +/// +/// get_market_account_market_info_user --> +/// get_market_account_market_info +/// +/// get_market_accounts --> get_all_market_account_ids_for_user +/// get_market_accounts --> get_market_id +/// get_market_accounts --> get_custodian_id +/// get_market_accounts --> get_market_account +/// +/// get_market_account --> get_market_account_id +/// get_market_account --> has_market_account_by_market_account_id +/// get_market_account --> vectorize_open_orders +/// +/// get_open_order_id_internal --> get_market_account_id +/// get_open_order_id_internal --> +/// has_market_account_by_market_account_id +/// +/// has_market_account --> has_market_account_by_market_account_id +/// has_market_account --> get_market_account_id +/// +/// get_market_event_handle_creation_numbers --> get_market_account_id +/// +/// ``` +/// +/// Market account registration: +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// register_market_account --> registry::is_registered_custodian_id +/// register_market_account --> register_market_account_account_entries +/// register_market_account --> register_market_account_collateral_entry +/// register_market_account --> init_market_event_handles_if_missing +/// +/// register_market_account_generic_base --> register_market_account +/// +/// register_market_account_account_entries --> +/// registry::get_market_info_for_market_account +/// +/// init_market_event_handles_if_missing --> has_market_account +/// +/// ``` +/// +/// Internal order management: +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// change_order_size_internal --> cancel_order_internal +/// change_order_size_internal --> place_order_internal +/// +/// ``` +/// +/// Market events: +/// +/// ```mermaid +/// +/// flowchart LR +/// +/// emit_limit_order_events_internal --> emit_maker_fill_event +/// emit_market_order_events_internal --> emit_maker_fill_event +/// emit_swap_maker_fill_events_internal --> emit_maker_fill_event +/// +/// ``` +/// +/// # Complete DocGen index +/// +/// The below index is automatically generated from source code: +module econia::user { + + // Uses >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + use aptos_framework::account; + use aptos_framework::coin::{Self, Coin}; + use aptos_framework::guid; + use aptos_framework::table::{Self, Table}; + use aptos_framework::event::{Self, EventHandle}; + use aptos_framework::type_info::{Self, TypeInfo}; + use econia::tablist::{Self, Tablist}; + use econia::registry::{ + Self, CustodianCapability, GenericAsset, UnderwriterCapability}; + use std::option::{Self, Option}; + use std::signer::address_of; + use std::string::String; + use std::vector; + + // Uses <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Friends >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + friend econia::market; + + // Friends <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Test-only uses >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test_only] + use econia::avl_queue::{u_128_by_32, u_64_by_32}; + #[test_only] + use econia::assets::{Self, BC, QC, UC}; + #[test_only] + use std::string; + + // Test-only uses <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Structs >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Emitted when an order is cancelled. + struct CancelOrderEvent has copy, drop, store { + /// Market ID for order. + market_id: u64, + /// Unique ID for order within market. + order_id: u128, + /// User for market account that placed order. + user: address, + /// Custodian ID for market account that placed order. + custodian_id: u64, + /// Reason for the cancel, for example + /// `CANCEL_REASON_MANUAL_CANCEL`. + reason: u8 + } + + /// Emitted when the size of an open order is manually changed. + struct ChangeOrderSizeEvent has copy, drop, store { + /// Market ID for order. + market_id: u64, + /// Unique ID for order within market. + order_id: u128, + /// User for market account that placed order. + user: address, + /// Custodian ID for market account that placed order. + custodian_id: u64, + /// `ASK` or `BID`. + side: bool, + /// Order size after manual size change operation. + new_size: u64 + } + + /// All of a user's collateral across all market accounts. + struct Collateral has key { + /// Map from market account ID to collateral for market account. + /// Separated into different table entries to reduce transaction + /// collisions across markets. Enables off-chain iterated + /// indexing by market account ID. + map: Tablist> + } + + /// Emitted when one order fills against another. + struct FillEvent has copy, drop, store { + /// Market ID for fill. + market_id: u64, + /// Amount filled, in lots. + size: u64, + /// Fill price, in ticks per lot. + price: u64, + /// `ASK` or `BID`, the side of the maker order. + maker_side: bool, + /// User address associated with market account for maker. + maker: address, + /// Custodian ID associated with market account for maker. + maker_custodian_id: u64, + /// Order ID for maker, unique within the market. + maker_order_id: u128, + /// User address associated with market account for taker. + taker: address, + /// Custodian ID associated with market account for taker. + taker_custodian_id: u64, + /// Order ID for taker, unique within the market. + taker_order_id: u128, + /// Amount of fees paid by taker on the fill, in indivisible + /// quote subunits. + taker_quote_fees_paid: u64, + /// Sequence number (0-indexed) of fill within a single trade, + /// which may have more than one fill. For example if a market + /// order results in two fills, the first will have sequence + /// number 0 and the second will have sequence number 1. + sequence_number_for_trade: u64 + } + + /// Represents a user's open orders and asset counts for a given + /// market account ID. Contains `registry::MarketInfo` field + /// duplicates to reduce global storage item queries against the + /// registry. + struct MarketAccount has store { + /// `registry::MarketInfo.base_type`. + base_type: TypeInfo, + /// `registry::MarketInfo.base_name_generic`. + base_name_generic: String, + /// `registry::MarketInfo.quote_type`. + quote_type: TypeInfo, + /// `registry::MarketInfo.lot_size`. + lot_size: u64, + /// `registry::MarketInfo.tick_size`. + tick_size: u64, + /// `registry::MarketInfo.min_size`. + min_size: u64, + /// `registry::MarketInfo.underwriter_id`. + underwriter_id: u64, + /// Map from order access key to open ask order. + asks: Tablist, + /// Map from order access key to open bid order. + bids: Tablist, + /// Access key of ask order at top of inactive stack, if any. + asks_stack_top: u64, + /// Access key of bid order at top of inactive stack, if any. + bids_stack_top: u64, + /// Total base asset units held as collateral. + base_total: u64, + /// Base asset units available to withdraw. + base_available: u64, + /// Amount `base_total` will increase to if all open bids fill. + base_ceiling: u64, + /// Total quote asset units held as collateral. + quote_total: u64, + /// Quote asset units available to withdraw. + quote_available: u64, + /// Amount `quote_total` will increase to if all open asks fill. + quote_ceiling: u64 + } + + /// User-friendly market account view function return. + struct MarketAccountView has store { + /// Market ID for given market account. + market_id: u64, + /// Custodian ID for given market account. + custodian_id: u64, + /// All open asks. + asks: vector, + /// All open bids. + bids: vector, + /// `MarketAccount.base_total`. + base_total: u64, + /// `MarketAccount.base_available`. + base_available: u64, + /// `MarketAccount.base_ceiling`. + base_ceiling: u64, + /// `MarketAccount.quote_total`. + quote_total: u64, + /// `MarketAccount.quote_available`. + quote_available: u64, + /// `MarketAccount.quote_ceiling`. + quote_ceiling: u64 + } + + /// All of a user's market accounts. + struct MarketAccounts has key { + /// Map from market account ID to `MarketAccount`. + map: Table, + /// Map from market ID to vector of custodian IDs for which + /// a market account has been registered on the given market. + /// Enables off-chain iterated indexing by market account ID and + /// assorted on-chain queries. + custodians: Tablist> + } + + /// View function return for getting event handle creation numbers + /// of a particular `MarketEventHandlesForMarketAccount`. + struct MarketEventHandleCreationNumbers has copy, drop { + /// Creation number of `cancel_order_events` handle in a + /// `MarketEventHandlesForMarketAccount`. + cancel_order_events_handle_creation_num: u64, + /// Creation number of `change_order_size_events` handle in a + /// `MarketEventHandlesForMarketAccount`. + change_order_size_events_handle_creation_num: u64, + /// Creation number of `fill_events` handle in a + /// `MarketEventHandlesForMarketAccount`. + fill_events_handle_creation_num: u64, + /// Creation number of `place_limit_order_events` handle in a + /// `MarketEventHandlesForMarketAccount`. + place_limit_order_events_handle_creation_num: u64, + /// Creation number of `place_market_order_events` handle in a + /// `MarketEventHandlesForMarketAccount`. + place_market_order_events_handle_creation_num: u64 + } + + /// All of a user's `MarketEventHandlesForMarketAccount`. + struct MarketEventHandles has key { + /// Map from market account ID to + /// `MarketEventHandlesForMarketAccount`. + map: Table + } + + /// Event handles for market events within a unique market account. + struct MarketEventHandlesForMarketAccount has store { + /// Event handle for `CancelOrderEvent`s. + cancel_order_events: EventHandle, + /// Event handle for `ChangeOrderSizeEvent`s. + change_order_size_events: EventHandle, + /// Event handle for `FillEvent`s. + fill_events: EventHandle, + /// Event handle for `PlaceLimitOrderEvent`s. + place_limit_order_events: EventHandle, + /// Event handle for `PlaceMarketOrderEvent`s. + place_market_order_events: EventHandle + } + + /// An open order, either ask or bid. + struct Order has store { + /// Market order ID. `NIL` if inactive. + market_order_id: u128, + /// Order size left to fill, in lots. When `market_order_id` is + /// `NIL`, indicates access key of next inactive order in stack. + size: u64 + } + + /// Emitted when a limit order is placed. + struct PlaceLimitOrderEvent has copy, drop, store { + /// Market ID for order. + market_id: u64, + /// User for market account that placed order. + user: address, + /// Custodian ID for market account that placed order. + custodian_id: u64, + /// Integrator address passed during limit order placement, + /// eligible for a portion of any generated taker fees. + integrator: address, + /// `ASK` or `BID`. + side: bool, + /// Size indicated during limit order placement. + size: u64, + /// Order limit price. + price: u64, + /// Restriction indicated during limit order placement, either + /// `market::FILL_OR_ABORT`, `market::IMMEDIATE_OR_CANCEL`, + /// `market::POST_OR_ABORT`, or `market::NO_RESTRICTION`. + restriction: u8, + /// Self match behavior indicated during limit order placement, + /// either `market::ABORT`, `market::CANCEL_BOTH`, + /// `market::CANCEL_MAKER`, or `market::CANCEL_TAKER`. + self_match_behavior: u8, + /// Order size remaining after the function call in which the + /// order was placed, which may include fills across the spread. + /// For example if an order of size 10 and restriction + /// `market::IMMEDIATE_OR_CANCEL` fills 6 lots across the + /// spread, the order will be cancelled and remaining size is 4. + remaining_size: u64, + /// Unique ID for order within market. + order_id: u128 + } + + /// Emitted when a market order is placed. + struct PlaceMarketOrderEvent has copy, drop, store { + /// Market ID for order. + market_id: u64, + /// User for market account that placed order. + user: address, + /// Custodian ID for market account that placed order. + custodian_id: u64, + /// Integrator address passed during market order placement, + /// eligible for a portion of any generated taker fees. + integrator: address, + /// Either `market::BUY` or `market::SELL`. + direction: bool, + /// Size indicated during market order placement. + size: u64, + /// Self match behavior indicated during market order placement, + /// either `market::ABORT`, `market::CANCEL_BOTH`, + /// `market::CANCEL_MAKER`, or `market::CANCEL_TAKER`. + self_match_behavior: u8, + /// Unique ID for order within market. + order_id: u128 + } + + // Structs <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Error codes >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Market account already exists. + const E_EXISTS_MARKET_ACCOUNT: u64 = 0; + /// Custodian ID has not been registered. + const E_UNREGISTERED_CUSTODIAN: u64 = 1; + /// No market accounts resource found. + const E_NO_MARKET_ACCOUNTS: u64 = 2; + /// No market account resource found. + const E_NO_MARKET_ACCOUNT: u64 = 3; + /// Asset type is not in trading pair for market. + const E_ASSET_NOT_IN_PAIR: u64 = 4; + /// Deposit would overflow asset ceiling. + const E_DEPOSIT_OVERFLOW_ASSET_CEILING: u64 = 5; + /// Underwriter is not valid for indicated market. + const E_INVALID_UNDERWRITER: u64 = 6; + /// Too little available for withdrawal. + const E_WITHDRAW_TOO_LITTLE_AVAILABLE: u64 = 7; + /// Price is zero. + const E_PRICE_0: u64 = 8; + /// Price exceeds maximum possible price. + const E_PRICE_TOO_HIGH: u64 = 9; + /// Ticks to fill an order overflows a `u64`. + const E_TICKS_OVERFLOW: u64 = 11; + /// Filling order would overflow asset received from trade. + const E_OVERFLOW_ASSET_IN: u64 = 12; + /// Not enough asset to trade away. + const E_NOT_ENOUGH_ASSET_OUT: u64 = 13; + /// No change in order size. + const E_CHANGE_ORDER_NO_CHANGE: u64 = 14; + /// Market order ID mismatch with user's open order. + const E_INVALID_MARKET_ORDER_ID: u64 = 15; + /// Mismatch between coin value and indicated amount. + const E_COIN_AMOUNT_MISMATCH: u64 = 16; + /// Expected order access key does not match assigned order access + /// key. + const E_ACCESS_KEY_MISMATCH: u64 = 17; + /// Coin type is generic asset. + const E_COIN_TYPE_IS_GENERIC_ASSET: u64 = 18; + /// Mismatch between expected size before operation and actual size + /// before operation. + const E_START_SIZE_MISMATCH: u64 = 19; + + // Error codes <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Constants >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Flag for ask side + const ASK: bool = true; + /// Flag for bid side + const BID: bool = false; + /// Order cancelled because it was evicted from the price-time + /// priority queue. + const CANCEL_REASON_EVICTION: u8 = 1; + /// Order cancelled because it was an immediate-or-cancel order + /// that did not immediately fill. + const CANCEL_REASON_IMMEDIATE_OR_CANCEL: u8 = 2; + /// Order cancelled because it was manually cancelled by either + /// signing user or custodian. + const CANCEL_REASON_MANUAL_CANCEL: u8 = 3; + /// Order cancelled because no more quote asset could be traded. + const CANCEL_REASON_MAX_QUOTE_TRADED: u8 = 4; + /// Order cancelled because there was not enough liquidity to take + /// from. + const CANCEL_REASON_NOT_ENOUGH_LIQUIDITY: u8 = 5; + /// Order cancelled because it was on the maker side of an fill + /// where self match behavior indicated cancelling the maker order. + const CANCEL_REASON_SELF_MATCH_MAKER: u8 = 6; + /// Order cancelled because it was on the taker side of an fill + /// where self match behavior indicated cancelling the taker order. + const CANCEL_REASON_SELF_MATCH_TAKER: u8 = 7; + /// Flag to indicate that order is only temporarily cancelled from + /// market account memory because it will be subsequently re-placed + /// as part of a size change. + const CANCEL_REASON_SIZE_CHANGE_INTERNAL: u8 = 0; + /// Swap order cancelled because the remaining base asset amount to + /// match was too small to fill a single lot. + const CANCEL_REASON_TOO_SMALL_TO_FILL_LOT: u8 = 8; + /// Swap order cancelled because the next order on the book to match + /// against violated the swap order limit price. + const CANCEL_REASON_VIOLATED_LIMIT_PRICE: u8 = 9; + /// `u64` bitmask with all bits set, generated in Python via + /// `hex(int('1' * 64, 2))`. + const HI_64: u64 = 0xffffffffffffffff; + /// Maximum possible price that can be encoded in 32 bits. Generated + /// in Python via `hex(int('1' * 32, 2))`. + const HI_PRICE: u64 = 0xffffffff; + /// Flag for null value when null defined as 0. + const NIL: u64 = 0; + /// Custodian ID flag for no custodian. + const NO_CUSTODIAN: u64 = 0; + /// Underwriter ID flag for no underwriter. + const NO_UNDERWRITER: u64 = 0; + /// Number of bits market ID is shifted in market account ID. + const SHIFT_MARKET_ID: u8 = 64; + + // Constants <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // View functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[view] + /// Public constant getter for `ASK`. + /// + /// # Testing + /// + /// * `test_get_ASK()` + public fun get_ASK(): bool {ASK} + + #[view] + /// Public constant getter for `BID`. + /// + /// # Testing + /// + /// * `test_get_BID()` + public fun get_BID(): bool {BID} + + #[view] + /// Public constant getter for `CANCEL_REASON_EVICTION`. + /// + /// # Testing + /// + /// * `test_get_cancel_reasons()` + public fun get_CANCEL_REASON_EVICTION(): u8 { + CANCEL_REASON_EVICTION + } + + #[view] + /// Public constant getter for `CANCEL_REASON_IMMEDIATE_OR_CANCEL`. + /// + /// # Testing + /// + /// * `test_get_cancel_reasons()` + public fun get_CANCEL_REASON_IMMEDIATE_OR_CANCEL(): u8 { + CANCEL_REASON_IMMEDIATE_OR_CANCEL + } + + #[view] + /// Public constant getter for `CANCEL_REASON_MANUAL_CANCEL`. + /// + /// # Testing + /// + /// * `test_get_cancel_reasons()` + public fun get_CANCEL_REASON_MANUAL_CANCEL(): u8 { + CANCEL_REASON_MANUAL_CANCEL + } + + #[view] + /// Public constant getter for `CANCEL_REASON_MAX_QUOTE_TRADED`. + /// + /// # Testing + /// + /// * `test_get_cancel_reasons()` + public fun get_CANCEL_REASON_MAX_QUOTE_TRADED(): u8 { + CANCEL_REASON_MAX_QUOTE_TRADED + } + + #[view] + /// Public constant getter for `CANCEL_REASON_NOT_ENOUGH_LIQUIDITY`. + /// + /// # Testing + /// + /// * `test_get_cancel_reasons()` + public fun get_CANCEL_REASON_NOT_ENOUGH_LIQUIDITY(): u8 { + CANCEL_REASON_NOT_ENOUGH_LIQUIDITY + } + + #[view] + /// Public constant getter for `CANCEL_REASON_SELF_MATCH_MAKER`. + /// + /// # Testing + /// + /// * `test_get_cancel_reasons()` + public fun get_CANCEL_REASON_SELF_MATCH_MAKER(): u8 { + CANCEL_REASON_SELF_MATCH_MAKER + } + + #[view] + /// Public constant getter for `CANCEL_REASON_SELF_MATCH_TAKER`. + /// + /// # Testing + /// + /// * `test_get_cancel_reasons()` + public fun get_CANCEL_REASON_SELF_MATCH_TAKER(): u8 { + CANCEL_REASON_SELF_MATCH_TAKER + } + + #[view] + /// Public constant getter for + /// `CANCEL_REASON_TOO_SMALL_TO_FILL_LOT`. + /// + /// # Testing + /// + /// * `test_get_cancel_reasons()` + public fun get_CANCEL_REASON_TOO_SMALL_TO_FILL_LOT(): u8 { + CANCEL_REASON_TOO_SMALL_TO_FILL_LOT + } + + #[view] + /// Public constant getter for `CANCEL_REASON_VIOLATED_LIMIT_PRICE`. + /// + /// # Testing + /// + /// * `test_get_cancel_reasons()` + public fun get_CANCEL_REASON_VIOLATED_LIMIT_PRICE(): u8 { + CANCEL_REASON_VIOLATED_LIMIT_PRICE + } + + #[view] + /// Public constant getter for `NO_CUSTODIAN`. + /// + /// # Testing + /// + /// * `test_get_NO_CUSTODIAN()` + public fun get_NO_CUSTODIAN(): u64 {NO_CUSTODIAN} + + #[view] + /// Return all market account IDs associated with market ID. + /// + /// # Parameters + /// + /// * `user`: Address of user to check market account IDs for. + /// * `market_id`: Market ID to check market accounts for. + /// + /// # Returns + /// + /// * `vector`: Vector of user's market account IDs for given + /// market, empty if no market accounts. + /// + /// # Gas considerations + /// + /// Loops over all elements within a vector that is itself a single + /// item in global storage, and returns a vector via pass-by-value. + /// + /// # Testing + /// + /// * `test_market_account_getters()` + public fun get_all_market_account_ids_for_market_id( + user: address, + market_id: u64 + ): vector + acquires MarketAccounts { + let market_account_ids = vector::empty(); // Init empty vector. + // Return empty if user has no market accounts resource. + if (!exists(user)) return market_account_ids; + let custodians_map_ref = // Immutably borrow custodians map. + &borrow_global(user).custodians; + // Return empty if user has no market accounts for given market. + if (!tablist::contains(custodians_map_ref, market_id)) + return market_account_ids; + // Immutably borrow list of custodians for given market. + let custodians_ref = tablist::borrow(custodians_map_ref, market_id); + // Initialize loop counter and number of elements in vector. + let (i, n_custodians) = (0, vector::length(custodians_ref)); + while (i < n_custodians) { // Loop over all elements. + // Get custodian ID. + let custodian_id = *vector::borrow(custodians_ref, i); + // Get market account ID. + let market_account_id = ((market_id as u128) << SHIFT_MARKET_ID) | + (custodian_id as u128); + // Push back onto ongoing market account ID vector. + vector::push_back(&mut market_account_ids, market_account_id); + i = i + 1; // Increment loop counter + }; + market_account_ids // Return market account IDs. + } + + #[view] + /// Return all of a user's market account IDs. + /// + /// # Parameters + /// + /// * `user`: Address of user to check market account IDs for. + /// + /// # Returns + /// + /// * `vector`: Vector of user's market account IDs, empty if + /// no market accounts. + /// + /// # Gas considerations + /// + /// For each market that a user has market accounts for, loops over + /// a separate item in global storage, incurring a per-item read + /// cost. Additionally loops over a vector for each such per-item + /// read, incurring linearly-scaled vector operation costs. Returns + /// a vector via pass-by-value. + /// + /// # Testing + /// + /// * `test_market_account_getters()` + public fun get_all_market_account_ids_for_user( + user: address, + ): vector + acquires MarketAccounts { + let market_account_ids = vector::empty(); // Init empty vector. + // Return empty if user has no market accounts resource. + if (!exists(user)) return market_account_ids; + let custodians_map_ref = // Immutably borrow custodians map. + &borrow_global(user).custodians; + // Get market ID option at head of market ID list. + let market_id_option = tablist::get_head_key(custodians_map_ref); + // While market IDs left to loop over: + while (option::is_some(&market_id_option)) { + // Get market ID. + let market_id = *option::borrow(&market_id_option); + // Immutably borrow list of custodians for given market and + // next market ID option in list. + let (custodians_ref, _, next) = tablist::borrow_iterable( + custodians_map_ref, market_id); + // Initialize loop counter and number of elements in vector. + let (i, n_custodians) = (0, vector::length(custodians_ref)); + while (i < n_custodians) { // Loop over all elements. + // Get custodian ID. + let custodian_id = *vector::borrow(custodians_ref, i); + let market_account_id = // Get market account ID. + ((market_id as u128) << SHIFT_MARKET_ID) | + (custodian_id as u128); + // Push back onto ongoing market account ID vector. + vector::push_back(&mut market_account_ids, market_account_id); + i = i + 1; // Increment loop counter + }; + // Review next market ID option in list. + market_id_option = next; + }; + market_account_ids // Return market account IDs. + } + + #[view] + /// Return custodian ID encoded in market account ID. + /// + /// # Testing + /// + /// * `test_market_account_id_getters()` + public fun get_custodian_id( + market_account_id: u128 + ): u64 { + ((market_account_id & (HI_64 as u128)) as u64) + } + + #[view] + /// Return human-readable `MarketAccountView`. + /// + /// Mutates state, so kept as a private view function. + /// + /// # Aborts + /// + /// * `E_NO_MARKET_ACCOUNT`: No such specified market account. + /// + /// # Testing + /// + /// * `test_deposits()` + /// * `test_get_market_account_no_market_account()` + /// * `test_get_market_accounts_open_orders()` + fun get_market_account( + user: address, + market_id: u64, + custodian_id: u64 + ): MarketAccountView + acquires MarketAccounts { + // Get market account ID from market ID, custodian ID. + let market_account_id = get_market_account_id(market_id, custodian_id); + // Verify user has market account. + assert!(has_market_account_by_market_account_id( + user, market_account_id), E_NO_MARKET_ACCOUNT); + // Mutably borrow market accounts map. + let market_accounts_map_ref_mut = + &mut borrow_global_mut(user).map; + // Mutably borrow market account. + let market_account_ref_mut = table::borrow_mut( + market_accounts_map_ref_mut, market_account_id); + // Return market account view with parsed fields. + MarketAccountView{ + market_id, + custodian_id, + asks: vectorize_open_orders(&mut market_account_ref_mut.asks), + bids: vectorize_open_orders(&mut market_account_ref_mut.bids), + base_total: market_account_ref_mut.base_total, + base_available: market_account_ref_mut.base_available, + base_ceiling: market_account_ref_mut.base_ceiling, + quote_total: market_account_ref_mut.quote_total, + quote_available: market_account_ref_mut.quote_available, + quote_ceiling: market_account_ref_mut.quote_ceiling + } + } + + #[view] + /// Return market account ID with encoded market and custodian IDs. + /// + /// # Testing + /// + /// * `test_market_account_id_getters()` + public fun get_market_account_id( + market_id: u64, + custodian_id: u64 + ): u128 { + ((market_id as u128) << SHIFT_MARKET_ID) | (custodian_id as u128) + } + + #[view] + /// Get user-friendly views of all of a `user`'s market accounts. + /// + /// Mutates state, so kept as a private view function. + /// + /// # Testing + /// + /// * `test_get_market_accounts_open_orders()` + fun get_market_accounts( + user: address + ): vector + acquires MarketAccounts { + // Get all of user's market account IDs. + let market_account_ids = get_all_market_account_ids_for_user(user); + // Initialize empty vector for open order IDs. + let market_accounts = vector::empty(); + // If no market account IDs, return empty vector. + if (vector::is_empty(&market_account_ids)) return market_accounts; + // For each market account ID: + vector::for_each(market_account_ids, |market_account_id| { + // Get encoded market ID. + let market_id = get_market_id(market_account_id); + // Get encoded custodian ID. + let custodian_id = get_custodian_id(market_account_id); + // Push back struct onto vector of ongoing structs. + vector::push_back(&mut market_accounts, get_market_account( + user, market_id, custodian_id)); + }); + market_accounts // Return market account views. + } + + #[view] + /// Return a `MarketEventHandleCreationNumbers` for `market_id` and + /// `custodian_id`, if `user` has event handles for indicated market + /// account. + /// + /// Restricted to private view function to prevent runtime handle + /// contention. + /// + /// # Testing + /// + /// * `test_register_market_accounts()` + fun get_market_event_handle_creation_numbers( + user: address, + market_id: u64, + custodian_id: u64 + ): Option + acquires MarketEventHandles { + // Return none if user does not have market event handles map, + if (!exists(user)) return option::none(); + // Return none if user has no handles for market account. + let market_event_handles_map_ref = + &borrow_global(user).map; + let market_account_id = get_market_account_id(market_id, custodian_id); + let has_handles = table::contains( + market_event_handles_map_ref, market_account_id); + if (!has_handles) return option::none(); + // Return option-packed creation numbers for all event handles. + let market_account_handles_ref = table::borrow( + market_event_handles_map_ref, market_account_id); + option::some(MarketEventHandleCreationNumbers{ + cancel_order_events_handle_creation_num: + guid::creation_num(event::guid( + &market_account_handles_ref.cancel_order_events)), + change_order_size_events_handle_creation_num: + guid::creation_num(event::guid( + &market_account_handles_ref.change_order_size_events)), + fill_events_handle_creation_num: + guid::creation_num(event::guid( + &market_account_handles_ref.fill_events)), + place_limit_order_events_handle_creation_num: + guid::creation_num(event::guid( + &market_account_handles_ref.place_limit_order_events)), + place_market_order_events_handle_creation_num: + guid::creation_num(event::guid( + &market_account_handles_ref.place_market_order_events)) + }) + } + + #[view] + /// Return market ID encoded in market account ID. + /// + /// # Testing + /// + /// * `test_market_account_id_getters()` + public fun get_market_id( + market_account_id: u128 + ): u64 { + (market_account_id >> SHIFT_MARKET_ID as u64) + } + + #[view] + /// Return `true` if `user` has market account registered with + /// given `market_id` and `custodian_id`. + /// + /// # Testing + /// + /// * `test_market_account_getters()` + public fun has_market_account( + user: address, + market_id: u64, + custodian_id: u64 + ): bool + acquires MarketAccounts { + has_market_account_by_market_account_id( + user, get_market_account_id(market_id, custodian_id)) + } + + #[view] + /// Return `true` if `user` has market account registered with + /// given `market_account_id`. + /// + /// # Testing + /// + /// * `test_market_account_getters()` + public fun has_market_account_by_market_account_id( + user: address, + market_account_id: u128 + ): bool + acquires MarketAccounts { + // Return false if user has no market accounts resource. + if (!exists(user)) return false; + // Immutably borrow market accounts map. + let market_accounts_map_ref = &borrow_global(user).map; + // Return if map has entry for given market account ID. + table::contains(market_accounts_map_ref, market_account_id) + } + + #[view] + /// Return `true` if `user` has at least one market account + /// registered with given `market_id`. + /// + /// # Testing + /// + /// * `test_market_account_getters()` + public fun has_market_account_by_market_id( + user: address, + market_id: u64 + ): bool + acquires MarketAccounts { + // Return false if user has no market accounts resource. + if (!exists(user)) return false; + let custodians_map_ref = // Immutably borrow custodians map. + &borrow_global(user).custodians; + // Return if custodians map has entry for given market ID. + tablist::contains(custodians_map_ref, market_id) + } + + // View functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Public functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Wrapped call to `deposit_asset()` for depositing coins. + /// + /// # Aborts + /// + /// * `E_COIN_TYPE_IS_GENERIC_ASSET`: Coin type is generic asset, + /// corresponding to the Econia account having initialized a coin + /// of type `GenericAsset`. + /// + /// # Testing + /// + /// * `test_deposit_coins_generic()` + /// * `test_deposits()` + public fun deposit_coins< + CoinType + >( + user_address: address, + market_id: u64, + custodian_id: u64, + coins: Coin + ) acquires + Collateral, + MarketAccounts + { + // Check if coin type is generic asset. + let coin_type_is_generic_asset = type_info::type_of() == + type_info::type_of(); + // Assert coin type is not generic asset. + assert!(!coin_type_is_generic_asset, E_COIN_TYPE_IS_GENERIC_ASSET); + deposit_asset( // Deposit asset. + user_address, + market_id, + custodian_id, + coin::value(&coins), + option::some(coins), + NO_UNDERWRITER); + } + + /// Wrapped call to `deposit_asset()` for depositing generic asset. + /// + /// # Testing + /// + /// * `test_deposits()` + public fun deposit_generic_asset( + user_address: address, + market_id: u64, + custodian_id: u64, + amount: u64, + underwriter_capability_ref: &UnderwriterCapability + ) acquires + Collateral, + MarketAccounts + { + deposit_asset( + user_address, + market_id, + custodian_id, + amount, + option::none(), + registry::get_underwriter_id(underwriter_capability_ref)); + } + + /// Wrapped call to `get_asset_counts_internal()` for custodian. + /// + /// Restricted to custodian for given market account to prevent + /// excessive public queries and thus transaction collisions. + /// + /// # Testing + /// + /// * `test_deposits()` + public fun get_asset_counts_custodian( + user_address: address, + market_id: u64, + custodian_capability_ref: &CustodianCapability + ): ( + u64, + u64, + u64, + u64, + u64, + u64 + ) acquires MarketAccounts { + get_asset_counts_internal( + user_address, market_id, + registry::get_custodian_id(custodian_capability_ref)) + } + + /// Wrapped call to `get_asset_counts_internal()` for signing user. + /// + /// Restricted to signing user for given market account to prevent + /// excessive public queries and thus transaction collisions. + /// + /// # Testing + /// + /// * `test_deposits()` + public fun get_asset_counts_user( + user: &signer, + market_id: u64 + ): ( + u64, + u64, + u64, + u64, + u64, + u64 + ) acquires MarketAccounts { + get_asset_counts_internal(address_of(user), market_id, NO_CUSTODIAN) + } + + /// Wrapped call to `get_market_account_market_info()` for + /// custodian. + /// + /// Restricted to custodian for given market account to prevent + /// excessive public queries and thus transaction collisions. + /// + /// # Testing + /// + /// * `test_register_market_accounts()` + public fun get_market_account_market_info_custodian( + user_address: address, + market_id: u64, + custodian_capability_ref: &CustodianCapability + ): ( + TypeInfo, + String, + TypeInfo, + u64, + u64, + u64, + u64 + ) acquires MarketAccounts { + get_market_account_market_info( + user_address, market_id, + registry::get_custodian_id(custodian_capability_ref)) + } + + /// Wrapped call to `get_market_account_market_info()` for signing + /// user. + /// + /// Restricted to signing user for given market account to prevent + /// excessive public queries and thus transaction collisions. + /// + /// # Testing + /// + /// * `test_register_market_accounts()` + public fun get_market_account_market_info_user( + user: &signer, + market_id: u64 + ): ( + TypeInfo, + String, + TypeInfo, + u64, + u64, + u64, + u64 + ) acquires MarketAccounts { + get_market_account_market_info( + address_of(user), market_id, NO_CUSTODIAN) + } + + /// Wrapped call to `withdraw_coins()` for withdrawing under + /// authority of delegated custodian. + /// + /// # Testing + /// + /// * `test_withdrawals()` + public fun withdraw_coins_custodian< + CoinType + >( + user_address: address, + market_id: u64, + amount: u64, + custodian_capability_ref: &CustodianCapability + ): Coin + acquires + Collateral, + MarketAccounts + { + withdraw_coins( + user_address, + market_id, + registry::get_custodian_id(custodian_capability_ref), + amount) + } + + /// Wrapped call to `withdraw_coins()` for withdrawing under + /// authority of signing user. + /// + /// # Testing + /// + /// * `test_withdrawals()` + public fun withdraw_coins_user< + CoinType + >( + user: &signer, + market_id: u64, + amount: u64, + ): Coin + acquires + Collateral, + MarketAccounts + { + withdraw_coins( + address_of(user), + market_id, + NO_CUSTODIAN, + amount) + } + + /// Wrapped call to `withdraw_generic_asset()` for withdrawing under + /// authority of delegated custodian. + /// + /// # Testing + /// + /// * `test_withdrawals()` + public fun withdraw_generic_asset_custodian( + user_address: address, + market_id: u64, + amount: u64, + custodian_capability_ref: &CustodianCapability, + underwriter_capability_ref: &UnderwriterCapability + ) acquires + Collateral, + MarketAccounts + { + withdraw_generic_asset( + user_address, + market_id, + registry::get_custodian_id(custodian_capability_ref), + amount, + underwriter_capability_ref) + } + + /// Wrapped call to `withdraw_generic_asset()` for withdrawing under + /// authority of signing user. + /// + /// # Testing + /// + /// * `test_withdrawals()` + public fun withdraw_generic_asset_user( + user: &signer, + market_id: u64, + amount: u64, + underwriter_capability_ref: &UnderwriterCapability + ) acquires + Collateral, + MarketAccounts + { + withdraw_generic_asset( + address_of(user), + market_id, + NO_CUSTODIAN, + amount, + underwriter_capability_ref) + } + + // Public functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Public entry functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Wrapped call to `deposit_coins()` for depositing from an + /// `aptos_framework::coin::CoinStore`. + /// + /// # Testing + /// + /// * `test_deposits()` + public entry fun deposit_from_coinstore< + CoinType + >( + user: &signer, + market_id: u64, + custodian_id: u64, + amount: u64 + ) acquires + Collateral, + MarketAccounts + { + deposit_coins( + address_of(user), + market_id, + custodian_id, + coin::withdraw(user, amount)); + } + + /// Initialize market event handles for a market account if missing. + /// + /// Since market event handles were implemented as part of a + /// compatible upgrade policy, it is possible for a user to have a + /// market account without associated market event handles, if they + /// registered a market account before an on-chain upgrade. + /// + /// # Parameters + /// + /// * `user`: User for market account. + /// * `market_id`: Market ID for market account. + /// * `custodian_id`: Custodian ID for market account. + /// + /// # Aborts + /// + /// * `E_NO_MARKET_ACCOUNT`: No such specified market account. + /// + /// # Testing + /// + /// * `test_init_market_event_handles_if_missing_no_account()` + /// * `test_register_market_accounts()` + public entry fun init_market_event_handles_if_missing( + user: &signer, + market_id: u64, + custodian_id: u64 + ) acquires + MarketAccounts, + MarketEventHandles + { + // Verify user has specified market account. + let user_address = address_of(user); + assert!(has_market_account(user_address, market_id, custodian_id), + E_NO_MARKET_ACCOUNT); + // Create market event handles map if user doesn't have one, + // and fill with handles for market account as needed. + if (!exists(address_of(user))) + move_to(user, MarketEventHandles{map: table::new()}); + let market_event_handles_map_ref_mut = + &mut borrow_global_mut(user_address).map; + let market_account_id = + ((market_id as u128) << SHIFT_MARKET_ID) | (custodian_id as u128); + let has_handles = table::contains( + market_event_handles_map_ref_mut, market_account_id); + if (!has_handles) { + let handles = MarketEventHandlesForMarketAccount{ + cancel_order_events: account::new_event_handle(user), + change_order_size_events: account::new_event_handle(user), + fill_events: account::new_event_handle(user), + place_limit_order_events: account::new_event_handle(user), + place_market_order_events: account::new_event_handle(user) + }; + table::add( + market_event_handles_map_ref_mut, market_account_id, handles); + }; + } + + /// Register market account for indicated market and custodian. + /// + /// Verifies market ID and asset types via internal call to + /// `register_market_account_account_entries()`. + /// + /// # Type parameters + /// + /// * `BaseType`: Base type for indicated market. If base asset is + /// a generic asset, must be passed as `registry::GenericAsset` + /// (alternatively use `register_market_account_base_generic()`). + /// * `QuoteType`: Quote type for indicated market. + /// + /// # Parameters + /// + /// * `user`: User registering a market account. + /// * `market_id`: Market ID for given market. + /// * `custodian_id`: Custodian ID to register account with, or + /// `NO_CUSTODIAN`. + /// + /// # Aborts + /// + /// * `E_UNREGISTERED_CUSTODIAN`: Custodian ID has not been + /// registered. + /// + /// # Testing + /// + /// * `test_register_market_account_unregistered_custodian()` + /// * `test_register_market_accounts()` + public entry fun register_market_account< + BaseType, + QuoteType + >( + user: &signer, + market_id: u64, + custodian_id: u64 + ) acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // If custodian ID indicated, assert it is registered. + if (custodian_id != NO_CUSTODIAN) assert!( + registry::is_registered_custodian_id(custodian_id), + E_UNREGISTERED_CUSTODIAN); + let market_account_id = // Get market account ID. + ((market_id as u128) << SHIFT_MARKET_ID) | (custodian_id as u128); + // Register market accounts map entries, verifying market ID and + // asset types. + register_market_account_account_entries( + user, market_account_id, market_id, custodian_id); + // Register collateral entry if base type is coin (otherwise + // is a generic asset and no collateral entry required). + if (coin::is_coin_initialized()) + register_market_account_collateral_entry( + user, market_account_id); + // Register quote asset collateral entry for quote coin type + // (quote type for a verified market must be a coin). + register_market_account_collateral_entry( + user, market_account_id); + init_market_event_handles_if_missing(user, market_id, custodian_id); + } + + /// Wrapped `register_market_account()` call for generic base asset. + /// + /// # Testing + /// + /// * `test_register_market_accounts()` + public entry fun register_market_account_generic_base< + QuoteType + >( + user: &signer, + market_id: u64, + custodian_id: u64 + ) acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + register_market_account( + user, market_id, custodian_id); + } + + /// Wrapped call to `withdraw_coins_user()` for withdrawing from + /// market account to user's `aptos_framework::coin::CoinStore`. + /// + /// # Testing + /// + /// * `test_withdrawals()` + public entry fun withdraw_to_coinstore< + CoinType + >( + user: &signer, + market_id: u64, + amount: u64, + ) acquires + Collateral, + MarketAccounts + { + // Register coin store if user does not have one. + if (!coin::is_account_registered(address_of(user))) + coin::register(user); + // Deposit to coin store coins withdrawn from market account. + coin::deposit(address_of(user), withdraw_coins_user( + user, market_id, amount)); + } + + // Public entry functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Public friend functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Cancel order from a user's tablist of open orders on given side. + /// + /// Updates asset counts, pushes order onto top of inactive orders + /// stack, and overwrites its fields accordingly. + /// + /// Accepts as an argument a market order ID, which is checked + /// against the market order ID in the user's corresponding `Order`. + /// This check is bypassed when the market order ID is passed as + /// `NIL`, which should only happen when cancellation is motivated + /// by an eviction or by a self match cancel: market order IDs are + /// not tracked in order book state, so during these two operations, + /// `cancel_order_internal()` is simply called with a `NIL` market + /// order ID argument. Custodians or users who manually trigger + /// order cancellations for their own order do have to pass market + /// order IDs, however, to verify that they are not passing a + /// malicious market order ID (portions of which essentially + /// function as pointers into AVL queue state). + /// + /// # Parameters + /// + /// * `user_address`: User address for market account. + /// * `market_id`: Market ID for market account. + /// * `custodian_id`: Custodian ID for market account. + /// * `side`: `ASK` or `BID`, the side on which an order was placed. + /// * `start_size`: The open order size before filling. + /// * `price`: Order price, in ticks per lot. + /// * `order_access_key`: Order access key for user order lookup. + /// * `market_order_id`: `NIL` if order cancellation originates from + /// an eviction or a self match cancel, otherwise the market order + /// ID encoded in the user's `Order`. + /// * `cancel_reason`: The reason for the cancel. Note that + /// user-side open order size changes are processed via + /// `change_order_size_internal()` as a cancellation followed by + /// immediate re-placement, corresponding to the cancel reason + /// `CANCEL_REASON_SIZE_CHANGE_INTERNAL`. When this is the case + /// no cancel event is emitted. + /// + /// # Returns + /// + /// * `u128`: Market order ID for corresponding order. + /// + /// # Terminology + /// + /// * The "inbound" asset is the asset that would have been received + /// from a trade if the cancelled order had been filled. + /// * The "outbound" asset is the asset that would have been traded + /// away if the cancelled order had been filled. + /// + /// # Aborts + /// + /// * `E_START_SIZE_MISMATCH`: Mismatch between expected size before + /// operation and actual size before operation. + /// * `E_INVALID_MARKET_ORDER_ID`: Market order ID mismatch with + /// user's open order, when market order ID not passed as `NIL`. + /// + /// # Emits + /// + /// * `CancelOrderEvent`: Information about a cancelled order. + /// + /// # Assumptions + /// + /// * Only called when also cancelling an order from the order book. + /// * User has an open order under indicated market account with + /// provided access key, but not necessarily with provided market + /// order ID (if market order ID is not `NIL`): if order + /// cancellation is manually actuated by a custodian or user, + /// then it had to have been successfully placed on the book to + /// begin with for the given access key. Market order IDs, + /// however, are not maintained in order book state and so could + /// be potentially be passed by a malicious user or custodian who + /// intends to alter order book state per above. + /// * If market order ID is `NIL`, is only called during an eviction + /// or a self match cancel. + /// * `price` matches that encoded in market order ID from cancelled + /// order if market order ID is not `NIL`. + /// + /// # Expected value testing + /// + /// * `test_place_cancel_order_ask()` + /// * `test_place_cancel_order_bid()` + /// * `test_place_cancel_order_stack()` + /// + /// # Failure testing + /// + /// * `test_cancel_order_internal_invalid_market_order_id()` + /// * `test_cancel_order_internal_start_size_mismatch()` + public(friend) fun cancel_order_internal( + user_address: address, + market_id: u64, + custodian_id: u64, + side: bool, + start_size: u64, + price: u64, + order_access_key: u64, + market_order_id: u128, + reason: u8 + ): u128 + acquires + MarketAccounts, + MarketEventHandles + { + // Mutably borrow market accounts map. + let market_accounts_map_ref_mut = + &mut borrow_global_mut(user_address).map; + let market_account_id = // Get market account ID. + ((market_id as u128) << SHIFT_MARKET_ID) | (custodian_id as u128); + let market_account_ref_mut = // Mutably borrow market account. + table::borrow_mut(market_accounts_map_ref_mut, market_account_id); + // Mutably borrow orders tablist, inactive orders stack top, + // inbound asset ceiling, and outbound asset available fields, + // and determine size multiplier for calculating change in + // available and ceiling fields, based on order side. + let (orders_ref_mut, stack_top_ref_mut, in_ceiling_ref_mut, + out_available_ref_mut, size_multiplier_ceiling, + size_multiplier_available) = if (side == ASK) ( + &mut market_account_ref_mut.asks, + &mut market_account_ref_mut.asks_stack_top, + &mut market_account_ref_mut.quote_ceiling, + &mut market_account_ref_mut.base_available, + price * market_account_ref_mut.tick_size, + market_account_ref_mut.lot_size + ) else ( + &mut market_account_ref_mut.bids, + &mut market_account_ref_mut.bids_stack_top, + &mut market_account_ref_mut.base_ceiling, + &mut market_account_ref_mut.quote_available, + market_account_ref_mut.lot_size, + price * market_account_ref_mut.tick_size); + let order_ref_mut = // Mutably borrow order to remove. + tablist::borrow_mut(orders_ref_mut, order_access_key); + let size = order_ref_mut.size; // Store order's size field. + // Assert order starts off with expected size. + assert!(size == start_size, E_START_SIZE_MISMATCH); + // If passed market order ID is null, reassign its value to the + // market order ID encoded in the order. Else assert that it is + // equal to market order ID in user's order. + if (market_order_id == (NIL as u128)) + market_order_id = order_ref_mut.market_order_id else + assert!(order_ref_mut.market_order_id == market_order_id, + E_INVALID_MARKET_ORDER_ID); + // Clear out order's market order ID field. + order_ref_mut.market_order_id = (NIL as u128); + // Mark order's size field to indicate top of inactive stack. + order_ref_mut.size = *stack_top_ref_mut; + // Reassign stack top field to indicate newly inactive order. + *stack_top_ref_mut = order_access_key; + // Calculate increment amount for outbound available field. + let available_increment_amount = size * size_multiplier_available; + *out_available_ref_mut = // Increment available field. + *out_available_ref_mut + available_increment_amount; + // Calculate decrement amount for inbound ceiling field. + let ceiling_decrement_amount = size * size_multiplier_ceiling; + *in_ceiling_ref_mut = // Decrement ceiling field. + *in_ceiling_ref_mut - ceiling_decrement_amount; + // If order is actually being cancelled and user has market + // event handles for the market account, emit a cancel event. + let changing_size = reason == CANCEL_REASON_SIZE_CHANGE_INTERNAL; + if (!changing_size && exists(user_address)) { + let market_event_handles_map_ref_mut = + &mut borrow_global_mut(user_address).map; + let has_handles_for_market_account = table::contains( + market_event_handles_map_ref_mut, market_account_id); + if (has_handles_for_market_account) { + let handles_ref_mut = table::borrow_mut( + market_event_handles_map_ref_mut, market_account_id); + event::emit_event( + &mut handles_ref_mut.cancel_order_events, + CancelOrderEvent{ + market_id, order_id: market_order_id, + user: user_address, custodian_id, reason}); + } + }; + market_order_id // Return market order ID. + } + + /// Change the size of a user's open order on given side. + /// + /// # Parameters + /// + /// * `user_address`: User address for market account. + /// * `market_id`: Market ID for market account. + /// * `custodian_id`: Custodian ID for market account. + /// * `side`: `ASK` or `BID`, the side on which an order was placed. + /// * `start_size`: The open order size before size change. + /// * `new_size`: New order size, in lots, checked during inner call + /// to `place_order_internal()`. + /// * `price`: Order price, in ticks per lot. + /// * `order_access_key`: Order access key for user order lookup. + /// * `market_order_id`: Market order ID for order book lookup. + /// + /// # Aborts + /// + /// * `E_CHANGE_ORDER_NO_CHANGE`: No change in order size. + /// + /// # Emits + /// + /// * `ChangeOrderSizeEvent`: Information about an order that had a + /// manual size change. + /// + /// # Assumptions + /// + /// * Only called when also changing order size on the order book. + /// * User has an open order under indicated market account with + /// provided access key, but not necessarily with provided market + /// order ID, which is checked in `cancel_order_internal()`. + /// * `price` matches that encoded in market order ID for changed + /// order. + /// + /// # Testing + /// + /// * `test_change_order_size_internal_ask()` + /// * `test_change_order_size_internal_bid()` + /// * `test_change_order_size_internal_no_change()` + public(friend) fun change_order_size_internal( + user_address: address, + market_id: u64, + custodian_id: u64, + side: bool, + start_size: u64, + new_size: u64, + price: u64, + order_access_key: u64, + market_order_id: u128 + ) acquires + MarketAccounts, + MarketEventHandles + { + // Mutably borrow market accounts map. + let market_accounts_map_ref_mut = + &mut borrow_global_mut(user_address).map; + let market_account_id = // Get market account ID. + ((market_id as u128) << SHIFT_MARKET_ID) | (custodian_id as u128); + let market_account_ref_mut = // Mutably borrow market account. + table::borrow_mut(market_accounts_map_ref_mut, market_account_id); + // Immutably borrow corresponding orders tablist based on side. + let orders_ref = if (side == ASK) + &market_account_ref_mut.asks else &market_account_ref_mut.bids; + // Immutably borrow order. + let order_ref = tablist::borrow(orders_ref, order_access_key); + // Assert change in size. + assert!(order_ref.size != new_size, E_CHANGE_ORDER_NO_CHANGE); + cancel_order_internal( // Cancel order with size to be changed. + user_address, market_id, custodian_id, side, start_size, price, + order_access_key, market_order_id, + CANCEL_REASON_SIZE_CHANGE_INTERNAL); + place_order_internal( // Place order with new size. + user_address, market_id, custodian_id, side, new_size, price, + market_order_id, order_access_key); + // If user has market event handles for the market account, emit + // a change order size event. + if (exists(user_address)) { + let market_event_handles_map_ref_mut = + &mut borrow_global_mut(user_address).map; + let has_handles_for_market_account = table::contains( + market_event_handles_map_ref_mut, market_account_id); + if (has_handles_for_market_account) { + let handles_ref_mut = table::borrow_mut( + market_event_handles_map_ref_mut, market_account_id); + event::emit_event( + &mut handles_ref_mut.change_order_size_events, + ChangeOrderSizeEvent{ + market_id, order_id: market_order_id, + user: user_address, custodian_id, side, new_size}); + } + } + } + + /// Return a `CancelOrderEvent` with the indicated fields. + public(friend) fun create_cancel_order_event_internal( + market_id: u64, + order_id: u128, + user: address, + custodian_id: u64, + reason: u8 + ): CancelOrderEvent { + CancelOrderEvent{ + market_id, + order_id, + user, + custodian_id, + reason + } + } + + /// Return a `FillEvent` with the indicated fields. + public(friend) fun create_fill_event_internal( + market_id: u64, + size: u64, + price: u64, + maker_side: bool, + maker: address, + maker_custodian_id: u64, + maker_order_id: u128, + taker: address, + taker_custodian_id: u64, + taker_order_id: u128, + taker_quote_fees_paid: u64, + sequence_number_for_trade: u64 + ): FillEvent { + FillEvent{ + market_id, + size, + price, + maker_side, + maker, + maker_custodian_id, + maker_order_id, + taker, + taker_custodian_id, + taker_order_id, + taker_quote_fees_paid, + sequence_number_for_trade + } + } + + /// Deposit base asset and quote coins when matching. + /// + /// Should only be called by the matching engine when matching from + /// a user's market account. + /// + /// # Type parameters + /// + /// * `BaseType`: Base type for market. + /// * `QuoteType`: Quote type for market. + /// + /// # Parameters + /// + /// * `user_address`: User address for market account. + /// * `market_id`: Market ID for market account. + /// * `custodian_id`: Custodian ID for market account. + /// * `base_amount`: Base asset amount to deposit. + /// * `optional_base_coins`: Optional base coins to deposit. + /// * `quote_coins`: Quote coins to deposit. + /// * `underwriter_id`: Underwriter ID for market. + /// + /// # Testing + /// + /// * `test_deposit_withdraw_assets_internal()` + public(friend) fun deposit_assets_internal< + BaseType, + QuoteType + >( + user_address: address, + market_id: u64, + custodian_id: u64, + base_amount: u64, + optional_base_coins: Option>, + quote_coins: Coin, + underwriter_id: u64 + ) acquires + Collateral, + MarketAccounts + { + deposit_asset( // Deposit base asset. + user_address, market_id, custodian_id, base_amount, + optional_base_coins, underwriter_id); + deposit_coins( // Deposit quote coins. + user_address, market_id, custodian_id, quote_coins); + } + + /// Emit limit order events to a user's market event handles. + /// + /// # Parameters + /// + /// * `market_id`: `PlaceLimitOrderEvent.market_id`. + /// * `user`: `PlaceLimitOrderEvent.user`. + /// * `custodian_id`: `PlaceLimitOrderEvent.custodian_id`. + /// * `integrator`: `PlaceLimitOrderEvent.integrator`. + /// * `side`: `PlaceLimitOrderEvent.side`. + /// * `size`: `PlaceLimitOrderEvent.size`. + /// * `price`: `PlaceLimitOrderEvent.price`. + /// * `restriction`: `PlaceLimitOrderEvent.restriction`. + /// * `self_match_behavior`: + /// `PlaceLimitOrderEvent.self_match_behavior`. + /// * `remaining_size`: `PlaceLimitOrderEvent.remaining_size`. + /// * `order_id`: `PlaceLimitOrderEvent.order_id`. + /// * `fill_event_queue_ref`: Immutable reference to a vector of + /// `FillEvent`s to emit as part of a limit order that filled + /// across the spread, may be empty. + /// * `cancel_reason_option_ref`: Immutable reference to an optional + /// cancel reason associated with a `CancelOrderEvent`. + /// + /// # Emits + /// + /// * `PlaceLimitOrderEvent`: Information about the limit order that + /// was placed. + /// * `FillEvent`(s): Information about fill(s) across the spread as + /// a taker. + /// * `CancelOrderEvent`: Optionally, information about why the + /// limit order may have had to be cancelled during the + /// transaction in which it was placed. + public(friend) fun emit_limit_order_events_internal( + market_id: u64, + user: address, + custodian_id: u64, + integrator: address, + side: bool, + size: u64, + price: u64, + restriction: u8, + self_match_behavior: u8, + remaining_size: u64, + order_id: u128, + fill_event_queue_ref: &vector, + cancel_reason_option_ref: &Option + ) acquires MarketEventHandles { + // Only emit events to handles for the market account that + // placed the order if they have been initialized. + if (exists(user)) { + let market_event_handles_map_ref_mut = + &mut borrow_global_mut(user).map; + let market_account_id = (((market_id as u128) << SHIFT_MARKET_ID) | + (custodian_id as u128)); + let has_handles_for_market_account = table::contains( + market_event_handles_map_ref_mut, market_account_id); + if (has_handles_for_market_account) { + let handles_ref_mut = table::borrow_mut( + market_event_handles_map_ref_mut, market_account_id); + event::emit_event( + &mut handles_ref_mut.place_limit_order_events, + PlaceLimitOrderEvent{ + market_id, user, custodian_id, integrator, side, size, + price, restriction, self_match_behavior, + remaining_size, order_id}); + // Loop over fill events, substituting order ID in case + // order posted after fill event creation. Looping here + // minimizes borrows from the user's account, but will + // require looping again later to emit maker fill events + // because the borrow checker prohibits simultaneous + // borrowing of the same resource from two addresses. + vector::for_each_ref(fill_event_queue_ref, |event_ref| { + let event: FillEvent = *event_ref; + event.taker_order_id = order_id; + event::emit_event(&mut handles_ref_mut.fill_events, event); + }); + if (option::is_some(cancel_reason_option_ref)) { + let event = CancelOrderEvent{ + market_id, order_id, user, custodian_id, + reason: *option::borrow(cancel_reason_option_ref)}; + event::emit_event( + &mut handles_ref_mut.cancel_order_events, event); + }; + }; + }; + // Emit fill events for all makers, similarly substituting + // order ID in case order posted after fill event creation. + vector::for_each_ref(fill_event_queue_ref, |event_ref| { + let event: FillEvent = *event_ref; + event.taker_order_id = order_id; + emit_maker_fill_event(&event); + }); + } + + /// Emit market order events to a user's market event handles. + /// + /// # Parameters + /// + /// * `market_id`: `PlaceMarketOrderEvent.market_id`. + /// * `user`: `PlaceMarketOrderEvent.user`. + /// * `custodian_id`: `PlaceMarketOrderEvent.custodian_id`. + /// * `integrator`: `PlaceMarketOrderEvent.integrator`. + /// * `direction`: `PlaceMarketOrderEvent.direction`. + /// * `size`: `PlaceMarketOrderEvent.size`. + /// * `self_match_behavior`: + /// `PlaceMarketOrderEvent.self_match_behavior`. + /// * `order_id`: `PlaceMarketOrderEvent.order_id`. + /// * `fill_event_queue_ref`: Immutable reference to a vector of + /// `FillEvent`s to emit, may be empty. + /// * `cancel_reason_option_ref`: Immutable reference to an optional + /// cancel reason associated with a `CancelOrderEvent`. + /// + /// # Emits + /// + /// * `PlaceMarketOrderEvent`: Information about the market order + /// that was placed. + /// * `FillEvent`(s): Information about fill(s). + /// * `CancelOrderEvent`: Optionally, information about why the + /// market order was cancelled without completely filling. + public(friend) fun emit_market_order_events_internal( + market_id: u64, + user: address, + custodian_id: u64, + integrator: address, + direction: bool, + size: u64, + self_match_behavior: u8, + order_id: u128, + fill_event_queue_ref: &vector, + cancel_reason_option_ref: &Option + ) acquires MarketEventHandles { + // Only emit events to handles for the market account that + // placed the order if they have been initialized. + if (exists(user)) { + let market_event_handles_map_ref_mut = + &mut borrow_global_mut(user).map; + let market_account_id = (((market_id as u128) << SHIFT_MARKET_ID) | + (custodian_id as u128)); + let has_handles_for_market_account = table::contains( + market_event_handles_map_ref_mut, market_account_id); + if (has_handles_for_market_account) { + let handles_ref_mut = table::borrow_mut( + market_event_handles_map_ref_mut, market_account_id); + event::emit_event( + &mut handles_ref_mut.place_market_order_events, + PlaceMarketOrderEvent{ + market_id, user, custodian_id, integrator, direction, + size, self_match_behavior, order_id}); + // Loop over fill events. Looping here minimizes borrows + // from the user's account, but will require looping + // again later to emit maker fill events because the + // borrow checker prohibits simultaneous borrowing of + // the same resource from two addresses. + vector::for_each_ref(fill_event_queue_ref, |event_ref| { + event::emit_event( + &mut handles_ref_mut.fill_events, *event_ref); + }); + if (option::is_some(cancel_reason_option_ref)) { + let event = CancelOrderEvent{ + market_id, order_id, user, custodian_id, + reason: *option::borrow(cancel_reason_option_ref)}; + event::emit_event( + &mut handles_ref_mut.cancel_order_events, event); + }; + }; + }; + // Emit fill events for all makers. + vector::for_each_ref(fill_event_queue_ref, |event_ref| { + emit_maker_fill_event(event_ref); + }); + } + + /// Emit a `FillEvent` for each maker associated with a swap. + public(friend) fun emit_swap_maker_fill_events_internal( + fill_event_queue_ref: &vector + ) acquires MarketEventHandles { + vector::for_each_ref(fill_event_queue_ref, |event_ref| { + emit_maker_fill_event(event_ref); + }); + } + + /// Fill a user's order, routing collateral appropriately. + /// + /// Updates asset counts in a user's market account. Transfers + /// coins as needed between a user's collateral, and an external + /// source of coins passing through the matching engine. If a + /// complete fill, pushes the newly inactive order to the top of the + /// inactive orders stack for the given side. + /// + /// Should only be called by the matching engine, which has already + /// calculated the corresponding amount of assets to fill. If the + /// matching engine gets to this stage, then the user has an open + /// order as indicated with sufficient assets to fill it. Hence no + /// error checking. + /// + /// # Type parameters + /// + /// * `BaseType`: Base type for indicated market. + /// * `QuoteType`: Quote type for indicated market. + /// + /// # Parameters + /// + /// * `user_address`: User address for market account. + /// * `market_id`: Market ID for market account. + /// * `custodian_id`: Custodian ID for market account. + /// * `side`: `ASK` or `BID`, the side of the open order. + /// * `order_access_key`: The open order's access key. + /// * `start_size`: The open order size before filling. + /// * `fill_size`: The number of lots filled. + /// * `complete_fill`: `true` if order is completely filled. + /// * `optional_base_coins`: Optional external base coins passing + /// through the matching engine. + /// * `quote_coins`: External quote coins passing through the + /// matching engine. + /// * `base_to_route`: Amount of base asset filled. + /// * `quote_to_route`: Amount of quote asset filled. + /// + /// # Returns + /// + /// * `Option>`: Optional external base coins passing + /// through the matching engine. + /// * `Coin`: External quote coins passing through the + /// matching engine. + /// * `u128`: Market order ID just filled against. + /// + /// # Aborts + /// + /// * `E_START_SIZE_MISMATCH`: Mismatch between expected size before + /// operation and actual size before operation. + /// + /// # Assumptions + /// + /// * Only called by the matching engine as described above. + /// + /// # Testing + /// + /// * `test_fill_order_internal_ask_complete_base_coin()` + /// * `test_fill_order_internal_bid_complete_base_coin()` + /// * `test_fill_order_internal_bid_partial_base_generic()` + /// * `test_fill_order_internal_start_size_mismatch()` + public(friend) fun fill_order_internal< + BaseType, + QuoteType + >( + user_address: address, + market_id: u64, + custodian_id: u64, + side: bool, + order_access_key: u64, + start_size: u64, + fill_size: u64, + complete_fill: bool, + optional_base_coins: Option>, + quote_coins: Coin, + base_to_route: u64, + quote_to_route: u64 + ): ( + Option>, + Coin, + u128 + ) acquires + Collateral, + MarketAccounts + { + // Mutably borrow market accounts map. + let market_accounts_map_ref_mut = + &mut borrow_global_mut(user_address).map; + let market_account_id = // Get market account ID. + ((market_id as u128) << SHIFT_MARKET_ID) | (custodian_id as u128); + let market_account_ref_mut = // Mutably borrow market account. + table::borrow_mut(market_accounts_map_ref_mut, market_account_id); + let ( // Mutably borrow corresponding orders tablist, + orders_ref_mut, + stack_top_ref_mut, // Inactive orders stack top, + asset_in, // Amount of inbound asset, + asset_in_total_ref_mut, // Inbound asset total field, + asset_in_available_ref_mut, // Available field, + asset_out, // Amount of outbound asset, + asset_out_total_ref_mut, // Outbound asset total field, + asset_out_ceiling_ref_mut, // And ceiling field. + ) = if (side == ASK) ( // If an ask is matched: + &mut market_account_ref_mut.asks, + &mut market_account_ref_mut.asks_stack_top, + quote_to_route, + &mut market_account_ref_mut.quote_total, + &mut market_account_ref_mut.quote_available, + base_to_route, + &mut market_account_ref_mut.base_total, + &mut market_account_ref_mut.base_ceiling, + ) else ( // If a bid is matched + &mut market_account_ref_mut.bids, + &mut market_account_ref_mut.bids_stack_top, + base_to_route, + &mut market_account_ref_mut.base_total, + &mut market_account_ref_mut.base_available, + quote_to_route, + &mut market_account_ref_mut.quote_total, + &mut market_account_ref_mut.quote_ceiling, + ); + let order_ref_mut = // Mutably borrow corresponding order. + tablist::borrow_mut(orders_ref_mut, order_access_key); + // Store market order ID. + let market_order_id = order_ref_mut.market_order_id; + // Assert order starts off with expected size. + assert!(order_ref_mut.size == start_size, E_START_SIZE_MISMATCH); + if (complete_fill) { // If completely filling order: + // Clear out order's market order ID field. + order_ref_mut.market_order_id = (NIL as u128); + // Mark order's size field to indicate inactive stack top. + order_ref_mut.size = *stack_top_ref_mut; + // Reassign stack top field to indicate new inactive order. + *stack_top_ref_mut = order_access_key; + } else { // If only partially filling the order: + // Decrement amount still unfilled on order. + order_ref_mut.size = order_ref_mut.size - fill_size; + }; + // Increment asset in total amount by asset in amount. + *asset_in_total_ref_mut = *asset_in_total_ref_mut + asset_in; + // Increment asset in available amount by asset in amount. + *asset_in_available_ref_mut = *asset_in_available_ref_mut + asset_in; + // Decrement asset out total amount by asset out amount. + *asset_out_total_ref_mut = *asset_out_total_ref_mut - asset_out; + // Decrement asset out ceiling amount by asset out amount. + *asset_out_ceiling_ref_mut = *asset_out_ceiling_ref_mut - asset_out; + // If base coins to route: + if (option::is_some(&optional_base_coins)) { + // Mutably borrow base collateral map. + let collateral_map_ref_mut = + &mut borrow_global_mut>(user_address).map; + let collateral_ref_mut = // Mutably borrow base collateral. + tablist::borrow_mut(collateral_map_ref_mut, market_account_id); + let base_coins_ref_mut = // Mutably borrow external coins. + option::borrow_mut(&mut optional_base_coins); + // If filling as ask, merge to external coins those + // extracted from user's collateral. Else if a bid, merge to + // user's collateral those extracted from external coins. + if (side == ASK) + coin::merge(base_coins_ref_mut, + coin::extract(collateral_ref_mut, base_to_route)) else + coin::merge(collateral_ref_mut, + coin::extract(base_coins_ref_mut, base_to_route)); + }; + // Mutably borrow quote collateral map. + let collateral_map_ref_mut = + &mut borrow_global_mut>(user_address).map; + let collateral_ref_mut = // Mutably borrow quote collateral. + tablist::borrow_mut(collateral_map_ref_mut, market_account_id); + // If filling an ask, merge to user's collateral coins extracted + // from external coins. Else if a bid, merge to external coins + // those extracted from user's collateral. + if (side == ASK) + coin::merge(collateral_ref_mut, + coin::extract(&mut quote_coins, quote_to_route)) else + coin::merge(&mut quote_coins, + coin::extract(collateral_ref_mut, quote_to_route)); + // Return optional base coins, quote coins, and market order ID. + (optional_base_coins, quote_coins, market_order_id) + } + + /// Return asset counts for specified market account. + /// + /// # Parameters + /// + /// * `user_address`: User address for market account. + /// * `market_id`: Market ID for market account. + /// * `custodian_id`: Custodian ID for market account. + /// + /// # Returns + /// + /// * `MarketAccount.base_total` + /// * `MarketAccount.base_available` + /// * `MarketAccount.base_ceiling` + /// * `MarketAccount.quote_total` + /// * `MarketAccount.quote_available` + /// * `MarketAccount.quote_ceiling` + /// + /// # Aborts + /// + /// * `E_NO_MARKET_ACCOUNTS`: No market accounts resource found. + /// * `E_NO_MARKET_ACCOUNT`: No market account resource found. + /// + /// # Testing + /// + /// * `test_deposits()` + /// * `test_get_asset_counts_internal_no_account()` + /// * `test_get_asset_counts_internal_no_accounts()` + public(friend) fun get_asset_counts_internal( + user_address: address, + market_id: u64, + custodian_id: u64 + ): ( + u64, + u64, + u64, + u64, + u64, + u64 + ) acquires MarketAccounts { + // Assert user has market accounts resource. + assert!(exists(user_address), E_NO_MARKET_ACCOUNTS); + // Immutably borrow market accounts map. + let market_accounts_map_ref = + &borrow_global(user_address).map; + let market_account_id = // Get market account ID. + ((market_id as u128) << SHIFT_MARKET_ID) | (custodian_id as u128); + // Assert user has market account for given market account ID. + assert!(table::contains(market_accounts_map_ref, market_account_id), + E_NO_MARKET_ACCOUNT); + let market_account_ref = // Immutably borrow market account. + table::borrow(market_accounts_map_ref, market_account_id); + (market_account_ref.base_total, + market_account_ref.base_available, + market_account_ref.base_ceiling, + market_account_ref.quote_total, + market_account_ref.quote_available, + market_account_ref.quote_ceiling) // Return asset count fields. + } + + /// Return all active market order IDs for given market account. + /// + /// # Parameters + /// + /// * `user_address`: User address for market account. + /// * `market_id`: Market ID for market account. + /// * `custodian_id`: Custodian ID for market account. + /// * `side`: `ASK` or `BID`, the side on which to check. + /// + /// # Returns + /// + /// * `vector`: Vector of all active market order IDs for + /// given market account and side, empty if none. + /// + /// # Aborts + /// + /// * `E_NO_MARKET_ACCOUNTS`: No market accounts resource found. + /// * `E_NO_MARKET_ACCOUNT`: No market account resource found. + /// + /// # Testing + /// + /// * `test_get_active_market_order_ids_internal()` + /// * `test_get_active_market_order_ids_internal_no_account()` + /// * `test_get_active_market_order_ids_internal_no_accounts()` + public(friend) fun get_active_market_order_ids_internal( + user_address: address, + market_id: u64, + custodian_id: u64, + side: bool, + ): vector + acquires MarketAccounts { + // Assert user has market accounts resource. + assert!(exists(user_address), E_NO_MARKET_ACCOUNTS); + // Immutably borrow market accounts map. + let market_accounts_map_ref = + &borrow_global(user_address).map; + let market_account_id = // Get market account ID. + ((market_id as u128) << SHIFT_MARKET_ID) | (custodian_id as u128); + // Assert user has market account for given market account ID. + assert!(table::contains(market_accounts_map_ref, market_account_id), + E_NO_MARKET_ACCOUNT); + let market_account_ref = // Immutably borrow market account. + table::borrow(market_accounts_map_ref, market_account_id); + // Immutably borrow corresponding orders tablist based on side. + let orders_ref = if (side == ASK) + &market_account_ref.asks else &market_account_ref.bids; + // Initialize empty vector of market order IDs. + let market_order_ids = vector::empty(); + // Initialize 1-indexed loop counter and get number of orders. + let (i, n) = (1, tablist::length(orders_ref)); + while (i <= n) { // Loop over all allocated orders. + // Immutably borrow order with given access key. + let order_ref = tablist::borrow(orders_ref, i); + // If order is active, push back its market order ID. + if (order_ref.market_order_id != (NIL as u128)) vector::push_back( + &mut market_order_ids, order_ref.market_order_id); + i = i + 1; // Increment loop counter. + }; + market_order_ids // Return market order IDs. + } + + /// Return order access key for next placed order. + /// + /// If inactive orders stack top is empty, will be next 1-indexed + /// order access key to be allocated. Otherwise is order access key + /// at top of inactive order stack. + /// + /// # Parameters + /// + /// * `user_address`: User address for market account. + /// * `market_id`: Market ID for market account. + /// * `custodian_id`: Custodian ID for market account. + /// * `side`: `ASK` or `BID`, the side on which an order will be + /// placed. + /// + /// # Returns + /// + /// * `u64`: Order access key of next order to be placed. + /// + /// # Aborts + /// + /// * `E_NO_MARKET_ACCOUNTS`: No market accounts resource found. + /// * `E_NO_MARKET_ACCOUNT`: No market account resource found. + /// + /// # Testing + /// + /// * `test_get_next_order_access_key_internal_no_account()` + /// * `test_get_next_order_access_key_internal_no_accounts()` + /// * `test_place_cancel_order_ask()` + /// * `test_place_cancel_order_stack()` + public(friend) fun get_next_order_access_key_internal( + user_address: address, + market_id: u64, + custodian_id: u64, + side: bool + ): u64 + acquires MarketAccounts { + // Assert user has market accounts resource. + assert!(exists(user_address), E_NO_MARKET_ACCOUNTS); + // Immutably borrow market accounts map. + let market_accounts_map_ref = + &borrow_global(user_address).map; + let market_account_id = // Get market account ID. + ((market_id as u128) << SHIFT_MARKET_ID) | (custodian_id as u128); + let has_market_account = // Check if user has market account. + table::contains(market_accounts_map_ref, market_account_id); + // Assert user has market account for given market account ID. + assert!(has_market_account, E_NO_MARKET_ACCOUNT); + let market_account_ref = // Mutably borrow market account. + table::borrow(market_accounts_map_ref, market_account_id); + // Get orders tablist and inactive order stack top for side. + let (orders_ref, stack_top_ref) = if (side == ASK) + (&market_account_ref.asks, &market_account_ref.asks_stack_top) else + (&market_account_ref.bids, &market_account_ref.bids_stack_top); + // If empty inactive order stack, return 1-indexed order access + // key for order that will need to be allocated. + if (*stack_top_ref == NIL) tablist::length(orders_ref) + 1 else + *stack_top_ref // Otherwise the top of the inactive stack. + } + + /// Return optional market order ID corresponding to open order for + /// `user`, `market_id`, `custodian_id`, `side`, and + /// `order_access_key`, if one exists. + /// + /// Restricted to public friend to prevent runtime user state + /// contention. + /// + /// # Testing + /// + /// * `test_market_account_getters()` + /// * `test_change_order_size_internal_ask()` + /// * `test_change_order_size_internal_bid()` + public(friend) fun get_open_order_id_internal( + user: address, + market_id: u64, + custodian_id: u64, + side: bool, + order_access_key: u64 + ): Option + acquires MarketAccounts { + // Get market account ID. + let market_account_id = get_market_account_id(market_id, custodian_id); + // Return empty option if no corresponding market account. + if (!has_market_account_by_market_account_id(user, market_account_id)) + return option::none(); + // Immutably borrow market accounts map. + let market_accounts_map_ref = &borrow_global(user).map; + // Immutably borrow market account. + let market_account_ref = table::borrow( + market_accounts_map_ref, market_account_id); + // Immutably borrow open orders for given side. + let open_orders_ref = if (side == ASK) &market_account_ref.asks else + &market_account_ref.bids; + // Return empty option if no open order with given access key. + if (!tablist::contains(open_orders_ref, order_access_key)) + return option::none(); + option::some( // Return option-packed market order ID. + tablist::borrow(open_orders_ref, order_access_key).market_order_id) + } + + /// Place order in user's tablist of open orders on given side. + /// + /// Range checks order parameters and updates asset counts + /// accordingly. + /// + /// Allocates a new order if the inactive order stack is empty, + /// otherwise pops one off the top of the stack and overwrites it. + /// + /// Should only be called when attempting to place an order on the + /// order book. Since order book entries list order access keys for + /// each corresponding user, `get_next_order_access_key_internal()` + /// needs to be called when generating an entry on the order book: + /// to insert to the order book, an order access key is first + /// required. Once an order book entry has been created, a market + /// order ID will then be made available. + /// + /// # Parameters + /// + /// * `user_address`: User address for market account. + /// * `market_id`: Market ID for market account. + /// * `custodian_id`: Custodian ID for market account. + /// * `side`: `ASK` or `BID`, the side on which an order is placed. + /// * `size`: Order size, in lots. + /// * `price`: Order price, in ticks per lot. + /// * `market_order_id`: Market order ID for order book access. + /// * `order_access_key_expected`: Expected order access key to be + /// assigned to order. + /// + /// # Terminology + /// + /// * The "inbound" asset is the asset received from a trade. + /// * The "outbound" asset is the asset traded away. + /// + /// # Assumptions + /// + /// * Only called when also placing an order on the order book. + /// * `price` matches that encoded in `market_order_id`. + /// * Existence of corresponding market account has already been + /// verified by `get_next_order_access_key_internal()`. + /// + /// # Aborts + /// + /// * `E_PRICE_0`: Price is zero. + /// * `E_PRICE_TOO_HIGH`: Price exceeds maximum possible price. + /// * `E_TICKS_OVERFLOW`: Ticks to fill order overflows a `u64`. + /// * `E_OVERFLOW_ASSET_IN`: Filling order would overflow asset + /// received from trade. + /// * `E_NOT_ENOUGH_ASSET_OUT`: Not enough asset to trade away. + /// * `E_ACCESS_KEY_MISMATCH`: Expected order access key does not + /// match assigned order access key. + /// + /// # Expected value testing + /// + /// * `test_place_cancel_order_ask()` + /// * `test_place_cancel_order_bid()` + /// * `test_place_cancel_order_stack()` + /// + /// # Failure testing + /// + /// * `test_place_order_internal_access_key_mismatch()` + /// * `test_place_order_internal_in_overflow()` + /// * `test_place_order_internal_out_underflow()` + /// * `test_place_order_internal_price_0()` + /// * `test_place_order_internal_price_hi()` + /// * `test_place_order_internal_ticks_overflow()` + public(friend) fun place_order_internal( + user_address: address, + market_id: u64, + custodian_id: u64, + side: bool, + size: u64, + price: u64, + market_order_id: u128, + order_access_key_expected: u64 + ) acquires MarketAccounts { + assert!(price > 0, E_PRICE_0); // Assert price is nonzero. + // Assert price is not too high. + assert!(price <= HI_PRICE, E_PRICE_TOO_HIGH); + // Mutably borrow market accounts map. + let market_accounts_map_ref_mut = + &mut borrow_global_mut(user_address).map; + let market_account_id = // Get market account ID. + ((market_id as u128) << SHIFT_MARKET_ID) | (custodian_id as u128); + let market_account_ref_mut = // Mutably borrow market account. + table::borrow_mut(market_accounts_map_ref_mut, market_account_id); + let base_fill = // Calculate base units needed to fill order. + (size as u128) * (market_account_ref_mut.lot_size as u128); + // Calculate ticks to fill order. + let ticks = (size as u128) * (price as u128); + // Assert ticks to fill order is not too large. + assert!(ticks <= (HI_64 as u128), E_TICKS_OVERFLOW); + // Calculate quote units to fill order. + let quote_fill = ticks * (market_account_ref_mut.tick_size as u128); + // Mutably borrow orders tablist, inactive orders stack top, + // inbound asset ceiling, and outbound asset available fields, + // and assign inbound and outbound asset fill amounts, based on + // order side. + let (orders_ref_mut, stack_top_ref_mut, in_ceiling_ref_mut, + out_available_ref_mut, in_fill, out_fill) = if (side == ASK) + (&mut market_account_ref_mut.asks, + &mut market_account_ref_mut.asks_stack_top, + &mut market_account_ref_mut.quote_ceiling, + &mut market_account_ref_mut.base_available, + quote_fill, base_fill) else + (&mut market_account_ref_mut.bids, + &mut market_account_ref_mut.bids_stack_top, + &mut market_account_ref_mut.base_ceiling, + &mut market_account_ref_mut.quote_available, + base_fill, quote_fill); + // Assert no inbound asset overflow. + assert!((in_fill + (*in_ceiling_ref_mut as u128)) <= (HI_64 as u128), + E_OVERFLOW_ASSET_IN); + // Assert enough outbound asset to cover the fill, which also + // ensures outbound fill amount does not overflow. + assert!((out_fill <= (*out_available_ref_mut as u128)), + E_NOT_ENOUGH_ASSET_OUT); + // Update ceiling for inbound asset. + *in_ceiling_ref_mut = *in_ceiling_ref_mut + (in_fill as u64); + // Update available amount for outbound asset. + *out_available_ref_mut = *out_available_ref_mut - (out_fill as u64); + // Get order access key. If empty inactive stack: + let order_access_key = if (*stack_top_ref_mut == NIL) { + // Get one-indexed order access key for new order. + let order_access_key = tablist::length(orders_ref_mut) + 1; + // Allocate new order. + tablist::add(orders_ref_mut, order_access_key, Order{ + market_order_id, size}); + order_access_key // Store order access key locally. + } else { // If inactive order stack not empty: + // Order access key is for inactive order at top of stack. + let order_access_key = *stack_top_ref_mut; + let order_ref_mut = // Mutably borrow order at top of stack. + tablist::borrow_mut(orders_ref_mut, order_access_key); + // Reassign stack top field to next in stack. + *stack_top_ref_mut = order_ref_mut.size; + // Reassign market order ID for active order. + order_ref_mut.market_order_id = market_order_id; + order_ref_mut.size = size; // Reassign order size field. + order_access_key // Store order access key locally. + }; + // Assert order access key is as expected. + assert!(order_access_key == order_access_key_expected, + E_ACCESS_KEY_MISMATCH); + } + + /// Withdraw base asset and quote coins when matching. + /// + /// Should only be called by the matching engine when matching from + /// a user's market account. + /// + /// # Type parameters + /// + /// * `BaseType`: Base type for market. + /// * `QuoteType`: Quote type for market. + /// + /// # Parameters + /// + /// * `user_address`: User address for market account. + /// * `market_id`: Market ID for market account. + /// * `custodian_id`: Custodian ID for market account. + /// * `base_amount`: Base asset amount to withdraw. + /// * `quote_amount`: Quote asset amount to withdraw. + /// * `underwriter_id`: Underwriter ID for market. + /// + /// # Returns + /// + /// * `Option>`: Optional base coins from user's + /// market account. + /// * ``: Quote coins from user's market account. + /// + /// # Testing + /// + /// * `test_deposit_withdraw_assets_internal()` + public(friend) fun withdraw_assets_internal< + BaseType, + QuoteType, + >( + user_address: address, + market_id: u64, + custodian_id: u64, + base_amount: u64, + quote_amount: u64, + underwriter_id: u64 + ): ( + Option>, + Coin + ) acquires + Collateral, + MarketAccounts + { + // Return optional base coins, and quote coins per respective + // withdrawal functions. + (withdraw_asset(user_address, market_id, custodian_id, + base_amount, underwriter_id), + withdraw_coins(user_address, market_id, custodian_id, + quote_amount)) + } + + // Public friend functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Private functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + /// Deposit an asset to a user's market account. + /// + /// Update asset counts, deposit optional coins as collateral. + /// + /// # Type parameters + /// + /// * `AssetType`: Asset type to deposit, `registry::GenericAsset` + /// if a generic asset. + /// + /// # Parameters + /// + /// * `user_address`: User address for market account. + /// * `market_id`: Market ID for market account. + /// * `custodian_id`: Custodian ID for market account. + /// * `amount`: Amount to deposit. + /// * `optional_coins`: Optional coins to deposit. + /// * `underwriter_id`: Underwriter ID for market, ignored when + /// depositing coins. + /// + /// # Aborts + /// + /// * `E_NO_MARKET_ACCOUNTS`: No market accounts resource found. + /// * `E_NO_MARKET_ACCOUNT`: No market account resource found. + /// * `E_ASSET_NOT_IN_PAIR`: Asset type is not in trading pair for + /// market. + /// * `E_DEPOSIT_OVERFLOW_ASSET_CEILING`: Deposit would overflow + /// asset ceiling. + /// * `E_INVALID_UNDERWRITER`: Underwriter is not valid for + /// indicated market, in the case of a generic asset deposit. + /// + /// # Assumptions + /// + /// * When depositing coins, if a market account exists, then so + /// does a corresponding collateral map entry. + /// + /// # Testing + /// + /// * `test_deposit_asset_amount_mismatch()` + /// * `test_deposit_asset_no_account()` + /// * `test_deposit_asset_no_accounts()` + /// * `test_deposit_asset_not_in_pair()` + /// * `test_deposit_asset_overflow()` + /// * `test_deposit_asset_underwriter()` + /// * `test_deposits()` + fun deposit_asset< + AssetType + >( + user_address: address, + market_id: u64, + custodian_id: u64, + amount: u64, + optional_coins: Option>, + underwriter_id: u64 + ) acquires + Collateral, + MarketAccounts + { + // Assert user has market accounts resource. + assert!(exists(user_address), E_NO_MARKET_ACCOUNTS); + // Mutably borrow market accounts map. + let market_accounts_map_ref_mut = + &mut borrow_global_mut(user_address).map; + let market_account_id = // Get market account ID. + ((market_id as u128) << SHIFT_MARKET_ID) | (custodian_id as u128); + let has_market_account = // Check if user has market account. + table::contains(market_accounts_map_ref_mut, market_account_id); + // Assert user has market account for given market account ID. + assert!(has_market_account, E_NO_MARKET_ACCOUNT); + let market_account_ref_mut = // Mutably borrow market account. + table::borrow_mut(market_accounts_map_ref_mut, market_account_id); + // Get asset type info. + let asset_type = type_info::type_of(); + // Get asset total, available, and ceiling amounts based on if + // asset is base or quote for trading pair, aborting if neither. + let (total_ref_mut, available_ref_mut, ceiling_ref_mut) = + if (asset_type == market_account_ref_mut.base_type) ( + &mut market_account_ref_mut.base_total, + &mut market_account_ref_mut.base_available, + &mut market_account_ref_mut.base_ceiling + ) else if (asset_type == market_account_ref_mut.quote_type) ( + &mut market_account_ref_mut.quote_total, + &mut market_account_ref_mut.quote_available, + &mut market_account_ref_mut.quote_ceiling + ) else abort E_ASSET_NOT_IN_PAIR; + assert!( // Assert deposit does not overflow asset ceiling. + ((*ceiling_ref_mut as u128) + (amount as u128)) <= (HI_64 as u128), + E_DEPOSIT_OVERFLOW_ASSET_CEILING); + *total_ref_mut = *total_ref_mut + amount; // Update total. + // Update available asset amount. + *available_ref_mut = *available_ref_mut + amount; + *ceiling_ref_mut = *ceiling_ref_mut + amount; // Update ceiling. + // If asset is generic: + if (asset_type == type_info::type_of()) { + assert!(underwriter_id == market_account_ref_mut.underwriter_id, + E_INVALID_UNDERWRITER); // Assert underwriter ID. + option::destroy_none(optional_coins); // Destroy option. + } else { // If asset is coin: + // Extract coins from option. + let coins = option::destroy_some(optional_coins); + // Assert passed amount matches coin value. + assert!(amount == coin::value(&coins), E_COIN_AMOUNT_MISMATCH); + // Mutably borrow collateral map. + let collateral_map_ref_mut = &mut borrow_global_mut< + Collateral>(user_address).map; + // Mutably borrow collateral for market account. + let collateral_ref_mut = tablist::borrow_mut( + collateral_map_ref_mut, market_account_id); + // Merge coins into collateral. + coin::merge(collateral_ref_mut, coins); + }; + } + + /// Emit a `FillEvent` for the market account of the maker + /// associated with a fill, if market event handles exist for the + /// indicated market account. + inline fun emit_maker_fill_event( + event_ref: &FillEvent + ) acquires MarketEventHandles { + let maker = event_ref.maker; + if (exists(maker)) { + let market_event_handles_map_ref_mut = + &mut borrow_global_mut(maker).map; + let market_id = event_ref.market_id; + let custodian_id = event_ref.maker_custodian_id; + let market_account_id = (((market_id as u128) << SHIFT_MARKET_ID) | + (custodian_id as u128)); + let has_handles_for_market_account = table::contains( + market_event_handles_map_ref_mut, market_account_id); + if (has_handles_for_market_account) { + let handles_ref_mut = table::borrow_mut( + market_event_handles_map_ref_mut, market_account_id); + event::emit_event( + &mut handles_ref_mut.fill_events, *event_ref); + }; + }; + } + + /// Return `registry::MarketInfo` fields stored in market account. + /// + /// # Parameters + /// + /// * `user_address`: User address for market account. + /// * `market_id`: Market ID for market account. + /// * `custodian_id`: Custodian ID for market account. + /// + /// # Returns + /// + /// * `MarketAccount.base_type` + /// * `MarketAccount.base_name_generic` + /// * `MarketAccount.quote_type` + /// * `MarketAccount.lot_size` + /// * `MarketAccount.tick_size` + /// * `MarketAccount.min_size` + /// * `MarketAccount.underwriter_id` + /// + /// # Aborts + /// + /// * `E_NO_MARKET_ACCOUNTS`: No market accounts resource found. + /// * `E_NO_MARKET_ACCOUNT`: No market account resource found. + /// + /// # Testing + /// + /// * `test_get_market_account_market_info_no_account()` + /// * `test_get_market_account_market_info_no_accounts()` + /// * `test_register_market_accounts()` + fun get_market_account_market_info( + user_address: address, + market_id: u64, + custodian_id: u64 + ): ( + TypeInfo, + String, + TypeInfo, + u64, + u64, + u64, + u64 + ) acquires MarketAccounts { + // Assert user has market accounts resource. + assert!(exists(user_address), E_NO_MARKET_ACCOUNTS); + // Immutably borrow market accounts map. + let market_accounts_map_ref = + &borrow_global(user_address).map; + let market_account_id = // Get market account ID. + ((market_id as u128) << SHIFT_MARKET_ID) | (custodian_id as u128); + // Assert user has market account for given market account ID. + assert!(table::contains(market_accounts_map_ref, market_account_id), + E_NO_MARKET_ACCOUNT); + let market_account_ref = // Immutably borrow market account. + table::borrow(market_accounts_map_ref, market_account_id); + // Return duplicate market info fields. + (market_account_ref.base_type, + market_account_ref.base_name_generic, + market_account_ref.quote_type, + market_account_ref.lot_size, + market_account_ref.tick_size, + market_account_ref.min_size, + market_account_ref.underwriter_id) + } + + /// Register market account entries for given market account info. + /// + /// Inner function for `register_market_account()`. + /// + /// Verifies market ID, base type, and quote type correspond to a + /// registered market, via call to + /// `registry::get_market_info_for_market_account()`. + /// + /// # Type parameters + /// + /// * `BaseType`: Base type for indicated market. + /// * `QuoteType`: Quote type for indicated market. + /// + /// # Parameters + /// + /// * `user`: User registering a market account. + /// * `market_account_id`: Market account ID for given market. + /// * `market_id`: Market ID for given market. + /// * `custodian_id`: Custodian ID to register account with, or + /// `NO_CUSTODIAN`. + /// + /// # Aborts + /// + /// * `E_EXISTS_MARKET_ACCOUNT`: Market account already exists. + /// + /// # Testing + /// + /// * `test_register_market_account_account_entries_exists()` + /// * `test_register_market_accounts()` + fun register_market_account_account_entries< + BaseType, + QuoteType + >( + user: &signer, + market_account_id: u128, + market_id: u64, + custodian_id: u64 + ) acquires MarketAccounts { + let user_address = address_of(user); // Get user address. + let (base_type, quote_type) = // Get base and quote types. + (type_info::type_of(), type_info::type_of()); + // Get market info and verify market ID, base and quote types. + let (base_name_generic, lot_size, tick_size, min_size, underwriter_id) + = registry::get_market_info_for_market_account( + market_id, base_type, quote_type); + // If user does not have a market accounts map initialized: + if (!exists(user_address)) + // Pack an empty one and move it to their account + move_to(user, MarketAccounts{ + map: table::new(), custodians: tablist::new()}); + // Mutably borrow market accounts map. + let market_accounts_map_ref_mut = + &mut borrow_global_mut(user_address).map; + assert!( // Assert no entry exists for given market account ID. + !table::contains(market_accounts_map_ref_mut, market_account_id), + E_EXISTS_MARKET_ACCOUNT); + table::add( // Add empty market account for market account ID. + market_accounts_map_ref_mut, market_account_id, MarketAccount{ + base_type, base_name_generic, quote_type, lot_size, tick_size, + min_size, underwriter_id, asks: tablist::new(), + bids: tablist::new(), asks_stack_top: NIL, bids_stack_top: NIL, + base_total: 0, base_available: 0, base_ceiling: 0, + quote_total: 0, quote_available: 0, quote_ceiling: 0}); + let custodians_ref_mut = // Mutably borrow custodians maps. + &mut borrow_global_mut(user_address).custodians; + // If custodians map has no entry for given market ID: + if (!tablist::contains(custodians_ref_mut, market_id)) { + // Add new entry indicating new custodian ID. + tablist::add(custodians_ref_mut, market_id, + vector::singleton(custodian_id)); + } else { // If already entry for given market ID: + // Mutably borrow vector of custodians for given market. + let market_custodians_ref_mut = + tablist::borrow_mut(custodians_ref_mut, market_id); + // Push back custodian ID for given market account. + vector::push_back(market_custodians_ref_mut, custodian_id); + } + } + + /// Create collateral entry upon market account registration. + /// + /// Inner function for `register_market_account()`. + /// + /// Does not check if collateral entry already exists for given + /// market account ID, as market account existence check already + /// performed by `register_market_account_accounts_entries()` in + /// `register_market_account()`. + /// + /// # Type parameters + /// + /// * `CoinType`: Phantom coin type for indicated market. + /// + /// # Parameters + /// + /// * `user`: User registering a market account. + /// * `market_account_id`: Market account ID for given market. + /// + /// # Testing + /// + /// * `test_register_market_accounts()` + fun register_market_account_collateral_entry< + CoinType + >( + user: &signer, + market_account_id: u128 + ) acquires Collateral { + let user_address = address_of(user); // Get user address. + // If user does not have a collateral map initialized, pack an + // empty one and move it to their account. + if (!exists>(user_address)) + move_to>(user, Collateral{ + map: tablist::new()}); + let collateral_map_ref_mut = // Mutably borrow collateral map. + &mut borrow_global_mut>(user_address).map; + // Add an empty entry for given market account ID. + tablist::add(collateral_map_ref_mut, market_account_id, + coin::zero()); + } + + /// Convert a tablist of `Order` into a vector of only open orders. + /// + /// # Testing + /// + /// * `test_get_market_accounts_open_orders()` + fun vectorize_open_orders( + tablist_ref_mut: &mut Tablist, + ): vector { + let open_orders = vector::empty(); // Get empty orders vector. + // Get optional head key. + let optional_access_key = tablist::get_head_key(tablist_ref_mut); + // While keys left to iterate on: + while (option::is_some(&optional_access_key)) { + // Get open order and next optional access key in tablist. + let (order, _, next) = tablist::remove_iterable( + tablist_ref_mut, *option::borrow(&optional_access_key)); + // If market order ID flagged as null: + if (order.market_order_id == (NIL as u128)) { + // Unpack order and drop fields. + let Order{market_order_id: _, size: _} = order; + } else { // Otherwise, if order is active: + // Push back onto vector of open orders. + vector::push_back(&mut open_orders, order); + }; + // Review next optional access key. + optional_access_key = next; + }; + open_orders // Return vectorized open orders. + } + + /// Withdraw an asset from a user's market account. + /// + /// Update asset counts, withdraw optional collateral coins. + /// + /// # Type parameters + /// + /// * `AssetType`: Asset type to withdraw, `registry::GenericAsset` + /// if a generic asset. + /// + /// # Parameters + /// + /// * `user_address`: User address for market account. + /// * `market_id`: Market ID for market account. + /// * `custodian_id`: Custodian ID for market account. + /// * `amount`: Amount to withdraw. + /// * `underwriter_id`: Underwriter ID for market, ignored when + /// withdrawing coins. + /// + /// # Returns + /// + /// * `Option>`: Optional collateral coins. + /// + /// # Aborts + /// + /// * `E_NO_MARKET_ACCOUNTS`: No market accounts resource found. + /// * `E_NO_MARKET_ACCOUNT`: No market account resource found. + /// * `E_ASSET_NOT_IN_PAIR`: Asset type is not in trading pair for + /// market. + /// * `E_WITHDRAW_TOO_LITTLE_AVAILABLE`: Too little available for + /// withdrawal. + /// * `E_INVALID_UNDERWRITER`: Underwriter is not valid for + /// indicated market, in the case of a generic asset withdrawal. + /// + /// # Testing + /// + /// * `test_withdraw_asset_no_account()` + /// * `test_withdraw_asset_no_accounts()` + /// * `test_withdraw_asset_not_in_pair()` + /// * `test_withdraw_asset_underflow()` + /// * `test_withdraw_asset_underwriter()` + /// * `test_withdrawals()` + fun withdraw_asset< + AssetType + >( + user_address: address, + market_id: u64, + custodian_id: u64, + amount: u64, + underwriter_id: u64 + ): Option> + acquires + Collateral, + MarketAccounts + { + // Assert user has market accounts resource. + assert!(exists(user_address), E_NO_MARKET_ACCOUNTS); + // Mutably borrow market accounts map. + let market_accounts_map_ref_mut = + &mut borrow_global_mut(user_address).map; + let market_account_id = // Get market account ID. + ((market_id as u128) << SHIFT_MARKET_ID) | (custodian_id as u128); + let has_market_account = // Check if user has market account. + table::contains(market_accounts_map_ref_mut, market_account_id); + // Assert user has market account for given market account ID. + assert!(has_market_account, E_NO_MARKET_ACCOUNT); + let market_account_ref_mut = // Mutably borrow market account. + table::borrow_mut(market_accounts_map_ref_mut, market_account_id); + // Get asset type info. + let asset_type = type_info::type_of(); + // Get asset total, available, and ceiling amounts based on if + // asset is base or quote for trading pair, aborting if neither. + let (total_ref_mut, available_ref_mut, ceiling_ref_mut) = + if (asset_type == market_account_ref_mut.base_type) ( + &mut market_account_ref_mut.base_total, + &mut market_account_ref_mut.base_available, + &mut market_account_ref_mut.base_ceiling + ) else if (asset_type == market_account_ref_mut.quote_type) ( + &mut market_account_ref_mut.quote_total, + &mut market_account_ref_mut.quote_available, + &mut market_account_ref_mut.quote_ceiling + ) else abort E_ASSET_NOT_IN_PAIR; + // Assert enough asset available for withdraw. + assert!(amount <= *available_ref_mut, E_WITHDRAW_TOO_LITTLE_AVAILABLE); + *total_ref_mut = *total_ref_mut - amount; // Update total. + // Update available asset amount. + *available_ref_mut = *available_ref_mut - amount; + *ceiling_ref_mut = *ceiling_ref_mut - amount; // Update ceiling. + // Return based on if asset type. If is generic: + return if (asset_type == type_info::type_of()) { + assert!(underwriter_id == market_account_ref_mut.underwriter_id, + E_INVALID_UNDERWRITER); // Assert underwriter ID. + option::none() // Return empty option. + } else { // If asset is coin: + // Mutably borrow collateral map. + let collateral_map_ref_mut = &mut borrow_global_mut< + Collateral>(user_address).map; + // Mutably borrow collateral for market account. + let collateral_ref_mut = tablist::borrow_mut( + collateral_map_ref_mut, market_account_id); + // Withdraw coin and return in an option. + option::some>( + coin::extract(collateral_ref_mut, amount)) + } + } + + /// Wrapped call to `withdraw_asset()` for withdrawing coins. + /// + /// # Testing + /// + /// * `test_withdrawals()` + fun withdraw_coins< + CoinType + >( + user_address: address, + market_id: u64, + custodian_id: u64, + amount: u64, + ): Coin + acquires + Collateral, + MarketAccounts + { + option::destroy_some(withdraw_asset( + user_address, + market_id, + custodian_id, + amount, + NO_UNDERWRITER)) + } + + /// Wrapped call to `withdraw_asset()` for withdrawing generic + /// asset. + /// + /// # Testing + /// + /// * `test_withdrawals()` + fun withdraw_generic_asset( + user_address: address, + market_id: u64, + custodian_id: u64, + amount: u64, + underwriter_capability_ref: &UnderwriterCapability + ) acquires + Collateral, + MarketAccounts + { + option::destroy_none(withdraw_asset( + user_address, + market_id, + custodian_id, + amount, + registry::get_underwriter_id(underwriter_capability_ref))) + } + + // Private functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Test-only constants >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test_only] + /// Base asset starting amount for testing. + const BASE_START: u64 = 7500000000; + #[test_only] + /// Quote asset starting amount for testing. + const QUOTE_START: u64 = 8000000000; + + #[test_only] + /// Custodian ID for market with delegated custodian. + const CUSTODIAN_ID: u64 = 123; + #[test_only] + /// Market ID for generic test market. + const MARKET_ID_GENERIC: u64 = 2; + #[test_only] + /// Market ID for pure coin test market. + const MARKET_ID_PURE_COIN: u64 = 1; + #[test_only] + /// From `registry::register_markets_test()`. Underwriter ID for + /// generic test market. + const UNDERWRITER_ID: u64 = 7; + + #[test_only] + /// From `registry::register_markets_test()`. + const LOT_SIZE_PURE_COIN: u64 = 1; + #[test_only] + /// From `registry::register_markets_test()`. + const TICK_SIZE_PURE_COIN: u64 = 2; + #[test_only] + /// From `registry::register_markets_test()`. + const MIN_SIZE_PURE_COIN: u64 = 3; + #[test_only] + /// From `registry::register_markets_test()`. + const LOT_SIZE_GENERIC: u64 = 4; + #[test_only] + /// From `registry::register_markets_test()`. + const TICK_SIZE_GENERIC: u64 = 5; + #[test_only] + /// From `registry::register_markets_test()`. + const MIN_SIZE_GENERIC: u64 = 6; + + // Test-only constants <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Test-only functions >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test_only] + /// Immutably borrow market event handles for a market account. + inline fun borrow_market_event_handles_for_market_account_test( + market_id: u64, + user: address, + custodian_id: u64 + ): &MarketEventHandlesForMarketAccount + acquires MarketEventHandles { + let market_event_handles_map_ref = + &borrow_global(user).map; + let market_account_id = get_market_account_id(market_id, custodian_id); + table::borrow(market_event_handles_map_ref, market_account_id) + } + + #[test_only] + /// Return a `ChangeOrderSizeEvent` with the indicated fields. + public fun create_change_order_size_event_test( + market_id: u64, + order_id: u128, + user: address, + custodian_id: u64, + side: bool, + new_size: u64 + ): ChangeOrderSizeEvent { + ChangeOrderSizeEvent{ + market_id, + order_id, + user, + custodian_id, + side, + new_size + } + } + + #[test_only] + /// Return a `PlaceLimitOrderEvent` with the indicated fields. + public fun create_place_limit_order_event_test( + market_id: u64, + user: address, + custodian_id: u64, + integrator: address, + side: bool, + size: u64, + price: u64, + restriction: u8, + self_match_behavior: u8, + remaining_size: u64, + order_id: u128 + ): PlaceLimitOrderEvent { + PlaceLimitOrderEvent{ + market_id, + user, + custodian_id, + integrator, + side, + size, + price, + restriction, + self_match_behavior, + remaining_size, + order_id + } + } + + #[test_only] + /// Return a `PlaceMarketOrderEvent` with the indicated fields. + public fun create_place_market_order_event_test( + market_id: u64, + user: address, + custodian_id: u64, + integrator: address, + direction: bool, + size: u64, + self_match_behavior: u8, + order_id: u128 + ): PlaceMarketOrderEvent { + PlaceMarketOrderEvent{ + market_id, + user, + custodian_id, + integrator, + direction, + size, + self_match_behavior, + order_id + } + } + + #[test_only] + /// Return `HI_PRICE`, for testing synchronization with + /// `market.move`. + public fun get_HI_PRICE_test(): u64 {HI_PRICE} + + #[test_only] + /// Return `NO_UNDERWRITER`, for testing synchronization with + /// `market.move`. + public fun get_NO_UNDERWRITER_test(): u64 {NO_UNDERWRITER} + + #[test_only] + /// Get `CancelOrderEvent`s at a market account handle. + public fun get_cancel_order_events_test( + market_id: u64, + user: address, + custodian_id: u64 + ): vector + acquires MarketEventHandles { + event::emitted_events_by_handle( + &(borrow_market_event_handles_for_market_account_test( + market_id, user, custodian_id). + cancel_order_events)) + } + + #[test_only] + /// Get `ChangeOrderSizeEvent`s at a market account handle. + public fun get_change_order_size_events_test( + market_id: u64, + user: address, + custodian_id: u64 + ): vector + acquires MarketEventHandles { + event::emitted_events_by_handle( + &(borrow_market_event_handles_for_market_account_test( + market_id, user, custodian_id). + change_order_size_events)) + } + + #[test_only] + /// Like `get_collateral_value_test()`, but accepts market id and + /// custodian ID. + public fun get_collateral_value_simple_test< + CoinType + >( + user_address: address, + market_id: u64, + custodian_id: u64 + ): u64 + acquires Collateral { + get_collateral_value_test( + user_address, get_market_account_id(market_id, custodian_id)) + } + + #[test_only] + /// Return `Coin.value` of entry in `Collateral` for given + /// `user_address`, `AssetType` and `market_account_id`. + public fun get_collateral_value_test< + CoinType + >( + user_address: address, + market_account_id: u128, + ): u64 + acquires Collateral { + let collateral_map_ref = // Immutably borrow collateral map. + &borrow_global>(user_address).map; + let coin_ref = // Immutably borrow coin collateral. + tablist::borrow(collateral_map_ref, market_account_id); + coin::value(coin_ref) // Return coin value. + } + + #[test_only] + /// Get `FillEvent`s at a market account handle. + public fun get_fill_events_test( + market_id: u64, + user: address, + custodian_id: u64 + ): vector + acquires MarketEventHandles { + event::emitted_events_by_handle( + &(borrow_market_event_handles_for_market_account_test( + market_id, user, custodian_id). + fill_events)) + } + + #[test_only] + /// Get order access key at top of inactive order stack. + public fun get_inactive_stack_top_test( + user_address: address, + market_account_id: u128, + side: bool, + ): u64 + acquires MarketAccounts { + // Immutably borrow market accounts map. + let market_accounts_map_ref = + &borrow_global(user_address).map; + let market_account_ref = // Immutably borrow market account. + table::borrow(market_accounts_map_ref, market_account_id); + // Return corresponding stack top field. + if (side == ASK) market_account_ref.asks_stack_top else + market_account_ref.bids_stack_top + } + + #[test_only] + /// Return next inactive order in inactive orders stack. + public fun get_next_inactive_order_test( + user_address: address, + market_account_id: u128, + side: bool, + order_access_key: u64 + ): u64 + acquires MarketAccounts { + assert!(!is_order_active_test( // Assert order is inactive. + user_address, market_account_id, side, order_access_key), 0); + // Get order's size field, indicating next inactive order. + let (_, next) = get_order_fields_test( + user_address, market_account_id, side, order_access_key); + next // Return next inactive order access key. + } + + #[test_only] + /// Wrapper for `get_order_fields_test()`, accepting market ID and + /// custodian ID. + public fun get_order_fields_simple_test( + user_address: address, + market_id: u64, + custodian_id: u64, + side: bool, + order_access_key: u64 + ): ( + u128, + u64 + ) acquires MarketAccounts { + get_order_fields_test( + user_address, get_market_account_id(market_id, custodian_id), + side, order_access_key) + } + + #[test_only] + /// Return order fields for given order parameters. + public fun get_order_fields_test( + user_address: address, + market_account_id: u128, + side: bool, + order_access_key: u64 + ): ( + u128, + u64 + ) acquires MarketAccounts { + // Immutably borrow market accounts map. + let market_accounts_map_ref = + &borrow_global(user_address).map; + let market_account_ref = // Immutably borrow market account. + table::borrow(market_accounts_map_ref, market_account_id); + // Immutably borrow corresponding orders tablist based on side. + let (orders_ref) = if (side == ASK) + &market_account_ref.asks else &market_account_ref.bids; + // Immutably borrow order. + let order_ref = tablist::borrow(orders_ref, order_access_key); + // Return order fields. + (order_ref.market_order_id, order_ref.size) + } + + #[test_only] + /// Get `PlaceLimitOrderEvent`s at a market account handle. + public fun get_place_limit_order_events_test( + market_id: u64, + user: address, + custodian_id: u64 + ): vector + acquires MarketEventHandles { + event::emitted_events_by_handle( + &(borrow_market_event_handles_for_market_account_test( + market_id, user, custodian_id). + place_limit_order_events)) + } + + #[test_only] + /// Get `PlaceMarketOrderEvent`s at a market account handle. + public fun get_place_market_order_events_test( + market_id: u64, + user: address, + custodian_id: u64 + ): vector + acquires MarketEventHandles { + event::emitted_events_by_handle( + &(borrow_market_event_handles_for_market_account_test( + market_id, user, custodian_id). + place_market_order_events)) + } + + #[test_only] + /// Return `true` if `user_adress` has an entry in `Collateral` for + /// given `AssetType` and `market_account_id`. + public fun has_collateral_test< + AssetType + >( + user_address: address, + market_account_id: u128, + ): bool + acquires Collateral { + // Return false if does not even have collateral map. + if (!exists>(user_address)) return false; + // Immutably borrow collateral map. + let collateral_map_ref = + &borrow_global>(user_address).map; + // Return if table contains entry for market account ID. + tablist::contains(collateral_map_ref, market_account_id) + } + + #[test_only] + /// Check if user has allocated order for given parameters. + public fun has_order_test( + user_address: address, + market_account_id: u128, + side: bool, + order_access_key: u64 + ): bool + acquires MarketAccounts { + // Immutably borrow market accounts map. + let market_accounts_map_ref = + &borrow_global(user_address).map; + let market_account_ref = // Immutably borrow market account. + table::borrow(market_accounts_map_ref, market_account_id); + // Immutably borrow corresponding orders tablist based on side. + let (orders_ref) = if (side == ASK) + &market_account_ref.asks else &market_account_ref.bids; + tablist::contains(orders_ref, order_access_key) + } + + #[test_only] + /// Register market accounts under test `@user`, return signer and + /// market account ID of: + /// + /// * Pure coin self-custodied market account. + /// * Pure coin market account with delegated custodian. + /// * Generic self-custodian market account. + /// * Generic market account with delegated custodian. + fun register_market_accounts_test(): ( + signer, + u128, + u128, + u128, + u128 + ) acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Get signer for test user account. + let user = account::create_signer_with_capability( + &account::create_test_signer_cap(@user)); + // Create Aptos account. + account::create_account_for_test(@user); + // Register a pure coin and a generic market, storing most + // returns. + let (market_id_pure_coin, _, lot_size_pure_coin, tick_size_pure_coin, + min_size_pure_coin, underwriter_id_pure_coin, market_id_generic, + _, lot_size_generic, tick_size_generic, min_size_generic, + underwriter_id_generic) = registry::register_markets_test(); + // Assert market info. + assert!(market_id_pure_coin == MARKET_ID_PURE_COIN, 0); + assert!(lot_size_pure_coin == LOT_SIZE_PURE_COIN, 0); + assert!(tick_size_pure_coin == TICK_SIZE_PURE_COIN, 0); + assert!(min_size_pure_coin == MIN_SIZE_PURE_COIN, 0); + assert!(underwriter_id_pure_coin == NO_UNDERWRITER, 0); + assert!(market_id_generic == MARKET_ID_GENERIC, 0); + assert!(lot_size_generic == LOT_SIZE_GENERIC, 0); + assert!(tick_size_generic == TICK_SIZE_GENERIC, 0); + assert!(min_size_generic == MIN_SIZE_GENERIC, 0); + assert!(underwriter_id_generic == UNDERWRITER_ID, 0); + // Register self-custodied pure coin account. + register_market_account( + &user, market_id_pure_coin, NO_CUSTODIAN); + // Set delegated custodian ID as registered. + registry::set_registered_custodian_test(CUSTODIAN_ID); + // Register delegated custody pure coin account. + register_market_account( + &user, market_id_pure_coin, CUSTODIAN_ID); + // Register self-custodied generic asset account. + register_market_account_generic_base( + &user, market_id_generic, NO_CUSTODIAN); + // Register delegated custody generic asset account. + register_market_account_generic_base( + &user, market_id_generic, CUSTODIAN_ID); + // Get market account IDs. + let market_account_id_coin_self = + get_market_account_id(market_id_pure_coin, NO_CUSTODIAN); + let market_account_id_coin_delegated = + get_market_account_id(market_id_pure_coin, CUSTODIAN_ID); + let market_account_id_generic_self = + get_market_account_id(market_id_generic , NO_CUSTODIAN); + let market_account_id_generic_delegated = + get_market_account_id(market_id_generic , CUSTODIAN_ID); + (user, // Return signing user and market account IDs. + market_account_id_coin_self, + market_account_id_coin_delegated, + market_account_id_generic_self, + market_account_id_generic_delegated) + } + + #[test_only] + public fun remove_market_event_handles_for_market_account_test( + user: address, + market_id: u64, + custodian_id: u64 + ) acquires MarketEventHandles { + let market_account_id = get_market_account_id(market_id, custodian_id); + let market_event_handles_map_ref_mut = + &mut borrow_global_mut(user).map; + let MarketEventHandlesForMarketAccount{ + cancel_order_events, + change_order_size_events, + fill_events, + place_limit_order_events, + place_market_order_events + } = table::remove(market_event_handles_map_ref_mut, market_account_id); + event::destroy_handle(cancel_order_events); + event::destroy_handle(change_order_size_events); + event::destroy_handle(fill_events); + event::destroy_handle(place_limit_order_events); + event::destroy_handle(place_market_order_events); + } + + #[test_only] + public fun remove_market_event_handles_test( + user: address + ) acquires MarketEventHandles { + let MarketEventHandles{map} = move_from(user); + table::drop_unchecked(map); + } + + #[test_only] + /// Return `true` if order is active. + public fun is_order_active_test( + user_address: address, + market_account_id: u128, + side: bool, + order_access_key: u64 + ): bool + acquires MarketAccounts { + // Get order's market order ID field. + let (market_order_id, _) = get_order_fields_test( + user_address, market_account_id, side, order_access_key); + market_order_id != (NIL as u128) // Return true if non-null ID. + } + + #[test_only] + /// Set market order ID for given order. + public fun set_market_order_id_test( + user_address: address, + market_id: u64, + custodian_id: u64, + side: bool, + order_access_key: u64, + market_order_id: u128 + ) acquires MarketAccounts { + // Mutably borrow market accounts map. + let market_accounts_map_ref_mut = + &mut borrow_global_mut(user_address).map; + // Get market account ID. + let market_account_id = get_market_account_id(market_id, custodian_id); + let market_account_ref_mut = // Mutably borrow market account. + table::borrow_mut(market_accounts_map_ref_mut, market_account_id); + // Mutably borrow corresponding orders tablist based on side. + let (orders_ref_mut) = if (side == ASK) + &mut market_account_ref_mut.asks else + &mut market_account_ref_mut.bids; + let order_ref_mut = // Mutably borrow order. + tablist::borrow_mut(orders_ref_mut, order_access_key); + // Set new market order ID. + order_ref_mut.market_order_id = market_order_id; + } + + // Test-only functions <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + // Tests >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + #[test] + #[expected_failure(abort_code = E_INVALID_MARKET_ORDER_ID)] + /// Verify failure for market account ID mismatch. + fun test_cancel_order_internal_invalid_market_order_id() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + register_market_accounts_test(); // Register test markets. + // Define order parameters. + let market_order_id = 123; + let size = MIN_SIZE_PURE_COIN; + let price = 1; + let side = BID; + // Deposit starting base and quote coins. + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(BASE_START)); + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(QUOTE_START)); + // Place order + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, market_order_id, 1); + // Attempt invalid cancellation. + cancel_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, 1, market_order_id + 1, + CANCEL_REASON_MANUAL_CANCEL); + } + + #[test] + #[expected_failure(abort_code = E_START_SIZE_MISMATCH)] + /// Verify failure for start size mismatch. + fun test_cancel_order_internal_start_size_mismatch() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + register_market_accounts_test(); // Register test markets. + // Define order parameters. + let market_order_id = 123; + let size = MIN_SIZE_PURE_COIN; + let price = 1; + let side = BID; + // Deposit starting base and quote coins. + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(BASE_START)); + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(QUOTE_START)); + // Place order + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, market_order_id, 1); + // Attempt invalid cancellation. + cancel_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size + 1, price, 1, market_order_id, + CANCEL_REASON_MANUAL_CANCEL); + } + + #[test] + /// Verify state updates for changing ask size. Based on + /// `test_place_cancel_order_ask()`. + fun test_change_order_size_internal_ask() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + register_market_accounts_test(); // Register test markets. + // Define order parameters. + let market_order_id = 1234; + let size = 789; + let size_old = size - 1; + let price = 321; + let side = ASK; + let order_access_key = 1; + // Calculate change in base asset and quote asset fields. + let base_delta = size * LOT_SIZE_PURE_COIN; + let quote_delta = size * price * TICK_SIZE_PURE_COIN; + // Deposit starting base and quote coins. + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(BASE_START)); + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(QUOTE_START)); + // Place order. + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size_old, price, market_order_id, 1); + // Remove market event handles. + remove_market_event_handles_for_market_account_test( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID); + change_order_size_internal( // Change order size. + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, size_old, size, + price, order_access_key, market_order_id); + // Assert asset counts. + let (base_total , base_available , base_ceiling, + quote_total, quote_available, quote_ceiling) = + get_asset_counts_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID); + assert!(base_total == BASE_START , 0); + assert!(base_available == BASE_START - base_delta, 0); + assert!(base_ceiling == BASE_START , 0); + assert!(quote_total == QUOTE_START, 0); + assert!(quote_available == QUOTE_START, 0); + assert!(quote_ceiling == QUOTE_START + quote_delta, 0); + // Check market order ID for valid access key. + let optional_market_order_id = get_open_order_id_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, order_access_key); + assert!( // Verify market order ID match. + *option::borrow(&optional_market_order_id) == market_order_id, 0); + // Check market order ID for invalid access key. + optional_market_order_id = get_open_order_id_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + order_access_key + 1); + // Verify empty option. + assert!(option::is_none(&optional_market_order_id), 0); + } + + #[test] + /// Verify state updates for changing bid size. Based on + /// `test_place_cancel_order_bid()`. + fun test_change_order_size_internal_bid() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + register_market_accounts_test(); // Register test markets. + // Define order parameters. + let market_order_id = 1234; + let size = 789; + let size_old = size - 1; + let price = 321; + let side = BID; + let order_access_key = 1; + // Calculate change in base asset and quote asset fields. + let base_delta = size * LOT_SIZE_PURE_COIN; + let quote_delta = size * price * TICK_SIZE_PURE_COIN; + // Deposit starting base and quote coins. + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(BASE_START)); + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(QUOTE_START)); + // Place order. + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size_old, price, market_order_id, 1); + change_order_size_internal( // Change order size. + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, size_old, size, + price, order_access_key, market_order_id); + // Assert asset counts. + let (base_total , base_available , base_ceiling, + quote_total, quote_available, quote_ceiling) = + get_asset_counts_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID); + assert!(base_total == BASE_START , 0); + assert!(base_available == BASE_START , 0); + assert!(base_ceiling == BASE_START + base_delta, 0); + assert!(quote_total == QUOTE_START, 0); + assert!(quote_available == QUOTE_START - quote_delta, 0); + assert!(quote_ceiling == QUOTE_START, 0); + // Check market order ID for valid access key. + let optional_market_order_id = get_open_order_id_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, order_access_key); + assert!( // Verify market order ID match. + *option::borrow(&optional_market_order_id) == market_order_id, 0); + } + + #[test] + #[expected_failure(abort_code = E_CHANGE_ORDER_NO_CHANGE)] + /// Verify failure for no change in size. + fun test_change_order_size_internal_no_change() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + register_market_accounts_test(); // Register test markets. + // Define order parameters. + let market_order_id = 123; + let size = MIN_SIZE_PURE_COIN; + let price = 1; + let side = BID; + // Deposit starting base and quote coins. + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(BASE_START)); + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(QUOTE_START)); + // Place order + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, market_order_id, 1); + change_order_size_internal( // Attempt invalid order size change. + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, size, size, price, + 1, market_order_id); + } + + #[test] + #[expected_failure(abort_code = E_COIN_AMOUNT_MISMATCH)] + /// Verify failure for amount mismatch. + fun test_deposit_asset_amount_mismatch() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test market accounts. + register_market_accounts_test(); + // Declore deposit invocation arguments. + let user_address = @user; + let market_id = MARKET_ID_PURE_COIN; + let custodian_id = NO_CUSTODIAN; + let amount = 123; + let optional_coins = option::some(assets::mint_test(amount + 1)); + let underwriter_id = NO_UNDERWRITER; + // Attempt invalid invocation. + deposit_asset(user_address, market_id, custodian_id, amount, + optional_coins, underwriter_id); + } + + #[test] + #[expected_failure(abort_code = E_NO_MARKET_ACCOUNT)] + /// Verify failure for no market account. + fun test_deposit_asset_no_account() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test market accounts. + register_market_accounts_test(); + // Attempt invalid invocation. + deposit_coins(@user, 0, 0, coin::zero()); + } + + #[test] + #[expected_failure(abort_code = E_NO_MARKET_ACCOUNTS)] + /// Verify failure for no market accounts. + fun test_deposit_asset_no_accounts() + acquires + Collateral, + MarketAccounts + { + // Attempt invalid invocation. + deposit_coins(@user, 0, 0, coin::zero()); + } + + #[test] + #[expected_failure(abort_code = E_ASSET_NOT_IN_PAIR)] + /// Verify failure for asset not in pair. + fun test_deposit_asset_not_in_pair() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test market accounts. + register_market_accounts_test(); + // Attempt invalid invocation. + deposit_coins(@user, MARKET_ID_PURE_COIN, NO_CUSTODIAN, + coin::zero()); + } + + #[test] + #[expected_failure(abort_code = E_DEPOSIT_OVERFLOW_ASSET_CEILING)] + /// Verify failure for ceiling overflow. + fun test_deposit_asset_overflow() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test market accounts. + register_market_accounts_test(); + let underwriter_capability = // Get underwriter capability. + registry::get_underwriter_capability_test(UNDERWRITER_ID); + // Deposit maximum amount of generic asset. + deposit_generic_asset(@user, MARKET_ID_GENERIC, NO_CUSTODIAN, + HI_64, &underwriter_capability); + // Attempt invalid deposit of one more unit. + deposit_generic_asset(@user, MARKET_ID_GENERIC, NO_CUSTODIAN, + 1, &underwriter_capability); + // Drop underwriter capability. + registry::drop_underwriter_capability_test(underwriter_capability); + } + + #[test] + #[expected_failure(abort_code = E_INVALID_UNDERWRITER)] + /// Verify failure for invalid underwriter. + fun test_deposit_asset_underwriter() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test market accounts. + register_market_accounts_test(); + let underwriter_capability = // Get underwriter capability. + registry::get_underwriter_capability_test(UNDERWRITER_ID + 1); + // Attempt deposit with invalid underwriter capability. + deposit_generic_asset(@user, MARKET_ID_GENERIC, NO_CUSTODIAN, + 1, &underwriter_capability); + // Drop underwriter capability. + registry::drop_underwriter_capability_test(underwriter_capability); + } + + #[test(econia = @econia)] + #[expected_failure(abort_code = E_COIN_TYPE_IS_GENERIC_ASSET)] + /// Assert failure for coin type is generic asset. + fun test_deposit_coins_generic( + econia: &signer + ) acquires + Collateral, + MarketAccounts + { + // Initialize coin, storing capabilities. + let (burn_cap, freeze_cap, mint_cap) = coin::initialize( + econia, string::utf8(b""), string::utf8(b""), 1, true); + // Mint a generic coin. + let generic_coin = coin::mint(1, &mint_cap); + // Attempt invalid deposit. + deposit_coins(@econia, 0, 0, generic_coin); + // Destroy capabilities. + coin::destroy_burn_cap(burn_cap); + coin::destroy_freeze_cap(freeze_cap); + coin::destroy_mint_cap(mint_cap); + } + + #[test] + /// Verify state updates, returns for pure coin and generic markets. + fun test_deposit_withdraw_assets_internal() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Get test market account IDs. + let (_, _, market_account_id_coin_delegated, + market_account_id_generic_self, _) = + register_market_accounts_test(); + // Declare withdrawal amounts for each market accounts. + let base_amount_0 = 123; + let quote_amount_0 = 234; + let base_amount_1 = 345; + let quote_amount_1 = 456; + // Deposit starting base and quote asset to each account. + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(BASE_START)); + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(QUOTE_START)); + deposit_asset( + @user, MARKET_ID_GENERIC, NO_CUSTODIAN, BASE_START, option::none(), + UNDERWRITER_ID); + deposit_coins(@user, MARKET_ID_GENERIC, NO_CUSTODIAN, + assets::mint_test(QUOTE_START)); + // Withdraw assets from pure coin market account. + let (optional_base_coins_0, quote_coins_0) = withdraw_assets_internal< + BC, QC>(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, base_amount_0, + quote_amount_0, NO_UNDERWRITER); + // Assert coin values. + assert!(coin::value(option::borrow(&optional_base_coins_0)) + == base_amount_0, 0); + assert!(coin::value("e_coins_0) == quote_amount_0, 0); + // Assert asset counts. + let (base_total , base_available , base_ceiling, + quote_total, quote_available, quote_ceiling) = + get_asset_counts_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID); + assert!(base_total == BASE_START - base_amount_0, 0); + assert!(base_available == BASE_START - base_amount_0, 0); + assert!(base_ceiling == BASE_START - base_amount_0, 0); + assert!(quote_total == QUOTE_START - quote_amount_0, 0); + assert!(quote_available == QUOTE_START - quote_amount_0, 0); + assert!(quote_ceiling == QUOTE_START - quote_amount_0, 0); + // Assert collateral amounts. + assert!(get_collateral_value_test( + @user, market_account_id_coin_delegated) + == BASE_START - base_amount_0, 0); + assert!(get_collateral_value_test( + @user, market_account_id_coin_delegated) + == QUOTE_START - quote_amount_0, 0); + // Deposit assets back to pure coin market account. + deposit_assets_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, base_amount_0, + optional_base_coins_0, quote_coins_0, NO_UNDERWRITER); + // Assert asset counts. + (base_total , base_available , base_ceiling, + quote_total, quote_available, quote_ceiling) = + get_asset_counts_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID); + assert!(base_total == BASE_START , 0); + assert!(base_available == BASE_START , 0); + assert!(base_ceiling == BASE_START , 0); + assert!(quote_total == QUOTE_START, 0); + assert!(quote_available == QUOTE_START, 0); + assert!(quote_ceiling == QUOTE_START, 0); + // Assert collateral amounts. + assert!(get_collateral_value_test( + @user, market_account_id_coin_delegated) == BASE_START, 0); + assert!(get_collateral_value_test( + @user, market_account_id_coin_delegated) == QUOTE_START, 0); + // Withdraw assets from generic market account. + let (optional_base_coins_1, quote_coins_1) = withdraw_assets_internal< + GenericAsset, QC>(@user, MARKET_ID_GENERIC, NO_CUSTODIAN, + base_amount_1, quote_amount_1, UNDERWRITER_ID); + // Assert no base asset. + assert!(option::is_none(&optional_base_coins_1), 0); + // Assert quote coin amount. + assert!(coin::value("e_coins_1) == quote_amount_1, 0); + // Assert asset counts. + (base_total , base_available , base_ceiling, + quote_total, quote_available, quote_ceiling) = + get_asset_counts_internal( + @user, MARKET_ID_GENERIC, NO_CUSTODIAN); + assert!(base_total == BASE_START - base_amount_1, 0); + assert!(base_available == BASE_START - base_amount_1, 0); + assert!(base_ceiling == BASE_START - base_amount_1, 0); + assert!(quote_total == QUOTE_START - quote_amount_1, 0); + assert!(quote_available == QUOTE_START - quote_amount_1, 0); + assert!(quote_ceiling == QUOTE_START - quote_amount_1, 0); + // Assert collateral state. + assert!(!has_collateral_test( + @user, market_account_id_generic_self), 0); + assert!(get_collateral_value_test( + @user, market_account_id_generic_self) + == QUOTE_START - quote_amount_1, 0); + // Deposit assets back to generic market account. + deposit_assets_internal( + @user, MARKET_ID_GENERIC, NO_CUSTODIAN, base_amount_1, + optional_base_coins_1, quote_coins_1, UNDERWRITER_ID); + // Assert asset counts. + (base_total , base_available , base_ceiling, + quote_total, quote_available, quote_ceiling) = + get_asset_counts_internal( + @user, MARKET_ID_GENERIC, NO_CUSTODIAN); + assert!(base_total == BASE_START , 0); + assert!(base_available == BASE_START , 0); + assert!(base_ceiling == BASE_START , 0); + assert!(quote_total == QUOTE_START, 0); + assert!(quote_available == QUOTE_START, 0); + assert!(quote_ceiling == QUOTE_START, 0); + // Assert collateral state. + assert!(!has_collateral_test( + @user, market_account_id_generic_self), 0); + assert!(get_collateral_value_test( + @user, market_account_id_generic_self) + == QUOTE_START, 0); + } + + #[test] + /// Verify state updates for assorted deposit styles. + fun test_deposits(): + vector + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Declare deposit parameters + let coin_amount = 700; + let generic_amount = 500; + // Get signing user and test market account IDs. + let (user, _, market_account_id_coin_delegated, + market_account_id_generic_self, _) = + register_market_accounts_test(); + coin::register(&user); // Register coin store. + // Deposit coin asset to user's coin store. + coin::deposit(@user, assets::mint_test(coin_amount)); + // Deposit to user's delegated pure coin market account. + deposit_from_coinstore(&user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + coin_amount); + let underwriter_capability = // Get underwriter capability. + registry::get_underwriter_capability_test(UNDERWRITER_ID); + // Deposit to user's generic market account. + deposit_generic_asset(@user, MARKET_ID_GENERIC, NO_CUSTODIAN, + generic_amount, &underwriter_capability); + // Drop underwriter capability. + registry::drop_underwriter_capability_test(underwriter_capability); + let custodian_capability = // Get custodian capability. + registry::get_custodian_capability_test(CUSTODIAN_ID); + // Assert state for quote deposit. + let ( base_total, base_available, base_ceiling, + quote_total, quote_available, quote_ceiling) = + get_asset_counts_custodian( + @user, MARKET_ID_PURE_COIN, &custodian_capability); + // Drop custodian capability. + registry::drop_custodian_capability_test(custodian_capability); + assert!(base_total == 0 , 0); + assert!(base_available == 0 , 0); + assert!(base_ceiling == 0 , 0); + assert!(quote_total == coin_amount , 0); + assert!(quote_available == coin_amount , 0); + assert!(quote_ceiling == coin_amount , 0); + assert!(get_collateral_value_test( + @user, market_account_id_coin_delegated) == 0, 0); + assert!(get_collateral_value_test( + @user, market_account_id_coin_delegated) == coin_amount, 0); + // Assert state for base deposit. + let ( base_total, base_available, base_ceiling, + quote_total, quote_available, quote_ceiling) = + get_asset_counts_user(&user, MARKET_ID_GENERIC); + assert!(base_total == generic_amount, 0); + assert!(base_available == generic_amount, 0); + assert!(base_ceiling == generic_amount, 0); + assert!(quote_total == 0 , 0); + assert!(quote_available == 0 , 0); + assert!(quote_ceiling == 0 , 0); + assert!(!has_collateral_test( + @user, market_account_id_generic_self), 0); + assert!(get_collateral_value_test( + @user, market_account_id_generic_self) == 0, 0); + // Initialize empty vector to return instead of dropping. + let return_instead_of_dropping = vector::empty(); + // Get market account view for quote deposit. + let market_account_view = get_market_account( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID); + // Assert market account view state. + assert!(market_account_view.market_id == MARKET_ID_PURE_COIN, 0); + assert!(market_account_view.custodian_id == CUSTODIAN_ID , 0); + assert!(market_account_view.base_total == 0 , 0); + assert!(market_account_view.base_available == 0 , 0); + assert!(market_account_view.base_ceiling == 0 , 0); + assert!(market_account_view.quote_total == coin_amount , 0); + assert!(market_account_view.quote_available == coin_amount , 0); + assert!(market_account_view.quote_ceiling == coin_amount , 0); + // Push back value to return instead of dropping. + vector::push_back( + &mut return_instead_of_dropping, market_account_view); + // Get market account view for base deposit. + market_account_view = get_market_account( + @user, MARKET_ID_GENERIC, NO_CUSTODIAN); + // Assert market account view state. + assert!(market_account_view.market_id == MARKET_ID_GENERIC , 0); + assert!(market_account_view.custodian_id == NO_CUSTODIAN , 0); + assert!(market_account_view.base_total == generic_amount , 0); + assert!(market_account_view.base_available == generic_amount , 0); + assert!(market_account_view.base_ceiling == generic_amount , 0); + assert!(market_account_view.quote_total == 0 , 0); + assert!(market_account_view.quote_available == 0 , 0); + assert!(market_account_view.quote_ceiling == 0 , 0); + // Push back value to return instead of dropping. + vector::push_back( + &mut return_instead_of_dropping, market_account_view); + return_instead_of_dropping // Return instead of dropping. + } + + #[test] + /// Verify state updates for: + /// + /// * Filling an ask. + /// * Fill is complete. + /// * Inactive stack top not null. + /// * Base asset is coin. + fun test_fill_order_internal_ask_complete_base_coin() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test markets, get market account ID for pure coin + // market with delegated custodian. + let (_, _, market_account_id, _, _) = register_market_accounts_test(); + // Define order parameters. + let market_order_id = 1234; + let size = 789; + let price = 321; + let side = ASK; + let complete_fill = true; + let order_access_key_filled = 1; + let order_access_key_cancelled = 2; + // Calculate base asset and quote asset fill amounts. + let base_fill = size * LOT_SIZE_PURE_COIN; + let quote_fill = size * price * TICK_SIZE_PURE_COIN; + // Deposit starting base and quote coins. + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(BASE_START)); + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(QUOTE_START)); + // Place order. + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, market_order_id, 1); + // Place duplicate order then cancel, so stack is not empty. + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, market_order_id, 2); + cancel_order_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, size, price, + order_access_key_cancelled, market_order_id, + CANCEL_REASON_MANUAL_CANCEL); + // Initialize external coins passing through matching engine. + let optional_base_coins = option::some(coin::zero()); + let quote_coins = assets::mint_test(quote_fill); + // Fill order, storing base and quote coins for matching engine, + // and market order ID. + (optional_base_coins, quote_coins, market_order_id) = + fill_order_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + order_access_key_filled, size, size, complete_fill, + optional_base_coins, quote_coins, base_fill, quote_fill); + // Assert market order ID. + assert!(market_order_id == market_order_id, 0); + // Assert external coin values after order fill. + let base_coins = option::destroy_some(optional_base_coins); + assert!(coin::value(&base_coins) == base_fill, 0); + assert!(coin::value("e_coins) == 0, 0); + // Destroy external coins. + assets::burn(base_coins); + coin::destroy_zero(quote_coins); + // Assert inactive stack top. + assert!(get_inactive_stack_top_test( + @user, market_account_id, side) == order_access_key_filled, 0); + assert!(!is_order_active_test( // Assert order marked inactive. + @user, market_account_id, side, order_access_key_filled), 0); + // Assert next inactive node field. + assert!(get_next_inactive_order_test( + @user, market_account_id, side, order_access_key_filled) + == order_access_key_cancelled, 0); + // Assert asset counts. + let (base_total , base_available , base_ceiling, + quote_total, quote_available, quote_ceiling) = + get_asset_counts_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID); + assert!(base_total == BASE_START - base_fill, 0); + assert!(base_available == BASE_START - base_fill, 0); + assert!(base_ceiling == BASE_START - base_fill, 0); + assert!(quote_total == QUOTE_START + quote_fill, 0); + assert!(quote_available == QUOTE_START + quote_fill, 0); + assert!(quote_ceiling == QUOTE_START + quote_fill, 0); + // Assert collateral amounts. + assert!(get_collateral_value_test( + @user, market_account_id) == BASE_START - base_fill, 0); + assert!(get_collateral_value_test( + @user, market_account_id) == QUOTE_START + quote_fill, 0); + } + + #[test] + /// Verify state updates for: + /// + /// * Filling a bid. + /// * Fill is complete. + /// * Inactive stack top is null. + /// * Base asset is coin. + fun test_fill_order_internal_bid_complete_base_coin() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test markets, get market account ID for pure coin + // market with delegated custodian. + let (_, _, market_account_id, _, _) = register_market_accounts_test(); + // Define order parameters. + let market_order_id = 1234; + let size = 789; + let price = 321; + let side = BID; + let complete_fill = true; + let order_access_key_filled = 1; + // Calculate base asset and quote asset fill amounts. + let base_fill = size * LOT_SIZE_PURE_COIN; + let quote_fill = size * price * TICK_SIZE_PURE_COIN; + // Deposit starting base and quote coins. + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(BASE_START)); + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(QUOTE_START)); + // Place order. + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, market_order_id, 1); + // Initialize external coins passing through matching engine. + let optional_base_coins = option::some(assets::mint_test(base_fill)); + let quote_coins = coin::zero(); + // Fill order, storing base and quote coins for matching engine, + // and market order ID. + (optional_base_coins, quote_coins, market_order_id) = + fill_order_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + order_access_key_filled, size, size, complete_fill, + optional_base_coins, quote_coins, base_fill, quote_fill); + // Assert market order ID. + assert!(market_order_id == market_order_id, 0); + // Assert external coin values after order fill. + let base_coins = option::destroy_some(optional_base_coins); + assert!(coin::value(&base_coins) == 0, 0); + assert!(coin::value("e_coins) == quote_fill, 0); + // Destroy external coins. + coin::destroy_zero(base_coins); + assets::burn(quote_coins); + // Assert inactive stack top. + assert!(get_inactive_stack_top_test( + @user, market_account_id, side) == order_access_key_filled, 0); + assert!(!is_order_active_test( // Assert order marked inactive. + @user, market_account_id, side, order_access_key_filled), 0); + // Assert next inactive node field. + assert!(get_next_inactive_order_test( + @user, market_account_id, side, order_access_key_filled) + == NIL, 0); + // Assert asset counts. + let (base_total , base_available , base_ceiling, + quote_total, quote_available, quote_ceiling) = + get_asset_counts_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID); + assert!(base_total == BASE_START + base_fill, 0); + assert!(base_available == BASE_START + base_fill, 0); + assert!(base_ceiling == BASE_START + base_fill, 0); + assert!(quote_total == QUOTE_START - quote_fill, 0); + assert!(quote_available == QUOTE_START - quote_fill, 0); + assert!(quote_ceiling == QUOTE_START - quote_fill, 0); + // Assert collateral amounts. + assert!(get_collateral_value_test( + @user, market_account_id) == BASE_START + base_fill, 0); + assert!(get_collateral_value_test( + @user, market_account_id) == QUOTE_START - quote_fill, 0); + } + + #[test] + /// Verify state updates for: + /// + /// * Filling a bid. + /// * Fill is not complete. + /// * Base asset is generic. + fun test_fill_order_internal_bid_partial_base_generic() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test markets, get market account ID for generic + // market with delegated custodian. + let (_, _, _, _, market_account_id) = register_market_accounts_test(); + // Define order parameters. + let market_order_id = 1234; + let size = 789; + let fill_size = size - 1; + let price = 321; + let side = BID; + let complete_fill = false; + let order_access_key_filled = 1; + // Calculate change in base ceiling and quote available if + // order were completely filled. + let base_ceiling_delta = size * LOT_SIZE_GENERIC; + let quote_available_delta = size * price * TICK_SIZE_GENERIC; + // Calculate base asset and quote asset fill amounts. + let base_fill = fill_size * LOT_SIZE_GENERIC; + let quote_fill = fill_size * price * TICK_SIZE_GENERIC; + // Deposit starting quote coins. + deposit_coins(@user, MARKET_ID_GENERIC, CUSTODIAN_ID, + assets::mint_test(QUOTE_START)); + // Place order. + place_order_internal(@user, MARKET_ID_GENERIC, CUSTODIAN_ID, side, + size, price, market_order_id, 1); + // Initialize external coins passing through matching engine. + let optional_base_coins = option::none(); + let quote_coins = coin::zero(); + // Fill order, storing base and quote coins for matching engine, + // and market order ID. + (optional_base_coins, quote_coins, market_order_id) = + fill_order_internal( + @user, MARKET_ID_GENERIC, CUSTODIAN_ID, side, + order_access_key_filled, size, fill_size, complete_fill, + optional_base_coins, quote_coins, base_fill, quote_fill); + // Assert market order ID. + assert!(market_order_id == market_order_id, 0); + // Assert quote coin values after order fill. + assert!(coin::value("e_coins) == quote_fill, 0); + // Destroy external coins. + option::destroy_none(optional_base_coins); + assets::burn(quote_coins); + // Assert inactive stack top. + assert!(get_inactive_stack_top_test( + @user, market_account_id, side) == NIL, 0); + assert!(is_order_active_test( // Assert order marked active. + @user, market_account_id, side, order_access_key_filled), 0); + // Assert order field returns. + let (market_order_id_r, size_r) = get_order_fields_test( + @user, market_account_id, side, order_access_key_filled); + assert!(market_order_id_r == market_order_id, 0); + assert!(size_r == size - fill_size, 0); + // Assert asset counts. + let (base_total , base_available , base_ceiling, + quote_total, quote_available, quote_ceiling) = + get_asset_counts_internal( + @user, MARKET_ID_GENERIC, CUSTODIAN_ID); + assert!(base_total == base_fill, 0); + assert!(base_available == base_fill, 0); + assert!(base_ceiling == base_ceiling_delta, 0); + assert!(quote_total == QUOTE_START - quote_fill, 0); + assert!(quote_available == QUOTE_START - quote_available_delta, 0); + assert!(quote_ceiling == QUOTE_START - quote_fill, 0); + assert!(!has_collateral_test( + @user, market_account_id), 0); + assert!(get_collateral_value_test( + @user, market_account_id) == QUOTE_START - quote_fill, 0); + } + + #[test] + #[expected_failure(abort_code = E_START_SIZE_MISMATCH)] + /// Verify failure for start size mismatch. Based on + /// `test_fill_order_internal_ask_complete_base_coin()`. + fun test_fill_order_internal_start_size_mismatch() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + register_market_accounts_test(); // Register test markets. + // Define order parameters. + let market_order_id = 1234; + let size = 789; + let price = 321; + let side = ASK; + let complete_fill = true; + let access_key = 1; + // Calculate base asset and quote asset fill amounts. + let base_fill = size * LOT_SIZE_PURE_COIN; + let quote_fill = size * price * TICK_SIZE_PURE_COIN; + // Deposit starting base and quote coins. + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(BASE_START)); + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(QUOTE_START)); + place_order_internal( // Place order. + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, size, price, + market_order_id, access_key); + // Initialize external coins passing through matching engine. + let optional_base_coins = option::some(coin::zero()); + let quote_coins = assets::mint_test(quote_fill); + // Fill order, storing base and quote coins for matching engine. + (optional_base_coins, quote_coins, _) = fill_order_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, access_key, + size + 1, size, complete_fill, optional_base_coins, + quote_coins, base_fill, quote_fill); + // Destroy external coins. + assets::burn(option::destroy_some(optional_base_coins)); + assets::burn(quote_coins); + } + + #[test] + /// Verify expected returns. + fun test_get_active_market_order_ids_internal() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test market accounts. + register_market_accounts_test(); + // Define order parameters. + let market_order_id_1 = 123; + let market_order_id_2 = 234; + let market_order_id_3 = 345; + let market_order_id_4 = 456; + let size = MIN_SIZE_PURE_COIN; + let price = 1; + // Deposit starting base and quote coins. + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(BASE_START)); + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(QUOTE_START)); + // Assert empty returns. + assert!(get_active_market_order_ids_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, ASK) == vector[], 0); + assert!(get_active_market_order_ids_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, BID) == vector[], 0); + // Place three asks, then cancel second ask. + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, ASK, + size, price, market_order_id_1, 1); + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, ASK, + size, price, market_order_id_2, 2); + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, ASK, + size, price, market_order_id_3, 3); + cancel_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, ASK, + size, price, 2, market_order_id_2, + CANCEL_REASON_MANUAL_CANCEL); + // Get expected market order IDs vector. + let expected = vector[market_order_id_1, market_order_id_3]; + // Assert expected return. + assert!(get_active_market_order_ids_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, ASK) == expected, 0); + // Place single bid. + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, BID, + size, price, market_order_id_4, 1); + // Get expected market order IDs vector. + expected = vector[market_order_id_4]; + // Assert expected return. + assert!(get_active_market_order_ids_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, BID) == expected, 0); + // Cancel order. + cancel_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, BID, + size, price, 1, market_order_id_4, + CANCEL_REASON_MANUAL_CANCEL); + // Assert expected return. + assert!(get_active_market_order_ids_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, BID) == vector[], 0); + } + + #[test] + #[expected_failure(abort_code = E_NO_MARKET_ACCOUNT)] + /// Verify failure for no market account resource. + fun test_get_active_market_order_ids_internal_no_account() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test market accounts. + register_market_accounts_test(); + // Attempt invalid invocation. + get_active_market_order_ids_internal( + @user, MARKET_ID_PURE_COIN + 10, CUSTODIAN_ID, BID); + } + + #[test] + #[expected_failure(abort_code = E_NO_MARKET_ACCOUNTS)] + /// Verify failure for no market accounts resource. + fun test_get_active_market_order_ids_internal_no_accounts() + acquires MarketAccounts { + // Attempt invalid invocation. + get_active_market_order_ids_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, BID); + } + + #[test] + /// Verify constant getter return. + fun test_get_ASK() {assert!(get_ASK() == ASK, 0)} + + #[test] + #[expected_failure(abort_code = E_NO_MARKET_ACCOUNT)] + /// Verify failure for no market account resource. + fun test_get_asset_counts_internal_no_account() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test market accounts. + register_market_accounts_test(); + // Attempt invalid invocation. + get_asset_counts_internal(@user, 0, 0); + } + + #[test] + #[expected_failure(abort_code = E_NO_MARKET_ACCOUNTS)] + /// Verify failure for no market accounts resource. + fun test_get_asset_counts_internal_no_accounts() + acquires MarketAccounts { + // Attempt invalid invocation. + get_asset_counts_internal(@user, 0, 0); + } + + #[test] + /// Verify constant getter return. + fun test_get_BID() {assert!(get_BID() == BID, 0)} + + #[test] + /// Verify constant getter returns. + fun test_get_cancel_reasons() { + assert!(get_CANCEL_REASON_EVICTION() == + CANCEL_REASON_EVICTION, 0); + assert!(get_CANCEL_REASON_IMMEDIATE_OR_CANCEL() == + CANCEL_REASON_IMMEDIATE_OR_CANCEL, 0); + assert!(get_CANCEL_REASON_MANUAL_CANCEL() == + CANCEL_REASON_MANUAL_CANCEL, 0); + assert!(get_CANCEL_REASON_MAX_QUOTE_TRADED() == + CANCEL_REASON_MAX_QUOTE_TRADED, 0); + assert!(get_CANCEL_REASON_NOT_ENOUGH_LIQUIDITY() == + CANCEL_REASON_NOT_ENOUGH_LIQUIDITY, 0); + assert!(get_CANCEL_REASON_SELF_MATCH_MAKER() == + CANCEL_REASON_SELF_MATCH_MAKER, 0); + assert!(get_CANCEL_REASON_SELF_MATCH_TAKER() == + CANCEL_REASON_SELF_MATCH_TAKER, 0); + assert!(get_CANCEL_REASON_TOO_SMALL_TO_FILL_LOT() == + CANCEL_REASON_TOO_SMALL_TO_FILL_LOT, 0); + assert!(get_CANCEL_REASON_VIOLATED_LIMIT_PRICE() == + CANCEL_REASON_VIOLATED_LIMIT_PRICE, 0); + } + + #[test] + #[expected_failure(abort_code = E_NO_MARKET_ACCOUNT)] + /// Verify failure for no market account resource. + fun test_get_market_account_market_info_no_account() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test market accounts. + register_market_accounts_test(); + // Attempt invalid invocation. + get_market_account_market_info(@user, 0, 0); + } + + #[test] + #[expected_failure(abort_code = E_NO_MARKET_ACCOUNTS)] + /// Verify failure for no market accounts resource. + fun test_get_market_account_market_info_no_accounts() + acquires MarketAccounts { + // Attempt invalid invocation. + get_market_account_market_info(@user, 0, 0); + } + + #[test] + #[expected_failure(abort_code = E_NO_MARKET_ACCOUNT)] + /// Verify failure for no market accounts resource. + fun test_get_market_account_no_market_account(): + MarketAccountView + acquires MarketAccounts { + // Attempt invalid invocation. + get_market_account(@user, 0, 0) + } + + #[test] + /// Verify returns for open order indexing. + fun test_get_market_accounts_open_orders(): + vector> + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Initialize empty vector to return instead of dropping. + let return_instead_of_dropping = vector::empty(); + // Define order parameters. + let market_order_id_ask_0 = 123; + let market_order_id_bid_0 = 456; + let market_order_id_bid_1 = 789; + let order_access_key_ask_0 = 1; + let order_access_key_bid_0 = 1; + let order_access_key_bid_1 = 2; + let size = MIN_SIZE_PURE_COIN; + let price = 1; + let market_id = MARKET_ID_PURE_COIN; + let user = @user; + // Get user's market account views. + let market_account_views = get_market_accounts(user); + // Assert empty vector. + assert!(vector::is_empty(&market_account_views), 0); + // Push back value to return instead of dropping. + vector::push_back( + &mut return_instead_of_dropping, market_account_views); + // Register test market accounts. + register_market_accounts_test(); + // Deposit starting base and quote coins. + deposit_coins( + user, market_id, NO_CUSTODIAN, assets::mint_test(BASE_START)); + deposit_coins( + user, market_id, CUSTODIAN_ID, assets::mint_test(BASE_START)); + deposit_coins( + user, market_id, NO_CUSTODIAN, assets::mint_test(QUOTE_START)); + deposit_coins( + user, market_id, CUSTODIAN_ID, assets::mint_test(QUOTE_START)); + // Place single ask for market account without custodian. + place_order_internal( + user, market_id, NO_CUSTODIAN, ASK, size, price, + market_order_id_ask_0, order_access_key_ask_0); + // Get user's market account views. + market_account_views = get_market_accounts(user); + // Immutably borrow first market account open orders element. + let market_account_view_ref = vector::borrow(&market_account_views, 0); + // Assert element state. + assert!(market_account_view_ref.market_id == market_id, 0); + assert!(market_account_view_ref.custodian_id == NO_CUSTODIAN, 0); + let asks_ref = &market_account_view_ref.asks; + assert!(vector::length(asks_ref) == 1, 0); + let bids_ref = &market_account_view_ref.bids; + assert!(vector::length(bids_ref) == 0, 0); + let order_ref = vector::borrow(asks_ref, 0); + assert!(order_ref.market_order_id == market_order_id_ask_0, 0); + assert!(order_ref.size == size, 0); + // Push back value to return instead of dropping. + vector::push_back( + &mut return_instead_of_dropping, market_account_views); + // Place single ask for market account without custodian, since + // consumed during previous operation. + place_order_internal( + user, market_id, NO_CUSTODIAN, ASK, size, price, + market_order_id_ask_0, order_access_key_ask_0); + // Place bids for market account with custodian. + place_order_internal( + user, market_id, CUSTODIAN_ID, BID, size, price, + market_order_id_bid_0, order_access_key_bid_0); + place_order_internal( + user, market_id, CUSTODIAN_ID, BID, size, price, + market_order_id_bid_1, order_access_key_bid_1); + // Cancel the first placed bid. + cancel_order_internal( + user, market_id, CUSTODIAN_ID, BID, size, price, + order_access_key_bid_0, market_order_id_bid_0, + CANCEL_REASON_MANUAL_CANCEL); + // Get all of user's market account views. + market_account_views = get_market_accounts(user); + // Immutably borrow first market account view element. + market_account_view_ref = vector::borrow(&market_account_views, 0); + // Assert element state. + assert!(market_account_view_ref.market_id == market_id, 0); + assert!(market_account_view_ref.custodian_id == NO_CUSTODIAN, 0); + let asks_ref = &market_account_view_ref.asks; + assert!(vector::length(asks_ref) == 1, 0); + let bids_ref = &market_account_view_ref.bids; + assert!(vector::length(bids_ref) == 0, 0); + let order_ref = vector::borrow(asks_ref, 0); + assert!(order_ref.market_order_id == market_order_id_ask_0, 0); + assert!(order_ref.size == size, 0); + // Immutably borrow second market account view element. + market_account_view_ref = vector::borrow(&market_account_views, 1); + // Assert element state. + assert!(market_account_view_ref.market_id == market_id, 0); + assert!(market_account_view_ref.custodian_id == CUSTODIAN_ID, 0); + let asks_ref = &market_account_view_ref.asks; + assert!(vector::length(asks_ref) == 0, 0); + let bids_ref = &market_account_view_ref.bids; + assert!(vector::length(bids_ref) == 1, 0); + let order_ref = vector::borrow(bids_ref, 0); + assert!(order_ref.market_order_id == market_order_id_bid_1, 0); + assert!(order_ref.size == size, 0); + // Push back value to return instead of dropping. + vector::push_back( + &mut return_instead_of_dropping, market_account_views); + return_instead_of_dropping // Return instead of dropping. + } + + #[test] + #[expected_failure(abort_code = E_NO_MARKET_ACCOUNT)] + /// Verify failure for no market account. + fun test_get_next_order_access_key_internal_no_account() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test market accounts. + register_market_accounts_test(); + // Attempt invalid invocation. + get_next_order_access_key_internal(@user, 0, 0, ASK); + } + + #[test] + #[expected_failure(abort_code = E_NO_MARKET_ACCOUNTS)] + /// Verify failure for no market accounts. + fun test_get_next_order_access_key_internal_no_accounts() + acquires MarketAccounts { + // Attempt invalid invocation. + get_next_order_access_key_internal(@user, 0, 0, ASK); + } + + #[test] + /// Verify constant getter return. + fun test_get_NO_CUSTODIAN() { + assert!(get_NO_CUSTODIAN() == NO_CUSTODIAN, 0); + assert!(get_NO_CUSTODIAN() == registry::get_NO_CUSTODIAN(), 0) + } + + #[test(user = @user)] + #[expected_failure(abort_code = E_NO_MARKET_ACCOUNT)] + /// Verify abort for user has no market account. + fun test_init_market_event_handles_if_missing_no_account( + user: &signer + ) acquires + MarketAccounts, + MarketEventHandles + { + init_market_event_handles_if_missing(user, 0, 0); + } + + #[test] + /// Verify valid returns. + fun test_market_account_getters() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Get market account IDs for test accounts. + let market_account_id_coin_self = get_market_account_id( + MARKET_ID_PURE_COIN, NO_CUSTODIAN); + let market_account_id_coin_delegated = get_market_account_id( + MARKET_ID_PURE_COIN, CUSTODIAN_ID); + let market_account_id_generic_self = get_market_account_id( + MARKET_ID_GENERIC , NO_CUSTODIAN); + let market_account_id_generic_delegated = get_market_account_id( + MARKET_ID_GENERIC , CUSTODIAN_ID); + // Assert empty returns. + assert!(get_all_market_account_ids_for_market_id( + @user, MARKET_ID_PURE_COIN) == vector[], 0); + assert!(get_all_market_account_ids_for_market_id( + @user, MARKET_ID_GENERIC) == vector[], 0); + assert!(get_all_market_account_ids_for_user( + @user) == vector[], 0); + assert!(option::is_none(&get_open_order_id_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, ASK, 0)), 0); + // Assert false returns. + assert!(!has_market_account_by_market_account_id( + @user, market_account_id_coin_self), 0); + assert!(!has_market_account_by_market_account_id( + @user, market_account_id_coin_delegated), 0); + assert!(!has_market_account_by_market_account_id( + @user, market_account_id_generic_self), 0); + assert!(!has_market_account_by_market_account_id( + @user, market_account_id_generic_delegated), 0); + assert!(!has_market_account_by_market_id( + @user, MARKET_ID_PURE_COIN), 0); + assert!(!has_market_account_by_market_id( + @user, MARKET_ID_GENERIC), 0); + assert!(!has_market_account( + @user, MARKET_ID_PURE_COIN, NO_CUSTODIAN), 0); + assert!(!has_market_account( + @user, MARKET_ID_GENERIC , NO_CUSTODIAN), 0); + assert!(!has_market_account( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID), 0); + assert!(!has_market_account( + @user, MARKET_ID_GENERIC , CUSTODIAN_ID), 0); + register_market_accounts_test(); // Register market accounts. + // Assert empty returns. + assert!(get_all_market_account_ids_for_market_id( + @user, 123) == vector[], 0); + // Get signer for another test user account. + let user_1 = account::create_signer_with_capability( + &account::create_test_signer_cap(@user_1)); + // Move to another user empty market accounts resource. + move_to(&user_1, MarketAccounts{ + map: table::new(), custodians: tablist::new()}); + // Assert empty returns. + assert!(get_all_market_account_ids_for_user( + @user_1) == vector[], 0); + // Assert non-empty returns. + let expected_ids = vector[market_account_id_coin_self, + market_account_id_coin_delegated]; + assert!(get_all_market_account_ids_for_market_id( + @user, MARKET_ID_PURE_COIN) == expected_ids, 0); + expected_ids = vector[market_account_id_generic_self, + market_account_id_generic_delegated]; + assert!(get_all_market_account_ids_for_market_id( + @user, MARKET_ID_GENERIC) == expected_ids, 0); + expected_ids = vector[market_account_id_coin_self, + market_account_id_coin_delegated, + market_account_id_generic_self, + market_account_id_generic_delegated]; + assert!(get_all_market_account_ids_for_user( + @user) == expected_ids, 0); + // Assert true returns. + assert!(has_market_account_by_market_account_id( + @user, market_account_id_coin_self), 0); + assert!(has_market_account_by_market_account_id( + @user, market_account_id_coin_delegated), 0); + assert!(has_market_account_by_market_account_id( + @user, market_account_id_generic_self), 0); + assert!(has_market_account_by_market_account_id( + @user, market_account_id_generic_delegated), 0); + assert!(has_market_account_by_market_id( + @user, MARKET_ID_PURE_COIN), 0); + assert!(has_market_account_by_market_id( + @user, MARKET_ID_GENERIC), 0); + assert!(has_market_account( + @user, MARKET_ID_PURE_COIN, NO_CUSTODIAN), 0); + assert!(has_market_account( + @user, MARKET_ID_GENERIC , NO_CUSTODIAN), 0); + assert!(has_market_account( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID), 0); + assert!(has_market_account( + @user, MARKET_ID_GENERIC , CUSTODIAN_ID), 0); + // Assert false returns. + assert!(!has_market_account_by_market_account_id( + @user_1, market_account_id_coin_self), 0); + assert!(!has_market_account_by_market_account_id( + @user_1, market_account_id_coin_delegated), 0); + assert!(!has_market_account_by_market_account_id( + @user_1, market_account_id_generic_self), 0); + assert!(!has_market_account_by_market_account_id( + @user_1, market_account_id_generic_delegated), 0); + assert!(!has_market_account_by_market_id( + @user_1, MARKET_ID_PURE_COIN), 0); + assert!(!has_market_account_by_market_id( + @user_1, MARKET_ID_GENERIC), 0); + } + + #[test] + /// Verify valid returns + fun test_market_account_id_getters() { + let market_id = u_64_by_32(b"10000000000000000000000000000000", + b"00000000000000000000000000000001"); + let custodian_id = u_64_by_32(b"11000000000000000000000000000000", + b"00000000000000000000000000000011"); + let market_account_id = get_market_account_id(market_id, custodian_id); + assert!(market_account_id == + u_128_by_32(b"10000000000000000000000000000000", + b"00000000000000000000000000000001", + b"11000000000000000000000000000000", + b"00000000000000000000000000000011"), 0); + assert!(get_market_id(market_account_id) == market_id, 0); + assert!(get_custodian_id(market_account_id) == custodian_id, 0); + } + + #[test] + /// Verify valid state updates for placing and cancelling an ask, + /// and next order access key lookup returns. + fun test_place_cancel_order_ask() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test markets, get market account ID for pure coin + // market with delegated custodian. + let (_, _, market_account_id, _, _) = register_market_accounts_test(); + // Define order parameters. + let market_order_id = 1234; + let size = 789; + let price = 321; + let side = ASK; + let order_access_key = 1; + // Calculate change in base asset and quote asset fields. + let base_delta = size * LOT_SIZE_PURE_COIN; + let quote_delta = size * price * TICK_SIZE_PURE_COIN; + // Deposit starting base and quote coins. + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(BASE_START)); + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(QUOTE_START)); + // Assert inactive stack top on given side. + assert!(get_inactive_stack_top_test(@user, market_account_id, side) + == NIL, 0); + // Assert next order access key. + assert!(get_next_order_access_key_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side) == 1, 0); + // Place order. + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, market_order_id, 1); + // Assert asset counts. + let (base_total , base_available , base_ceiling, + quote_total, quote_available, quote_ceiling) = + get_asset_counts_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID); + assert!(base_total == BASE_START , 0); + assert!(base_available == BASE_START - base_delta, 0); + assert!(base_ceiling == BASE_START , 0); + assert!(quote_total == QUOTE_START, 0); + assert!(quote_available == QUOTE_START, 0); + assert!(quote_ceiling == QUOTE_START + quote_delta, 0); + // Assert inactive stack top on given side. + assert!(get_inactive_stack_top_test(@user, market_account_id, side) + == NIL, 0); + // Assert next order access key. + assert!(get_next_order_access_key_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side) == 2, 0); + // Assert order fields. + let (market_order_id_r, size_r) = get_order_fields_test( + @user, market_account_id, side, order_access_key); + assert!(market_order_id_r == market_order_id, 0); + assert!(size_r == size, 0); + // Remove market event handles. + remove_market_event_handles_for_market_account_test( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID); + // Evict order, storing returned market order ID. + market_order_id_r = cancel_order_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, size, price, + order_access_key, (NIL as u128), CANCEL_REASON_MANUAL_CANCEL); + // Assert returned market order ID. + assert!(market_order_id_r == market_order_id, 0); + // Assert next order access key. + assert!(get_next_order_access_key_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side) == 1, 0); + // Assert asset counts. + (base_total , base_available , base_ceiling, + quote_total, quote_available, quote_ceiling) = + get_asset_counts_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID); + assert!(base_total == BASE_START , 0); + assert!(base_available == BASE_START , 0); + assert!(base_ceiling == BASE_START , 0); + assert!(quote_total == QUOTE_START, 0); + assert!(quote_available == QUOTE_START, 0); + assert!(quote_ceiling == QUOTE_START, 0); + // Assert inactive stack top on given side. + assert!(get_inactive_stack_top_test(@user, market_account_id, side) + == order_access_key, 0); + // Assert order marked inactive. + assert!(!is_order_active_test( + @user, market_account_id, side, order_access_key), 0); + // Assert next inactive node field. + assert!(get_next_inactive_order_test(@user, market_account_id, side, + order_access_key) == NIL, 0); + } + + #[test] + /// Verify valid state updates for placing and cancelling a bid. + fun test_place_cancel_order_bid() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test markets, get market account ID for pure coin + // market with delegated custodian. + let (_, _, market_account_id, _, _) = register_market_accounts_test(); + // Define order parameters. + let market_order_id = 1234; + let size = 789; + let price = 321; + let side = BID; + let order_access_key = 1; + // Calculate change in base asset and quote asset fields. + let base_delta = size * LOT_SIZE_PURE_COIN; + let quote_delta = size * price * TICK_SIZE_PURE_COIN; + // Deposit starting base and quote coins. + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(BASE_START)); + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(QUOTE_START)); + // Assert inactive stack top on given side. + assert!(get_inactive_stack_top_test(@user, market_account_id, side) + == NIL, 0); + // Place order. + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, market_order_id, 1); + // Assert asset counts. + let (base_total , base_available , base_ceiling, + quote_total, quote_available, quote_ceiling) = + get_asset_counts_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID); + assert!(base_total == BASE_START , 0); + assert!(base_available == BASE_START , 0); + assert!(base_ceiling == BASE_START + base_delta, 0); + assert!(quote_total == QUOTE_START, 0); + assert!(quote_available == QUOTE_START - quote_delta, 0); + assert!(quote_ceiling == QUOTE_START, 0); + // Assert inactive stack top on given side. + assert!(get_inactive_stack_top_test(@user, market_account_id, side) + == NIL, 0); + // Assert order fields. + let (market_order_id_r, size_r) = get_order_fields_test( + @user, market_account_id, side, order_access_key); + assert!(market_order_id_r == market_order_id, 0); + assert!(size_r == size, 0); + // Cancel order, storing returned market order ID. + market_order_id_r = cancel_order_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, size, price, + order_access_key, market_order_id, CANCEL_REASON_MANUAL_CANCEL); + // Assert returned market order ID. + assert!(market_order_id_r == market_order_id, 0); + // Assert asset counts. + (base_total , base_available , base_ceiling, + quote_total, quote_available, quote_ceiling) = + get_asset_counts_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID); + assert!(base_total == BASE_START , 0); + assert!(base_available == BASE_START , 0); + assert!(base_ceiling == BASE_START , 0); + assert!(quote_total == QUOTE_START, 0); + assert!(quote_available == QUOTE_START, 0); + assert!(quote_ceiling == QUOTE_START, 0); + // Assert inactive stack top on given side. + assert!(get_inactive_stack_top_test(@user, market_account_id, side) + == order_access_key, 0); + // Assert order marked inactive. + assert!(!is_order_active_test( + @user, market_account_id, side, order_access_key), 0); + // Assert next inactive node field. + assert!(get_next_inactive_order_test(@user, market_account_id, side, + order_access_key) == NIL, 0); + } + + #[test] + /// Verify state updates for multiple pushes and pops from stack, + /// and next order access key lookup returns. + fun test_place_cancel_order_stack() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test markets, get market account ID for pure coin + // market with delegated custodian. + let (_, _, market_account_id, _, _) = register_market_accounts_test(); + // Define order parameters. + let market_order_id_1 = 123; + let market_order_id_2 = 234; + let market_order_id_3 = 345; + let size = MIN_SIZE_PURE_COIN; + let price = 1; + let side = BID; + // Deposit starting base and quote coins. + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(BASE_START)); + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(QUOTE_START)); + // Assert inactive stack top on given side. + assert!(get_inactive_stack_top_test(@user, market_account_id, side) + == NIL, 0); + // Assert next order access key. + assert!(get_next_order_access_key_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side) == 1, 0); + // Place two orders. + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, market_order_id_1, 1); + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, market_order_id_2, 2); + // Assert inactive stack top on given side. + assert!(get_inactive_stack_top_test(@user, market_account_id, side) + == NIL, 0); + // Assert next order access key. + assert!(get_next_order_access_key_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side) == 3, 0); + // Cancel first order, storing market order ID. + let market_order_id = cancel_order_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, size, price, 1, + market_order_id_1, CANCEL_REASON_MANUAL_CANCEL); + // Assert returned market order ID. + assert!(market_order_id == market_order_id_1, 0); + // Assert inactive stack top on given side. + assert!(get_inactive_stack_top_test(@user, market_account_id, side) + == 1, 0); + // Assert next order access key. + assert!(get_next_order_access_key_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side) == 1, 0); + // Cancel second order, storting market order ID. + market_order_id = cancel_order_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, size, price, 2, + market_order_id_2, CANCEL_REASON_MANUAL_CANCEL); + // Assert returned market order ID. + assert!(market_order_id == market_order_id_2, 0); + // Assert inactive stack top on given side. + assert!(get_inactive_stack_top_test(@user, market_account_id, side) + == 2, 0); + // Assert next order access key. + assert!(get_next_order_access_key_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side) == 2, 0); + // Assert both orders marked inactive. + assert!(!is_order_active_test(@user, market_account_id, side, 1), 0); + assert!(!is_order_active_test(@user, market_account_id, side, 2), 0); + // Assert next inactive node fields. + assert!(get_next_inactive_order_test( + @user, market_account_id, side, 2) == 1, 0); + assert!(get_next_inactive_order_test( + @user, market_account_id, side, 1) == NIL, 0); + // Place an order, assigning access key at top of stack. + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, market_order_id_3, 2); + // Assert inactive stack top on given side. + assert!(get_inactive_stack_top_test(@user, market_account_id, side) + == 1, 0); + // Assert next order access key. + assert!(get_next_order_access_key_internal( + @user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side) == 1, 0); + // Assert order fields. + let (market_order_id_r, size_r) = get_order_fields_test( + @user, market_account_id, side, 2); + assert!(market_order_id_r == market_order_id_3, 0); + assert!(size_r == size, 0); + } + + #[test] + #[expected_failure(abort_code = E_ACCESS_KEY_MISMATCH)] + /// Verify failure for access key mismatch. + fun test_place_order_internal_access_key_mismatch() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + register_market_accounts_test(); // Register market accounts. + // Declare order parameters + let market_order_id = 123; + let size = MIN_SIZE_PURE_COIN; + let price = 1; + let side = BID; + // Calculate minimum base fill amount for price of 1. + let min_fill_base = MIN_SIZE_PURE_COIN * LOT_SIZE_PURE_COIN; + // Calculate starting base coin amount for fill to ceiling. + let base_start = HI_64 - min_fill_base; + // Deposit base coins. + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(base_start)); + // Deposit max quote coins. + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(HI_64)); + // Attempt invalid invocation. + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, market_order_id, 2); + } + #[test] + #[expected_failure(abort_code = E_OVERFLOW_ASSET_IN)] + /// Verify failure for overflowed inbound asset. + fun test_place_order_internal_in_overflow() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + register_market_accounts_test(); // Register market accounts. + // Declare order parameters + let market_order_id = 123; + let size = MIN_SIZE_PURE_COIN; + let price = 1; + let side = BID; + // Calculate minimum base fill amount for price of 1. + let min_fill_base = MIN_SIZE_PURE_COIN * LOT_SIZE_PURE_COIN; + // Calculate starting base coin amount for barely overflowing. + let base_start = HI_64 - min_fill_base + 1; + // Deposit base coins. + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(base_start)); + // Deposit max quote coins. + deposit_coins(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, + assets::mint_test(HI_64)); + // Attempt invalid invocation. + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, market_order_id, 1); + } + + #[test] + #[expected_failure(abort_code = E_NOT_ENOUGH_ASSET_OUT)] + /// Verify failure for underflowed outbound asset. + fun test_place_order_internal_out_underflow() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + register_market_accounts_test(); // Register market accounts. + // Declare order parameters + let market_order_id = 123; + let size = MIN_SIZE_PURE_COIN; + let price = 1; + let side = BID; + // Attempt invalid invocation. + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, market_order_id, 1); + } + + #[test] + #[expected_failure(abort_code = E_PRICE_0)] + /// Verify failure for price 0. + fun test_place_order_internal_price_0() + acquires + MarketAccounts + { + // Declare order parameters + let market_order_id = 123; + let size = MIN_SIZE_PURE_COIN; + let price = 0; + let side = ASK; + // Attempt invalid invocation. + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, market_order_id, 1); + } + + #[test] + #[expected_failure(abort_code = E_PRICE_TOO_HIGH)] + /// Verify failure for price too high. + fun test_place_order_internal_price_hi() + acquires + MarketAccounts + { + // Declare order parameters + let market_order_id = 123; + let size = MIN_SIZE_PURE_COIN; + let price = HI_PRICE + 1; + let side = ASK; + // Attempt invalid invocation. + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, market_order_id, 1); + } + + #[test] + #[expected_failure(abort_code = E_TICKS_OVERFLOW)] + /// Verify failure for overflowed ticks. + fun test_place_order_internal_ticks_overflow() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + register_market_accounts_test(); // Register market accounts. + // Declare order parameters + let market_order_id = 123; + let size = HI_64 / HI_PRICE + 1; + let price = HI_PRICE; + let side = ASK; + // Attempt invalid invocation. + place_order_internal(@user, MARKET_ID_PURE_COIN, CUSTODIAN_ID, side, + size, price, market_order_id, 1); + } + + #[test(user = @user)] + #[expected_failure(abort_code = E_EXISTS_MARKET_ACCOUNT)] + /// Verify failure for market account already exists. + fun test_register_market_account_account_entries_exists( + user: &signer + ) acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + account::create_account_for_test(address_of(user)); + // Register test markets, storing pure coin market ID. + let (market_id_pure_coin, _, _, _, _, _, _, _, _, _, _, _) = + registry::register_markets_test(); + // Register user with market account. + register_market_account( + user, market_id_pure_coin, NO_CUSTODIAN); + // Attempt invalid re-registration. + register_market_account( + user, market_id_pure_coin, NO_CUSTODIAN); + } + + #[test(user = @user)] + #[expected_failure(abort_code = E_UNREGISTERED_CUSTODIAN)] + /// Verify failure for unregistered custodian. + fun test_register_market_account_unregistered_custodian( + user: &signer + ) acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + registry::init_test(); // Initialize registry. + // Attempt invalid invocation. + register_market_account(user, 1, 123); + } + + #[test(user = @user)] + /// Verify state updates for market account registration. + /// + /// Exercises all non-assert conditional branches for: + /// + /// * `get_market_event_handle_creation_numbers()` + /// * `init_market_event_handles_if_missing()` + /// * `register_market_account()` + /// * `register_market_account_account_entries()` + /// * `register_market_account_collateral_entry()` + fun test_register_market_accounts( + user: &signer + ) acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + account::create_account_for_test(address_of(user)); + // Register test markets, storing market info. + let (market_id_pure_coin, base_name_generic_pure_coin, + lot_size_pure_coin, tick_size_pure_coin, min_size_pure_coin, + underwriter_id_pure_coin, market_id_generic, + base_name_generic_generic, lot_size_generic, tick_size_generic, + min_size_generic, underwriter_id_generic) = + registry::register_markets_test(); + // Verify no event handle creation numbers. + assert!(get_market_event_handle_creation_numbers( + @user, market_id_pure_coin, NO_CUSTODIAN) == option::none(), 0); + // Set custodian ID as registered. + registry::set_registered_custodian_test(CUSTODIAN_ID); + // Register pure coin market account. + register_market_account( + user, market_id_pure_coin, NO_CUSTODIAN); + assert!(get_market_event_handle_creation_numbers( + @user, market_id_pure_coin, NO_CUSTODIAN) == option::some( + MarketEventHandleCreationNumbers{ + cancel_order_events_handle_creation_num: 2, + change_order_size_events_handle_creation_num: 3, + fill_events_handle_creation_num: 4, + place_limit_order_events_handle_creation_num: 5, + place_market_order_events_handle_creation_num: 6}), 0); + // Invoke init call for handles already initialized. + init_market_event_handles_if_missing( + user, market_id_pure_coin, NO_CUSTODIAN); + assert!(get_market_event_handle_creation_numbers( + @user, market_id_pure_coin, CUSTODIAN_ID) == option::none(), 0); + register_market_account( // Register delegated account. + user, market_id_pure_coin, CUSTODIAN_ID); + assert!(get_market_event_handle_creation_numbers( + @user, market_id_pure_coin, CUSTODIAN_ID) == option::some( + MarketEventHandleCreationNumbers{ + cancel_order_events_handle_creation_num: 7, + change_order_size_events_handle_creation_num: 8, + fill_events_handle_creation_num: 9, + place_limit_order_events_handle_creation_num: 10, + place_market_order_events_handle_creation_num: 11}), 0); + // Register generic asset account. + register_market_account_generic_base( + user, market_id_generic, NO_CUSTODIAN); + // Get market account IDs. + let market_account_id_self = get_market_account_id( + market_id_pure_coin, NO_CUSTODIAN); + let market_account_id_delegated = get_market_account_id( + market_id_pure_coin, CUSTODIAN_ID); + let market_account_id_generic = get_market_account_id( + market_id_generic, NO_CUSTODIAN); + // Immutably borrow base coin collateral. + let collateral_map_ref = &borrow_global>(@user).map; + // Assert entries only made for pure coin market accounts. + assert!(coin::value(tablist::borrow( + collateral_map_ref, market_account_id_self)) == 0, 0); + assert!(coin::value(tablist::borrow( + collateral_map_ref, market_account_id_delegated)) == 0, 0); + assert!(!tablist::contains( + collateral_map_ref, market_account_id_generic), 0); + // Immutably borrow quote coin collateral. + let collateral_map_ref = &borrow_global>(@user).map; + // Assert entries made for all market accounts. + assert!(coin::value(tablist::borrow( + collateral_map_ref, market_account_id_self)) == 0, 0); + assert!(coin::value(tablist::borrow( + collateral_map_ref, market_account_id_delegated)) == 0, 0); + assert!(coin::value(tablist::borrow( + collateral_map_ref, market_account_id_generic)) == 0, 0); + let custodians_map_ref = // Immutably borrow custodians map. + &borrow_global(@user).custodians; + // Immutably borrow custodians entry for pure coin market. + let custodians_ref = + tablist::borrow(custodians_map_ref, market_id_pure_coin); + // Assert listed custodians. + assert!(*custodians_ref + == vector[NO_CUSTODIAN, CUSTODIAN_ID], 0); + // Immutably borrow custodians entry for generic market. + custodians_ref = + tablist::borrow(custodians_map_ref, market_id_generic); + assert!( // Assert listed custodian. + *custodians_ref == vector[NO_CUSTODIAN], 0); + // Immutably borrow market accounts map. + let market_accounts_map_ref = + &borrow_global(@user).map; + // Immutably borrow pure coin self-custodied market account. + let market_account_ref = + table::borrow(market_accounts_map_ref, market_account_id_self); + // Assert state. + assert!(market_account_ref.base_type == type_info::type_of(), 0); + assert!(market_account_ref.base_name_generic + == base_name_generic_pure_coin, 0); + assert!(market_account_ref.quote_type == type_info::type_of(), 0); + assert!(market_account_ref.lot_size == lot_size_pure_coin, 0); + assert!(market_account_ref.tick_size == tick_size_pure_coin, 0); + assert!(market_account_ref.min_size == min_size_pure_coin, 0); + assert!(market_account_ref.underwriter_id + == underwriter_id_pure_coin, 0); + assert!(tablist::is_empty(&market_account_ref.asks), 0); + assert!(tablist::is_empty(&market_account_ref.bids), 0); + assert!(market_account_ref.asks_stack_top == NIL, 0); + assert!(market_account_ref.bids_stack_top == NIL, 0); + assert!(market_account_ref.base_total == 0, 0); + assert!(market_account_ref.base_available == 0, 0); + assert!(market_account_ref.base_ceiling == 0, 0); + assert!(market_account_ref.quote_total == 0, 0); + assert!(market_account_ref.quote_available == 0, 0); + assert!(market_account_ref.quote_ceiling == 0, 0); + // Immutably borrow pure coin delegated market account. + market_account_ref = table::borrow(market_accounts_map_ref, + market_account_id_delegated); + // Assert state. + assert!(market_account_ref.base_type == type_info::type_of(), 0); + assert!(market_account_ref.base_name_generic + == base_name_generic_pure_coin, 0); + assert!(market_account_ref.quote_type == type_info::type_of(), 0); + assert!(market_account_ref.lot_size == lot_size_pure_coin, 0); + assert!(market_account_ref.tick_size == tick_size_pure_coin, 0); + assert!(market_account_ref.min_size == min_size_pure_coin, 0); + assert!(market_account_ref.underwriter_id + == underwriter_id_pure_coin, 0); + assert!(tablist::is_empty(&market_account_ref.asks), 0); + assert!(tablist::is_empty(&market_account_ref.bids), 0); + assert!(market_account_ref.asks_stack_top == NIL, 0); + assert!(market_account_ref.bids_stack_top == NIL, 0); + assert!(market_account_ref.base_total == 0, 0); + assert!(market_account_ref.base_available == 0, 0); + assert!(market_account_ref.base_ceiling == 0, 0); + assert!(market_account_ref.quote_total == 0, 0); + assert!(market_account_ref.quote_available == 0, 0); + assert!(market_account_ref.quote_ceiling == 0, 0); + // Immutably borrow generic market account. + market_account_ref = + table::borrow(market_accounts_map_ref, market_account_id_generic); + // Assert state. + assert!(market_account_ref.base_type + == type_info::type_of(), 0); + assert!(market_account_ref.base_name_generic + == base_name_generic_generic, 0); + assert!(market_account_ref.quote_type == type_info::type_of(), 0); + assert!(market_account_ref.lot_size == lot_size_generic, 0); + assert!(market_account_ref.tick_size == tick_size_generic, 0); + assert!(market_account_ref.min_size == min_size_generic, 0); + assert!(market_account_ref.underwriter_id + == underwriter_id_generic, 0); + assert!(tablist::is_empty(&market_account_ref.asks), 0); + assert!(tablist::is_empty(&market_account_ref.bids), 0); + assert!(market_account_ref.asks_stack_top == NIL, 0); + assert!(market_account_ref.bids_stack_top == NIL, 0); + assert!(market_account_ref.base_total == 0, 0); + assert!(market_account_ref.base_available == 0, 0); + assert!(market_account_ref.base_ceiling == 0, 0); + assert!(market_account_ref.quote_total == 0, 0); + assert!(market_account_ref.quote_available == 0, 0); + assert!(market_account_ref.quote_ceiling == 0, 0); + // Verify market info getter returns for self-custodied pure + // coin market account. + let (base_type_r, base_name_generic_r, quote_type_r, lot_size_r, + tick_size_r, min_size_r, underwriter_id_r) = + get_market_account_market_info_user(user, market_id_pure_coin); + assert!(base_type_r == type_info::type_of(), 0); + assert!(base_name_generic_r == base_name_generic_pure_coin, 0); + assert!(quote_type_r == type_info::type_of(), 0); + assert!(lot_size_r == lot_size_pure_coin, 0); + assert!(tick_size_r == tick_size_pure_coin, 0); + assert!(min_size_r == min_size_pure_coin, 0); + assert!(underwriter_id_r == underwriter_id_pure_coin, 0); + let custodian_capability = registry::get_custodian_capability_test( + CUSTODIAN_ID); // Get custodian capability. + // Verify market info getter returns for delegated pure coin + // market account. + let (base_type_r, base_name_generic_r, quote_type_r, lot_size_r, + tick_size_r, min_size_r, underwriter_id_r) = + get_market_account_market_info_custodian( + @user, market_id_pure_coin, &custodian_capability); + assert!(base_type_r == type_info::type_of(), 0); + assert!(base_name_generic_r == base_name_generic_pure_coin, 0); + assert!(quote_type_r == type_info::type_of(), 0); + assert!(lot_size_r == lot_size_pure_coin, 0); + assert!(tick_size_r == tick_size_pure_coin, 0); + assert!(min_size_r == min_size_pure_coin, 0); + assert!(underwriter_id_r == underwriter_id_pure_coin, 0); + // Drop custodian capability. + registry::drop_custodian_capability_test(custodian_capability); + // Verify market info getter returns for self-custodied generic + // market account. + let (base_type_r, base_name_generic_r, quote_type_r, lot_size_r, + tick_size_r, min_size_r, underwriter_id_r) = + get_market_account_market_info_user(user, market_id_generic); + assert!(base_type_r == type_info::type_of(), 0); + assert!(base_name_generic_r == base_name_generic_generic, 0); + assert!(quote_type_r == type_info::type_of(), 0); + assert!(lot_size_r == lot_size_generic, 0); + assert!(tick_size_r == tick_size_generic, 0); + assert!(min_size_r == min_size_generic, 0); + assert!(underwriter_id_r == underwriter_id_generic, 0); + } + + #[test] + #[expected_failure(abort_code = E_NO_MARKET_ACCOUNT)] + /// Verify failure for no market account. + fun test_withdraw_asset_no_account() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test market accounts. + let (user, _, _, _, _) = register_market_accounts_test(); + // Attempt invalid invocation, burning returned coins. + assets::burn(withdraw_coins_user(&user, 0, 0)); + } + + #[test(user = @user)] + #[expected_failure(abort_code = E_NO_MARKET_ACCOUNTS)] + /// Verify failure for no market accounts. + fun test_withdraw_asset_no_accounts( + user: &signer + ) acquires + Collateral, + MarketAccounts + { + // Attempt invalid invocation, burning returned coins. + assets::burn(withdraw_coins_user(user, 0, 0)); + } + + #[test] + #[expected_failure(abort_code = E_ASSET_NOT_IN_PAIR)] + /// Verify failure for asset not in pair. + fun test_withdraw_asset_not_in_pair() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test market accounts. + let (user, _, _, _, _) = register_market_accounts_test(); + // Attempt invalid invocation, burning returned coins. + assets::burn(withdraw_coins_user(&user, MARKET_ID_PURE_COIN, 0)); + } + + #[test] + #[expected_failure(abort_code = E_WITHDRAW_TOO_LITTLE_AVAILABLE)] + /// Verify failure for not enough asset available to withdraw. + fun test_withdraw_asset_underflow() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test market accounts. + let (user, _, _, _, _) = register_market_accounts_test(); + // Attempt invalid invocation, burning returned coins. + assets::burn(withdraw_coins_user(&user, MARKET_ID_PURE_COIN, 1)); + } + + #[test] + #[expected_failure(abort_code = E_INVALID_UNDERWRITER)] + /// Verify failure for invalid underwriter. + fun test_withdraw_asset_underwriter() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Register test market accounts. + let (user, _, _, _, _) = register_market_accounts_test(); + let underwriter_capability = // Get underwriter capability. + registry::get_underwriter_capability_test(UNDERWRITER_ID + 1); + // Attempt invalid invocation. + withdraw_generic_asset_user(&user, MARKET_ID_GENERIC, 0, + &underwriter_capability); + // Drop underwriter capability. + registry::drop_underwriter_capability_test(underwriter_capability); + } + + #[test] + /// Verify state updates for assorted withdrawal styles. + fun test_withdrawals() + acquires + Collateral, + MarketAccounts, + MarketEventHandles + { + // Declare start amount parameters. + let amount_start_coin = 700; + let amount_start_generic = 500; + // Declare withdrawal amount parameters. + let amount_withdraw_coin_0 = 350; + let amount_withdraw_generic_0 = 450; + let amount_withdraw_coin_1 = 300; + let amount_withdraw_generic_1 = 400; + // Declare final amounts. + let amount_final_coin_0 = amount_start_coin - amount_withdraw_coin_0; + let amount_final_generic_0 = amount_start_generic + - amount_withdraw_generic_0; + let amount_final_coin_1 = amount_start_coin - amount_withdraw_coin_1; + let amount_final_generic_1 = amount_start_generic + - amount_withdraw_generic_1; + // Get signing user and test market account IDs. + let (user, _, _, market_account_id_generic_self, + market_account_id_generic_delegated) = + register_market_accounts_test(); + let custodian_capability = // Get custodian capability. + registry::get_custodian_capability_test(CUSTODIAN_ID); + let underwriter_capability = // Get underwriter capability. + registry::get_underwriter_capability_test(UNDERWRITER_ID); + // Deposit to both market accounts. + deposit_coins(@user, MARKET_ID_GENERIC, NO_CUSTODIAN, + assets::mint_test(amount_start_coin)); + deposit_coins(@user, MARKET_ID_GENERIC, CUSTODIAN_ID, + assets::mint_test(amount_start_coin)); + deposit_generic_asset(@user, MARKET_ID_GENERIC, NO_CUSTODIAN, + amount_start_generic, &underwriter_capability); + deposit_generic_asset(@user, MARKET_ID_GENERIC, CUSTODIAN_ID, + amount_start_generic, &underwriter_capability); + // Withdraw coins to coin store under authority of signing user. + withdraw_to_coinstore(&user, MARKET_ID_GENERIC, 1); + withdraw_to_coinstore(&user, MARKET_ID_GENERIC, + amount_withdraw_coin_0 - 1); + // Assert coin store balance. + assert!(coin::balance(@user) == amount_withdraw_coin_0, 0); + // Withdraw coins under authority of delegated custodian. + let coins = withdraw_coins_custodian( + @user, MARKET_ID_GENERIC, amount_withdraw_coin_1, + &custodian_capability); + // Assert withdrawn coin value. + assert!(coin::value(&coins) == amount_withdraw_coin_1, 0); + assets::burn(coins); // Burn coins. + // Withdraw generic asset under authority of signing user. + withdraw_generic_asset_user( + &user, MARKET_ID_GENERIC, amount_withdraw_generic_0, + &underwriter_capability); + // Withdraw generic asset under authority of delegated + // custodian. + withdraw_generic_asset_custodian( + @user, MARKET_ID_GENERIC, amount_withdraw_generic_1, + &custodian_capability, &underwriter_capability); + // Assert state for self-custodied account. + let ( base_total, base_available, base_ceiling, + quote_total, quote_available, quote_ceiling) = + get_asset_counts_user(&user, MARKET_ID_GENERIC); + assert!(base_total == amount_final_generic_0, 0); + assert!(base_available == amount_final_generic_0, 0); + assert!(base_ceiling == amount_final_generic_0, 0); + assert!(quote_total == amount_final_coin_0 , 0); + assert!(quote_available == amount_final_coin_0 , 0); + assert!(quote_ceiling == amount_final_coin_0 , 0); + assert!(!has_collateral_test( + @user, market_account_id_generic_self), 0); + assert!(get_collateral_value_test( + @user, market_account_id_generic_self) == amount_final_coin_0, 0); + // Assert state for delegated custody account. + let ( base_total, base_available, base_ceiling, + quote_total, quote_available, quote_ceiling) = + get_asset_counts_custodian( + @user, MARKET_ID_GENERIC, &custodian_capability); + assert!(base_total == amount_final_generic_1, 0); + assert!(base_available == amount_final_generic_1, 0); + assert!(base_ceiling == amount_final_generic_1, 0); + assert!(quote_total == amount_final_coin_1 , 0); + assert!(quote_available == amount_final_coin_1 , 0); + assert!(quote_ceiling == amount_final_coin_1 , 0); + assert!(!has_collateral_test( + @user, market_account_id_generic_delegated), 0); + assert!(get_collateral_value_test( + @user, market_account_id_generic_delegated) == + amount_final_coin_1, 0); + // Drop custodian capability. + registry::drop_custodian_capability_test(custodian_capability); + // Drop underwriter capability. + registry::drop_underwriter_capability_test(underwriter_capability); + } + + // Tests <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +} \ No newline at end of file diff --git a/testsuite/module-publish/src/packages/simple/sources/Simple.move b/testsuite/module-publish/src/packages/simple/sources/Simple.move index 96bfe823f49dc..f10ce0689a079 100644 --- a/testsuite/module-publish/src/packages/simple/sources/Simple.move +++ b/testsuite/module-publish/src/packages/simple/sources/Simple.move @@ -473,7 +473,7 @@ module 0xABCD::simple { simple_events: EventHandle, } - fun emit_events(owner: &signer, count: u64) acquires EventStore + public entry fun emit_events(owner: &signer, count: u64) acquires EventStore { let owner_address = signer::address_of(owner); if (!exists(owner_address)) { diff --git a/testsuite/single_node_performance.py b/testsuite/single_node_performance.py index cd98a98953ca9..df0efd2d3e43f 100755 --- a/testsuite/single_node_performance.py +++ b/testsuite/single_node_performance.py @@ -31,13 +31,14 @@ class Flow(Flag): AGG_V2 = auto() # Test resource groups RESOURCE_GROUPS = auto() + # Econia tests + ECONIA = auto() # Tests that are run on LAND_BLOCKING and continuously on main LAND_BLOCKING_AND_C = Flow.LAND_BLOCKING | Flow.CONTINUOUS -SELECTED_FLOW = Flow[os.environ.get("FLOW", default="LAND_BLOCKING")] - +SELECTED_FLOW = Flow[os.environ.get("FLOW", default="ECONIA")] print(f"Executing flow: {SELECTED_FLOW}") IS_MAINNET = SELECTED_FLOW in [Flow.MAINNET, Flow.MAINNET_LARGE_DB] SOURCE = os.environ.get("SOURCE", default="LOCAL") @@ -48,19 +49,21 @@ class Flow(Flag): RUNNER_NAME = os.environ.get("RUNNER_NAME", default="none") DEFAULT_NUM_INIT_ACCOUNTS = ( - "100000000" if SELECTED_FLOW == Flow.MAINNET_LARGE_DB else "2000000" + "100000000" if SELECTED_FLOW == Flow.MAINNET_LARGE_DB else "300000" ) DEFAULT_MAX_BLOCK_SIZE = "10000" MAX_BLOCK_SIZE = int(os.environ.get("MAX_BLOCK_SIZE", default=DEFAULT_MAX_BLOCK_SIZE)) -NUM_BLOCKS = int(os.environ.get("NUM_BLOCKS_PER_TEST", default=15)) +NUM_BLOCKS = int(os.environ.get("NUM_BLOCKS_PER_TEST", default=200)) NUM_BLOCKS_DETAILED = 10 -NUM_ACCOUNTS = max( - [ - int(os.environ.get("NUM_INIT_ACCOUNTS", default=DEFAULT_NUM_INIT_ACCOUNTS)), - (2 + 2 * NUM_BLOCKS) * MAX_BLOCK_SIZE, - ] -) +# NUM_ACCOUNTS = max( +# [ +# int(os.environ.get("NUM_INIT_ACCOUNTS", default=DEFAULT_NUM_INIT_ACCOUNTS)), +# (2 + 2 * NUM_BLOCKS) * MAX_BLOCK_SIZE, +# ] +# ) +# NUM_ACCOUNTS = (2 + 2 * NUM_BLOCKS) * MAX_BLOCK_SIZE +NUM_ACCOUNTS = 10000 MAIN_SIGNER_ACCOUNTS = 2 * MAX_BLOCK_SIZE NOISE_LOWER_LIMIT = 0.98 if IS_MAINNET else 0.8 @@ -207,6 +210,18 @@ class RunGroupConfig: DEFAULT_MODULE_WORKING_SET_SIZE = 100 TESTS = [ + # RunGroupConfig(expected_tps=10000, key=RunGroupKey("econia-basic1-market"), expected_stages=10, included_in=Flow.ECONIA), + # RunGroupConfig(expected_tps=10000, key=RunGroupKey("econia-mixed1-market"), expected_stages=10, included_in=Flow.ECONIA), + # RunGroupConfig(expected_tps=10000, key=RunGroupKey("econia-mixed10-market"), expected_stages=10, included_in=Flow.ECONIA), + # RunGroupConfig(expected_tps=10000, key=RunGroupKey("econia-mixed100-market"), expected_stages=10, included_in=Flow.ECONIA), + RunGroupConfig(expected_tps=10000, key=RunGroupKey("econia-basic1-market"), included_in=Flow.ECONIA), + # RunGroupConfig(expected_tps=10000, key=RunGroupKey("econia-market1-market"), included_in=Flow.ECONIA), + # RunGroupConfig(expected_tps=10000, key=RunGroupKey("econia-market10-market"), included_in=Flow.ECONIA), + # RunGroupConfig(expected_tps=10000, key=RunGroupKey("econia-market100-market"), included_in=Flow.ECONIA), + # RunGroupConfig(expected_tps=10000, key=RunGroupKey("econia-mixed1-market"), included_in=Flow.ECONIA), + # RunGroupConfig(expected_tps=10000, key=RunGroupKey("econia-mixed10-market"), included_in=Flow.ECONIA), + # RunGroupConfig(expected_tps=10000, key=RunGroupKey("econia-mixed100-market"), included_in=Flow.ECONIA), + # RunGroupConfig(expected_tps=10000, key=RunGroupKey("econia-real"), included_in=Flow.ECONIA), RunGroupConfig(key=RunGroupKey("no-op"), included_in=LAND_BLOCKING_AND_C), RunGroupConfig(key=RunGroupKey("no-op", module_working_set_size=1000), included_in=LAND_BLOCKING_AND_C), RunGroupConfig(key=RunGroupKey("apt-fa-transfer"), included_in=LAND_BLOCKING_AND_C | Flow.REPRESENTATIVE | Flow.MAINNET), diff --git a/testsuite/smoke-test/src/txn_emitter.rs b/testsuite/smoke-test/src/txn_emitter.rs index 0a8b2c6fac653..7996dda1e3682 100644 --- a/testsuite/smoke-test/src/txn_emitter.rs +++ b/testsuite/smoke-test/src/txn_emitter.rs @@ -8,8 +8,9 @@ use crate::{ }; use anyhow::ensure; use aptos_forge::{ - args::TransactionTypeArg, emitter::NumAccountsMode, AccountType, EmitJobMode, EmitJobRequest, - EntryPoints, NodeExt, Result, Swarm, TransactionType, TxnEmitter, TxnStats, WorkflowProgress, + args::TransactionTypeArg, emitter::NumAccountsMode, EconiaFlowType, EmitJobMode, AccountType, + EmitJobRequest, EntryPoints, NodeExt, Result, Swarm, TransactionType, TxnEmitter, TxnStats, + WorkflowKind, WorkflowProgress, }; use aptos_sdk::{transaction_builder::TransactionFactory, types::PeerId}; use aptos_types::keyless::test_utils::{get_sample_esk, get_sample_exp_date, get_sample_jwt_token}; @@ -171,6 +172,87 @@ async fn test_txn_emmitter() { &all_validators, Duration::from_secs(20), 100, + vec![ + vec![( + TransactionType::Workflow { + workflow_kind: WorkflowKind::Econia { + num_users: 100, + flow_type: EconiaFlowType::Basic, + num_markets: 1, + reuse_accounts_for_orders: false, + publish_packages: false, + }, + num_modules: 1, + use_account_pool: true, + progress_type: WorkflowProgress::MoveByPhases, + }, + 1, + )], + // vec![( + // TransactionType::AccountGeneration { + // add_created_accounts_to_pool: true, + // max_account_working_set: 1_000_000, + // creation_balance: 1_000_000, + // }, + // 20, + // )], + // vec![ + // (TransactionTypeArg::CoinTransfer.materialize_default(), 20), + // // // commenting this out given it consistently fails smoke test + // // // and it seems to be called only from `test_txn_emmitter` + // ( + // TransactionType::PublishPackage { + // use_account_pool: false, + // }, + // 20, + // ), + // ], + vec![ + ( + TransactionTypeArg::NoOp.materialize( + 100, + false, + WorkflowProgress::when_done_default(), + ), + 20, + ), + ( + TransactionType::CallCustomModules { + entry_point: EntryPoints::MakeOrChangeTable { + offset: 0, + count: 60, + }, + num_modules: 1, + use_account_pool: false, + }, + 20, + ), + ], + // vec![( + // TransactionType::CallCustomModules { + // entry_point: EntryPoints::TokenV1MintAndStoreNFTSequential, + // num_modules: 1, + // use_account_pool: false, + // }, + // 20, + // )], + // vec![( + // TransactionType::CallCustomModules { + // entry_point: EntryPoints::TokenV1MintAndTransferNFTParallel, + // num_modules: 1, + // use_account_pool: false, + // }, + // 20, + // )], + // vec![( + // TransactionType::CallCustomModules { + // entry_point: EntryPoints::TokenV1MintAndTransferNFTSequential, + // num_modules: 1, + // use_account_pool: false, + // }, + // 20, + // )], + ], TRANSACTION_MIX_PER_PHASE.to_vec(), ) .await diff --git a/testsuite/testcases/src/lib.rs b/testsuite/testcases/src/lib.rs index 3e3bc617c2fd7..adde202c176d0 100644 --- a/testsuite/testcases/src/lib.rs +++ b/testsuite/testcases/src/lib.rs @@ -341,6 +341,7 @@ pub async fn create_buffered_load( mut inner_test_and_report: Option<(&dyn NetworkLoadTest, &mut TestReport)>, mut synchronized_with_job: Option<&mut EmitJob>, ) -> Result> { + // let duration = Duration::from_secs(1500); // Generate some traffic let (mut emitter, emit_job_request) = create_emitter_and_request( swarm.clone(), diff --git a/third_party/move/move-compiler/src/cfgir/cfg.rs b/third_party/move/move-compiler/src/cfgir/cfg.rs index 8691a73e11c6a..2a5b7c39128ef 100644 --- a/third_party/move/move-compiler/src/cfgir/cfg.rs +++ b/third_party/move/move-compiler/src/cfgir/cfg.rs @@ -27,12 +27,12 @@ pub trait CFG { fn predecessors(&self, label: Label) -> &BTreeSet